diff --git a/PwTech.cbproj b/PwTech.cbproj index 8ef8d09..af7f911 100644 --- a/PwTech.cbproj +++ b/PwTech.cbproj @@ -161,13 +161,13 @@ graphics\pwtech.ico LUA_INT_TYPE=1;$(Defines) 3 - CompanyName=;FileDescription=Password Tech executable;FileVersion=3.5.4.0;InternalName=;LegalCopyright=Copyright 2002-2023 by Christian Thöing;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=Password Tech;ProductVersion=3.5.4;Comments= + CompanyName=;FileDescription=Password Tech executable;FileVersion=3.5.5.0;InternalName=;LegalCopyright=Copyright 2002-2023 by Christian Thöing;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=Password Tech;ProductVersion=3.5.5;Comments= PerMonitorV2 false 5 Debug true - 4 + 5 false true @@ -176,7 +176,7 @@ $(BDS)\bin\default_app.manifest PerMonitorV2 true - pwtech.ico + PwTech_Icon.ico 3 5 false @@ -184,10 +184,34 @@ Debug true false - 4 - CompanyName=;FileDescription=Password Tech executable;FileVersion=3.5.4.0;InternalName=;LegalCopyright=Copyright (c) 2002-2024 Christian Thöing;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=3.5.4;Comments= + 5 + CompanyName=;FileDescription=Password Tech executable;FileVersion=3.5.5.0;InternalName=;LegalCopyright=Copyright (c) 2002-2024 Christian Thöing;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=3.5.5;Comments= + + ICON + A + + + ICON + Icon_Blue + + + ICON + Icon_Colors + + + ICON + Icon_Green + + + ICON + Icon_Orange + + + ICON + Icon_Red + 0 @@ -564,25 +588,25 @@ 90 - - - - - - - - + + + - - + + + + - + + + + @@ -700,8 +724,50 @@ + + + PwTech.exe + true + + + + + .\ + true + + + + + .\ + true + + + + + .\ + true + + + + + .\ + true + + + + + .\ + true + + + + + .\ + true + + diff --git a/PwTech.cpp b/PwTech.cpp index 2309ef4..207eb16 100644 --- a/PwTech.cpp +++ b/PwTech.cpp @@ -10,25 +10,25 @@ //--------------------------------------------------------------------------- #include #include -USEFORM("src\main\PasswManager.cpp", PasswMngForm); -USEFORM("src\main\PasswMngColSelect.cpp", PasswMngColDlg); -USEFORM("src\main\PasswMngDbProp.cpp", PasswMngDbPropDlg); -USEFORM("src\main\MPPasswGen.cpp", MPPasswGenForm); USEFORM("src\main\PasswEnter.cpp", PasswEnterDlg); USEFORM("src\main\PasswList.cpp", PasswListForm); -USEFORM("src\main\PasswOptions.cpp", PasswOptionsDlg); -USEFORM("src\main\ProfileEditor.cpp", ProfileEditDlg); -USEFORM("src\main\Progress.cpp", ProgressForm); -USEFORM("src\main\PasswMngDbSettings.cpp", PasswDbSettingsDlg); +USEFORM("src\main\PasswManager.cpp", PasswMngForm); +USEFORM("src\main\Main.cpp", MainForm); +USEFORM("src\main\MPPasswGen.cpp", MPPasswGenForm); USEFORM("src\main\PasswMngKeyValEdit.cpp", PasswMngKeyValDlg); USEFORM("src\main\PasswMngPwHistory.cpp", PasswHistoryDlg); -USEFORM("src\main\About.cpp", AboutForm); -USEFORM("src\main\CharSetBuilder.cpp", CharSetBuilderForm); +USEFORM("src\main\PasswOptions.cpp", PasswOptionsDlg); +USEFORM("src\main\PasswMngColSelect.cpp", PasswMngColDlg); +USEFORM("src\main\PasswMngDbProp.cpp", PasswMngDbPropDlg); +USEFORM("src\main\PasswMngDbSettings.cpp", PasswDbSettingsDlg); USEFORM("src\main\InfoBox.cpp", InfoBoxForm); -USEFORM("src\main\Main.cpp", MainForm); USEFORM("src\main\Configuration.cpp", ConfigurationDlg); USEFORM("src\main\CreateRandDataFile.cpp", CreateRandDataFileDlg); USEFORM("src\main\CreateTrigramFile.cpp", CreateTrigramFileDlg); +USEFORM("src\main\About.cpp", AboutForm); +USEFORM("src\main\CharSetBuilder.cpp", CharSetBuilderForm); +USEFORM("src\main\ProfileEditor.cpp", ProfileEditDlg); +USEFORM("src\main\Progress.cpp", ProgressForm); USEFORM("src\main\ProvideEntropy.cpp", ProvideEntropyDlg); USEFORM("src\main\QuickHelp.cpp", QuickHelpForm); //--------------------------------------------------------------------------- @@ -144,6 +144,18 @@ int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) if (g_sAppDataPath.IsEmpty()) g_sAppDataPath = g_sExePath; + AnsiString asDonorKey = g_pIni->ReadString("Main", "DonorKey", ""); + if (!asDonorKey.IsEmpty()) { + auto result = CheckDonorKey(asDonorKey); + + g_donorInfo.Valid = std::get<0>(result); + if (g_donorInfo.Valid == DONOR_KEY_VALID) { + g_donorInfo.Key = asDonorKey.Trim(); + g_donorInfo.Id = std::get<2>(result); + g_donorInfo.Type = std::get<1>(result); + } + } + const WString DEFAULT_STYLE_NAME = "Windows"; g_config.UiStyleName = DEFAULT_STYLE_NAME; @@ -155,6 +167,19 @@ int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) g_config.UiStyleName = sStyleName; } + WString sAppIconName = g_pIni->ReadString("Main", "AppIcon", WString()); + if (!sAppIconName.IsEmpty() && g_donorInfo.Valid == DONOR_KEY_VALID) { + auto it = std::find_if(AppIconNames.begin(), AppIconNames.end(), + [&sAppIconName](const std::pair& p) + { return p.first == sAppIconName; }); + if (it != AppIconNames.end()) { + g_config.AppIconName = sAppIconName; + if (it != AppIconNames.begin()) + Application->Icon->LoadFromResourceName(reinterpret_cast( + HInstance), it->second); + } + } + Application->CreateForm(__classid(TMainForm), &MainForm); Application->CreateForm(__classid(TAboutForm), &AboutForm); Application->CreateForm(__classid(TConfigurationDlg), &ConfigurationDlg); diff --git a/PwTech_resources.rc b/PwTech_resources.rc new file mode 100644 index 0000000..32f253e --- /dev/null +++ b/PwTech_resources.rc @@ -0,0 +1,6 @@ +A ICON "graphics\\pwtech_default.ico" +Icon_Blue ICON "graphics\\pwtech_icon_blue.ico" +Icon_Colors ICON "graphics\\pwtech_icon_colored.ico" +Icon_Green ICON "graphics\\pwtech_icon_green.ico" +Icon_Orange ICON "graphics\\pwtech_icon_orange.ico" +Icon_Red ICON "graphics\\pwtech_icon_red.ico" diff --git a/changes.txt b/changes.txt index 6f79bda..4347f78 100644 --- a/changes.txt +++ b/changes.txt @@ -3,7 +3,32 @@ Copyright (c) 2002-2024 by Christian Thöing -Version 3.5.4 +Version 3.5.5 + +NEW FEATURES: + +- Advanced Password Options: New option "Remove leading and trailing whitespace + characters" to remove space and tab characters at the beginning and end of + passwords +- Configuration | General: New option "Application icon" to change PwTech's + icon displayed on the task bar, system tray, etc. during runtime +- Configuration | Language: New "Install" and "Remove" buttons to add or + remove language files to/from the current PwTech installation + +CHANGES & IMPROVEMENTS: + +- Language files can also be copied to the "AppData" folder (usually + "C:\Users\{user}\AppData\Roaming\Password Tech"), which makes it easier + to install new languages if PwTech has been installed to the "Program Files" + folder (which usually requires admin privileges for write access) + +FIXES: + +- PO language files with empty fields in header not loaded properly + +---------- + +Version 3.5.4 (2024-02-14) NEW FEATURES: diff --git a/graphics/pwtech_default.ico b/graphics/pwtech_default.ico new file mode 100644 index 0000000..a6f821f Binary files /dev/null and b/graphics/pwtech_default.ico differ diff --git a/graphics/pwtech_icon_blue.ico b/graphics/pwtech_icon_blue.ico new file mode 100644 index 0000000..64ab128 Binary files /dev/null and b/graphics/pwtech_icon_blue.ico differ diff --git a/graphics/pwtech_icon_colored.ico b/graphics/pwtech_icon_colored.ico new file mode 100644 index 0000000..2479247 Binary files /dev/null and b/graphics/pwtech_icon_colored.ico differ diff --git a/graphics/pwtech_icon_green.ico b/graphics/pwtech_icon_green.ico new file mode 100644 index 0000000..adb8ee9 Binary files /dev/null and b/graphics/pwtech_icon_green.ico differ diff --git a/graphics/pwtech_icon_orange.ico b/graphics/pwtech_icon_orange.ico new file mode 100644 index 0000000..4106de3 Binary files /dev/null and b/graphics/pwtech_icon_orange.ico differ diff --git a/graphics/pwtech_icon_red.ico b/graphics/pwtech_icon_red.ico new file mode 100644 index 0000000..38605d3 Binary files /dev/null and b/graphics/pwtech_icon_red.ico differ diff --git a/languages/English.pot b/languages/English.pot index dad0a5e..994a3b8 100644 --- a/languages/English.pot +++ b/languages/English.pot @@ -15,10 +15,9 @@ # for floating point numbers, "%s" for strings, etc.), and the original strings # are still provided in the old format to avoid merge conflicts when updating # existing translations. - msgid "" msgstr "" -"Project-Id-Version: Password Tech 3.5.4\n" +"Project-Id-Version: Password Tech 3.5.5\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: \n" @@ -1016,6 +1015,10 @@ msgstr "" msgid "User interface style:" msgstr "" +#. Configuration window, General +msgid "Application icon:" +msgstr "" + #. Configuration window, General msgid "Change font for the GUI controls:" msgstr "" @@ -1192,6 +1195,52 @@ msgstr "" msgid "Select language:" msgstr "" +#. Configuration window, Language +msgid "Install..." +msgstr "" + +#. Configuration window, Language, Install +msgid "Language already installed" +msgstr "" + +#. Configuration window, Language, Install +msgid "" +"Language file already exists:\n" +"\"%1\"" +msgstr "" + +#. Configuration window, Language, Install +msgid "Language \"%1\" installed successfully." +msgstr "" + +#. Configuration window, Language, Install +msgid "" +"Could not copy file \"%1\" to\n" +"\"%2\"" +msgstr "" + +#. Configuration window, Language, Install +msgid "" +"Could not install language:\n" +"%1." +msgstr "" + +#. Configuration window, Language, Remove +msgid "" +"Are you sure you want to remove\n" +"\"%1\"?" +msgstr "" + +#. Configuration window, Language, Remove +msgid "Language \"%1\" successfully removed." +msgstr "" + +#. Configuration window, Language, Remove +msgid "" +"Could not delete file\n" +"\"%1\"." +msgstr "" + #. Configuration window, Database msgid "Clear clipboard when closing/locking database" msgstr "" @@ -1413,6 +1462,10 @@ msgstr "" msgid "Exclude repeating consecutive characters (e.g. aa, 11)" msgstr "" +#. Advanced Password Options window, list of boolean options (checkboxes) +msgid "Remove leading and trailing whitespace characters" +msgstr "" + #. Advanced Password Options window, list of boolean options (checkboxes) msgid "Exclude duplicate entries in password lists" msgstr "" diff --git a/languages/German.po b/languages/German.po index 5404f29..ac8ca02 100644 --- a/languages/German.po +++ b/languages/German.po @@ -1,4 +1,4 @@ -# German translation of Password Tech 3.5.3 +# German translation of Password Tech 3.5.5 # Released under the GNU General Public License # # !!! NOTE ON FORMAT STRINGS !!! @@ -20,7 +20,7 @@ #. Add header entry "X-PasswordTech-Manual: ..." to provide language-specific manual msgid "" msgstr "" -"Project-Id-Version: Password Tech 3.5.4\n" +"Project-Id-Version: Password Tech 3.5.5\n" "Last-Translator: Christian Thöing\n" "Language: de\n" @@ -1136,6 +1136,10 @@ msgstr "Datenbank" msgid "User interface style:" msgstr "Stil der Benutzeroberfläche:" +#. Configuration window, General +msgid "Application icon:" +msgstr "Programm-Symbol:" + #. Configuration window, General msgid "Change font for the GUI controls:" msgstr "Schriftart für GUI-Steuerelemente ändern:" @@ -1312,6 +1316,42 @@ msgstr "Zeichensequenz für Zeilenumbruch (\"newline\")" msgid "Select language:" msgstr "Sprache auswählen:" +#. Configuration window, Language +msgid "Install..." +msgstr "Installieren..." + +#. Configuration window, Language, Install +msgid "Language already installed" +msgstr "Sprache bereits installiert" + +#. Configuration window, Language, Install +msgid "Language file already exists:\n\"%1\"" +msgstr "Sprachdatei existiert bereits:\n"%1\"" + +#. Configuration window, Language, Install +msgid "Language \"%1\" installed successfully." +msgstr "Sprache \"%1\" erfolgreich installiert." + +#. Configuration window, Language, Install +msgid "Could not copy file \"%1\" to\n\"%2\"" +msgstr "Konnte Datei \"%1\" nicht kopieren nach\n\"%2\"" + +#. Configuration window, Language, Install +msgid "Could not install language:\n%1." +msgstr "Konnte Sprache nicht installieren:\n%1." + +#. Configuration window, Language, Remove +msgid "Are you sure you want to remove\n\"%1\"?" +msgstr "Sind Sie sicher, dass Sie\n\"%1\" entfernen möchten?" + +#. Configuration window, Language, Remove +msgid "Language \"%1\" successfully removed." +msgstr "Sprache \"%1\" erfolgreich entfernt." + +#. Configuration window, Language, Remove +msgid "Could not delete file\n\"%1\"." +msgstr "Konnte Datei nicht löschen:\n\"%1\"." + #. Configuration window, Database msgid "Clear clipboard when closing/locking database" msgstr "Zwischenablage löschen, wenn Datenbank geschlossen/gesperrt wird" @@ -1538,6 +1578,10 @@ msgstr "Nur Zeichen aus benutzerdefiniertem Zeichensatz verwenden" msgid "Exclude repeating consecutive characters (e.g. aa, 11)" msgstr "Folge desselben Zeichens ausschließen (z.B. aa, 11)" +#. Advanced Password Options window, list of boolean options (checkboxes) +msgid "Remove leading and trailing whitespace characters" +msgstr "Leerraum- (whitespace) Zeichen am Anfang und Ende entfernen" + #. Advanced Password Options window, list of boolean options (checkboxes) msgid "Exclude duplicate entries in password lists" msgstr "Doppelte Einträge in Passwort-Listen ausschließen" diff --git a/manual/manual.odt b/manual/manual.odt index ff63a79..55ad014 100644 Binary files a/manual/manual.odt and b/manual/manual.odt differ diff --git a/setup/PWTech.iss b/setup/PWTech.iss index f3c12e9..d68033a 100644 --- a/setup/PWTech.iss +++ b/setup/PWTech.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Password Tech" -#define MyAppVersion "3.5.4" +#define MyAppVersion "3.5.5" #define MyAppPublisher "Christian Thöing" #define MyAppURL "http://pwgen-win.sourceforge.net" #define MyAppExeName "PwTech.exe" @@ -62,7 +62,7 @@ Source: "C:\Projekte\PWGen3\manual\scripting.pdf"; DestDir: "{app}"; Flags: igno Source: "C:\Projekte\PWGen3\license.txt"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\Projekte\PWGen3\common_passwords.txt"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\Projekte\PWGen3\changes.txt"; DestDir: "{app}"; Flags: ignoreversion -Source: "C:\Projekte\PWGen3\Win64\Release\German.po"; DestDir: "{app}"; Flags: ignoreversion +Source: "C:\Projekte\PWGen3\German.po"; DestDir: "{app}"; Flags: ignoreversion ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] diff --git a/src/crypto/CryptText.cpp b/src/crypto/CryptText.cpp index 45efc98..3369172 100644 --- a/src/crypto/CryptText.cpp +++ b/src/crypto/CryptText.cpp @@ -60,7 +60,8 @@ static void initCrypto(aes_context* pCryptCtx, if (nVersion == 0) { derivedKey.Zeroize(); - memcpy(derivedKey, pSalt, 16); + //memcpy(derivedKey, pSalt, 16); + derivedKey.Copy(0, pSalt, 16); // hash the salt and the key together 8192 times for (int i = 0; i < 8192; i++) { @@ -119,9 +120,9 @@ int EncryptText(const SecureWString* psText, word32 lBufSize = HEADER_SIZE + lTextLen + lTextLen / 16 + 64 + 3 + HMAC_LENGTH; // ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // header security margins for compression - lBufSize = 16 + 16 * ((lBufSize + 15) / 16); - // ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // IV adjust to blocklength + lBufSize = 16 + alignToBlockSize(lBufSize, 16); + // ^^ + // IV // allocate some memory for the output buffer and the work buffer SecureMem buf(lBufSize), workBuf(LZO1X_1_MEM_COMPRESS); @@ -129,20 +130,25 @@ int EncryptText(const SecureWString* psText, // get a new initialization vector (IV) randGen.GetData(buf, 16); - word8* pCryptBuf = &buf[16]; + //word8* pCryptBuf = &buf[16]; + word32 lBufPos = 16; // create header and copy it to the buffer CryptTextHeader header; memcpy(header.Magic, CRYPTTEXT_MAGIC, sizeof(CRYPTTEXT_MAGIC)); header.Version = static_cast(CRYPTTEXT_VERSION); header.TextBytes = lTextLen; - memcpy(pCryptBuf, &header, HEADER_SIZE); + //memcpy(pCryptBuf, &header, HEADER_SIZE); + buf.Copy(lBufPos, reinterpret_cast(&header), HEADER_SIZE); + lBufPos += HEADER_SIZE; // compress the text lzo_uint comprLen; - lzo1x_1_compress(asTextUtf8.Bytes(), lTextLen, pCryptBuf + HEADER_SIZE, + lzo1x_1_compress(asTextUtf8.Bytes(), lTextLen, &buf[lBufPos], &comprLen, workBuf); + lBufPos += comprLen; + // destroy the text buffer and the work memory asTextUtf8.Clear(); workBuf.Clear(); @@ -170,16 +176,17 @@ int EncryptText(const SecureWString* psText, // compute the HMAC of the compressed message word8 hmac[32]; - sha256_hmac_update(hashCtx, pCryptBuf, HEADER_SIZE + comprLen); + sha256_hmac_update(hashCtx, &buf[16], HEADER_SIZE + comprLen); sha256_hmac_finish(hashCtx, hmac); hashCtx.Clear(); // copy the HMAC to the buffer - memcpy(pCryptBuf + HEADER_SIZE + comprLen, hmac, HMAC_LENGTH); + //memcpy(pCryptBuf + HEADER_SIZE + comprLen, hmac, HMAC_LENGTH); + buf.Copy(lBufPos, hmac, HMAC_LENGTH); // now encrypt the buffer (*with* HMAC) - aes_crypt_cbc(cryptCtx, AES_ENCRYPT, lCryptLen, iv, pCryptBuf, pCryptBuf); + aes_crypt_cbc(cryptCtx, AES_ENCRYPT, lCryptLen, iv, &buf[16], &buf[16]); cryptCtx.Clear(); @@ -190,10 +197,10 @@ int EncryptText(const SecureWString* psText, base64_encode(nullptr, &outBufSize, nullptr, lConvertLen, BASE64_LINE_LENGTH); // create a new buffer for base64 - SecureMem outBuf(outBufSize + 1); + SecureMem outBuf(outBufSize); base64_encode(outBuf, &outBufSize, buf, lConvertLen, BASE64_LINE_LENGTH); - outBuf.back() = '\0'; + //outBuf.back() = '\0'; // copy the output buffer to the clipboard SetClipboardTextBufAnsi(reinterpret_cast(outBuf.c_str())); @@ -230,8 +237,8 @@ int DecryptText(const SecureWString* psText, if (psText->IsStrEmpty()) return CRYPTTEXT_ERROR_NOTEXT; lTextLen = psText->StrLen(); - asText.New(lTextLen + 1); - for (word32 i = 0; i <= lTextLen; i++) // also copy terminating zero + asText.NewStr(lTextLen); + for (word32 i = 0; i < lTextLen; i++) asText[i] = (*psText)[i]; } else { @@ -249,7 +256,7 @@ int DecryptText(const SecureWString* psText, return CRYPTTEXT_ERROR_TEXTCORRUPTED; SecureMem buf(bufSize); - base64_decode(buf, &bufSize, reinterpret_cast(asText.Data()), lTextLen); + base64_decode(buf, &bufSize, asText.Bytes(), lTextLen); asText.Clear(); @@ -257,7 +264,7 @@ int DecryptText(const SecureWString* psText, if ((bufSize & 0x0F) != 0) return CRYPTTEXT_ERROR_TEXTCORRUPTED; - word8* pCryptBuf = &buf[16]; + //word8* pCryptBuf = &buf[16]; word32 lHmacLen = (nVersion == 0) ? 16 : HMAC_LENGTH; word32 lCryptLen = bufSize - 16; @@ -292,20 +299,20 @@ int DecryptText(const SecureWString* psText, hashCtx.Clear(); // verify the HMAC - if (memcmp(pCryptBuf + lCryptLen, hmac, lHmacLen) != 0) + if (memcmp(&buf[16 + lCryptLen], hmac, lHmacLen) != 0) return CRYPTTEXT_ERROR_BADKEY; // decrypt the buffer - aes_crypt_cbc(cryptCtx, AES_DECRYPT, lCryptLen, iv, pCryptBuf, pCryptBuf); + aes_crypt_cbc(cryptCtx, AES_DECRYPT, lCryptLen, iv, &buf[16], &buf[16]); // get the text length - memcpy(&header.TextBytes, pCryptBuf, lHeaderSize); + memcpy(&header.TextBytes, &buf[16], lHeaderSize); } else { // decrypt the first block and check the magic string - aes_crypt_cbc(cryptCtx, AES_DECRYPT, 16, iv, pCryptBuf, pCryptBuf); + aes_crypt_cbc(cryptCtx, AES_DECRYPT, 16, iv, &buf[16], &buf[16]); - memcpy(&header, pCryptBuf, HEADER_SIZE); + memcpy(&header, &buf[16], HEADER_SIZE); if (memcmp(header.Magic, CRYPTTEXT_MAGIC, sizeof(CRYPTTEXT_MAGIC)) != 0) return CRYPTTEXT_ERROR_BADKEY; @@ -320,21 +327,21 @@ int DecryptText(const SecureWString* psText, // now decrypt the rest if (lCryptLen > 16) aes_crypt_cbc(cryptCtx, AES_DECRYPT, lCryptLen - 16, iv, - pCryptBuf + 16, pCryptBuf + 16); + &buf[32], &buf[32]); if (encHmac) lCryptLen -= 16 - bLastBlock; // compute the HMAC of the plaintext - sha256_hmac_update(hashCtx, pCryptBuf, (encHmac) ? + sha256_hmac_update(hashCtx, &buf[16], encHmac ? lCryptLen - lHmacLen : lCryptLen); sha256_hmac_finish(hashCtx, hmac); hashCtx.Clear(); // verify the HMAC - if (memcmp((encHmac) ? pCryptBuf + lCryptLen - lHmacLen : - pCryptBuf + lCryptLen, hmac, lHmacLen) != 0) + if (memcmp(encHmac ? &buf[16 + lCryptLen - lHmacLen] : + &buf[16 + lCryptLen], hmac, lHmacLen) != 0) return CRYPTTEXT_ERROR_BADKEY; } @@ -354,7 +361,7 @@ int DecryptText(const SecureWString* psText, // decompress this buffer lzo_uint decomprLen = header.TextBytes; - if (lzo1x_decompress_safe(pCryptBuf + lHeaderSize, lToDecompr, + if (lzo1x_decompress_safe(&buf[16 + lHeaderSize], lToDecompr, reinterpret_cast(asOutBuf.Data()), &decomprLen, nullptr) != LZO_E_OK) return CRYPTTEXT_ERROR_DECOMPRFAILED; diff --git a/src/crypto/chacha.c b/src/crypto/chacha.c index 67b036c..fb88089 100644 --- a/src/crypto/chacha.c +++ b/src/crypto/chacha.c @@ -31,9 +31,12 @@ Modified for Password Tech by C.T. c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + #define DEFAULT_NROUNDS 20 + static const char sigma[16] = "expand 32-byte k"; static const char tau[16] = "expand 16-byte k"; + void chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits) { const char *constants; @@ -205,9 +208,113 @@ void chacha_decrypt_bytes(chacha_ctx *x,const u8 *c,u8 *m,u32 bytes) } void chacha_keystream_bytes(chacha_ctx *x,u8 *stream,u32 bytes) { + /* alternative: memset(stream, 0, bytes); chacha_encrypt_bytes(x,stream,stream,bytes); + */ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *c = stream; + u8 *ctarget; + u8 tmp[64]; + int i; + if (!bytes) return; + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + for (;;) { + if (bytes < 64) { + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = x->nrounds;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + } + U32TO8_LITTLE(c + 0,x0); + U32TO8_LITTLE(c + 4,x1); + U32TO8_LITTLE(c + 8,x2); + U32TO8_LITTLE(c + 12,x3); + U32TO8_LITTLE(c + 16,x4); + U32TO8_LITTLE(c + 20,x5); + U32TO8_LITTLE(c + 24,x6); + U32TO8_LITTLE(c + 28,x7); + U32TO8_LITTLE(c + 32,x8); + U32TO8_LITTLE(c + 36,x9); + U32TO8_LITTLE(c + 40,x10); + U32TO8_LITTLE(c + 44,x11); + U32TO8_LITTLE(c + 48,x12); + U32TO8_LITTLE(c + 52,x13); + U32TO8_LITTLE(c + 56,x14); + U32TO8_LITTLE(c + 60,x15); + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; + } } + /* Test vectors taken from RFC 7539 */ static const u8 test_key[3][32] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, diff --git a/src/main/About.cpp b/src/main/About.cpp index afa9397..2cb096d 100644 --- a/src/main/About.cpp +++ b/src/main/About.cpp @@ -92,9 +92,9 @@ void __fastcall TAboutForm::LinkClick(TObject *Sender, const UnicodeString Link, //--------------------------------------------------------------------------- void __fastcall TAboutForm::SetDonorUI(void) { - if (g_asDonorInfo.IsEmpty()) - AboutForm->DonorLbl->Caption = "Community"; + if (g_donorInfo.Valid == DONOR_KEY_VALID) + AboutForm->DonorLbl->Caption = TRL("Donor ID:") + " " + g_donorInfo.Id; else - AboutForm->DonorLbl->Caption = TRL("Donor ID:") + " " + g_asDonorInfo; + AboutForm->DonorLbl->Caption = "Community"; } //--------------------------------------------------------------------------- diff --git a/src/main/About.dfm b/src/main/About.dfm index 9a6cb9d..79da460 100644 Binary files a/src/main/About.dfm and b/src/main/About.dfm differ diff --git a/src/main/Configuration.cpp b/src/main/Configuration.cpp index c89db49..f5a5390 100644 --- a/src/main/Configuration.cpp +++ b/src/main/Configuration.cpp @@ -53,6 +53,15 @@ const int RANDOM_POOL_CIPHER_INFO[NUM_RANDOM_POOL_CIPHERS][2] = //{ 256, 128 } }; +const std::vector> AppIconNames = { + { "Default (dark)", "A" }, + { "Blue", "Icon_Blue" }, + { "Red", "Icon_Red" }, + { "Orange", "Icon_Orange" }, + { "Green", "Icon_Green" }, + { "Colors", "Icon_Colors" } +}; + //--------------------------------------------------------------------------- __fastcall TConfigurationDlg::TConfigurationDlg(TComponent* Owner) : TForm(Owner) //, m_pLangList(nullptr) @@ -70,7 +79,15 @@ __fastcall TConfigurationDlg::TConfigurationDlg(TComponent* Owner) UiStylesList->Items->Add(sName); } - for (int i = 0; i < NUM_RANDOM_POOL_CIPHERS; i++) { + auto icon = std::make_unique(); + int i = 0; + for (const auto& p : AppIconNames) { + icon->LoadFromResourceName(reinterpret_cast(HInstance), p.second); + AppIconImageList->AddIcon(icon.get()); + AppIconList->ItemsEx->AddItem(p.first, i++, -1, -1, -1, nullptr); + } + + for (i = 0; i < NUM_RANDOM_POOL_CIPHERS; i++) { WString sCipher = TRLFormat("%1 (%2-bit key, operates on %3-bit blocks)", { RANDOM_POOL_CIPHER_NAMES[i], IntToStr(RANDOM_POOL_CIPHER_INFO[i][0]), @@ -78,7 +95,7 @@ __fastcall TConfigurationDlg::TConfigurationDlg(TComponent* Owner) RandomPoolCipherList->Items->Add(sCipher); } - for (int i = 0; i <= 10; i++) + for (i = 0; i <= 10; i++) BenchmarkMemList->Items->Add(IntToStr(1 << i) + " MB"); BenchmarkMemList->ItemIndex = 6; @@ -86,6 +103,7 @@ __fastcall TConfigurationDlg::TConfigurationDlg(TComponent* Owner) TRLCaption(this); TRLCaption(GeneralSheet); TRLCaption(UiStyleLbl); + TRLCaption(AppIconLbl); TRLCaption(ChangeFontLbl); TRLCaption(SelectFontBtn); TRLCaption(ShowSysTrayIconConstCheck); @@ -113,7 +131,6 @@ __fastcall TConfigurationDlg::TConfigurationDlg(TComponent* Owner) TRLCaption(HotKeyView->Columns->Items[0]); TRLCaption(HotKeyView->Columns->Items[1]); - int i; for (i = 0; i < HotKeyActionsList->Items->Count; i++) HotKeyActionsList->Items->Strings[i] = TRL(HotKeyActionsList->Items->Strings[i]); @@ -130,6 +147,8 @@ __fastcall TConfigurationDlg::TConfigurationDlg(TComponent* Owner) TRLCaption(LanguageSheet); TRLCaption(SelectLanguageLbl); + TRLCaption(InstallLanguageBtn); + TRLCaption(RemoveLanguageBtn); TRLCaption(DatabaseSheet); TRLCaption(ClearClipCloseLockCheck); @@ -177,6 +196,7 @@ void __fastcall TConfigurationDlg::SaveConfig(void) void __fastcall TConfigurationDlg::GetOptions(Configuration& config) { config.UiStyleName = UiStylesList->Text; + config.AppIconName = AppIconList->Text; config.GUIFontString = FontToString(FontDlg->Font); config.AutoClearClip = AutoClearClipCheck->Checked; config.AutoClearClipTime = AutoClearClipTimeSpinBtn->Position; @@ -197,7 +217,7 @@ void __fastcall TConfigurationDlg::GetOptions(Configuration& config) config.FileEncoding = CharacterEncoding(CharEncodingGroup->ItemIndex); config.FileNewlineChar = NewlineChar(NewlineCharGroup->ItemIndex); config.HotKeys = m_hotKeys; - config.LanguageIndex = LanguageList->ItemIndex; + config.Language = g_languages.at(LanguageList->ItemIndex); //config.Database.ClearClipMinimize = ClearClipMinimizeCheck->Checked; config.Database.ClearClipCloseLock = ClearClipCloseLockCheck->Checked; config.Database.LockMinimize = LockMinimizeCheck->Checked; @@ -229,6 +249,7 @@ void __fastcall TConfigurationDlg::SetOptions(const Configuration& config) if (nIndex < 0) nIndex = UiStylesList->Items->IndexOf("Windows"); UiStylesList->ItemIndex = nIndex; + AppIconList->ItemIndex = std::max(0, AppIconList->Items->IndexOf(config.AppIconName)); StringToFont(config.GUIFontString, FontDlg->Font); ShowFontSample(FontDlg->Font); RandomPoolCipherList->ItemIndex = config.RandomPoolCipher; @@ -252,7 +273,8 @@ void __fastcall TConfigurationDlg::SetOptions(const Configuration& config) UpdateCheckGroup->ItemIndex = config.AutoCheckUpdates; CharEncodingGroup->ItemIndex = static_cast(config.FileEncoding); NewlineCharGroup->ItemIndex = static_cast(config.FileNewlineChar); - LanguageList->ItemIndex = config.LanguageIndex; + auto it = std::find(g_languages.begin(), g_languages.end(), config.Language.Code); + LanguageList->ItemIndex = it != g_languages.end() ? it - g_languages.begin() : 0; LanguageListSelect(this); m_hotKeys = config.HotKeys; UpdateHotKeyList(); @@ -279,11 +301,10 @@ void __fastcall TConfigurationDlg::SetOptions(const Configuration& config) DefaultAutotypeSeqBox->Text = config.Database.DefaultAutotypeSequence; } //--------------------------------------------------------------------------- -void __fastcall TConfigurationDlg::SetLanguageList( - const std::vector& languages) +void __fastcall TConfigurationDlg::LoadLanguages(void) { - m_langList = languages; - for (const auto& entry : m_langList) + LanguageList->Clear(); + for (const auto& entry : g_languages) { LanguageList->Items->Add(FormatW("%1 (v%2)", { entry.Name, entry.Version })); @@ -468,9 +489,9 @@ void __fastcall TConfigurationDlg::BenchmarkBtnClick(TObject *Sender) rp.SetCipher(static_cast(i)); Stopwatch clock; rp.GetData(buf.get(), lBufSize); - double rate = lDataSizeMB / clock.ElapsedSeconds(); - sResult += "\n" + FormatW("%1: %2 MB/s", { RANDOM_POOL_CIPHER_NAMES[i], - FormatFloat("0.00", rate) }); + long double rate = lDataSizeMB / clock.ElapsedSeconds(); + sResult += "\n" + Format("%s: %.2f MB/s", ARRAYOFCONST(( + RANDOM_POOL_CIPHER_NAMES[i], rate))); } Screen->Cursor = crDefault; MsgBox(TRLFormat("Benchmark results (data size: %1 MB):", @@ -480,8 +501,8 @@ void __fastcall TConfigurationDlg::BenchmarkBtnClick(TObject *Sender) void __fastcall TConfigurationDlg::ConvertLangFileBtnClick(TObject *Sender) { int nIndex = LanguageList->ItemIndex; - if (nIndex > 0 && nIndex < m_langList.size()) { - const auto& entry = m_langList[nIndex]; + if (nIndex > 0 && nIndex < g_languages.size()) { + const auto& entry = g_languages[nIndex]; //if (SameText(ExtractFileExt(entry.FileName), ".lng")) { TopMostManager::GetInstance().NormalizeTopMosts(this); bool blSuccess = SaveDlg->Execute(); @@ -505,11 +526,12 @@ void __fastcall TConfigurationDlg::LanguageListSelect(TObject *Sender) { bool blEnabled = false; int nIndex = LanguageList->ItemIndex; - if (nIndex > 0 && nIndex < m_langList.size()) { - const auto& entry = m_langList[nIndex]; + if (nIndex > 0 && nIndex < g_languages.size()) { + const auto& entry = g_languages[nIndex]; blEnabled = SameText(ExtractFileExt(entry.FileName), ".lng"); } ConvertLangFileBtn->Enabled = blEnabled; + RemoveLanguageBtn->Enabled = nIndex > 0; } //--------------------------------------------------------------------------- void __fastcall TConfigurationDlg::SelectFontMenu_RestoreDefaultClick(TObject *Sender) @@ -527,4 +549,84 @@ void __fastcall TConfigurationDlg::LoadProfileStartupCheckClick(TObject *Sender) LoadProfileBox->Enabled = LoadProfileStartupCheck->Checked; } //--------------------------------------------------------------------------- +void __fastcall TConfigurationDlg::InstallLanguageBtnClick(TObject *Sender) +{ + TopMostManager::GetInstance().NormalizeTopMosts(this); + bool blSuccess = OpenDlg->Execute(); + TopMostManager::GetInstance().RestoreTopMosts(this); + if (!blSuccess) return; + + WString sMsg; + try { + WString sSrc = OpenDlg->FileName; + LanguageSupport ls(sSrc, true); + if (std::find(g_languages.begin(), g_languages.end(), ls.LanguageCode) + != g_languages.end()) + { + throw Exception(TRL("Language already installed")); + } + + const auto destinations = { g_sAppDataPath, g_sExePath }; + WString sDest; + for (const auto& sPath : destinations) { + if (sPath.IsEmpty()) continue; + sDest = sPath + ExtractFileName(sSrc); + if (FileExists(sDest)) { + throw Exception(TRLFormat("Language file already exists:\n\"%1\"", { sDest })); + } + if (CopyFile(sSrc.c_str(), sDest.c_str(), true)) { + LanguageEntry e; + e.FileName = sSrc; + e.Code = ls.LanguageCode; + e.Name = ls.LanguageName; + e.Version = ls.LanguageVersion; + g_languages.push_back(e); + int nIndex = LanguageList->ItemIndex; + LoadLanguages(); + LanguageList->ItemIndex = nIndex; + MsgBox(TRLFormat("Language \"%1\" installed successfully.", { ls.LanguageName }), + MB_ICONINFORMATION); + return; + } + break; + } + + sMsg = TRLFormat("Could not copy file \"%1\" to\n\"%2\"", { sSrc, sDest }); + } + catch (Exception& e) { + sMsg = e.Message; + } + + MsgBox(TRLFormat("Could not install language:\n%1.", { sMsg }), MB_ICONERROR); +} +//--------------------------------------------------------------------------- +void __fastcall TConfigurationDlg::RemoveLanguageBtnClick(TObject *Sender) +{ + int nIndex = LanguageList->ItemIndex; + if (nIndex > 0 && nIndex < g_languages.size()) { + auto e = g_languages[nIndex]; + if (MsgBox(TRLFormat("Are you sure you want to remove\n\"%1\"?", { e.Name }), + MB_ICONQUESTION + MB_YESNO + MB_DEFBUTTON2) == IDYES) + { + if (DeleteFile(e.FileName.c_str())) { + g_languages.erase(g_languages.begin() + nIndex); + LoadLanguages(); + LanguageList->ItemIndex = 0; + MsgBox(TRLFormat("Language \"%1\" successfully removed.", { e.Name }), + MB_ICONINFORMATION); + } + else { + MsgBox(TRLFormat("Could not delete file\n\"%1\".", { e.FileName }), + MB_ICONERROR); + } + } + } +} +//--------------------------------------------------------------------------- +void __fastcall TConfigurationDlg::SetDonorUI(void) +{ + AppIconLbl->Enabled = g_donorInfo.Valid == DONOR_KEY_VALID; + AppIconList->Enabled = g_donorInfo.Valid == DONOR_KEY_VALID; +} +//--------------------------------------------------------------------------- diff --git a/src/main/Configuration.dfm b/src/main/Configuration.dfm index f21c26f..366e75e 100644 --- a/src/main/Configuration.dfm +++ b/src/main/Configuration.dfm @@ -3,8 +3,8 @@ object ConfigurationDlg: TConfigurationDlg Top = 187 BorderIcons = [biSystemMenu, biMaximize] Caption = 'Configuration' - ClientHeight = 519 - ClientWidth = 551 + ClientHeight = 517 + ClientWidth = 552 Color = clBtnFace Constraints.MinHeight = 456 Constraints.MinWidth = 443 @@ -84,6 +84,17 @@ object ConfigurationDlg: TConfigurationDlg Margins.Bottom = 4 Caption = 'User interface style:' end + object AppIconLbl: TLabel + Left = 280 + Top = 20 + Width = 99 + Height = 17 + Margins.Left = 4 + Margins.Top = 4 + Margins.Right = 4 + Margins.Bottom = 4 + Caption = 'Application icon:' + end object SelectFontBtn: TButton Left = 10 Top = 114 @@ -96,7 +107,7 @@ object ConfigurationDlg: TConfigurationDlg Caption = 'Select font' DropDownMenu = SelectFontMenu Style = bsSplitButton - TabOrder = 1 + TabOrder = 2 OnClick = SelectFontBtnClick end object ShowSysTrayIconConstCheck: TCheckBox @@ -109,7 +120,7 @@ object ConfigurationDlg: TConfigurationDlg Margins.Right = 4 Margins.Bottom = 4 Caption = 'Show system tray icon constantly' - TabOrder = 2 + TabOrder = 3 end object MinimizeToSysTrayCheck: TCheckBox Left = 10 @@ -121,7 +132,7 @@ object ConfigurationDlg: TConfigurationDlg Margins.Right = 4 Margins.Bottom = 4 Caption = 'Minimize program to system tray' - TabOrder = 3 + TabOrder = 4 end object AutotypeDelayBox: TEdit Left = 326 @@ -133,7 +144,7 @@ object ConfigurationDlg: TConfigurationDlg Margins.Right = 4 Margins.Bottom = 4 AutoSelect = False - TabOrder = 5 + TabOrder = 6 Text = '0' end object AutotypeDelaySpinBtn: TUpDown @@ -147,7 +158,7 @@ object ConfigurationDlg: TConfigurationDlg Margins.Bottom = 4 Associate = AutotypeDelayBox Max = 1000 - TabOrder = 6 + TabOrder = 7 end object MinimizeAutotypeCheck: TCheckBox Left = 10 @@ -159,7 +170,7 @@ object ConfigurationDlg: TConfigurationDlg Margins.Right = 4 Margins.Bottom = 4 Caption = 'Minimize before performing autotype' - TabOrder = 4 + TabOrder = 5 end object AskBeforeExitCheck: TCheckBox Left = 10 @@ -172,7 +183,7 @@ object ConfigurationDlg: TConfigurationDlg Margins.Bottom = 4 Anchors = [akLeft, akTop, akRight] Caption = 'Ask before exiting application' - TabOrder = 7 + TabOrder = 8 end object LaunchSystemStartupCheck: TCheckBox Left = 10 @@ -184,19 +195,18 @@ object ConfigurationDlg: TConfigurationDlg Margins.Right = 4 Margins.Bottom = 4 Caption = 'Launch application on system startup (for current user)' - TabOrder = 8 + TabOrder = 11 end object UiStylesList: TComboBox Left = 10 Top = 44 - Width = 492 + Width = 241 Height = 25 Margins.Left = 4 Margins.Top = 4 Margins.Right = 4 Margins.Bottom = 4 Style = csDropDownList - Anchors = [akLeft, akTop, akRight] Sorted = True TabOrder = 0 end @@ -226,6 +236,20 @@ object ConfigurationDlg: TConfigurationDlg Anchors = [akLeft, akTop, akRight] TabOrder = 10 end + object AppIconList: TComboBoxEx + Left = 280 + Top = 44 + Width = 222 + Height = 26 + Margins.Left = 4 + Margins.Top = 4 + Margins.Right = 4 + Margins.Bottom = 4 + ItemsEx = <> + Style = csExDropDownList + TabOrder = 1 + Images = AppIconImageList + end end object SecuritySheet: TTabSheet Margins.Left = 4 @@ -652,8 +676,8 @@ object ConfigurationDlg: TConfigurationDlg OnSelect = LanguageListSelect end object ConvertLangFileBtn: TButton - Left = 10 - Top = 78 + Left = 220 + Top = 77 Width = 248 Height = 31 Margins.Left = 4 @@ -662,9 +686,35 @@ object ConfigurationDlg: TConfigurationDlg Margins.Bottom = 4 Caption = 'Convert to new PO file format...' Enabled = False - TabOrder = 1 + TabOrder = 3 OnClick = ConvertLangFileBtnClick end + object InstallLanguageBtn: TButton + Left = 10 + Top = 77 + Width = 100 + Height = 31 + Margins.Left = 4 + Margins.Top = 4 + Margins.Right = 4 + Margins.Bottom = 4 + Caption = 'Install...' + TabOrder = 1 + OnClick = InstallLanguageBtnClick + end + object RemoveLanguageBtn: TButton + Left = 118 + Top = 77 + Width = 94 + Height = 31 + Margins.Left = 4 + Margins.Top = 4 + Margins.Right = 4 + Margins.Bottom = 4 + Caption = 'Remove' + TabOrder = 2 + OnClick = RemoveLanguageBtnClick + end end object DatabaseSheet: TTabSheet Margins.Left = 4 @@ -998,11 +1048,21 @@ object ConfigurationDlg: TConfigurationDlg Top = 452 end object SelectFontMenu: TPopupMenu - Left = 31 - Top = 408 + Left = 29 + Top = 470 object SelectFontMenu_RestoreDefault: TMenuItem Caption = 'Restore Default' OnClick = SelectFontMenu_RestoreDefaultClick end end + object AppIconImageList: TImageList + Left = 489 + Top = 405 + end + object OpenDlg: TOpenDialog + Filter = 'Language files|*.po|All files|*.*' + Title = 'Select language file to install' + Left = 31 + Top = 163 + end end diff --git a/src/main/Configuration.h b/src/main/Configuration.h index 7adc406..e4f1980 100644 --- a/src/main/Configuration.h +++ b/src/main/Configuration.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include //--------------------------------------------------------------------------- @@ -81,6 +83,8 @@ enum NewlineChar nlcUnix }; +extern const std::vector> AppIconNames; + inline WString NewlineCharToString(NewlineChar c) { return (c == nlcWindows) ? "\r\n" : "\n"; @@ -98,10 +102,19 @@ struct LanguageEntry { WString Name; WString Version; WString FileName; + + LanguageEntry() {} + LanguageEntry(const WString& sCode) : Code(sCode) {} + + bool operator== (const LanguageEntry& other) const + { + return Code == other.Code; + } }; struct Configuration { WString UiStyleName; + WString AppIconName; WString GUIFontString; bool AutoClearClip = true; int AutoClearClipTime = AUTOCLEARCLIPTIME_DEFAULT; @@ -122,7 +135,8 @@ struct Configuration { CharacterEncoding FileEncoding = ceUtf8; NewlineChar FileNewlineChar = nlcWindows; HotKeyList HotKeys; - int LanguageIndex = 0; + LanguageEntry Language; + //int LanguageIndex = 0; struct { //bool ClearClipMinimize = true; bool ClearClipCloseLock = true; @@ -224,6 +238,12 @@ class TConfigurationDlg : public TForm TMenuItem *SelectFontMenu_RestoreDefault; TCheckBox *LoadProfileStartupCheck; TComboBox *LoadProfileBox; + TComboBoxEx *AppIconList; + TLabel *AppIconLbl; + TImageList *AppIconImageList; + TButton *InstallLanguageBtn; + TOpenDialog *OpenDlg; + TButton *RemoveLanguageBtn; void __fastcall SelectFontBtnClick(TObject *Sender); void __fastcall AutoClearClipCheckClick(TObject *Sender); void __fastcall FormShow(TObject *Sender); @@ -243,9 +263,11 @@ class TConfigurationDlg : public TForm void __fastcall LanguageListSelect(TObject *Sender); void __fastcall SelectFontMenu_RestoreDefaultClick(TObject *Sender); void __fastcall LoadProfileStartupCheckClick(TObject *Sender); + void __fastcall InstallLanguageBtnClick(TObject *Sender); + void __fastcall RemoveLanguageBtnClick(TObject *Sender); private: // User declarations HotKeyList m_hotKeys; - std::vector m_langList; + //std::vector m_langList; void __fastcall ShowFontSample(TFont* pFont); void __fastcall UpdateHotKeyList(void); void __fastcall UpdateProfileList(void); @@ -255,7 +277,8 @@ class TConfigurationDlg : public TForm void __fastcall SaveConfig(void); void __fastcall GetOptions(Configuration& config); void __fastcall SetOptions(const Configuration& config); - void __fastcall SetLanguageList(const std::vector& languages); + void __fastcall LoadLanguages(void); + void __fastcall SetDonorUI(void); }; //--------------------------------------------------------------------------- extern PACKAGE TConfigurationDlg *ConfigurationDlg; diff --git a/src/main/Language.cpp b/src/main/Language.cpp index 959a81e..43e1f46 100644 --- a/src/main/Language.cpp +++ b/src/main/Language.cpp @@ -173,7 +173,7 @@ LanguageSupport::LanguageSupport(const WString& sFileName, wchar_t wszParsed[MSG_BUF_SIZE]; int nMsgLen, nState = 0; - const char* pszUnknown = "(Unknown)"; + const char UNKNOWN[] = "(Unknown)"; if (SameText(ExtractFileExt(sFileName), ".po")) { m_format = FileFormat::PO; @@ -238,7 +238,7 @@ LanguageSupport::LanguageSupport(const WString& sFileName, break; // prepare regex search - std::wregex re(L"([a-zA-Z-]+)\\s*:\\s*(.+)"); + std::wregex re(L"([a-zA-Z-]+) *: *(.*)$"); std::wsregex_iterator keyValIt(sMsgStr.begin(), sMsgStr.end(), re), keyValEnd; @@ -306,7 +306,7 @@ LanguageSupport::LanguageSupport(const WString& sFileName, if (it != keyVal.end()) m_sTranslatorName = WString(it->second.c_str()); else - m_sTranslatorName = pszUnknown; + m_sTranslatorName = UNKNOWN; // help file name it = keyVal.find(L"X-PasswordTech-Manual"); @@ -481,7 +481,7 @@ LanguageSupport::LanguageSupport(const WString& sFileName, } if (m_sTranslatorName.IsEmpty()) - m_sTranslatorName = pszUnknown; + m_sTranslatorName = UNKNOWN; } if (!blLoadHeaderOnly && m_transl.empty()) diff --git a/src/main/MPPasswGen.dfm b/src/main/MPPasswGen.dfm index 2f83fdb..ec740cd 100644 --- a/src/main/MPPasswGen.dfm +++ b/src/main/MPPasswGen.dfm @@ -464,7 +464,7 @@ object MPPasswGenForm: TMPPasswGenForm Left = 10 Top = 170 Width = 272 - Height = 25 + Height = 23 Margins.Left = 4 Margins.Top = 4 Margins.Right = 4 @@ -472,8 +472,8 @@ object MPPasswGenForm: TMPPasswGenForm Anchors = [akLeft, akTop, akRight] Font.Charset = ANSI_CHARSET Font.Color = clWindowText - Font.Height = -14 - Font.Name = 'Tahoma' + Font.Height = -13 + Font.Name = 'Consolas' Font.Style = [] ParentFont = False PopupMenu = PasswBoxMenu diff --git a/src/main/Main.cpp b/src/main/Main.cpp index 2ecf931..9a3384c 100644 --- a/src/main/Main.cpp +++ b/src/main/Main.cpp @@ -82,6 +82,7 @@ TMainForm *MainForm; CmdLineOptions g_cmdLineOptions; +DonorInfo g_donorInfo; std::unique_ptr g_pIni; bool g_blFakeIniFile = false; std::vector> g_profileList; @@ -96,6 +97,7 @@ Configuration g_config; AnsiString g_asDonorInfo; WString g_sNewline; TerminateAction g_terminateAction = TerminateAction::None; +std::vector g_languages; extern HANDLE g_hAppMutex; @@ -464,6 +466,22 @@ __fastcall TMainForm::TMainForm(TComponent* Owner) SaveDlg->Filter = OpenDlg->Filter; + switch (g_donorInfo.Valid) { + case DONOR_KEY_VALID: + break; + case DONOR_KEY_EXPIRED: + DelayStartupError(TRL("Your donor key has expired: The maximum\n" + "number of updates has been reached.")); + break; + case DONOR_KEY_INVALID: + DelayStartupError(TRL("Donor key is invalid.")); + break; + default: // empty key (-1) + break; + } + + SetDonorUI(); + // load configuration from INI file LoadConfig(); } @@ -562,10 +580,11 @@ void __fastcall TMainForm::StartupAction(void) PasswOptionsDlg->SetOptions(m_passwOptions); SetAdvancedBtnCaption(); - ConfigurationDlg->SetLanguageList(m_languages); + ConfigurationDlg->LoadLanguages(); ConfigurationDlg->SetOptions(g_config); AboutForm->SetDonorUI(); + ConfigurationDlg->SetDonorUI(); } } //--------------------------------------------------------------------------- @@ -586,7 +605,7 @@ void __fastcall TMainForm::FormActivate(TObject *Sender) m_sStartupErrors = WString(); } - if (m_asDonorKey.IsEmpty() && + if (g_donorInfo.Valid != DONOR_KEY_VALID && (g_pIni->ReadString(CONFIG_ID, "LastVersion", "") != PROGRAM_VERSION || fprng_rand(15) == 0)) MsgBox(TRLFormat("If you like %1, please consider\nmaking a donation. Thank you!", @@ -660,76 +679,68 @@ void __fastcall TMainForm::DelayStartupError(const WString& sMsg) //--------------------------------------------------------------------------- void __fastcall TMainForm::LoadLangConfig(void) { - // which language do we have to set? - WString sLangStr = g_pIni->ReadString(CONFIG_ID, "Language", ""); - if (sLangStr.IsEmpty()) - sLangStr = LANGUAGE_DEFAULT_CODE; - - bool blDefaultLang = sLangStr == LANGUAGE_DEFAULT_CODE || - SameText(sLangStr, WString(LANGUAGE_DEFAULT_NAME)); + LanguageEntry defaultLang; + defaultLang.Code = LANGUAGE_DEFAULT_CODE; + defaultLang.Name = LANGUAGE_DEFAULT_NAME; + defaultLang.Version = PROGRAM_VERSION; + g_languages.push_back(defaultLang); - LanguageEntry e; - e.Code = LANGUAGE_DEFAULT_CODE; - e.Name = LANGUAGE_DEFAULT_NAME; - e.Version = PROGRAM_VERSION; - m_languages.push_back(e); + // look for language files in the program folder and in the "app data" folder + const auto searchFolders = { g_sExePath, g_sAppDataPath }; + for (const auto& sFolder : searchFolders) { + if (sFolder.IsEmpty()) continue; - // look for language files in the program directory - WString sFindFile = g_sExePath + "*.*"; - TSearchRec srw; + TSearchRec srw; - WString sLangFileName; - int nLangIndex = 0; - - if (FindFirst(sFindFile, faAnyFile, srw) == 0) { - do { - WString sExt = ExtractFileExt(srw.Name); - if (!SameText(sExt, ".lng") && !SameText(sExt, ".po")) - continue; - - WString sFileName = g_sExePath + ExtractFileName(srw.Name); + if (FindFirst(sFolder + "*", faAnyFile, srw) == 0) { + do { + WString sExt = ExtractFileExt(srw.Name); + if (!SameText(sExt, ".lng") && !SameText(sExt, ".po")) + continue; - try { - LanguageSupport ls(sFileName, true); + WString sFileName = sFolder + ExtractFileName(srw.Name); - if (std::find_if(m_languages.begin(), m_languages.end(), - [&ls](const LanguageEntry& e) { return e.Code == ls.LanguageCode; }) - != m_languages.end()) - continue; + try { + LanguageSupport ls(sFileName, true); - LanguageEntry e; - e.FileName = sFileName; - e.Code = ls.LanguageCode; - e.Name = ls.LanguageName; - e.Version = ls.LanguageVersion; + if (std::find(g_languages.begin(), g_languages.end(), ls.LanguageCode) + != g_languages.end()) + continue; - m_languages.push_back(e); + LanguageEntry e; + e.FileName = sFileName; + e.Code = ls.LanguageCode; + e.Name = ls.LanguageName; + e.Version = ls.LanguageVersion; - if (!blDefaultLang && nLangIndex == 0 && e.Code == sLangStr) - { - sLangFileName = sFileName; - nLangIndex = m_languages.size() - 1; + g_languages.push_back(e); + } + catch (ELanguageError& e) { + DelayStartupError(srw.Name + ": " + e.Message + "."); + } + catch (...) { } } - catch (ELanguageError& e) { - DelayStartupError(srw.Name + ": " + e.Message + "."); - } - catch (...) { - } - } - while (FindNext(srw) == 0 && m_languages.size() < LANGUAGE_MAX_ITEMS); + while (FindNext(srw) == 0 && g_languages.size() < LANGUAGE_MAX_ITEMS); - FindClose(srw); + FindClose(srw); + } } - // nothing found? - if (!blDefaultLang && nLangIndex == 0) - DelayStartupError(FormatW("Could not find language \"%1\".", - { sLangStr })); + // which language do we have to set? + WString sSetLang = g_pIni->ReadString(CONFIG_ID, "Language", ""); + if (sSetLang.IsEmpty()) + sSetLang = LANGUAGE_DEFAULT_CODE; + + auto langIt = std::find(g_languages.begin(), g_languages.end(), sSetLang); + if (langIt == g_languages.end()) { + DelayStartupError(FormatW("Could not find language \"%1\".", { sSetLang })); + langIt = g_languages.begin(); + } // change language if necessary - if (nLangIndex != 0 && !ChangeLanguage(sLangFileName)) - nLangIndex = 0; + if (langIt != g_languages.begin() && !ChangeLanguage(langIt->FileName)) + langIt = g_languages.begin(); m_sHelpFileName = g_sExePath; if (g_pLangSupp && !g_pLangSupp->HelpFileName.IsEmpty()) @@ -737,7 +748,7 @@ void __fastcall TMainForm::LoadLangConfig(void) else m_sHelpFileName += PROGRAM_HELPFILE; - g_config.LanguageIndex = nLangIndex; + g_config.Language = *langIt; //MainMenu_Options_Language->Items[nLangIndex]->Checked = true; } //--------------------------------------------------------------------------- @@ -820,40 +831,20 @@ void __fastcall TMainForm::WriteRandSeedFile(bool blShowError) } } //--------------------------------------------------------------------------- -void __fastcall TMainForm::SetDonorUI(int nDonorType) +void __fastcall TMainForm::SetDonorUI(void) { WString sInfo = WString(PROGRAM_NAME) + " " + WString(PROGRAM_VERSION); - if (nDonorType < 0) + if (g_donorInfo.Type < 0) sInfo += " (Community)"; else - sInfo += ((nDonorType == DONOR_TYPE_PRO) ? " (DONOR PRO)" : " (DONOR)"); + sInfo += ((g_donorInfo.Type == DONOR_TYPE_PRO) ? " (DONOR PRO)" : " (DONOR)"); StatusBar->Panels->Items[0]->Text = sInfo; } //--------------------------------------------------------------------------- void __fastcall TMainForm::LoadConfig(void) { //AnsiString asLastVersion = g_pIni->ReadString(CONFIG_ID, "LastVersion", ""); - AnsiString asDonorKey = g_pIni->ReadString(CONFIG_ID, "DonorKey", ""); - if (!asDonorKey.IsEmpty()) { - AnsiString asDonorId; - int nDonorType; - switch (CheckDonorKey(asDonorKey, &asDonorId, &nDonorType)) { - case DONOR_KEY_VALID: - m_asDonorKey = asDonorKey.Trim(); - g_asDonorInfo = asDonorId; - SetDonorUI(nDonorType); - break; - case DONOR_KEY_EXPIRED: - DelayStartupError(TRL("Your donor key has expired: The maximum\n" - "number of updates has been reached.")); - break; - default: - DelayStartupError(TRL("Donor key is invalid.")); - } - } - - if (m_asDonorKey.IsEmpty()) - SetDonorUI(-1); + //AnsiString asDonorKey = g_pIni->ReadString(CONFIG_ID, "DonorKey", ""); int nTop = g_pIni->ReadInteger(CONFIG_ID, "WindowTop", INT_MAX); int nLeft = g_pIni->ReadInteger(CONFIG_ID, "WindowLeft", INT_MAX); @@ -1221,12 +1212,13 @@ bool __fastcall TMainForm::SaveConfig(void) g_pIni->WriteString(CONFIG_ID, "LastVersion", PROGRAM_VERSION); g_pIni->WriteString(CONFIG_ID, "Language", - m_languages[g_config.LanguageIndex].Code); + g_config.Language.Code); if (m_lastUpdateCheck != TDateTime()) g_pIni->WriteDate(CONFIG_ID, "LastUpdateCheck", m_lastUpdateCheck); - g_pIni->WriteString(CONFIG_ID, "DonorKey", m_asDonorKey); + g_pIni->WriteString(CONFIG_ID, "DonorKey", g_donorInfo.Key); g_pIni->WriteString(CONFIG_ID, "GUIStyle", g_config.UiStyleName); g_pIni->WriteString(CONFIG_ID, "GUIFont", g_config.GUIFontString); + g_pIni->WriteString(CONFIG_ID, "AppIcon", g_config.AppIconName); g_pIni->WriteInteger(CONFIG_ID, "WindowTop", Top); g_pIni->WriteInteger(CONFIG_ID, "WindowLeft", Left); g_pIni->WriteInteger(CONFIG_ID, "WindowHeight", Height); @@ -1683,8 +1675,8 @@ void __fastcall TMainForm::ShowPasswInfo(int nPasswLen, bool __fastcall TMainForm::ApplyConfig(const Configuration& config) { bool blLangChanged = false; - if (config.LanguageIndex != g_config.LanguageIndex) { - const WString& sLangVersion = m_languages[config.LanguageIndex].Version; + if (config.Language.Code != g_config.Language.Code) { + const WString& sLangVersion = config.Language.Version; if (CompareVersionNumbers(sLangVersion, PROGRAM_LANGVER_MIN) < 0) { if (MsgBox(TRLFormat("The version of this language (%1) is not\ncompatible " @@ -1767,6 +1759,16 @@ bool __fastcall TMainForm::ApplyConfig(const Configuration& config) if (!config.AutoClearClip) m_nAutoClearClipCnt = 0; + if (!SameText(config.AppIconName, g_config.AppIconName)) { + auto it = std::find_if(AppIconNames.begin(), AppIconNames.end(), + [&config](const std::pair& p) { return p.first == config.AppIconName; }); + if (it != AppIconNames.end()) { + Application->Icon->LoadFromResourceName(reinterpret_cast( + HInstance), it->second); + TrayIcon->Icon = Application->Icon; + } + } + /*if (!blLangChanged && !TStyleManager::TrySetStyle(config.UiStyleName, false)) { MsgBox(TRLFormat("Could not apply user interface style\n\"%s\".", config.UiStyleName.c_str()), MB_ICONERROR); @@ -2326,6 +2328,8 @@ void __fastcall TMainForm::GeneratePassw(GeneratePasswDest dest, nPasswFlags |= PASSW_FLAG_EACHCHARONLYONCE; if (nCharSetSize >= 128 && nCharsLen >= 128) nPasswFlags |= PASSW_FLAG_CHECKDUPLICATESBYSET; + if (nFlags & PASSWOPTION_REMOVEWHITESPACE) + nPasswFlags |= PASSW_FLAG_REMOVEWHITESPACE; for (int nI = 0; nI < PASSWGEN_NUMINCLUDECHARSETS; nI++) { if (nFlags & (PASSWOPTION_INCLUDEUPPERCASE << nI)) nPasswFlags |= PASSW_FLAG_INCLUDEUPPERCASE << nI; @@ -2369,6 +2373,8 @@ void __fastcall TMainForm::GeneratePassw(GeneratePasswDest dest, if (!sFormatPassw.empty()) { if (nFlags & PASSWOPTION_EXCLUDEREPCHARS) nFormatFlags |= PASSFORMAT_FLAG_EXCLUDEREPCHARS; + if (nFlags & PASSWOPTION_REMOVEWHITESPACE) + nFormatFlags |= PASSFORMAT_FLAG_REMOVEWHITESPACE; sFormatted.New(PASSWFORMAT_MAX_CHARS + 1); sFormatted.SetClearMark(0); @@ -2419,6 +2425,12 @@ void __fastcall TMainForm::GeneratePassw(GeneratePasswDest dest, bool blKeepPrevPassw = false; bool blCheckEachPassw = qNumOfPassw > 1 && (nFlags & PASSWOPTION_CHECKEACHPASSW); + bool blVariablePasswLen = nCharsLen != 0 && + (nPasswFlags & PASSW_FLAG_REMOVEWHITESPACE) && + (m_passwGen.CustomCharSetType == cstStandard || + m_passwGen.CustomCharSetType == cstStandardWithFreq) && + m_passwGen.CustomCharSetW32.find_first_of( + WCharToW32String(L" \t")) != w32string::npos; std::unordered_set uniquePasswList; std::unique_ptr pFile; @@ -2446,7 +2458,7 @@ void __fastcall TMainForm::GeneratePassw(GeneratePasswDest dest, nGenCharsLen = m_passwGen.GetPhoneticPassw(sChars, nCharsLen, nPasswFlags); } - if (blFirstGen) { + if (blFirstGen || blVariablePasswLen) { if ((m_passwGen.CustomCharSetType == cstStandard || m_passwGen.CustomCharSetType == cstStandardWithFreq) && m_passwOptions.Flags & PASSWOPTION_EACHCHARONLYONCE) @@ -2478,7 +2490,7 @@ void __fastcall TMainForm::GeneratePassw(GeneratePasswDest dest, } } - if (blFirstGen) { + if (blFirstGen || blVariablePasswLen) { if (m_passwOptions.Flags & PASSWOPTION_EACHWORDONLYONCE) dBasePasswSec += m_passwGen.CalcPermSetEntropy( m_passwGen.WordListSize, nNumOfWords); @@ -2727,12 +2739,8 @@ void __fastcall TMainForm::GeneratePassw(GeneratePasswDest dest, case gpdFileList: - if (!pFile->WriteString(pwszPassw, nPasswLenWChars)) - OutOfDiskSpaceError(); - - if (!pFile->WriteString(sPasswAppendix.c_str(), sPasswAppendix.Length())) - OutOfDiskSpaceError(); - + pFile->WriteString(pwszPassw, nPasswLenWChars); + pFile->WriteString(sPasswAppendix.c_str(), sPasswAppendix.Length()); break; case gpdMsgBox: @@ -4091,8 +4099,7 @@ void __fastcall TMainForm::PasswBoxMenu_SaveAsFileClick(TObject *Sender) std::unique_ptr pFile(new TStringFileStreamW( sFileName, fmCreate, g_config.FileEncoding, true, PASSW_MAX_BYTES)); - if (!pFile->WriteString(sPassw, sPassw.StrLen())) - OutOfDiskSpaceError(); + pFile->WriteString(sPassw, sPassw.StrLen()); blSuccess = true; sMsg = TRLFormat("File \"%1\" successfully created.", @@ -4135,32 +4142,37 @@ void __fastcall TMainForm::MainMenu_Options_ClearPasswCacheClick(TObject *Sender //--------------------------------------------------------------------------- void __fastcall TMainForm::MainMenu_Help_EnterDonorKeyClick(TObject *Sender) { - WString sInput = m_asDonorKey; + WString sInput = g_donorInfo.Key; BeforeDisplayDlg(); if (InputQuery(TRL("Donor Key"), TRL("Enter donor key:"), sInput)) { - AnsiString asId; - int nType; - switch (CheckDonorKey(sInput, &asId, &nType)) { + int nStatus, nType; + AnsiString asDonorId; + std::tie(nStatus, nType, asDonorId) = CheckDonorKey(sInput); + switch (nStatus) { case DONOR_KEY_VALID: { - m_asDonorKey = sInput.Trim(); - g_asDonorInfo = asId.Trim(); + g_donorInfo.Key = sInput.Trim(); + g_donorInfo.Id = asDonorId; + g_donorInfo.Valid = nStatus; + g_donorInfo.Type = nType; WString sMsg = TRLFormat("Your Donor ID is: %1\n" "Supported number of updates: %2", - { WString(asId), + { WString(asDonorId), nType == DONOR_TYPE_STD ? IntToStr(DONOR_STD_NUM_UPDATES) : TRL("Unlimited") }); if (MsgBox(TRL("Thank you for your support!") + "\n\n" + sMsg + "\n\n" + TRL("Copy this information to the clipboard?"), MB_ICONQUESTION + MB_YESNO) == IDYES) Clipboard()->AsText = sMsg; - SetDonorUI(nType); + SetDonorUI(); AboutForm->SetDonorUI(); + ConfigurationDlg->SetDonorUI(); break; } case DONOR_KEY_EXPIRED: MsgBox(TRL("Donor key has expired."), MB_ICONERROR); break; + case DONOR_KEY_INVALID: default: MsgBox(TRL("Donor key is invalid."), MB_ICONERROR); } diff --git a/src/main/Main.dfm b/src/main/Main.dfm index b8350e3..5a5afe8 100644 Binary files a/src/main/Main.dfm and b/src/main/Main.dfm differ diff --git a/src/main/Main.h b/src/main/Main.h index 5acbdaa..d77c637 100644 --- a/src/main/Main.h +++ b/src/main/Main.h @@ -70,6 +70,13 @@ struct CmdLineOptions { bool ConfigReadOnly = false; }; +struct DonorInfo { + AnsiString Key; + AnsiString Id; + int Valid = -1; + int Type = -1; +}; + struct PWGenProfile { WString ProfileName; bool IncludeChars; @@ -102,6 +109,7 @@ const int APPSTATE_AUTOTYPE = 0x04; extern CmdLineOptions g_cmdLineOptions; +extern DonorInfo g_donorInfo; extern std::unique_ptr g_pIni; extern bool g_blFakeIniFile; extern std::vector> g_profileList; @@ -115,6 +123,7 @@ extern int g_nDisplayDlg; extern Configuration g_config; extern AnsiString g_asDonorInfo; extern WString g_sNewline; +extern std::vector g_languages; enum class TerminateAction { None, RestartProgram, SystemShutdown @@ -452,13 +461,13 @@ class TMainForm : public TForm IDropTarget* m_pPasswBoxDropTarget; TUpdateCheckThread* m_pUpdCheckThread; //std::atomic m_blUpdCheckThreadRunning; - std::vector m_languages; + //std::vector m_languages; std::vector m_hotKeys; std::unordered_set m_commonPassw; double m_dCommonPasswEntropy; std::unique_ptr m_pScript; TDateTime m_lastUpdateCheck; - AnsiString m_asDonorKey; + //AnsiString m_asDonorKey; void __fastcall DelayStartupError(const WString& sMsg); void __fastcall LoadLangConfig(void); @@ -489,7 +498,7 @@ class TMainForm : public TForm void __fastcall ShowInfoBox(const WString& sInfo); void __fastcall SetAdvancedBtnCaption(void); void __fastcall OnUpdCheckThreadTerminate(TObject* Sender); - void __fastcall SetDonorUI(int nDonorType); + void __fastcall SetDonorUI(void); void __fastcall RestoreAction(void); void __fastcall OnSetSensitiveClipboardData(void); void __fastcall OnQueryEndSession(TWMQueryEndSession& msg); diff --git a/src/main/PasswList.cpp b/src/main/PasswList.cpp index 657ef24..0ce127f 100644 --- a/src/main/PasswList.cpp +++ b/src/main/PasswList.cpp @@ -145,8 +145,7 @@ void __fastcall TPasswListForm::PasswListMenu_SaveAsFileClick(TObject *Sender) std::unique_ptr pFile(new TStringFileStreamW( sFileName, fmCreate, g_config.FileEncoding, true, PASSW_MAX_BYTES)); - if (!pFile->WriteString(sPasswList, sPasswList.StrLen())) - OutOfDiskSpaceError(); + pFile->WriteString(sPasswList, sPasswList.StrLen()); blSuccess = true; sMsg = TRLFormat("File \"%1\" successfully created.", diff --git a/src/main/PasswList.dfm b/src/main/PasswList.dfm index 697398e..2aa7fc2 100644 Binary files a/src/main/PasswList.dfm and b/src/main/PasswList.dfm differ diff --git a/src/main/PasswManager.dfm b/src/main/PasswManager.dfm index b13d8d2..c5381cb 100644 --- a/src/main/PasswManager.dfm +++ b/src/main/PasswManager.dfm @@ -1,8 +1,8 @@ object PasswMngForm: TPasswMngForm Left = 594 Top = 119 - ClientHeight = 669 - ClientWidth = 826 + ClientHeight = 668 + ClientWidth = 830 Color = clBtnFace Constraints.MinHeight = 600 Constraints.MinWidth = 625 @@ -23,7 +23,7 @@ object PasswMngForm: TPasswMngForm Left = 469 Top = 83 Width = 4 - Height = 586 + Height = 585 Margins.Left = 4 Margins.Top = 4 Margins.Right = 4 @@ -36,7 +36,7 @@ object PasswMngForm: TPasswMngForm object Splitter1: TSplitter Left = 0 Top = 79 - Width = 826 + Width = 830 Height = 4 Cursor = crVSplit Margins.Left = 4 @@ -51,7 +51,7 @@ object PasswMngForm: TPasswMngForm Left = 0 Top = 83 Width = 469 - Height = 586 + Height = 585 Margins.Left = 4 Margins.Top = 4 Margins.Right = 4 @@ -73,13 +73,13 @@ object PasswMngForm: TPasswMngForm OnKeyDown = DbViewKeyDown OnMouseMove = DbViewMouseMove OnSelectItem = DbViewSelectItem - ExplicitHeight = 585 + ExplicitHeight = 584 end object EditPanel: TPanel Left = 473 Top = 83 - Width = 353 - Height = 586 + Width = 357 + Height = 585 Margins.Left = 4 Margins.Top = 4 Margins.Right = 4 @@ -89,8 +89,8 @@ object PasswMngForm: TPasswMngForm Enabled = False TabOrder = 1 OnResize = EditPanelResize - ExplicitWidth = 341 - ExplicitHeight = 585 + ExplicitWidth = 351 + ExplicitHeight = 584 object TitleLbl: TLabel Left = 10 Top = 11 @@ -578,8 +578,8 @@ object PasswMngForm: TPasswMngForm AutoSize = False Font.Charset = ANSI_CHARSET Font.Color = clWindowText - Font.Height = -14 - Font.Name = 'Courier New' + Font.Height = -13 + Font.Name = 'Consolas' Font.Style = [] ParentFont = False TabOrder = 2 @@ -814,7 +814,7 @@ object PasswMngForm: TPasswMngForm object ToolBar: TToolBar Left = 0 Top = 0 - Width = 826 + Width = 830 Height = 26 Margins.Left = 4 Margins.Top = 4 @@ -826,7 +826,7 @@ object PasswMngForm: TPasswMngForm Images = ImageList16 TabOrder = 2 Transparent = True - ExplicitWidth = 814 + ExplicitWidth = 824 object NewBtn: TToolButton Left = 0 Top = 0 @@ -1012,7 +1012,7 @@ object PasswMngForm: TPasswMngForm object TagView: TListView Left = 0 Top = 26 - Width = 826 + Width = 830 Height = 53 Margins.Left = 4 Margins.Top = 4 @@ -1028,7 +1028,7 @@ object PasswMngForm: TPasswMngForm ViewStyle = vsSmallIcon OnCompare = TagViewCompare OnSelectItem = TagViewSelectItem - ExplicitWidth = 814 + ExplicitWidth = 824 end object MainMenu: TMainMenu Left = 16 diff --git a/src/main/PasswOptions.cpp b/src/main/PasswOptions.cpp index 1ebb12d..73dd49b 100644 --- a/src/main/PasswOptions.cpp +++ b/src/main/PasswOptions.cpp @@ -36,7 +36,7 @@ const WString const int OPTION_INDEX_TO_BIT[PASSWOPTIONS_NUM] = - { 0, 1, 13, 10, 2, 3, 4, 15, 12, 14, 5, 6, 7, 8, 17, 9, 11, 16 }; + { 0, 1, 13, 10, 18, 2, 3, 4, 15, 12, 14, 5, 6, 7, 8, 17, 9, 11, 16 }; template class _bitToOptionIndex { @@ -88,26 +88,28 @@ __fastcall TPasswOptionsDlg::TPasswOptionsDlg(TComponent* Owner) else m_pOptionsList->Assign(pStrList); - pStrList->Strings[BIT_TO_OPTION_INDEX[0]] += " (B8G6I1l|0OQDS5Z2) [1-3]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[1]] += " [1-4]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[2]] += " [2]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[3]] += " [2]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[4]] += " [1,2]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[5]] += " [1,4]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[6]] += " [1,4]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[7]] += " [1,4]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[8]] += " [1,4]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[9]] += " [1]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[10]] += " [1,3]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[11]] += " [1-4]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[12]] += " [2,3]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[13]] += " [1]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[14]] += " [2]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[15]] += " [2]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[16]] += " [1-4]"; - pStrList->Strings[BIT_TO_OPTION_INDEX[17]] += " [1]"; - - for (int i = 0; i < PASSWOPTIONS_NUM; i++) { + int i = 0; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " (B8G6I1l|0OQDS5Z2) [1-3]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1-4]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [2]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [2]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1,2]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1,4]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1,4]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1,4]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1,4]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1,3]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1-4]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [2,3]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [2]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [2]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1-4]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1]"; + pStrList->Strings[BIT_TO_OPTION_INDEX[i++]] += " [1,3]"; + + for (i = 0; i < PASSWOPTIONS_NUM; i++) { if (PASSWOPTIONS_STARRED[i]) { pStrList->Strings[BIT_TO_OPTION_INDEX[i]] += " *"; } diff --git a/src/main/PasswOptions.dfm b/src/main/PasswOptions.dfm index 6e4852f..5128b96 100644 Binary files a/src/main/PasswOptions.dfm and b/src/main/PasswOptions.dfm differ diff --git a/src/main/PasswOptions.h b/src/main/PasswOptions.h index c51813b..bb174d1 100644 --- a/src/main/PasswOptions.h +++ b/src/main/PasswOptions.h @@ -34,7 +34,7 @@ #include "UnicodeUtil.h" const int -PASSWOPTIONS_NUM = 18, +PASSWOPTIONS_NUM = 19, PASSWOPTION_EXCLUDEAMBIG = 0x00001, PASSWOPTION_FIRSTCHARNOTLC = 0x00002, @@ -53,11 +53,12 @@ PASSWOPTION_EACHCHARONLYONCE = 0x02000, PASSWOPTION_EACHWORDONLYONCE = 0x04000, PASSWOPTION_CAPITALIZEWORDS = 0x08000, PASSWOPTION_CHECKEACHPASSW = 0x10000, -PASSWOPTION_INCLUDE_CHAR_FROM_EACH_SUBSET = 0x20000; +PASSWOPTION_INCLUDE_CHAR_FROM_EACH_SUBSET = 0x20000, +PASSWOPTION_REMOVEWHITESPACE = 0x40000; const bool PASSWOPTIONS_STARRED[PASSWOPTIONS_NUM] = { true, true, false, false, false, false, false, false, false, false, true, - true, true, true, true, false, false + true, true, true, true, false, false, false }; template diff --git a/src/main/ProgramDef.h b/src/main/ProgramDef.h index eff0dd6..cdd83e7 100644 --- a/src/main/ProgramDef.h +++ b/src/main/ProgramDef.h @@ -24,8 +24,8 @@ const wchar_t PROGRAM_NAME[] = L"Password Tech", -PROGRAM_VERSION[] = L"3.5.4", -PROGRAM_LANGVER_MIN[] = L"3.5.4", +PROGRAM_VERSION[] = L"3.5.5", +PROGRAM_LANGVER_MIN[] = L"3.5.5", PROGRAM_AUTHOR[] = L"Christian Th\xF6ing", PROGRAM_AUTHOR_EMAIL[] = L"c.thoeing@web.de", PROGRAM_COPYRIGHT[] = L"Copyright \xa9 2002-2024", @@ -39,7 +39,7 @@ PROGRAM_INIFILE[] = L"PwTech.ini", PROGRAM_RANDSEEDFILE[] = L"randseed.dat"; const int -PROGRAM_MAINVER_UPDATE_NUM = 14; +PROGRAM_MAINVER_UPDATE_NUM = 15; const wchar_t LANGUAGE_DEFAULT_NAME[]= L"English", diff --git a/src/main/QuickHelp.dfm b/src/main/QuickHelp.dfm index adb0923..901ce5e 100644 --- a/src/main/QuickHelp.dfm +++ b/src/main/QuickHelp.dfm @@ -32,8 +32,8 @@ object QuickHelpForm: TQuickHelpForm BevelOuter = bvNone Font.Charset = ANSI_CHARSET Font.Color = clWindowText - Font.Height = -12 - Font.Name = 'Courier New' + Font.Height = -13 + Font.Name = 'Consolas' Font.Style = [] ParentFont = False PopupMenu = QuickHelpBoxMenu @@ -41,6 +41,8 @@ object QuickHelpForm: TQuickHelpForm ScrollBars = ssBoth TabOrder = 0 WordWrap = False + ExplicitWidth = 564 + ExplicitHeight = 99 end object QuickHelpBoxMenu: TPopupMenu Left = 8 diff --git a/src/main/SecureMem.h b/src/main/SecureMem.h index 1a52143..812d2e8 100644 --- a/src/main/SecureMem.h +++ b/src/main/SecureMem.h @@ -56,6 +56,18 @@ word32 _tcslen(const T* pStr) return static_cast(pEnd - pStr); } +inline word32 roundUpNextPowerOf2(word32 v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + // class for secure memory operations, fast implementation // inspired by secblock.h in the Crypto++ package by Wei Dai @@ -69,16 +81,17 @@ class SecureMem word32 m_lSize; word32 m_lClearMark; + void CheckBounds(word32 lIndex) + { + if (lIndex >= m_lSize) + throw SecureMemError("SecureMem: Index ouf of bounds"); + } + public: static const word32 npos = -1; static constexpr word32 MAX_SIZE = 0xfffffffe / sizeof(T); - /*static word32 max_size() - { - return 0xfffffffe / sizeof(T); - }*/ - // constructor SecureMem() : m_pData(nullptr), m_lSize(0), m_lClearMark(npos) @@ -195,6 +208,44 @@ class SecureMem std::swap(m_lClearMark, other.m_lClearMark); } + // copy elements to data array at specified position + // -> position at which first byte is copied + // -> pointer to source array + // -> number of elements to copy + void Copy(word32 lOffset, + const T* pSrc, + word32 lSrcSize) + { + if (lOffset + lSrcSize > m_lSize) + throw SecureMemError("SecureMem::Copy() overflow"); + + memcpy(m_pData + lOffset, pSrc, lSrcSize * sizeof(T)); + } + + // append content from another object + void Append(const SecureMem& item) + { + if (item.IsEmpty()) + return; + + const word32 lOldSize = m_lSize; + GrowBy(item.m_lSize); + memcpy(m_pData + lOldSize, item.m_pData, item.SizeBytes()); + } + + // append content from another data array + // -> pointer to data array + // -> number of elements contained in array + void Append(const T* pSrc, word32 lSrcSize) + { + if (lSrcSize == 0) + return; + + const word32 lOldSize = m_lSize; + GrowBy(lSrcSize); + memcpy(m_pData + lOldSize, pSrc, lSrcSize); + } + // sets mark/position within the array up to which the array is to be zeroized // upon clearing or destruction void SetClearMark(word32 lMark) @@ -259,8 +310,11 @@ class SecureMem // -> number of elements to add void GrowBy(word32 lAddSize) { - if (lAddSize != 0) + if (lAddSize != 0) { + if (MAX_SIZE - m_lSize < lAddSize) + throw SecureMemSizeError(); Resize(m_lSize + lAddSize, true); + } } // expands the array to a size corresponding to the next higher power of 2 @@ -271,9 +325,10 @@ class SecureMem if (lNewSize > m_lSize) { if (lNewSize > MAX_SIZE) throw SecureMemSizeError(); - word32 lNewSizeLog = __builtin_clz(lNewSize) ^ 31; - lNewSize = (lNewSizeLog < 31) ? std::min(MAX_SIZE, - 1u << (lNewSizeLog + 1)) : MAX_SIZE; + //word32 lNewSizeLog = __builtin_clz(lNewSize) ^ 31; + //lNewSize = (lNewSizeLog < 31) ? std::min(MAX_SIZE, + // 1u << (lNewSizeLog + 1)) : MAX_SIZE; + lNewSize = std::min(MAX_SIZE, roundUpNextPowerOf2(lNewSize)); Resize(lNewSize, true); } } @@ -422,32 +477,39 @@ class SecureMem // front/back: Access first/last element as a reference T& front(void) { - if (IsEmpty()) - throw SecureMemError("SecureMem::front(): Array is empty"); + CheckBounds(0); return m_pData[0]; } const T& front(void) const { - if (IsEmpty()) - throw SecureMemError("SecureMem::front(): Array is empty"); + CheckBounds(0); return m_pData[0]; } T& back(void) { - if (IsEmpty()) - throw SecureMemError("SecureMem::back(): Array is empty"); + CheckBounds(0); return m_pData[m_lSize - 1]; } const T& back(void) const { - if (IsEmpty()) - throw SecureMemError("SecureMem::back(): Array is empty"); + CheckBounds(0); return m_pData[m_lSize - 1]; } + T& at(word32 lIndex) + { + CheckBounds(lIndex); + return m_pData[lIndex]; + } + + const T& at(word32 lIndex) const + { + CheckBounds(lIndex); + return m_pData[lIndex]; + } // operators @@ -505,7 +567,7 @@ class SecureMem { if (this != &src) { Assign(src.m_pData, src.m_lSize); - } + } return *this; } @@ -519,17 +581,52 @@ class SecureMem SecureMem& operator+= (const SecureMem& src) { - if (!src.IsEmpty()) { - word32 lOldSize = m_lSize; - Grow(m_lSize + src.m_lSize); - memcpy(m_pData + lOldSize, src.m_pData, src.SizeBytes()); - } + Append(src); return *this; } - friend SecureMem operator+ (SecureMem& a, const SecureMem& b) + SecureMem operator+ (const SecureMem& src) const + { + if (src.IsEmpty()) + return *this; + + if (MAX_SIZE - m_lSize < src.m_lSize) + throw SecureMemSizeError(); + + SecureMem result(m_lSize + src.m_lSize); + if (!IsEmpty()) + memcpy(result.m_pData, m_pData, SizeBytes()); + memcpy(result.m_pData + m_lSize, src.m_pData, src.SizeBytes()); + + return result; + } + + bool operator== (const SecureMem& other) const + { + if (this == &other) + return true; + // avoid applying memcmp() to null pointers + if (IsEmpty() && other.IsEmpty()) + return true; + return m_lSize == other.m_lSize && + memcmp(m_pData, other.m_pData, SizeBytes()) == 0; + } + + bool operator!= (const SecureMem& other) const + { + return !operator==(other); + } + + bool operator< (const SecureMem& other) const + { + // only call memcmp() if both data pointers are valid + return (!IsEmpty() && m_lSize == other.m_lSize) ? + memcmp(m_pData, other.m_pData, SizeBytes()) < 0 : m_lSize < other.m_lSize; + } + + bool operator> (const SecureMem& other) const { - return a += b; + return other.operator<(*this); } }; @@ -606,13 +703,15 @@ void SecureMem::StrCat(const T* pStr, word32 lLen, word32& lPos) throw SecureMemError("SecureMem::StrCat(): Invalid position"); if (lLen == 0) return; + if (MAX_SIZE - lPos < lLen + 1) + throw SecureMemSizeError(); BufferedGrow(lPos + lLen + 1); memcpy(m_pData + lPos, pStr, lLen * sizeof(T)); lPos += lLen; m_pData[lPos] = '\0'; } -template inline bool operator== (const SecureMem& a, +/*template inline bool operator== (const SecureMem& a, const SecureMem& b) { // avoid applying memcmp() to null pointers @@ -640,7 +739,7 @@ template inline bool operator> (const SecureMem& a, const SecureMem& b) { return b < a; -} +}*/ typedef SecureMem SecureAnsiString; diff --git a/src/passw/PasswDatabase.cpp b/src/passw/PasswDatabase.cpp index ec2775d..1c1e5b4 100644 --- a/src/passw/PasswDatabase.cpp +++ b/src/passw/PasswDatabase.cpp @@ -311,7 +311,7 @@ SecureWString PasswDbEntry::GetTagsAsString(wchar_t sep) const word32 lPos = 0; for (const auto& tag : m_tags) { if (lPos != 0) - sDest.StrCat(&sep, 1, lPos); + sDest.StrCat(sep, lPos); sDest.StrCat(tag, lPos); } @@ -865,7 +865,8 @@ void PasswDatabase::Write(const void* pBuf, word32 lNumOfBytes) // std::max(lNumOfBytes, m_cryptBuf.Size()), DEFAULT_BUF_SIZE)); //} - memcpy(&m_cryptBuf[m_lCryptBufPos], pSrcBuf, lNumOfBytes); + //memcpy(&m_cryptBuf[m_lCryptBufPos], pSrcBuf, lNumOfBytes); + m_cryptBuf.Copy(m_lCryptBufPos, pSrcBuf, lNumOfBytes); m_lCryptBufPos += lNumOfBytes; m_cryptBuf.GrowClearMark(m_lCryptBufPos); @@ -1094,7 +1095,8 @@ void PasswDatabase::SaveToFile(const WString& sFileName) comprBuf.BufferedGrow(lComprBufPos + lChunkSize); //if (lComprBufPos + lChunkSize > comprBuf.Size()) // comprBuf.GrowBy(comprBuf.Size()); - memcpy(comprBuf + lComprBufPos, workBuf, lChunkSize); + //memcpy(comprBuf + lComprBufPos, workBuf, lChunkSize); + comprBuf.Copy(lComprBufPos, workBuf, lChunkSize); lComprBufPos += lChunkSize; } lToCompress = 0; @@ -1189,8 +1191,6 @@ int PasswDatabase::ReadFieldIndex(void) { if (m_lCryptBufPos + 1 > m_cryptBuf.Size()) throw EPasswDbInvalidFormat(E_INVALID_FORMAT); - //word8 bIndex; - //memcpy(&bIndex, &m_cryptBuf[m_lCryptBufPos++], 1); return m_cryptBuf[m_lCryptBufPos++]; } //--------------------------------------------------------------------------- @@ -1201,9 +1201,10 @@ SecureAnsiString PasswDatabase::ReadAnsiString(void) if (lSize != 0) { if (m_lCryptBufPos + lSize > m_cryptBuf.Size()) throw EPasswDbInvalidFormat(E_INVALID_FORMAT); - asDest.New(lSize + 1); - memcpy(asDest, &m_cryptBuf[m_lCryptBufPos], lSize); - asDest[lSize] = '\0'; + //asDest.New(lSize + 1); + //memcpy(asDest, &m_cryptBuf[m_lCryptBufPos], lSize); + asDest.AssignStr(reinterpret_cast(&m_cryptBuf[m_lCryptBufPos]), lSize); + //asDest[lSize] = '\0'; m_lCryptBufPos += lSize; } return asDest; diff --git a/src/passw/PasswGen.cpp b/src/passw/PasswGen.cpp index 1de1b51..7b4778c 100644 --- a/src/passw/PasswGen.cpp +++ b/src/passw/PasswGen.cpp @@ -151,6 +151,28 @@ template inline int strchpos(const T* pStr, int nLen, T c) return -1; } +template int removeWhitespace(T* pStr, int nLen) +{ + if (nLen < 2) + return nLen; + + auto isWhitespace = [](T ch) + { + return ch == ' ' || ch == '\t'; + }; + + int nStart, nEnd; + for (nStart = 0; nStart < nLen - 1 && isWhitespace(pStr[nStart]); nStart++); + for (nEnd = nLen - 1; nEnd > nStart && isWhitespace(pStr[nEnd]); nEnd--); + + nLen = nEnd - nStart + 1; + if (nStart > 0) + memmove(pStr, &pStr[nStart], nLen * sizeof(T)); + + pStr[nLen] = '\0'; + return nLen; +} + static w32string s_charSetCodes[PASSWGEN_NUMCHARSETCODES_EXT]; @@ -792,7 +814,7 @@ int PasswordGenerator::GetPassword(SecureW32String& sDest, int nLength, int nFlags) const { - if (nLength <= 0) + if (nLength < 1) return 0; word32 lChar; @@ -969,6 +991,9 @@ int PasswordGenerator::GetPassword(SecureW32String& sDest, } #endif + if (nFlags & PASSW_FLAG_REMOVEWHITESPACE) + nLength = removeWhitespace(sDest.begin(), nLength); + return nLength; } //--------------------------------------------------------------------------- @@ -979,7 +1004,7 @@ int PasswordGenerator::GetPassphrase(SecureW32String& sDest, int nFlags, int* pnNetWordsLen) const { - if (nWords <= 0) + if (nWords < 1) return 0; //int nCharsLen = (pChars != nullptr) ? w32strlen(pChars) : 0; @@ -1464,6 +1489,9 @@ int PasswordGenerator::GetFormatPassw(SecureW32String& sDest, pDest[nDestIdx] = '\0'; + if (nFlags & PASSFORMAT_FLAG_REMOVEWHITESPACE) + nDestIdx = removeWhitespace(pDest, nDestIdx); + lRand = 0; return nDestIdx; @@ -1594,7 +1622,7 @@ int PasswordGenerator::GetPhoneticPassw(SecureW32String& sDest, int nLength, int nFlags) const { - if (nLength <= 0) + if (nLength < 1) return 0; // word32* pTris = (m_pPhoneticTris == nullptr) ? (word32*) PHONETIC_TRIS : m_pPhoneticTris; diff --git a/src/passw/PasswGen.h b/src/passw/PasswGen.h index 2ff9862..f8dc614 100644 --- a/src/passw/PasswGen.h +++ b/src/passw/PasswGen.h @@ -50,6 +50,7 @@ PASSW_FLAG_PHONETICUPPERCASE = 0x0080, PASSW_FLAG_PHONETICMIXEDCASE = 0x0100, // mixed-case characters in phonetic passwords PASSW_FLAG_EACHCHARONLYONCE = 0x0200, // each character must occur only once PASSW_FLAG_CHECKDUPLICATESBYSET = 0x0400, +PASSW_FLAG_REMOVEWHITESPACE = 0x0800, PASSPHR_FLAG_COMBINEWCH = 0x0001, // combine words & chars PASSPHR_FLAG_CAPITALIZEWORDS = 0x0002, // capitalize first letter of word @@ -58,7 +59,8 @@ PASSPHR_FLAG_DONTSEPWCH = 0x0008, // don't separate words & chars by '- PASSPHR_FLAG_REVERSEWCHORDER = 0x0010, PASSPHR_FLAG_EACHWORDONLYONCE = 0x0020, -PASSFORMAT_FLAG_EXCLUDEREPCHARS = 1, +PASSFORMAT_FLAG_EXCLUDEREPCHARS = 0x0001, +PASSFORMAT_FLAG_REMOVEWHITESPACE= 0x0002, PASSFORMAT_PWUSED_NOSPECIFIER = -1, // password provided, but no "P" specifier PASSFORMAT_PWUSED_EMPTYPASSW = -2; // "%P" specified, but no password available diff --git a/src/util/SendKeys.cpp b/src/util/SendKeys.cpp index f455598..e00b4c8 100644 --- a/src/util/SendKeys.cpp +++ b/src/util/SendKeys.cpp @@ -28,9 +28,9 @@ //--------------------------------------------------------------------------- #pragma package(smart_init) -static std::unordered_map keyPlaceholders; +static std::unordered_map s_keyPlaceholders; -static const int +const int VIRTUAL_KEY_DELAY = 200, INIT_DELAY = 250; @@ -44,42 +44,42 @@ SendKeys::KeySequence::~KeySequence() SendKeys::SendKeys(int nDelay) : m_nDelay(nDelay) { - if (keyPlaceholders.empty()) { - for (int nI = 0; nI < PasswDbEntry::NUM_STRING_FIELDS; nI++) { + if (s_keyPlaceholders.empty()) { + for (int i = 0; i < PasswDbEntry::NUM_STRING_FIELDS; i++) { AnsiString sFieldName = AnsiString(PasswDbEntry::GetFieldName( - static_cast(nI))).LowerCase(); - keyPlaceholders[std::string(sFieldName.c_str())] = -nI; + static_cast(i))).LowerCase(); + s_keyPlaceholders[std::string(sFieldName.c_str())] = -i; } - keyPlaceholders["parameter"] = -PasswDbEntry::USERNAME; + s_keyPlaceholders["parameter"] = -PasswDbEntry::USERNAME; - keyPlaceholders["tab"] = VK_TAB; - keyPlaceholders["return"] = VK_RETURN; - keyPlaceholders["enter"] = VK_RETURN; - keyPlaceholders["ctrl"] = VK_CONTROL; - keyPlaceholders["backspace"] = VK_BACK; - keyPlaceholders["clear"] = VK_CLEAR; - keyPlaceholders["shift"] = VK_SHIFT; - keyPlaceholders["alt"] = VK_MENU; - keyPlaceholders["pause"] = VK_PAUSE; - keyPlaceholders["capslock"] = VK_CAPITAL; - keyPlaceholders["escape"] = VK_ESCAPE; - keyPlaceholders["space"] = VK_SPACE; - keyPlaceholders["pageup"] = VK_PRIOR; - keyPlaceholders["pagedown"] = VK_NEXT; - keyPlaceholders["end"] = VK_END; - keyPlaceholders["home"] = VK_HOME; - keyPlaceholders["left"] = VK_LEFT; - keyPlaceholders["right"] = VK_RIGHT; - keyPlaceholders["down"] = VK_DOWN; - keyPlaceholders["up"] = VK_UP; - keyPlaceholders["select"] = VK_SELECT; - keyPlaceholders["print"] = VK_PRINT; - keyPlaceholders["execute"] = VK_EXECUTE; - keyPlaceholders["snapshot"] = VK_SNAPSHOT; - keyPlaceholders["insert"] = VK_INSERT; - keyPlaceholders["delete"] = VK_DELETE; - keyPlaceholders["help"] = VK_HELP; + s_keyPlaceholders["tab"] = VK_TAB; + s_keyPlaceholders["return"] = VK_RETURN; + s_keyPlaceholders["enter"] = VK_RETURN; + s_keyPlaceholders["ctrl"] = VK_CONTROL; + s_keyPlaceholders["backspace"] = VK_BACK; + s_keyPlaceholders["clear"] = VK_CLEAR; + s_keyPlaceholders["shift"] = VK_SHIFT; + s_keyPlaceholders["alt"] = VK_MENU; + s_keyPlaceholders["pause"] = VK_PAUSE; + s_keyPlaceholders["capslock"] = VK_CAPITAL; + s_keyPlaceholders["escape"] = VK_ESCAPE; + s_keyPlaceholders["space"] = VK_SPACE; + s_keyPlaceholders["pageup"] = VK_PRIOR; + s_keyPlaceholders["pagedown"] = VK_NEXT; + s_keyPlaceholders["end"] = VK_END; + s_keyPlaceholders["home"] = VK_HOME; + s_keyPlaceholders["left"] = VK_LEFT; + s_keyPlaceholders["right"] = VK_RIGHT; + s_keyPlaceholders["down"] = VK_DOWN; + s_keyPlaceholders["up"] = VK_UP; + s_keyPlaceholders["select"] = VK_SELECT; + s_keyPlaceholders["print"] = VK_PRINT; + s_keyPlaceholders["execute"] = VK_EXECUTE; + s_keyPlaceholders["snapshot"] = VK_SNAPSHOT; + s_keyPlaceholders["insert"] = VK_INSERT; + s_keyPlaceholders["delete"] = VK_DELETE; + s_keyPlaceholders["help"] = VK_HELP; } } //--------------------------------------------------------------------------- @@ -231,8 +231,8 @@ void SendKeys::SendComplexString(const WString& sStr, {} } else { - auto it = keyPlaceholders.find(sPlaceholder); - if (it != keyPlaceholders.end()) { + auto it = s_keyPlaceholders.find(sPlaceholder); + if (it != s_keyPlaceholders.end()) { blFound = true; pwszStr += it->first.length() + 2; // including brackets @@ -336,10 +336,10 @@ void SendKeys::SendComplexString(const WString& sStr, void SendKeys::SendKeySequence(KeySequence& input) { Sleep(INIT_DELAY); - for (int nI = 0; nI < input.keys.size(); nI++) { - if (!input.keys[nI].empty()) - SendInput(input.keys[nI].size(), input.keys[nI].data(), sizeof(INPUT)); - Sleep(input.delays[nI]); + for (word32 i = 0; i < input.keys.size(); i++) { + if (!input.keys[i].empty()) + SendInput(input.keys[i].size(), input.keys[i].data(), sizeof(INPUT)); + Sleep(input.delays[i]); } } //--------------------------------------------------------------------------- diff --git a/src/util/StringFileStreamW.cpp b/src/util/StringFileStreamW.cpp index 70c19ab..96557d5 100644 --- a/src/util/StringFileStreamW.cpp +++ b/src/util/StringFileStreamW.cpp @@ -24,6 +24,7 @@ #include "StringFileStreamW.h" #include "types.h" #include "Language.h" +#include "Util.h" //--------------------------------------------------------------------------- #pragma package(smart_init) @@ -42,9 +43,8 @@ __fastcall TStringFileStreamW::TStringFileStreamW(const WString& sFileName, bool blWriteOrAutodetectBOM, int nBufSize, const AnsiString& asSepChars) - : TFileStream(sFileName, wMode), m_enc(enc), m_buf(nBufSize + nBufSize % 2 + 2), - m_nBufLen(0), m_nBufPos(0), m_asSepChars(asSepChars), m_sSepChars(asSepChars), - m_nBOMLen(0) + : TFileStream(sFileName, wMode), m_enc(enc), m_nBufLen(0), m_nBufPos(0), + m_asSepChars(asSepChars), m_sSepChars(asSepChars), m_nBOMLen(0) { if (blWriteOrAutodetectBOM) { if (wMode == fmOpenRead || wMode == fmOpenReadWrite) { @@ -88,38 +88,45 @@ __fastcall TStringFileStreamW::TStringFileStreamW(const WString& sFileName, } } m_nCodeUnitSize = (m_enc == ceAnsi || m_enc == ceUtf8) ? 1 : 2; + if (m_nCodeUnitSize == 1) + m_cbuf.New(nBufSize); + else + m_wbuf.New(nBufSize); } //--------------------------------------------------------------------------- int __fastcall TStringFileStreamW::ReadString(wchar_t* pwszDest, int nDestBufSize) { - wchar_t* pwszBuf = reinterpret_cast(m_buf.Data()); + //wchar_t* pwszBuf = reinterpret_cast(m_buf.Data()); + if (nDestBufSize < 1) + return 0; while (true) { if (m_nBufPos < m_nBufLen) { - int nStrBytes; + int nStrLen; if (m_nCodeUnitSize == 1) - nStrBytes = strcspn(&m_buf[m_nBufPos], m_asSepChars.c_str()); + nStrLen = strcspn(&m_cbuf[m_nBufPos], m_asSepChars.c_str()); else - nStrBytes = wcscspn(&pwszBuf[m_nBufPos/2], m_sSepChars.c_str()) * 2; + nStrLen = wcscspn(&m_wbuf[m_nBufPos], m_sSepChars.c_str()); - bool blInBuf = nStrBytes < m_nBufLen - m_nBufPos; + bool blInBuf = nStrLen < m_nBufLen - m_nBufPos; if (blInBuf || Position == Size) { if (blInBuf) - nStrBytes += m_nCodeUnitSize; + nStrLen++; int nResult; - if (m_enc == ceAnsi || m_enc == ceUtf8) + if (m_enc == ceAnsi || m_enc == ceUtf8) { nResult = MultiByteToWideChar((m_enc == ceAnsi) ? CP_ACP : CP_UTF8, - 0, &m_buf[m_nBufPos], nStrBytes, pwszDest, nDestBufSize - 1); + 0, &m_cbuf[m_nBufPos], nStrLen, pwszDest, nDestBufSize - 1); + } else { - wcsncpy(pwszDest, &pwszBuf[m_nBufPos/2], nDestBufSize - 1); - nResult = std::min(nStrBytes/2, nDestBufSize - 1); + wcsncpy(pwszDest, &m_wbuf[m_nBufPos], nDestBufSize - 1); + nResult = std::min(nStrLen, nDestBufSize - 1); } - m_nBufPos += nStrBytes; + m_nBufPos += nStrLen; if (nResult == 0) throw EStringFileStreamError( @@ -134,9 +141,10 @@ int __fastcall TStringFileStreamW::ReadString(wchar_t* pwszDest, if (m_nBufPos == 0 && m_nBufLen != 0) throw EStringFileStreamError(TRL("Unicode string too long")); - Seek(m_nBufPos - m_nBufLen, soFromCurrent); + Seek((m_nBufPos - m_nBufLen) * m_nCodeUnitSize, soFromCurrent); - int nBytesRead = Read(m_buf, m_buf.Size() - 2); + const int nBytesRead = (m_nCodeUnitSize == 1) ? + Read(m_cbuf, m_cbuf.Size() - 1) : Read(m_wbuf, (m_wbuf.Size() - 1) * 2); if (nBytesRead == 0) return 0; @@ -144,68 +152,62 @@ int __fastcall TStringFileStreamW::ReadString(wchar_t* pwszDest, if (m_nCodeUnitSize == 2 && nBytesRead % 2 != 0) throw EStringFileStreamError(TRL("Invalid UTF-16 character encoding")); + m_nBufLen = (m_nCodeUnitSize == 1) ? nBytesRead : nBytesRead / 2; + if (m_enc == ceUtf16BigEndian) - swapUtf16ByteOrder(pwszBuf, nBytesRead/2); + swapUtf16ByteOrder(m_wbuf, m_nBufLen); - m_buf[nBytesRead] = '\0'; - m_buf[nBytesRead+1] = '\0'; + if (m_nCodeUnitSize == 1) + m_cbuf[m_nBufLen] = '\0'; + else + m_wbuf[m_nBufLen] = '\0'; - m_nBufLen = nBytesRead; m_nBufPos = 0; } } //--------------------------------------------------------------------------- -bool __fastcall TStringFileStreamW::WriteString(const wchar_t* pwszSrc, - int nStrLen, - int* pnBytesWritten) +void __fastcall TStringFileStreamW::WriteString(const wchar_t* pwszSrc, + int nStrLen) { - if (nStrLen == 0) { - if (pnBytesWritten) - *pnBytesWritten = 0; - return true; - } - - SecureAnsiString sEncBuf; - int nEncBytes, nBytesWritten; + if (nStrLen < 1) + return; switch (m_enc) { case ceAnsi: case ceUtf8: + { + const int nEncBytes = WideCharToMultiByte( + (m_enc == ceAnsi) ? CP_ACP : CP_UTF8, + 0, pwszSrc, nStrLen, nullptr, 0, nullptr, nullptr); - nEncBytes = WideCharToMultiByte((m_enc == ceAnsi) ? CP_ACP : CP_UTF8, - 0, pwszSrc, nStrLen, NULL, 0, NULL, NULL); + if (nEncBytes == 0) + throw EStringFileStreamError(TRL("Error while encoding Unicode string")); - if (nEncBytes == 0) - throw EStringFileStreamError(TRL("Error while encoding Unicode string")); + SecureAnsiString sEncBuf(nEncBytes); - sEncBuf.New(nEncBytes); - - WideCharToMultiByte((m_enc == ceAnsi) ? CP_ACP : CP_UTF8, 0, pwszSrc, nStrLen, - sEncBuf, nEncBytes, NULL, NULL); - - break; + WideCharToMultiByte( + (m_enc == ceAnsi) ? CP_ACP : CP_UTF8, 0, pwszSrc, nStrLen, + sEncBuf, nEncBytes, nullptr, nullptr); + if (Write(sEncBuf, nEncBytes) != nEncBytes) + OutOfDiskSpaceError(); + } case ceUtf16: - nEncBytes = nStrLen * 2; - - // write the data directly to the file - nBytesWritten = Write(pwszSrc, nEncBytes); - if (pnBytesWritten) - *pnBytesWritten = nBytesWritten; - - return nBytesWritten == nEncBytes; - + { + // write the data directly to the file + const int nBytes = nStrLen * 2; + if (Write(pwszSrc, nBytes) != nBytes) + OutOfDiskSpaceError(); + break; + } case ceUtf16BigEndian: - nEncBytes = nStrLen * 2; - sEncBuf.Assign(reinterpret_cast(pwszSrc), nEncBytes); - swapUtf16ByteOrder(reinterpret_cast(sEncBuf.Data()), nStrLen); - break; + { + SecureWString sEncBuf(pwszSrc, nStrLen); + swapUtf16ByteOrder(sEncBuf, nStrLen); + if (Write(sEncBuf, sEncBuf.SizeBytes()) != sEncBuf.SizeBytes()) + OutOfDiskSpaceError(); + break; + } } - - nBytesWritten = Write(sEncBuf, nEncBytes); - if (pnBytesWritten) - *pnBytesWritten = nBytesWritten; - - return nBytesWritten == nEncBytes; } //--------------------------------------------------------------------------- diff --git a/src/util/StringFileStreamW.h b/src/util/StringFileStreamW.h index 6c2d217..a1def83 100644 --- a/src/util/StringFileStreamW.h +++ b/src/util/StringFileStreamW.h @@ -52,7 +52,8 @@ class TStringFileStreamW : public TFileStream { private: CharacterEncoding m_enc; - SecureMem m_buf; + SecureMem m_cbuf; + SecureMem m_wbuf; int m_nBufLen; int m_nBufPos; AnsiString m_asSepChars; @@ -92,14 +93,11 @@ class TStringFileStreamW : public TFileStream int nDestBufSize); // write string to file + // throws exception if string encoding is invalid or write error occurred // -> pointer to the source buffer (wide string) // -> string length (no. of characters) - // -> variable which obtains the number of bytes written to the file - // <- 'true': write operation was successful - // 'false': write error - bool __fastcall WriteString(const wchar_t* pwszSrc, - int nStrLen, - int* pnBytesWritten = nullptr); + void __fastcall WriteString(const wchar_t* pwszSrc, + int nStrLen); // set file pointer to beginning of file void __fastcall FileBeginning(void) diff --git a/src/util/UpdateCheck.cpp b/src/util/UpdateCheck.cpp index aa38c10..4245299 100644 --- a/src/util/UpdateCheck.cpp +++ b/src/util/UpdateCheck.cpp @@ -39,7 +39,8 @@ std::atomic TUpdateCheckThread::s_blThreadRunning(false); //--------------------------------------------------------------------------- -TUpdateCheckThread::CheckResult __fastcall TUpdateCheckThread::CheckForUpdates(bool blShowError) +TUpdateCheckThread::CheckResult __fastcall TUpdateCheckThread::CheckForUpdates( + bool blShowError) { try { const WString sAltUrl = Format("%s?fakeParam=%.8x", ARRAYOFCONST(( @@ -52,14 +53,14 @@ TUpdateCheckThread::CheckResult __fastcall TUpdateCheckThread::CheckForUpdates(b wszFileName[0] = '\0'; //const wchar_t* pwszUrl = L"http://pwgen-win.sourceforge.net/manual.pdf"; - const wchar_t* pwszUrl = PROGRAM_URL_VERSION; + const wchar_t* pwszUrl = PROGRAM_URL_VERSION; // first try to delete a cache entry of the file before downloading it // to ensure that we get the latest version from the server if (!DeleteUrlCacheEntry(pwszUrl) && GetLastError() == ERROR_ACCESS_DENIED) pwszUrl = sAltUrl.c_str(); - HRESULT hResult = URLDownloadToCacheFile(nullptr, pwszUrl, wszFileName, + HRESULT hResult = URLDownloadToCacheFile(nullptr, pwszUrl, wszFileName, MAX_PATH, 0, NULL); WString sFileName(wszFileName); @@ -110,9 +111,13 @@ TUpdateCheckThread::CheckResult __fastcall TUpdateCheckThread::CheckForUpdates(b } } catch (Exception& e) { - if (blShowError) - MsgBox(TRLFormat("Error while checking for updates:\n%1.", - { e.Message }), MB_ICONERROR); + if (blShowError) { + TThread::Synchronize(nullptr, [&e]() + { + MsgBox(TRLFormat("Error while checking for updates:\n%1.", + { e.Message }), MB_ICONERROR); + }); + } return CheckResult::Error; } diff --git a/src/util/UpdateCheck.h b/src/util/UpdateCheck.h index 941b7ea..3ff0ec2 100644 --- a/src/util/UpdateCheck.h +++ b/src/util/UpdateCheck.h @@ -36,7 +36,7 @@ class TUpdateCheckThread : public TThread }; __fastcall TUpdateCheckThread(std::function terminateFunc) - : TThread(false), m_nResult(CheckResult::NotAvailable), + : TThread(false), m_result(CheckResult::NotAvailable), m_terminateFunc(terminateFunc) { FreeOnTerminate = true; @@ -46,7 +46,7 @@ class TUpdateCheckThread : public TThread } __property CheckResult Result = - { read=m_nResult }; + { read=m_result }; static CheckResult __fastcall CheckForUpdates(bool blShowError); @@ -56,14 +56,14 @@ class TUpdateCheckThread : public TThread } private: - CheckResult m_nResult; + CheckResult m_result; std::function m_terminateFunc; static std::atomic s_blThreadRunning; virtual void __fastcall Execute(void) override { - m_nResult = CheckForUpdates(false); - ReturnValue = static_cast(m_nResult); + m_result = CheckForUpdates(false); + ReturnValue = static_cast(m_result); } void __fastcall ThreadTerminate(TObject* Sender) diff --git a/src/util/Util.cpp b/src/util/Util.cpp index 310f302..fc45560 100644 --- a/src/util/Util.cpp +++ b/src/util/Util.cpp @@ -44,7 +44,7 @@ #pragma package(smart_init) WString g_msgBoxCaptionList[4] = -{ L"Info", L"Warning", L"Question", L"Error" }; +{ "Info", "Warning", "Question", "Error" }; //--------------------------------------------------------------------------- SecureWString strCr2Crlf(const SecureWString& sSrc) @@ -745,17 +745,18 @@ const word8 CUSTOM_BASE64_DEC_MAP[128] = 49, 50, 51, 127, 127, 127, 127, 127 }; -int CheckDonorKey(const AnsiString& asInput, - AnsiString* pasId, - int* pnType) +std::tuple CheckDonorKey(const AnsiString& asInput) { + int nType = DONOR_TYPE_STD; + AnsiString asDonorId; + if (asInput.Length() < 16) - return DONOR_KEY_INVALID; + return { DONOR_KEY_INVALID, nType, asDonorId }; AnsiString asKey = asInput.Trim(); if (asKey.Length() != 16) - return DONOR_KEY_INVALID; + return { DONOR_KEY_INVALID, nType, asDonorId }; word8 buf[13]; buf[12] = '\0'; @@ -764,42 +765,37 @@ int CheckDonorKey(const AnsiString& asInput, base64_decode(buf, &destLen, reinterpret_cast(asKey.c_str()), 16); if (destLen != 12) - return DONOR_KEY_INVALID; + return { DONOR_KEY_INVALID, nType, asDonorId }; const word32 param[4] = { 0x77adb64b, 0x959561b7, 0x8799de93, 0x22ef89dd }; if (!decode_96bit(reinterpret_cast(buf), param)) - return DONOR_KEY_INVALID; + return { DONOR_KEY_INVALID, nType, asDonorId }; if (buf[0] != 'P' || buf[1] != '3') - return DONOR_KEY_INVALID; + return { DONOR_KEY_INVALID, nType, asDonorId }; for (int nI = 2; nI < 10; nI++) { if (buf[nI] < ' ' || buf[nI] > '~') - return DONOR_KEY_INVALID; + return { DONOR_KEY_INVALID, nType, asDonorId }; } int n1 = CUSTOM_BASE64_DEC_MAP[buf[5]]; int n2 = CUSTOM_BASE64_DEC_MAP[buf[6]]; if (n1 == 127 || n2 == 127) - return DONOR_KEY_INVALID; + return { DONOR_KEY_INVALID, nType, asDonorId }; int nVersion = ((n2 & 15) << 6) | n1; - int nType = n2 >> 4; + nType = n2 >> 4; if (nType != DONOR_TYPE_PRO && PROGRAM_MAINVER_UPDATE_NUM - nVersion > DONOR_STD_NUM_UPDATES) - return DONOR_KEY_EXPIRED; - - if (pasId != nullptr) { - buf[10] = '\0'; - *pasId = AnsiString(reinterpret_cast(buf) + 2); - } + return { DONOR_KEY_EXPIRED, nType, asDonorId }; - if (pnType != nullptr) - *pnType = nType; + buf[10] = '\0'; + asDonorId = AnsiString(reinterpret_cast(buf) + 2); - return DONOR_KEY_VALID; + return { DONOR_KEY_VALID, nType, asDonorId }; } //--------------------------------------------------------------------------- diff --git a/src/util/Util.h b/src/util/Util.h index 5c71fd0..47e78b4 100644 --- a/src/util/Util.h +++ b/src/util/Util.h @@ -22,6 +22,7 @@ #define UtilH //--------------------------------------------------------------------------- #include +#include #include "SecureMem.h" #include "UnicodeUtil.h" @@ -160,8 +161,6 @@ enum { DONOR_STD_NUM_UPDATES = 3 }; -int CheckDonorKey(const AnsiString& asInput, - AnsiString* pasId = NULL, - int* pnType = NULL); +std::tuple CheckDonorKey(const AnsiString& asInput); #endif