summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2019-10-17 15:59:39 -0400
committerB Stack <bgstack15@gmail.com>2019-10-17 15:59:39 -0400
commit5b604dd360ffc162f163962ccb2b1af109a5f93f (patch)
tree65292208a81994782e1c16dd84dfcdcc221d0cd7 /zen
parentMerge branch '10.16' into 'master' (diff)
downloadFreeFileSync-5b604dd360ffc162f163962ccb2b1af109a5f93f.tar.gz
FreeFileSync-5b604dd360ffc162f163962ccb2b1af109a5f93f.tar.bz2
FreeFileSync-5b604dd360ffc162f163962ccb2b1af109a5f93f.zip
add upstream 10.17
Diffstat (limited to 'zen')
-rw-r--r--zen/basic_math.h3
-rw-r--r--zen/build_info.h22
-rw-r--r--zen/file_access.cpp4
-rw-r--r--zen/i18n.h5
-rw-r--r--zen/json.h4
-rw-r--r--zen/legacy_compiler.cpp32
-rw-r--r--zen/legacy_compiler.h10
-rw-r--r--zen/open_ssl.cpp458
-rw-r--r--zen/open_ssl.h10
-rw-r--r--zen/shutdown.cpp5
-rw-r--r--zen/socket.h2
-rw-r--r--zen/stl_tools.h1
-rw-r--r--zen/string_base.h11
-rw-r--r--zen/string_tools.h64
-rw-r--r--zen/string_traits.h67
-rw-r--r--zen/sys_error.h4
-rw-r--r--zen/thread.h6
-rw-r--r--zen/time.h2
-rw-r--r--zen/warn_static.h13
-rw-r--r--zen/zlib_wrap.cpp4
-rw-r--r--zen/zlib_wrap.h6
-rw-r--r--zen/zstring.cpp2
22 files changed, 615 insertions, 120 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h
index b9be28be..8a32ee69 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -59,6 +59,9 @@ const double e = 2.71828182845904523536;
const double sqrt2 = 1.41421356237309504880;
const double ln2 = 0.693147180559945309417;
+#if __cpp_lib_math_constants //C++20
+ #error implement math constants from <numbers> header
+#endif
//static_assert(pi + e + sqrt2 + ln2 == 7.9672352249818781, "whoopsie");
//----------------------------------------------------------------------------------
diff --git a/zen/build_info.h b/zen/build_info.h
index e80f3721..01f1aeb8 100644
--- a/zen/build_info.h
+++ b/zen/build_info.h
@@ -7,20 +7,24 @@
#ifndef BUILD_INFO_H_5928539285603428657
#define BUILD_INFO_H_5928539285603428657
-//determine build info: defines ZEN_BUILD_32BIT or ZEN_BUILD_64BIT
+ #include <bit> //std::endian
+
+#define ZEN_ARCH_32BIT 32
+#define ZEN_ARCH_64BIT 64
#ifdef __LP64__
- #define ZEN_BUILD_64BIT
+ #define ZEN_BUILD_ARCH ZEN_ARCH_64BIT
#else
- #define ZEN_BUILD_32BIT
+ #define ZEN_BUILD_ARCH ZEN_ARCH_32BIT
#endif
-#ifdef ZEN_BUILD_32BIT
- static_assert(sizeof(void*) == 4);
-#endif
+static_assert(ZEN_BUILD_ARCH == sizeof(void*) * 8);
+
+//--------------------------------------------------------------------
-#ifdef ZEN_BUILD_64BIT
- static_assert(sizeof(void*) == 8);
-#endif
+constexpr bool usingLittleEndian()
+{
+ return std::endian::native == std::endian::little;
+}
#endif //BUILD_INFO_H_5928539285603428657
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 483d4b01..e23d48be 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -138,7 +138,7 @@ std::optional<ItemType> zen::itemStillExists(const Zstring& itemPath) //throw Fi
}
catch (const ItemType&) //finding the item after getItemType() previously failed is exceptional
{
- throw e; //yes, slicing
+ throw FileError(_("Temporary access error:") + L' ' + e.toString());
}
return {};
}
@@ -577,7 +577,7 @@ void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath, bool
if (::symlink(linkPath.c_str(), targetPath.c_str()) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourcePath)), L"%y", L"\n" + fmtPath(targetPath)), L"symlink");
- //allow only consistent objects to be created -> don't place before ::symlink, targetPath may already exist!
+ //allow only consistent objects to be created -> don't place before ::symlink(); targetPath may already exist!
ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetPath); /*throw FileError*/ }
catch (FileError&) {});
diff --git a/zen/i18n.h b/zen/i18n.h
index 2ecee45a..a70649fe 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -22,6 +22,8 @@
//source and translation are required to use %x as number placeholder
//for plural form, which will be substituted automatically!!!
+ static_assert(WXINTL_NO_GETTEXT_MACRO, "...must be defined to deactivate wxWidgets underscore macro");
+
namespace zen
{
//implement handler to enable program-wide localizations:
@@ -52,9 +54,6 @@ std::shared_ptr<const TranslationHandler> getTranslator();
-
-
-
//######################## implementation ##############################
namespace impl
{
diff --git a/zen/json.h b/zen/json.h
index 15157cf7..e6464286 100644
--- a/zen/json.h
+++ b/zen/json.h
@@ -403,7 +403,7 @@ public:
for (auto it = pos_; it != stream_.begin(); )
{
--it;
- if (*it == '\r' || *it == '\n')
+ if (isLineBreak(*it))
return pos_ - it - 1;
}
return pos_ - stream_.begin();
@@ -418,7 +418,7 @@ private:
bool startsWith(const std::string& prefix) const
{
- return zen::startsWith(StringRef<const char>(pos_, stream_.end()), prefix);
+ return zen::startsWith(makeStringView(pos_, stream_.end()), prefix);
}
const std::string stream_;
diff --git a/zen/legacy_compiler.cpp b/zen/legacy_compiler.cpp
new file mode 100644
index 00000000..3e3b7ba7
--- /dev/null
+++ b/zen/legacy_compiler.cpp
@@ -0,0 +1,32 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "legacy_compiler.h"
+#include <charconv>
+//1. including this one in string_tools.h blows up VC++:
+// "An internal error has occurred in the compiler. (compiler file 'd:\agent\_work\1\s\src\vctools\Compiler\Utc\src\p2\p2symtab.c', line 2618)"
+//2. using inside PCH: "fatal error C1076: compiler limit: internal heap limit reached"
+
+
+#if __cpp_lib_to_chars
+ #error get rid of workarounds
+#endif
+
+double zen::from_chars(const char* first, const char* last)
+{
+ return std::strtod(std::string(first, last).c_str(), nullptr);
+}
+
+
+const char* zen::to_chars(char* first, char* last, double num)
+{
+ const size_t bufSize = last - first;
+ const int charsWritten = std::snprintf(first, bufSize, "%g", num);
+ //C99: returns number of chars written if successful, < 0 or >= bufferSize on failure
+
+ return 0 <= charsWritten && charsWritten < static_cast<int>(bufSize) ?
+ first + charsWritten : first;
+}
diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h
index 5b69ed94..d0b4d3fe 100644
--- a/zen/legacy_compiler.h
+++ b/zen/legacy_compiler.h
@@ -16,6 +16,10 @@ namespace std
+#if __cpp_lib_span
+ #error get rid of workarounds
+#endif
+
//requires C++20! until then, this should suffice...
template <class T>
class span
@@ -49,4 +53,10 @@ private:
};
}
+namespace zen
+{
+double from_chars(const char* first, const char* last);
+const char* to_chars(char* first, char* last, double num);
+}
+
#endif //LEGACY_COMPILER_H_839567308565656789
diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp
index f3fd7219..ce05de53 100644
--- a/zen/open_ssl.cpp
+++ b/zen/open_ssl.cpp
@@ -5,6 +5,8 @@
// *****************************************************************************
#include "open_ssl.h"
+#include "base64.h"
+#include "build_info.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
@@ -16,9 +18,7 @@ using namespace zen;
#error FFS, we are royally screwed!
#endif
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- #error OpenSSL version too old
-#endif
+static_assert(OPENSSL_VERSION_NUMBER >= 0x10100000L, "OpenSSL version too old");
void zen::openSslInit()
@@ -108,11 +108,11 @@ std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToEvpF
throw SysError(formatLastOpenSSLError(L"BIO_new_mem_buf"));
ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
- if (EVP_PKEY* evpKey = bioToEvp(bio, //BIO* bp,
- nullptr, //EVP_PKEY** x,
- nullptr, //pem_password_cb* cb,
- nullptr)) //void* u
- return std::shared_ptr<EVP_PKEY>(evpKey, ::EVP_PKEY_free);
+ if (EVP_PKEY* evp = bioToEvp(bio, //BIO* bp,
+ nullptr, //EVP_PKEY** x,
+ nullptr, //pem_password_cb* cb,
+ nullptr)) //void* u
+ return std::shared_ptr<EVP_PKEY>(evp, ::EVP_PKEY_free);
throw SysError(formatLastOpenSSLError(functionName));
}
@@ -134,12 +134,12 @@ std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToRsaF
throw SysError(formatLastOpenSSLError(functionName));
ZEN_ON_SCOPE_EXIT(::RSA_free(rsa));
- EVP_PKEY* evpKey = ::EVP_PKEY_new();
- if (!evpKey)
+ EVP_PKEY* evp = ::EVP_PKEY_new();
+ if (!evp)
throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
- std::shared_ptr<EVP_PKEY> sharedKey(evpKey, ::EVP_PKEY_free);
+ std::shared_ptr<EVP_PKEY> sharedKey(evp, ::EVP_PKEY_free);
- if (::EVP_PKEY_set1_RSA(evpKey, rsa) != 1) //calls RSA_up_ref() + transfers ownership to evpKey
+ if (::EVP_PKEY_set1_RSA(evp, rsa) != 1) //no ownership transfer (internally ref-counted)
throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_RSA"));
return sharedKey;
@@ -161,32 +161,32 @@ std::shared_ptr<EVP_PKEY> streamToKey(const std::string& keyStream, RsaStreamTyp
streamToEvpKey(keyStream, ::PEM_read_bio_RSAPublicKey, L"PEM_read_bio_RSAPublicKey") : //throw SysError
streamToEvpKey(keyStream, ::PEM_read_bio_RSAPrivateKey, L"PEM_read_bio_RSAPrivateKey"); //
- case RsaStreamType::pkcs1_raw:
+ case RsaStreamType::raw:
break;
}
auto tmp = reinterpret_cast<const unsigned char*>(keyStream.c_str());
- EVP_PKEY* evpKey = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type,
- nullptr, //EVP_PKEY** a,
- &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp,
- static_cast<long>(keyStream.size())); //long length
- if (!evpKey)
+ EVP_PKEY* evp = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type,
+ nullptr, //EVP_PKEY** a,
+ &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp,
+ static_cast<long>(keyStream.size())); //long length
+ if (!evp)
throw SysError(formatLastOpenSSLError(publicKey ? L"d2i_PublicKey" : L"d2i_PrivateKey"));
- return std::shared_ptr<EVP_PKEY>(evpKey, ::EVP_PKEY_free);
+ return std::shared_ptr<EVP_PKEY>(evp, ::EVP_PKEY_free);
}
//================================================================================
-using EvpToBioFunc = int (*)(BIO* bio, EVP_PKEY* evpKey);
+using EvpToBioFunc = int (*)(BIO* bio, EVP_PKEY* evp);
-std::string evpKeyToStream(EVP_PKEY* evpKey, EvpToBioFunc evpToBio, const wchar_t* functionName) //throw SysError
+std::string evpKeyToStream(EVP_PKEY* evp, EvpToBioFunc evpToBio, const wchar_t* functionName) //throw SysError
{
BIO* bio = ::BIO_new(BIO_s_mem());
if (!bio)
throw SysError(formatLastOpenSSLError(L"BIO_new"));
ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
- if (evpToBio(bio, evpKey) != 1)
+ if (evpToBio(bio, evp) != 1)
throw SysError(formatLastOpenSSLError(functionName));
//---------------------------------------------
const int keyLen = BIO_pending(bio);
@@ -205,14 +205,14 @@ std::string evpKeyToStream(EVP_PKEY* evpKey, EvpToBioFunc evpToBio, const wchar_
using RsaToBioFunc = int (*)(BIO* bp, RSA* x);
-std::string evpKeyToStream(EVP_PKEY* evpKey, RsaToBioFunc rsaToBio, const wchar_t* functionName) //throw SysError
+std::string evpKeyToStream(EVP_PKEY* evp, RsaToBioFunc rsaToBio, const wchar_t* functionName) //throw SysError
{
BIO* bio = ::BIO_new(BIO_s_mem());
if (!bio)
throw SysError(formatLastOpenSSLError(L"BIO_new"));
ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
- RSA* rsa = ::EVP_PKEY_get0_RSA(evpKey); //unowned reference!
+ RSA* rsa = ::EVP_PKEY_get0_RSA(evp); //unowned reference!
if (!rsa)
throw SysError(formatLastOpenSSLError(L"EVP_PKEY_get0_RSA"));
@@ -260,26 +260,26 @@ int PEM_write_bio_RSAPublicKey2(BIO* bio, RSA* rsa) { return ::PEM_write_bio_RSA
//--------------------------------------------------------------------------------
-std::string keyToStream(EVP_PKEY* evpKey, RsaStreamType streamType, bool publicKey) //throw SysError
+std::string keyToStream(EVP_PKEY* evp, RsaStreamType streamType, bool publicKey) //throw SysError
{
switch (streamType)
{
case RsaStreamType::pkix:
return publicKey ?
- evpKeyToStream(evpKey, ::PEM_write_bio_PUBKEY, L"PEM_write_bio_PUBKEY") : //throw SysError
- evpKeyToStream(evpKey, ::PEM_write_bio_PrivateKey2, L"PEM_write_bio_PrivateKey"); //
+ evpKeyToStream(evp, ::PEM_write_bio_PUBKEY, L"PEM_write_bio_PUBKEY") : //throw SysError
+ evpKeyToStream(evp, ::PEM_write_bio_PrivateKey2, L"PEM_write_bio_PrivateKey"); //
case RsaStreamType::pkcs1:
return publicKey ?
- evpKeyToStream(evpKey, ::PEM_write_bio_RSAPublicKey2, L"PEM_write_bio_RSAPublicKey") : //throw SysError
- evpKeyToStream(evpKey, ::PEM_write_bio_RSAPrivateKey2, L"PEM_write_bio_RSAPrivateKey"); //
+ evpKeyToStream(evp, ::PEM_write_bio_RSAPublicKey2, L"PEM_write_bio_RSAPublicKey") : //throw SysError
+ evpKeyToStream(evp, ::PEM_write_bio_RSAPrivateKey2, L"PEM_write_bio_RSAPrivateKey"); //
- case RsaStreamType::pkcs1_raw:
+ case RsaStreamType::raw:
break;
}
unsigned char* buf = nullptr;
- const int bufSize = (publicKey ? ::i2d_PublicKey : ::i2d_PrivateKey)(evpKey, &buf);
+ const int bufSize = (publicKey ? ::i2d_PublicKey : ::i2d_PrivateKey)(evp, &buf);
if (bufSize <= 0)
throw SysError(formatLastOpenSSLError(publicKey ? L"i2d_PublicKey" : L"i2d_PrivateKey"));
ZEN_ON_SCOPE_EXIT(::OPENSSL_free(buf)); //memory is only allocated for bufSize > 0
@@ -359,8 +359,8 @@ void verifySignature(const std::string& message, const std::string& signature, E
std::string zen::convertRsaKey(const std::string& keyStream, RsaStreamType typeFrom, RsaStreamType typeTo, bool publicKey) //throw SysError
{
assert(typeFrom != typeTo);
- std::shared_ptr<EVP_PKEY> evpKey = streamToKey(keyStream, typeFrom, publicKey); //throw SysError
- return keyToStream(evpKey.get(), typeTo, publicKey); //throw SysError
+ std::shared_ptr<EVP_PKEY> evp = streamToKey(keyStream, typeFrom, publicKey); //throw SysError
+ return keyToStream(evp.get(), typeTo, publicKey); //throw SysError
}
@@ -520,7 +520,7 @@ public:
::SSL_set_verify(ssl_, SSL_VERIFY_PEER, nullptr);
//2. enable check that the certificate matches our host: see SSL_get_verify_result()
- if (::SSL_set1_host(ssl_, server.c_str()) != 1)
+ if (::SSL_set1_host(ssl_, server.c_str()) != 1) //no ownership transfer
throw SysError(L"SSL_set1_host failed."); //no more error details
}
@@ -615,3 +615,393 @@ zen::TlsContext::TlsContext(int socket, const Zstring& server, const Zstring* ca
zen::TlsContext::~TlsContext() {}
size_t zen::TlsContext::tryRead ( void* buffer, size_t bytesToRead ) { return pimpl_->tryRead(buffer, bytesToRead); } //throw SysError
size_t zen::TlsContext::tryWrite(const void* buffer, size_t bytesToWrite) { return pimpl_->tryWrite(buffer, bytesToWrite); } //throw SysError
+
+
+bool zen::isPuttyKeyStream(const std::string& keyStream)
+{
+ std::string firstLine(keyStream.begin(), std::find_if(keyStream.begin(), keyStream.end(), isLineBreak<char>));
+ trim(firstLine);
+ return startsWith(firstLine, "PuTTY-User-Key-File-2:");
+}
+
+
+std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::string& passphrase) //throw SysError
+{
+ std::vector<std::string> lines;
+
+ for (auto it = keyStream.begin();;) //=> keep local: "warning: declaration of ‘it’ shadows a previous local"
+ {
+ auto itLineBegin = std::find_if_not(it, keyStream.end(), isLineBreak<char>);
+ if (itLineBegin == keyStream.end())
+ break;
+
+ it = std::find_if(itLineBegin + 1, keyStream.end(), isLineBreak<char>);
+ lines.emplace_back(itLineBegin, it);
+ }
+ //----------- parse PuTTY ppk structure ----------------------------------
+ auto itLine = lines.begin();
+ if (itLine == lines.end() || !startsWith(*itLine, "PuTTY-User-Key-File-2: "))
+ throw SysError(L"Unknown key file format");
+ const std::string algorithm = afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE);
+ ++itLine;
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Encryption: "))
+ throw SysError(L"Unknown key encryption");
+ const std::string keyEncryption = afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE);
+ ++itLine;
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Comment: "))
+ throw SysError(L"Invalid key comment");
+ const std::string comment = afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE);
+ ++itLine;
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Public-Lines: "))
+ throw SysError(L"Invalid key: invalid public lines");
+ size_t pubLineCount = stringTo<size_t>(afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE));
+ ++itLine;
+
+ std::string publicBlob64;
+ while (pubLineCount-- != 0)
+ if (itLine != lines.end())
+ publicBlob64 += *itLine++;
+ else
+ throw SysError(L"Invalid key: incomplete public lines");
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Private-Lines: "))
+ throw SysError(L"Invalid key: invalid private lines");
+ size_t privLineCount = stringTo<size_t>(afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE));
+ ++itLine;
+
+ std::string privateBlob64;
+ while (privLineCount-- != 0)
+ if (itLine != lines.end())
+ privateBlob64 += *itLine++;
+ else
+ throw SysError(L"Invalid key: incomplete private lines");
+
+ if (itLine == lines.end() || !startsWith(*itLine, "Private-MAC: "))
+ throw SysError(L"Invalid key: MAC missing");
+ const std::string macHex = afterFirst(*itLine, ' ', IF_MISSING_RETURN_NONE);
+ ++itLine;
+
+ //----------- unpack key file elements ---------------------
+ const bool keyEncrypted = keyEncryption == "aes256-cbc";
+ if (!keyEncrypted && keyEncryption != "none")
+ throw SysError(L"Unknown key encryption");
+
+ if (macHex.size() % 2 != 0 || !std::all_of(macHex.begin(), macHex.end(), isHexDigit<char>))
+ throw SysError(L"Invalid key: invalid MAC");
+
+ std::string mac;
+ for (size_t i = 0; i < macHex.size(); i += 2)
+ mac += unhexify(macHex[i], macHex[i + 1]);
+
+ const std::string publicBlob = stringDecodeBase64(publicBlob64);
+ const std::string privateBlobEnc = stringDecodeBase64(privateBlob64);
+
+ std::string privateBlob;
+ if (!keyEncrypted)
+ privateBlob = privateBlobEnc;
+ else
+ {
+ if (passphrase.empty())
+ throw SysError(L"Passphrase required to access private key");
+
+ const auto block1 = std::string("\0\0\0\0", 4) + passphrase;
+ const auto block2 = std::string("\0\0\0\1", 4) + passphrase;
+
+ unsigned char key[2 * SHA_DIGEST_LENGTH] = {};
+ SHA1(reinterpret_cast<const unsigned char*>(block1.c_str()), block1.size(), &key[0]); //no-fail
+ SHA1(reinterpret_cast<const unsigned char*>(block2.c_str()), block2.size(), &key[SHA_DIGEST_LENGTH]); //
+
+ EVP_CIPHER_CTX* cipCtx = ::EVP_CIPHER_CTX_new();
+ if (!cipCtx)
+ throw SysError(L"EVP_CIPHER_CTX_new failed."); //no more error details
+ ZEN_ON_SCOPE_EXIT(::EVP_CIPHER_CTX_free(cipCtx));
+
+ if (::EVP_DecryptInit_ex(cipCtx, //EVP_CIPHER_CTX* ctx,
+ EVP_aes_256_cbc(), //const EVP_CIPHER* type,
+ nullptr, //ENGINE* impl,
+ key, //const unsigned char* key, => implied length of 256 bit!
+ nullptr) != 1) //const unsigned char* iv
+ throw SysError(formatLastOpenSSLError(L"EVP_DecryptInit_ex"));
+
+ if (::EVP_CIPHER_CTX_set_padding(cipCtx, 0 /*padding*/) != 1)
+ throw SysError(L"EVP_CIPHER_CTX_set_padding failed."); //no more error details
+
+ privateBlob.resize(privateBlobEnc.size() + ::EVP_CIPHER_block_size(EVP_aes_256_cbc()));
+ //"EVP_DecryptUpdate() should have room for (inl + cipher_block_size) bytes"
+
+ int decLen1 = 0;
+ if (::EVP_DecryptUpdate(cipCtx, //EVP_CIPHER_CTX* ctx,
+ reinterpret_cast<unsigned char*>(&privateBlob[0]), //unsigned char* out,
+ &decLen1, //int* outl,
+ reinterpret_cast<const unsigned char*>(privateBlobEnc.c_str()), //const unsigned char* in,
+ static_cast<int>(privateBlobEnc.size())) != 1) //int inl
+ throw SysError(formatLastOpenSSLError(L"EVP_DecryptUpdate"));
+
+ int decLen2 = 0;
+ if (::EVP_DecryptFinal_ex(cipCtx, //EVP_CIPHER_CTX* ctx,
+ reinterpret_cast<unsigned char*>(&privateBlob[decLen1]), //unsigned char* outm,
+ &decLen2) != 1) //int* outl
+ throw SysError(formatLastOpenSSLError(L"EVP_DecryptFinal_ex"));
+
+ privateBlob.resize(decLen1 + decLen2);
+ }
+
+ //----------- verify key consistency ---------------------
+ std::string macKeyBlob = "putty-private-key-file-mac-key";
+ if (keyEncrypted)
+ macKeyBlob += passphrase;
+
+ unsigned char macKey[SHA_DIGEST_LENGTH] = {};
+ SHA1(reinterpret_cast<const unsigned char*>(macKeyBlob.c_str()), macKeyBlob.size(), &macKey[0]); //no-fail
+
+ auto numToBeString = [](size_t n) -> std::string
+ {
+ static_assert(usingLittleEndian()&& sizeof(n) >= 4);
+ const char* numStr = reinterpret_cast<const char*>(&n);
+ return { numStr[3], numStr[2], numStr[1], numStr[0] }; //big endian!
+ };
+
+ const std::string macData = numToBeString(algorithm .size()) + algorithm +
+ numToBeString(keyEncryption.size()) + keyEncryption +
+ numToBeString(comment .size()) + comment +
+ numToBeString(publicBlob .size()) + publicBlob +
+ numToBeString(privateBlob .size()) + privateBlob;
+ char md[EVP_MAX_MD_SIZE] = {};
+ unsigned int mdLen = 0;
+ if (!::HMAC(EVP_sha1(), //const EVP_MD* evp_md,
+ macKey, //const void* key,
+ sizeof(macKey), //int key_len,
+ reinterpret_cast<const unsigned char*>(macData.c_str()), //const unsigned char* d,
+ static_cast<int>(macData.size()), //int n,
+ reinterpret_cast<unsigned char*>(md), //unsigned char* md,
+ &mdLen)) //unsigned int* md_len
+ throw SysError(L"HMAC failed."); //no more error details
+
+ const bool hashValid = mac == std::string_view(md, mdLen);
+ if (!hashValid)
+ throw SysError(keyEncrypted ? L"MAC validation failed: wrong passphrase or corrupted key" : L"MAC validation failed: corrupted key");
+ //----------------------------------------------------------
+
+ auto extractString = [](auto& it, auto itEnd)
+ {
+ uint32_t byteCount = 0;
+ if (itEnd - it < makeSigned(sizeof(byteCount)))
+ throw SysError(L"String extraction failed: unexpected end of stream");
+
+ static_assert(usingLittleEndian());
+ char* numStr = reinterpret_cast<char*>(&byteCount);
+ numStr[3] = *it++; //
+ numStr[2] = *it++; //Putty uses big endian!
+ numStr[1] = *it++; //
+ numStr[0] = *it++; //
+
+ if (makeUnsigned(itEnd - it) < byteCount)
+ throw SysError(L"String extraction failed: unexpected end of stream(2)");
+
+ std::string str(it, it + byteCount);
+ it += byteCount;
+ return str;
+ };
+
+ struct BnFree { void operator()(BIGNUM* num) const { ::BN_free(num); } };
+ auto createBigNum = []
+ {
+ BIGNUM* bn = ::BN_new();
+ if (!bn)
+ throw SysError(formatLastOpenSSLError(L"BN_new"));
+ return std::unique_ptr<BIGNUM, BnFree>(bn);
+ };
+
+ auto extractBigNum = [&extractString](auto& it, auto itEnd)
+ {
+ const std::string bytes = extractString(it, itEnd);
+
+ BIGNUM* bn = ::BN_bin2bn(reinterpret_cast<const unsigned char*>(&bytes[0]), static_cast<int>(bytes.size()), nullptr);
+ if (!bn)
+ throw SysError(formatLastOpenSSLError(L"BN_bin2bn"));
+ return std::unique_ptr<BIGNUM, BnFree>(bn);
+ };
+
+ auto itPub = publicBlob .begin();
+ auto itPriv = privateBlob.begin();
+
+ auto extractStringPub = [&] { return extractString(itPub, publicBlob .end()); };
+ auto extractStringPriv = [&] { return extractString(itPriv, privateBlob.end()); };
+
+ auto extractBigNumPub = [&] { return extractBigNum(itPub, publicBlob .end()); };
+ auto extractBigNumPriv = [&] { return extractBigNum(itPriv, privateBlob.end()); };
+
+ //----------- parse public/private key blobs ----------------
+ if (extractStringPub() != algorithm)
+ throw SysError(L"Invalid public key stream (header)");
+
+ if (algorithm == "ssh-rsa")
+ {
+ std::unique_ptr<BIGNUM, BnFree> e = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> n = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> d = extractBigNumPriv(); //throw SysError
+ std::unique_ptr<BIGNUM, BnFree> p = extractBigNumPriv(); //
+ std::unique_ptr<BIGNUM, BnFree> q = extractBigNumPriv(); //
+ std::unique_ptr<BIGNUM, BnFree> iqmp = extractBigNumPriv(); //
+
+ //------ calculate missing numbers: dmp1, dmq1 -------------
+ std::unique_ptr<BIGNUM, BnFree> dmp1 = createBigNum(); //
+ std::unique_ptr<BIGNUM, BnFree> dmq1 = createBigNum(); //throw SysError
+ std::unique_ptr<BIGNUM, BnFree> tmp = createBigNum(); //
+
+ BN_CTX* bnCtx = BN_CTX_new();
+ if (!bnCtx)
+ throw SysError(formatLastOpenSSLError(L"BN_CTX_new"));
+ ZEN_ON_SCOPE_EXIT(::BN_CTX_free(bnCtx));
+
+ if (::BN_sub(tmp.get(), p.get(), BN_value_one()) != 1)
+ throw SysError(formatLastOpenSSLError(L"BN_sub"));
+
+ if (::BN_mod(dmp1.get(), d.get(), tmp.get(), bnCtx) != 1)
+ throw SysError(formatLastOpenSSLError(L"BN_mod"));
+
+ if (::BN_sub(tmp.get(), q.get(), BN_value_one()) != 1)
+ throw SysError(formatLastOpenSSLError(L"BN_sub"));
+
+ if (::BN_mod(dmq1.get(), d.get(), tmp.get(), bnCtx) != 1)
+ throw SysError(formatLastOpenSSLError(L"BN_mod"));
+ //----------------------------------------------------------
+
+ RSA* rsa = ::RSA_new();
+ if (!rsa)
+ throw SysError(formatLastOpenSSLError(L"RSA_new"));
+ ZEN_ON_SCOPE_EXIT(::RSA_free(rsa));
+
+ if (::RSA_set0_key(rsa, n.release(), e.release(), d.release()) != 1) //pass BIGNUM ownership
+ throw SysError(formatLastOpenSSLError(L"RSA_set0_key"));
+
+ if (::RSA_set0_factors(rsa, p.release(), q.release()) != 1)
+ throw SysError(formatLastOpenSSLError(L"RSA_set0_factors"));
+
+ if (::RSA_set0_crt_params(rsa, dmp1.release(), dmq1.release(), iqmp.release()) != 1)
+ throw SysError(formatLastOpenSSLError(L"RSA_set0_crt_params"));
+
+ EVP_PKEY* evp = ::EVP_PKEY_new();
+ if (!evp)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp));
+
+ if (::EVP_PKEY_set1_RSA(evp, rsa) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_RSA"));
+
+ return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError
+ }
+ //----------------------------------------------------------
+ else if (algorithm == "ssh-dss")
+ {
+ std::unique_ptr<BIGNUM, BnFree> p = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> q = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> g = extractBigNumPub (); //throw SysError
+ std::unique_ptr<BIGNUM, BnFree> pub = extractBigNumPub (); //
+ std::unique_ptr<BIGNUM, BnFree> pri = extractBigNumPriv(); //
+ //----------------------------------------------------------
+
+ DSA* dsa = ::DSA_new();
+ if (!dsa)
+ throw SysError(formatLastOpenSSLError(L"DSA_new"));
+ ZEN_ON_SCOPE_EXIT(::DSA_free(dsa));
+
+ if (::DSA_set0_pqg(dsa, p.release(), q.release(), g.release()) != 1) //pass BIGNUM ownership
+ throw SysError(formatLastOpenSSLError(L"DSA_set0_pqg"));
+
+ if (::DSA_set0_key(dsa, pub.release(), pri.release()) != 1)
+ throw SysError(formatLastOpenSSLError(L"DSA_set0_key"));
+
+ EVP_PKEY* evp = ::EVP_PKEY_new();
+ if (!evp)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp));
+
+ if (::EVP_PKEY_set1_DSA(evp, dsa) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_DSA"));
+
+ return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError
+ }
+ //----------------------------------------------------------
+ else if (algorithm == "ecdsa-sha2-nistp256" ||
+ algorithm == "ecdsa-sha2-nistp384" ||
+ algorithm == "ecdsa-sha2-nistp521")
+ {
+ const std::string algoShort = afterLast(algorithm, '-', IF_MISSING_RETURN_NONE);
+ if (extractStringPub() != algoShort)
+ throw SysError(L"Invalid public key stream (header)");
+
+ const std::string pointStream = extractStringPub();
+ std::unique_ptr<BIGNUM, BnFree> pri = extractBigNumPriv(); //throw SysError
+ //----------------------------------------------------------
+
+ const int curveNid = [&]
+ {
+ if (algoShort == "nistp256")
+ return NID_X9_62_prime256v1; //same as SECG secp256r1
+ if (algoShort == "nistp384")
+ return NID_secp384r1;
+ if (algoShort == "nistp521")
+ return NID_secp521r1;
+ throw SysError(L"Unknown elliptic curve: " + utfTo<std::wstring>(algorithm));
+ }();
+
+ EC_KEY* ecKey = ::EC_KEY_new_by_curve_name(curveNid);
+ if (!ecKey)
+ throw SysError(formatLastOpenSSLError(L"EC_KEY_new_by_curve_name"));
+ ZEN_ON_SCOPE_EXIT(::EC_KEY_free(ecKey));
+
+ const EC_GROUP* ecGroup = ::EC_KEY_get0_group(ecKey);
+ if (!ecGroup)
+ throw SysError(formatLastOpenSSLError(L"EC_KEY_get0_group"));
+
+ EC_POINT* ecPoint = ::EC_POINT_new(ecGroup);
+ if (!ecPoint)
+ throw SysError(formatLastOpenSSLError(L"EC_POINT_new"));
+ ZEN_ON_SCOPE_EXIT(::EC_POINT_free(ecPoint));
+
+ if (::EC_POINT_oct2point(ecGroup, //const EC_GROUP* group,
+ ecPoint, //EC_POINT* p,
+ reinterpret_cast<const unsigned char*>(&pointStream[0]), //const unsigned char* buf,
+ pointStream.size(), //size_t len,
+ nullptr) != 1) //BN_CTX* ctx
+ throw SysError(formatLastOpenSSLError(L"EC_POINT_oct2point"));
+
+ if (::EC_KEY_set_public_key(ecKey, ecPoint) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EC_KEY_set_public_key"));
+
+ if (::EC_KEY_set_private_key(ecKey, pri.get()) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EC_KEY_set_private_key"));
+
+ EVP_PKEY* evp = ::EVP_PKEY_new();
+ if (!evp)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp));
+
+ if (::EVP_PKEY_set1_EC_KEY(evp, ecKey) != 1) //no ownership transfer (internally ref-counted)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_EC_KEY"));
+
+ return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError
+ }
+ //----------------------------------------------------------
+ else if (algorithm == "ssh-ed25519")
+ {
+ //const std::string pubStream = extractStringPub(); -> we don't need the public key
+ const std::string priStream = extractStringPriv();
+
+ EVP_PKEY* evpPriv = ::EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, //int type,
+ nullptr, //ENGINE* e,
+ reinterpret_cast<const unsigned char*>(&priStream[0]), //const unsigned char* priv,
+ priStream.size()); //size_t len
+ if (!evpPriv)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new_raw_private_key"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evpPriv));
+
+ return keyToStream(evpPriv, RsaStreamType::pkix, false /*publicKey*/); //throw SysError
+ }
+ else
+ throw SysError(L"Unknown key algorithm: " + utfTo<std::wstring>(algorithm));
+}
diff --git a/zen/open_ssl.h b/zen/open_ssl.h
index 350e3776..2b0c7245 100644
--- a/zen/open_ssl.h
+++ b/zen/open_ssl.h
@@ -20,9 +20,9 @@ void openSslTearDown();
enum class RsaStreamType
{
- pkix, //base-64-encoded SubjectPublicKeyInfo structure ("BEGIN PUBLIC KEY")
- pkcs1, //base-64-encoded RSA number and exponent ("BEGIN RSA PUBLIC KEY")
- pkcs1_raw
+ pkix, //base-64-encoded X.509 SubjectPublicKeyInfo structure ("BEGIN PUBLIC KEY")
+ pkcs1, //base-64-encoded PKCS#1 RSAPublicKey: RSA number and exponent ("BEGIN RSA PUBLIC KEY")
+ raw //raw bytes: DER-encoded PKCS#1
};
//verify signatures produced with: "openssl dgst -sha256 -sign private.pem -out file.sig file.txt"
@@ -34,6 +34,10 @@ void verifySignature(const std::string& message,
std::string convertRsaKey(const std::string& keyStream, RsaStreamType typeFrom, RsaStreamType typeTo, bool publicKey); //throw SysError
+bool isPuttyKeyStream(const std::string& keyStream);
+std::string convertPuttyKeyToPkix(const std::string& keyStream, const std::string& passphrase); //throw SysError
+
+
class TlsContext
{
public:
diff --git a/zen/shutdown.cpp b/zen/shutdown.cpp
index 4fc687d6..5ce586f0 100644
--- a/zen/shutdown.cpp
+++ b/zen/shutdown.cpp
@@ -33,8 +33,9 @@ void zen::suspendSystem() //throw FileError
void zen::terminateProcess(int exitCode)
{
- std::exit(exitCode); //[[noreturn]]; "Stack is not unwound: destructors of variables with automatic storage duration are not called." => perfect
- //don't use std::abort() => crashes process with "EXC_CRASH (SIGABRT)" on macOS
+ std::quick_exit(exitCode); //[[noreturn]]; "Causes normal program termination to occur without completely cleaning the resources." => perfect
+
+
for (;;) //why still here?? => crash deliberately!
*reinterpret_cast<volatile int*>(0) = 0; //crude but at least we'll get crash dumps if it ever happens
}
diff --git a/zen/socket.h b/zen/socket.h
index 7ca0c93f..827d446b 100644
--- a/zen/socket.h
+++ b/zen/socket.h
@@ -142,7 +142,7 @@ size_t tryWriteSocket(SocketType socket, const void* buffer, size_t bytesToWrite
inline
void shutdownSocketSend(SocketType socket) //throw SysError
{
- if (::shutdown(socket, SHUT_WR) != 0)
+ if (::shutdown(socket, SHUT_WR) != 0)
THROW_LAST_SYS_ERROR_WSA(L"shutdown");
}
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 15e7f7ca..9014b0f7 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -15,7 +15,6 @@
#include <algorithm>
#include <optional>
#include "string_traits.h"
-//#include "build_info.h"
//enhancements for <algorithm>
diff --git a/zen/string_base.h b/zen/string_base.h
index 2247f93a..a417b7f6 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -189,8 +189,8 @@ private:
length (static_cast<uint32_t>(len)),
capacity(static_cast<uint32_t>(cap))
{
- static_assert(ATOMIC_INT_LOCK_FREE == 2); //2: "The atomic type is always lock-free"
- //static_assert(decltype(refCount)::is_always_lock_free); //C++17 variant (not yet supported on GCC 6.3)
+ //static_assert(ATOMIC_INT_LOCK_FREE == 2); //2: "The atomic type is always lock-free"
+ static_assert(decltype(refCount)::is_always_lock_free); //C++17 variant (not yet supported on GCC 6.3)
}
std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default!
@@ -313,6 +313,11 @@ template <class Char, template <class> class SP> inline Zbase<Char, SP> operator
template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+( Char lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(&lhs, 1) += rhs; }
template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Char* lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs ) += rhs; }
+#if __cpp_impl_three_way_comparison
+#error implement:
+std::strong_ordering operator<=>(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
+bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs);
+#endif
@@ -329,7 +334,7 @@ template <class Char, template <class> class SP> inline Zbase<Char, SP> operator
template <class Char, template <class> class SP> inline
Zbase<Char, SP>::Zbase()
{
- //resist the temptation to avoid this allocation by referening a static global: NO performance advantage, MT issues!
+ //resist the temptation to avoid this allocation by referencing a static global: NO performance advantage, MT issues!
rawStr_ = this->create(0);
rawStr_[0] = 0;
}
diff --git a/zen/string_tools.h b/zen/string_tools.h
index c3970d05..dcb5a54a 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -14,15 +14,16 @@
#include <algorithm>
#include <cassert>
#include <vector>
-#include <sstream> //std::basic_ostringstream
#include "stl_tools.h"
#include "string_traits.h"
+#include "legacy_compiler.h" //<charconv> (without compiler crashes)
//enhance arbitray string class with useful non-member functions:
namespace zen
{
template <class Char> bool isWhiteSpace(Char c);
+template <class Char> bool isLineBreak (Char c);
template <class Char> bool isDigit (Char c); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only!
template <class Char> bool isHexDigit (Char c);
template <class Char> bool isAsciiAlpha(Char c);
@@ -116,6 +117,14 @@ bool isWhiteSpace(Char c)
}
template <class Char> inline
+bool isLineBreak(Char c)
+{
+ static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
+ return c == static_cast<Char>('\r') || c == static_cast<Char>('\n');
+}
+
+
+template <class Char> inline
bool isDigit(Char c) //similar to implementation of std::isdigit()!
{
static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
@@ -552,6 +561,10 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const
template <class S, class T, class Num> inline
S printNumber(const T& format, const Num& number) //format a single number using ::sprintf
{
+#if __cpp_lib_format
+#error refactor
+#endif
+
static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const int BUFFER_SIZE = 128;
@@ -573,22 +586,31 @@ enum class NumberType
};
+template <class S, class Num> S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>) = delete;
+#if 0 //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
template <class S, class Num> inline
-S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
+S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>)
{
std::basic_ostringstream<GetCharTypeT<S>> ss;
ss << number;
return copyStringTo<S>(ss.str());
}
+#endif
-template <class S, class Num> inline S floatToString(const Num& number, char ) { return printNumber<S>( "%g", static_cast<double>(number)); }
-template <class S, class Num> inline S floatToString(const Num& number, wchar_t) { return printNumber<S>(L"%g", static_cast<double>(number)); }
-
template <class S, class Num> inline
S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::FLOATING_POINT>)
{
- return floatToString<S>(number, GetCharTypeT<S>());
+ //don't use sprintf("%g"): way SLOWWWWWWER than std::to_chars()
+
+ char buffer[128]; //zero-initialize?
+ //let's give some leeway, but 24 chars should suffice: https://www.reddit.com/r/cpp/comments/dgj89g/cppcon_2019_stephan_t_lavavej_floatingpoint/f3j7d3q/
+ const char* strEnd = zen::to_chars(std::begin(buffer), std::end(buffer), number);
+
+ S output;
+ std::for_each(static_cast<const char*>(buffer), strEnd,
+ [&](char c) { output += static_cast<GetCharTypeT<S>>(c); });
+ return output;
}
@@ -665,25 +687,46 @@ S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::UNS
//--------------------------------------------------------------------------------
+template <class Num, class S> Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>) = delete;
+#if 0 //default string to number conversion using streams: convenient, but SLOW
template <class Num, class S> inline
-Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>) //default string to number conversion using streams: convenient, but SLOW
+Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>)
{
using CharType = GetCharTypeT<S>;
Num number = 0;
std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType>>(str)) >> number;
return number;
}
+#endif
-template <class Num> inline Num stringToFloat(const char* str) { return std::strtod(str, nullptr); }
-template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, nullptr); }
+inline
+double stringToFloat(const char* first, const char* last)
+{
+ //don't use std::strtod(): 1. requires null-terminated string 2. SLOWER than std::from_chars()
+ return zen::from_chars(first, last);
+}
+
+
+inline
+double stringToFloat(const wchar_t* first, const wchar_t* last)
+{
+ std::string buf(last - first, '\0');
+ std::transform(first, last, buf.begin(), [](wchar_t c) { return static_cast<char>(c); });
+
+ return zen::from_chars(buf.c_str(), buf.c_str() + buf.size());
+}
+
template <class Num, class S> inline
Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::FLOATING_POINT>)
{
- return stringToFloat<Num>(strBegin(str));
+ const auto* const first = strBegin(str);
+ const auto* const last = first + strLength(str);
+ return static_cast<Num>(stringToFloat(first, last));
}
+
template <class Num, class S>
Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
{
@@ -695,7 +738,6 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i
while (first != last && isWhiteSpace(*first)) //skip leading whitespace
++first;
- //handle minus sign
hasMinusSign = false;
if (first != last)
{
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 93cfd81c..d0f34d54 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -7,6 +7,7 @@
#ifndef STRING_TRAITS_H_813274321443234
#define STRING_TRAITS_H_813274321443234
+#include <string_view>
#include <cstring> //strlen
#include "type_traits.h"
@@ -36,27 +37,9 @@ strBegin(): -> not null-terminated! -> may be nullptr if length is 0!
//reference a sub-string for consumption by zen string_tools
-template <class Char>
-class StringRef
-{
-public:
- template <class Iterator>
- StringRef(Iterator first, Iterator last) : len_(last - first),
- str_(first != last ? &*first : reinterpret_cast<Char*>(this) /*Win32 APIs like CompareStringOrdinal() choke on nullptr!*/)
- {
- static_assert(alignof(StringRef) % alignof(Char) == 0); //even though str_ is never dereferenced, make sure the pointer value respects alignment (why? because we can)
- }
- //StringRef(const Char* str, size_t len) : str_(str), len_(len) {} -> needless constraint! Char* not available for empty range!
-
- Char* data () const { return str_; } //no null-termination!
- size_t length() const { return len_; }
-
-private:
- const size_t len_;
- Char* const str_;
-};
-
-
+//=> std::string_view seems decent, but of course fucks up in one regard: construction
+template <class Iterator> auto makeStringView(Iterator first, Iterator last);
+template <class Iterator> auto makeStringView(Iterator first, size_t len);
@@ -84,7 +67,7 @@ public:
};
-template <class S, bool isStringClass> struct GetCharTypeImpl { using Type = void; };
+template <class S, bool isStringClass> struct GetCharTypeImpl { using Type = void; };
template <class S>
struct GetCharTypeImpl<S, true>
@@ -103,10 +86,10 @@ struct GetCharTypeImpl<S, true>
template <> struct GetCharTypeImpl<char, false> { using Type = char; };
template <> struct GetCharTypeImpl<wchar_t, false> { using Type = wchar_t; };
-template <> struct GetCharTypeImpl<StringRef<char >, false> { using Type = char; };
-template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> { using Type = wchar_t; };
-template <> struct GetCharTypeImpl<StringRef<const char >, false> { using Type = char; };
-template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> { using Type = wchar_t; };
+template <> struct GetCharTypeImpl<std::basic_string_view<char >, false> { using Type = char; };
+template <> struct GetCharTypeImpl<std::basic_string_view<wchar_t >, false> { using Type = wchar_t; };
+template <> struct GetCharTypeImpl<std::basic_string_view<const char >, false> { using Type = char; };
+template <> struct GetCharTypeImpl<std::basic_string_view<const wchar_t>, false> { using Type = wchar_t; };
ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
@@ -184,11 +167,10 @@ inline const wchar_t* strBegin(const wchar_t* str) { return str; }
inline const char* strBegin(const char& ch) { return &ch; }
inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; }
-inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); }
-inline const wchar_t* strBegin(const StringRef<wchar_t >& ref) { return ref.data(); }
-inline const char* strBegin(const StringRef<const char >& ref) { return ref.data(); }
-inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); }
-
+inline const char* strBegin(const std::basic_string_view<char >& ref) { return ref.data(); }
+inline const wchar_t* strBegin(const std::basic_string_view<wchar_t >& ref) { return ref.data(); }
+inline const char* strBegin(const std::basic_string_view<const char >& ref) { return ref.data(); }
+inline const wchar_t* strBegin(const std::basic_string_view<const wchar_t>& ref) { return ref.data(); }
template <class S, typename = std::enable_if_t<StringTraits<S>::isStringClass>> inline
size_t strLength(const S& str) //SFINAE: T must be a "string"
@@ -201,15 +183,15 @@ inline size_t strLength(const wchar_t* str) { return cStringLength(str); }
inline size_t strLength(char) { return 1; }
inline size_t strLength(wchar_t) { return 1; }
-inline size_t strLength(const StringRef<char >& ref) { return ref.length(); }
-inline size_t strLength(const StringRef<wchar_t >& ref) { return ref.length(); }
-inline size_t strLength(const StringRef<const char >& ref) { return ref.length(); }
-inline size_t strLength(const StringRef<const wchar_t>& ref) { return ref.length(); }
+inline size_t strLength(const std::basic_string_view<char >& ref) { return ref.length(); }
+inline size_t strLength(const std::basic_string_view<wchar_t >& ref) { return ref.length(); }
+inline size_t strLength(const std::basic_string_view<const char >& ref) { return ref.length(); }
+inline size_t strLength(const std::basic_string_view<const wchar_t>& ref) { return ref.length(); }
}
template <class S> inline
-auto strBegin(S&& str) -> const GetCharTypeT<S>*
+auto strBegin(S&& str)
{
static_assert(IsStringLikeV<S>);
return impl::strBegin(std::forward<S>(str));
@@ -222,6 +204,19 @@ size_t strLength(S&& str)
static_assert(IsStringLikeV<S>);
return impl::strLength(std::forward<S>(str));
}
+
+
+template <class Iterator> inline
+auto makeStringView(Iterator first, Iterator last)
+{
+ using CharType = GetCharTypeT<decltype(&*first)>;
+
+ return std::basic_string_view<CharType>(first != last ? &*first :
+ reinterpret_cast<CharType*>(0x1000), /*Win32 APIs like CompareStringOrdinal() choke on nullptr!*/
+ last - first);
+}
+
+template <class Iterator> inline auto makeStringView(Iterator first, size_t len) { return makeStringView(first, first + len); }
}
#endif //STRING_TRAITS_H_813274321443234
diff --git a/zen/sys_error.h b/zen/sys_error.h
index a087172f..7c746258 100644
--- a/zen/sys_error.h
+++ b/zen/sys_error.h
@@ -47,6 +47,10 @@ private:
do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false)
+//helper for error checking macros:
+inline bool validatBool(bool b) { return b; }
+inline bool validatBool(void* b) { return b != nullptr; }
+bool validatBool(int) = delete; //catch unintended bool conversions, e.g. HRESULT
diff --git a/zen/thread.h b/zen/thread.h
index 791aec67..d6cafab7 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -18,6 +18,9 @@ namespace zen
{
class InterruptionStatus;
+#if __cpp_lib_jthread
+ #error refactor!
+#endif
class InterruptibleThread
{
public:
@@ -81,6 +84,7 @@ template <class Function>
auto runAsync(Function&& fun);
//wait for all with a time limit: return true if *all* results are available!
+//TODO: use std::when_all when available
template<class InputIterator, class Duration>
bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration);
@@ -89,6 +93,7 @@ bool isReady(const std::future<T>& f) { return f.wait_for(std::chrono::seconds(0
//------------------------------------------------------------------------------------------
//wait until first job is successful or all failed: substitute until std::when_any is available
+//TODO: use std::when_any when available
template <class T>
class AsyncFirstResult
{
@@ -113,6 +118,7 @@ private:
//------------------------------------------------------------------------------------------
//value associated with mutex and guaranteed protected access:
+//TODO: use std::synchronized_value when available
template <class T>
class Protected
{
diff --git a/zen/time.h b/zen/time.h
index a32e28e3..27ce518f 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -335,7 +335,7 @@ TimeComp parseTime(const String& format, const String2& str, UserDefinedFormatTa
if (!std::all_of(itStr, itStr + digitCount, isDigit<CharType>))
return false;
- result = zen::stringTo<int>(StringRef<const CharType>(itStr, itStr + digitCount));
+ result = zen::stringTo<int>(makeStringView(itStr, digitCount));
itStr += digitCount;
return true;
};
diff --git a/zen/warn_static.h b/zen/warn_static.h
index 17e7cf25..d5f78b5d 100644
--- a/zen/warn_static.h
+++ b/zen/warn_static.h
@@ -8,17 +8,18 @@
#define WARN_STATIC_H_08724567834560832745
/*
- Portable Compile-Time Warning
- -----------------------------
- Usage:
- warn_static("my message")
+ Portable Compile-Time Warning
+ -----------------------------
+ Usage:
+ warn_static("my message")
*/
-#define ZEN_STATIC_WARNING_STRINGIZE(NUM) #NUM
+#define ZEN_STRINGIZE_STRING(NUM) #NUM
+#define ZEN_STRINGIZE_NUMBER(NUM) ZEN_STRINGIZE_STRING(NUM)
#if defined __GNUC__ //Clang also defines __GNUC__!
#define warn_static(MSG) \
- _Pragma(ZEN_STATIC_WARNING_STRINGIZE(GCC warning MSG))
+ _Pragma(ZEN_STRINGIZE_STRING(GCC warning MSG))
#endif
#endif //WARN_STATIC_H_08724567834560832745
diff --git a/zen/zlib_wrap.cpp b/zen/zlib_wrap.cpp
index 8979efa6..f7418b88 100644
--- a/zen/zlib_wrap.cpp
+++ b/zen/zlib_wrap.cpp
@@ -6,7 +6,7 @@
#include "zlib_wrap.h"
//Windows: use the SAME zlib version that wxWidgets is linking against! //C:\Data\Projects\wxWidgets\Source\src\zlib\zlib.h
-//Linux/macOS: use zlib system header for both wxWidgets and libcurl (zlib is required for HTTP)
+//Linux/macOS: use zlib system header for both wxWidgets and libcurl (zlib is required for HTTP, SFTP)
// => don't compile wxWidgets with: --with-zlib=builtin
#include <zlib.h> //https://www.zlib.net/manual.html
#include <zen/scope_guard.h>
@@ -53,7 +53,7 @@ size_t zen::impl::zlib_compress(const void* src, size_t srcLen, void* trg, size_
// Z_MEM_ERROR: not enough memory
// Z_BUF_ERROR: not enough room in the output buffer
if (rv != Z_OK || bufferSize > trgLen)
- throw SysError(formatSystemError(L"compress2", formatZlibStatusCode(rv), L"zlib error"));
+ throw SysError(formatSystemError(L"compress2", formatZlibStatusCode(rv), L"zlib error"));
return bufferSize;
}
diff --git a/zen/zlib_wrap.h b/zen/zlib_wrap.h
index fbe26193..9d9229ac 100644
--- a/zen/zlib_wrap.h
+++ b/zen/zlib_wrap.h
@@ -90,14 +90,14 @@ BinContainer decompress(const BinContainer& stream) //throw SysError
//retrieve size of uncompressed data
uint64_t uncompressedSize = 0; //use portable number type!
if (stream.size() < sizeof(uncompressedSize))
- throw SysError(L"zlib error: stream size < 8");
+ throw SysError(L"zlib error: stream size < 8");
std::memcpy(&uncompressedSize, &*stream.begin(), sizeof(uncompressedSize));
//attention: contOut MUST NOT be empty! Else it will pass a nullptr to zlib_decompress() => Z_STREAM_ERROR although "uncompressedSize == 0"!!!
//secondary bug: don't dereference iterator into empty container!
if (uncompressedSize == 0) //cannot be 0: compress() directly maps empty -> empty container skipping zlib!
- throw SysError(L"zlib error: uncompressed size == 0");
+ throw SysError(L"zlib error: uncompressed size == 0");
try
{
@@ -105,7 +105,7 @@ BinContainer decompress(const BinContainer& stream) //throw SysError
}
catch (const std::bad_alloc& e) //most likely due to data corruption!
{
- throw SysError(L"zlib error: " + _("Out of memory.") + L" " + utfTo<std::wstring>(e.what()));
+ throw SysError(L"zlib error: " + _("Out of memory.") + L" " + utfTo<std::wstring>(e.what()));
}
const size_t bytesWritten = impl::zlib_decompress(&*stream.begin() + sizeof(uncompressedSize),
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index ad736d04..f018b14f 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -8,7 +8,7 @@
#include <stdexcept>
#include "utf.h"
-#include <glib.h>
+ #include <glib.h>
#include "sys_error.h"
using namespace zen;
bgstack15