diff options
30 files changed, 314 insertions, 126 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5ad51c745..1a3f1a064 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -26,7 +26,7 @@ ## Your Environment <!--- Include relevant details about the environment you experienced the bug in --> -* KeePassXR version/commit used: +* KeePassXC version/commit used: (can be found under Help -> About) * Qt version (e.g. Qt 5.3): * Compiler (e.g. Clang++3.6.0): * Operating System and version: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 51436aada..c83ca4e53 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,4 @@ -<!--- Provide a general summary of your changes in the Title above --> +<!--- Provide a general summary of your changes in the title above --> ## Description <!--- Describe your changes in detail --> @@ -15,21 +15,19 @@ ## Screenshots (if appropriate): ## Types of changes -<!--- What types of changes does your code introduce? If it apply to your pull request, --> -<!--- replace all the `:negative_squared_cross_mark:` with `:white_check_mark:` --> -<!--- Everybody loves emoji --> -- :negative_squared_cross_mark: Bug fix (non-breaking change which fixes an issue) -- :negative_squared_cross_mark: New feature (non-breaking change which adds functionality) -- :negative_squared_cross_mark: Breaking change (fix or feature that would cause existing functionality to change) +<!--- What types of changes does your code introduce? --> +<!--- Please remove all lines which don't apply. --> +- ✅ Bug fix (non-breaking change which fixes an issue) +- ✅ New feature (non-breaking change which adds functionality) +- ✅ Breaking change (fix or feature that would cause existing functionality to change) ## Checklist: -<!--- Go over all the following points, if it apply to your pull request, --> -<!--- replace all the `:negative_squared_cross_mark:` with `:white_check_mark:`. --> -<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> -<!--- Pull Requests that fail the [REQUIRED] field will likely be sent back for corrections or rejected --> -- :negative_squared_cross_mark: I have read the **CONTRIBUTING** document. [REQUIRED] -- :negative_squared_cross_mark: My code follows the code style of this project. [REQUIRED] -- :negative_squared_cross_mark: All new and existing tests passed. [REQUIRED] -- :negative_squared_cross_mark: My change requires a change to the documentation. -- :negative_squared_cross_mark: I have updated the documentation accordingly. -- :negative_squared_cross_mark: I have added tests to cover my changes. +<!--- Please go over all the following points. --> +<!--- Again, remove any lines which don't apply. --> +<!--- Pull Requests that don't fulfill all [REQUIRED] requisites are likely --> +<!--- to be sent back to you for correction or will be rejected. --> +- ✅ I have read the **CONTRIBUTING** document. **[REQUIRED]** +- ✅ My code follows the code style of this project. **[REQUIRED]** +- ✅ All new and existing tests passed. **[REQUIRED]** +- ✅ My change requires a change to the documentation and I have updated it accordingly. +- ✅ I have added tests to cover my changes. diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7b05e5a..0843d7ccb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo Debug Debugfull Profile MinSizeRel." + "Choose the type of build, options are: None Debug Release RelWithDebInfo Debug DebugFull Profile MinSizeRel." FORCE) endif() diff --git a/src/autotype/test/AutoTypeTest.cpp b/src/autotype/test/AutoTypeTest.cpp index 1ac815b0e..0eaf71602 100644 --- a/src/autotype/test/AutoTypeTest.cpp +++ b/src/autotype/test/AutoTypeTest.cpp @@ -65,7 +65,7 @@ int AutoTypePlatformTest::platformEventFilter(void* event) AutoTypeExecutor* AutoTypePlatformTest::createExecutor() { - return new AutoTypeExecturorTest(this); + return new AutoTypeExecutorTest(this); } void AutoTypePlatformTest::setActiveWindowTitle(const QString& title) @@ -127,17 +127,17 @@ bool AutoTypePlatformTest::raiseOwnWindow() } #endif -AutoTypeExecturorTest::AutoTypeExecturorTest(AutoTypePlatformTest* platform) +AutoTypeExecutorTest::AutoTypeExecutorTest(AutoTypePlatformTest* platform) : m_platform(platform) { } -void AutoTypeExecturorTest::execChar(AutoTypeChar* action) +void AutoTypeExecutorTest::execChar(AutoTypeChar* action) { m_platform->addActionChar(action); } -void AutoTypeExecturorTest::execKey(AutoTypeKey* action) +void AutoTypeExecutorTest::execKey(AutoTypeKey* action) { m_platform->addActionKey(action); } diff --git a/src/autotype/test/AutoTypeTest.h b/src/autotype/test/AutoTypeTest.h index 33487778a..4feaab942 100644 --- a/src/autotype/test/AutoTypeTest.h +++ b/src/autotype/test/AutoTypeTest.h @@ -69,10 +69,10 @@ private: QString m_actionChars; }; -class AutoTypeExecturorTest : public AutoTypeExecutor +class AutoTypeExecutorTest : public AutoTypeExecutor { public: - explicit AutoTypeExecturorTest(AutoTypePlatformTest* platform); + explicit AutoTypeExecutorTest(AutoTypePlatformTest* platform); void execChar(AutoTypeChar* action) override; void execKey(AutoTypeKey* action) override; diff --git a/src/autotype/xcb/AutoTypeXCB.cpp b/src/autotype/xcb/AutoTypeXCB.cpp index 23a211d37..f419875dc 100644 --- a/src/autotype/xcb/AutoTypeXCB.cpp +++ b/src/autotype/xcb/AutoTypeXCB.cpp @@ -24,7 +24,7 @@ #include <xcb/xcb.h> bool AutoTypePlatformX11::m_catchXErrors = false; -bool AutoTypePlatformX11::m_xErrorOccured = false; +bool AutoTypePlatformX11::m_xErrorOccurred = false; int (*AutoTypePlatformX11::m_oldXErrorHandler)(Display*, XErrorEvent*) = nullptr; AutoTypePlatformX11::AutoTypePlatformX11() @@ -153,7 +153,7 @@ bool AutoTypePlatformX11::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifi GrabModeAsync, GrabModeAsync); stopCatchXErrors(); - if (!m_xErrorOccured) { + if (!m_xErrorOccurred) { m_currentGlobalKey = key; m_currentGlobalModifiers = modifiers; m_currentGlobalKeycode = keycode; @@ -247,7 +247,7 @@ int AutoTypePlatformX11::platformEventFilter(void* event) AutoTypeExecutor* AutoTypePlatformX11::createExecutor() { - return new AutoTypeExecturorX11(this); + return new AutoTypeExecutorX11(this); } QString AutoTypePlatformX11::windowTitle(Window window, bool useBlacklist) @@ -556,7 +556,7 @@ void AutoTypePlatformX11::startCatchXErrors() Q_ASSERT(!m_catchXErrors); m_catchXErrors = true; - m_xErrorOccured = false; + m_xErrorOccurred = false; m_oldXErrorHandler = XSetErrorHandler(x11ErrorHandler); } @@ -575,7 +575,7 @@ int AutoTypePlatformX11::x11ErrorHandler(Display* display, XErrorEvent* error) Q_UNUSED(error) if (m_catchXErrors) { - m_xErrorOccured = true; + m_xErrorOccurred = true; } return 1; @@ -823,17 +823,17 @@ int AutoTypePlatformX11::MyErrorHandler(Display* my_dpy, XErrorEvent* event) } -AutoTypeExecturorX11::AutoTypeExecturorX11(AutoTypePlatformX11* platform) +AutoTypeExecutorX11::AutoTypeExecutorX11(AutoTypePlatformX11* platform) : m_platform(platform) { } -void AutoTypeExecturorX11::execChar(AutoTypeChar* action) +void AutoTypeExecutorX11::execChar(AutoTypeChar* action) { m_platform->SendKeyPressedEvent(m_platform->charToKeySym(action->character)); } -void AutoTypeExecturorX11::execKey(AutoTypeKey* action) +void AutoTypeExecutorX11::execKey(AutoTypeKey* action) { m_platform->SendKeyPressedEvent(m_platform->keyToKeySym(action->key)); } diff --git a/src/autotype/xcb/AutoTypeXCB.h b/src/autotype/xcb/AutoTypeXCB.h index 8adee7701..26d1e8102 100644 --- a/src/autotype/xcb/AutoTypeXCB.h +++ b/src/autotype/xcb/AutoTypeXCB.h @@ -100,7 +100,7 @@ private: uint m_currentGlobalNativeModifiers; int m_modifierMask; static bool m_catchXErrors; - static bool m_xErrorOccured; + static bool m_xErrorOccurred; static int (*m_oldXErrorHandler)(Display*, XErrorEvent*); static const int m_unicodeToKeysymLen; @@ -119,10 +119,10 @@ private: bool m_loaded; }; -class AutoTypeExecturorX11 : public AutoTypeExecutor +class AutoTypeExecutorX11 : public AutoTypeExecutor { public: - explicit AutoTypeExecturorX11(AutoTypePlatformX11* platform); + explicit AutoTypeExecutorX11(AutoTypePlatformX11* platform); void execChar(AutoTypeChar* action) override; void execKey(AutoTypeKey* action) override; diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 629f5141c..b2b06e7c8 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -449,7 +449,7 @@ void Entry::truncateHistory() int histMaxSize = db->metadata()->historyMaxSize(); if (histMaxSize > -1) { int size = 0; - QSet<QByteArray> foundAttachements = attachments()->values().toSet(); + QSet<QByteArray> foundAttachments = attachments()->values().toSet(); QMutableListIterator<Entry*> i(m_history); i.toBack(); @@ -460,11 +460,11 @@ void Entry::truncateHistory() if (size <= histMaxSize) { size += historyItem->attributes()->attributesSize(); - const QSet<QByteArray> newAttachments = historyItem->attachments()->values().toSet() - foundAttachements; + const QSet<QByteArray> newAttachments = historyItem->attachments()->values().toSet() - foundAttachments; for (const QByteArray& attachment : newAttachments) { size += attachment.size(); } - foundAttachements += newAttachments; + foundAttachments += newAttachments; } if (size > histMaxSize) { diff --git a/src/core/SignalMultiplexer.cpp b/src/core/SignalMultiplexer.cpp index 7b5fab93b..0f99b8e65 100644 --- a/src/core/SignalMultiplexer.cpp +++ b/src/core/SignalMultiplexer.cpp @@ -36,7 +36,7 @@ QObject* SignalMultiplexer::currentObject() const void SignalMultiplexer::setCurrentObject(QObject* object) { - // remove all Connections from the list whoes senders/receivers have been deleted + // remove all Connections from the list whose senders/receivers have been deleted QMutableListIterator<Connection> i = m_connections; while (i.hasNext()) { const Connection& con = i.next(); diff --git a/src/crypto/Crypto.cpp b/src/crypto/Crypto.cpp index 4669de69a..d00be720b 100644 --- a/src/crypto/Crypto.cpp +++ b/src/crypto/Crypto.cpp @@ -153,14 +153,14 @@ bool Crypto::testAes256Cbc() return false; } - SymmetricCipher aes256Descrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - if (!aes256Descrypt.init(key, iv)) { - raiseError(aes256Descrypt.errorString()); + SymmetricCipher aes256Decrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); + if (!aes256Decrypt.init(key, iv)) { + raiseError(aes256Decrypt.errorString()); return false; } - QByteArray decryptedText = aes256Descrypt.process(cipherText, &ok); + QByteArray decryptedText = aes256Decrypt.process(cipherText, &ok); if (!ok) { - raiseError(aes256Descrypt.errorString()); + raiseError(aes256Decrypt.errorString()); return false; } if (decryptedText != plainText) { @@ -196,14 +196,14 @@ bool Crypto::testAes256Ecb() return false; } - SymmetricCipher aes256Descrypt(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Decrypt); - if (!aes256Descrypt.init(key, iv)) { - raiseError(aes256Descrypt.errorString()); + SymmetricCipher aes256Decrypt(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Decrypt); + if (!aes256Decrypt.init(key, iv)) { + raiseError(aes256Decrypt.errorString()); return false; } - QByteArray decryptedText = aes256Descrypt.process(cipherText, &ok); + QByteArray decryptedText = aes256Decrypt.process(cipherText, &ok); if (!ok) { - raiseError(aes256Descrypt.errorString()); + raiseError(aes256Decrypt.errorString()); return false; } if (decryptedText != plainText) { diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index 73960a7a1..0512be601 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -301,7 +301,7 @@ bool KeePass2Reader::readHeaderField() break; case KeePass2::TransformRounds: - setTansformRounds(fieldData); + setTransformRounds(fieldData); break; case KeePass2::EncryptionIV: @@ -382,7 +382,7 @@ void KeePass2Reader::setTransformSeed(const QByteArray& data) } } -void KeePass2Reader::setTansformRounds(const QByteArray& data) +void KeePass2Reader::setTransformRounds(const QByteArray& data) { if (data.size() != 8) { raiseError("Invalid transform rounds size"); diff --git a/src/format/KeePass2Reader.h b/src/format/KeePass2Reader.h index 827e671cd..f8b962535 100644 --- a/src/format/KeePass2Reader.h +++ b/src/format/KeePass2Reader.h @@ -48,7 +48,7 @@ private: void setCompressionFlags(const QByteArray& data); void setMasterSeed(const QByteArray& data); void setTransformSeed(const QByteArray& data); - void setTansformRounds(const QByteArray& data); + void setTransformRounds(const QByteArray& data); void setEncryptionIV(const QByteArray& data); void setProtectedStreamKey(const QByteArray& data); void setStreamStartBytes(const QByteArray& data); diff --git a/src/format/KeePass2XmlReader.cpp b/src/format/KeePass2XmlReader.cpp index f70672592..dfb03bd06 100644 --- a/src/format/KeePass2XmlReader.cpp +++ b/src/format/KeePass2XmlReader.cpp @@ -178,7 +178,7 @@ bool KeePass2XmlReader::parseKeePassFile() Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "KeePassFile"); bool rootElementFound = false; - bool rootParsedSuccesfully = false; + bool rootParsedSuccessfully = false; while (!m_xml.error() && m_xml.readNextStartElement()) { if (m_xml.name() == "Meta") { @@ -186,11 +186,11 @@ bool KeePass2XmlReader::parseKeePassFile() } else if (m_xml.name() == "Root") { if (rootElementFound) { - rootParsedSuccesfully = false; + rootParsedSuccessfully = false; raiseError("Multiple root elements"); } else { - rootParsedSuccesfully = parseRoot(); + rootParsedSuccessfully = parseRoot(); rootElementFound = true; } } @@ -199,7 +199,7 @@ bool KeePass2XmlReader::parseKeePassFile() } } - return rootParsedSuccesfully; + return rootParsedSuccessfully; } void KeePass2XmlReader::parseMeta() @@ -458,12 +458,12 @@ bool KeePass2XmlReader::parseRoot() Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Root"); bool groupElementFound = false; - bool groupParsedSuccesfully = false; + bool groupParsedSuccessfully = false; while (!m_xml.error() && m_xml.readNextStartElement()) { if (m_xml.name() == "Group") { if (groupElementFound) { - groupParsedSuccesfully = false; + groupParsedSuccessfully = false; raiseError("Multiple group elements"); continue; } @@ -473,7 +473,7 @@ bool KeePass2XmlReader::parseRoot() Group* oldRoot = m_db->rootGroup(); m_db->setRootGroup(rootGroup); delete oldRoot; - groupParsedSuccesfully = true; + groupParsedSuccessfully = true; } groupElementFound = true; @@ -486,7 +486,7 @@ bool KeePass2XmlReader::parseRoot() } } - return groupParsedSuccesfully; + return groupParsedSuccessfully; } Group* KeePass2XmlReader::parseGroup() diff --git a/src/format/KeePass2XmlWriter.cpp b/src/format/KeePass2XmlWriter.cpp index 6c92c4b39..f8dfa1581 100644 --- a/src/format/KeePass2XmlWriter.cpp +++ b/src/format/KeePass2XmlWriter.cpp @@ -566,9 +566,9 @@ QString KeePass2XmlWriter::stripInvalidXml10Chars(QString str) // keep valid surrogate pair i--; } - else if ((uc < 0x20 && uc != 0x09 && uc != 0x0A && uc != 0x0D) // control chracters - || (uc >= 0x7F && uc <= 0x84) // control chracters, valid but discouraged by XML - || (uc >= 0x86 && uc <= 0x9F) // control chracters, valid but discouraged by XML + else if ((uc < 0x20 && uc != 0x09 && uc != 0x0A && uc != 0x0D) // control characters + || (uc >= 0x7F && uc <= 0x84) // control characters, valid but discouraged by XML + || (uc >= 0x86 && uc <= 0x9F) // control characters, valid but discouraged by XML || (uc > 0xFFFD) // noncharacter || ch.isLowSurrogate() // single low surrogate || ch.isHighSurrogate()) // single high surrogate diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 263bc43fa..985374c49 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -333,8 +333,8 @@ void DatabaseWidget::deleteEntries() selectedEntries.append(m_entryView->entryFromIndex(index)); } - bool inRecylceBin = Tools::hasChild(m_db->metadata()->recycleBin(), selectedEntries.first()); - if (inRecylceBin || !m_db->metadata()->recycleBinEnabled()) { + bool inRecycleBin = Tools::hasChild(m_db->metadata()->recycleBin(), selectedEntries.first()); + if (inRecycleBin || !m_db->metadata()->recycleBinEnabled()) { QMessageBox::StandardButton result; if (selected.size() == 1) { @@ -525,10 +525,10 @@ void DatabaseWidget::deleteGroup() return; } - bool inRecylceBin = Tools::hasChild(m_db->metadata()->recycleBin(), currentGroup); + bool inRecycleBin = Tools::hasChild(m_db->metadata()->recycleBin(), currentGroup); bool isRecycleBin = (currentGroup == m_db->metadata()->recycleBin()); bool isRecycleBinSubgroup = Tools::hasChild(currentGroup, m_db->metadata()->recycleBin()); - if (inRecylceBin || isRecycleBin || isRecycleBinSubgroup || !m_db->metadata()->recycleBinEnabled()) { + if (inRecycleBin || isRecycleBin || isRecycleBinSubgroup || !m_db->metadata()->recycleBinEnabled()) { QMessageBox::StandardButton result = MessageBox::question( this, tr("Delete group?"), tr("Do you really want to delete the group \"%1\" for good?") @@ -1033,13 +1033,13 @@ void DatabaseWidget::reloadDatabaseFile() else { MessageBox::critical(this, tr("Autoreload Failed"), tr("Could not parse or unlock the new database file while attempting" - "to autoreload this database.")); + " to autoreload this database.")); } } else { MessageBox::critical(this, tr("Autoreload Failed"), tr("Could not open the new database file while attempting to autoreload" - "this database.")); + " this database.")); } // Rewatch the database file diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 344a329fa..d9ba5bd83 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -90,7 +90,7 @@ void EditEntryWidget::setupMain() m_mainUi->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); connect(m_mainUi->togglePasswordButton, SIGNAL(toggled(bool)), m_mainUi->passwordEdit, SLOT(setShowPassword(bool))); - connect(m_mainUi->tooglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool))); + connect(m_mainUi->togglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool))); connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool))); m_mainUi->passwordRepeatEdit->enableVerifyMode(m_mainUi->passwordEdit); connect(m_mainUi->passwordGenerator, SIGNAL(appliedPassword(QString)), SLOT(setGeneratedPassword(QString))); @@ -299,8 +299,8 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore) m_mainUi->expireCheck->setEnabled(!m_history); m_mainUi->expireDatePicker->setReadOnly(m_history); m_mainUi->notesEdit->setReadOnly(m_history); - m_mainUi->tooglePasswordGeneratorButton->setChecked(false); - m_mainUi->tooglePasswordGeneratorButton->setDisabled(m_history); + m_mainUi->togglePasswordGeneratorButton->setChecked(false); + m_mainUi->togglePasswordGeneratorButton->setDisabled(m_history); m_mainUi->passwordGenerator->reset(); m_advancedUi->addAttachmentButton->setEnabled(!m_history); updateAttachmentButtonsEnabled(m_advancedUi->attachmentsView->currentIndex()); @@ -529,7 +529,7 @@ void EditEntryWidget::setGeneratedPassword(const QString& password) m_mainUi->passwordEdit->setText(password); m_mainUi->passwordRepeatEdit->setText(password); - m_mainUi->tooglePasswordGeneratorButton->setChecked(false); + m_mainUi->togglePasswordGeneratorButton->setChecked(false); } void EditEntryWidget::insertAttribute() diff --git a/src/gui/entry/EditEntryWidgetMain.ui b/src/gui/entry/EditEntryWidgetMain.ui index ebf32425f..083f1c033 100644 --- a/src/gui/entry/EditEntryWidgetMain.ui +++ b/src/gui/entry/EditEntryWidgetMain.ui @@ -76,7 +76,7 @@ </widget> </item> <item> - <widget class="QToolButton" name="tooglePasswordGeneratorButton"> + <widget class="QToolButton" name="togglePasswordGeneratorButton"> <property name="text"> <string>Generate</string> </property> @@ -166,7 +166,7 @@ <tabstop>passwordEdit</tabstop> <tabstop>passwordRepeatEdit</tabstop> <tabstop>togglePasswordButton</tabstop> - <tabstop>tooglePasswordGeneratorButton</tabstop> + <tabstop>togglePasswordGeneratorButton</tabstop> <tabstop>urlEdit</tabstop> <tabstop>expireCheck</tabstop> <tabstop>expireDatePicker</tabstop> diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index ae654eb74..4e79cd05c 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -19,12 +19,15 @@ #include "CompositeKey_p.h" #include "ChallengeResponseKey.h" -#include <QtConcurrent> #include <QElapsedTimer> +#include <QFile> +#include <QtConcurrent> #include "core/Global.h" #include "crypto/CryptoHash.h" #include "crypto/SymmetricCipher.h" +#include "keys/FileKey.h" +#include "keys/PasswordKey.h" CompositeKey::CompositeKey() { @@ -77,6 +80,29 @@ CompositeKey& CompositeKey::operator=(const CompositeKey& key) return *this; } +/* + * Read a key from a line of input. + * If the line references a valid file + * path, the key is loaded from file. + */ +CompositeKey CompositeKey::readFromLine(QString line) +{ + + CompositeKey key; + if (QFile::exists(line)) { + FileKey fileKey; + fileKey.load(line); + key.addKey(fileKey); + } + else { + PasswordKey password; + password.setPassword(line); + key.addKey(password); + } + return key; + +} + QByteArray CompositeKey::rawKey() const { CryptoHash cryptoHash(CryptoHash::Sha256); diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h index b5f973d20..531c2d9b2 100644 --- a/src/keys/CompositeKey.h +++ b/src/keys/CompositeKey.h @@ -19,6 +19,7 @@ #define KEEPASSX_COMPOSITEKEY_H #include <QList> +#include <QString> #include "keys/Key.h" #include "keys/ChallengeResponseKey.h" @@ -43,6 +44,7 @@ public: void addChallengeResponseKey(const ChallengeResponseKey& key); static int transformKeyBenchmark(int msec); + static CompositeKey readFromLine(QString line); private: static QByteArray transformKeyRaw(const QByteArray& key, const QByteArray& seed, diff --git a/src/keys/FileKey.cpp b/src/keys/FileKey.cpp index d399f545f..d3cdfe040 100644 --- a/src/keys/FileKey.cpp +++ b/src/keys/FileKey.cpp @@ -190,18 +190,18 @@ bool FileKey::loadXml(QIODevice* device) bool FileKey::loadXmlMeta(QXmlStreamReader& xmlReader) { - bool corectVersion = false; + bool correctVersion = false; while (!xmlReader.error() && xmlReader.readNextStartElement()) { if (xmlReader.name() == "Version") { // TODO: error message about incompatible key file version if (xmlReader.readElementText() == "1.00") { - corectVersion = true; + correctVersion = true; } } } - return corectVersion; + return correctVersion; } QByteArray FileKey::loadXmlKey(QXmlStreamReader& xmlReader) diff --git a/src/streams/qtiocompressor.cpp b/src/streams/qtiocompressor.cpp index be6ac5dfd..97955e472 100644 --- a/src/streams/qtiocompressor.cpp +++ b/src/streams/qtiocompressor.cpp @@ -135,7 +135,7 @@ void QtIOCompressorPrivate::flushZlib(int flushMode) if (!writeBytes(buffer, outputSize)) return; - // If the mode is Z_FNISH we must loop until we get Z_STREAM_END, + // If the mode is Z_FINISH we must loop until we get Z_STREAM_END, // else we loop as long as zlib is able to fill the output buffer. } while ((flushMode == Z_FINISH && status != Z_STREAM_END) || (flushMode != Z_FINISH && zlibStream.avail_out == 0)); @@ -522,11 +522,11 @@ qint64 QtIOCompressor::readData(char *data, qint64 maxSize) // Read data if if the input buffer is empty. There could be data in the buffer // from a previous readData call. if (d->zlibStream.avail_in == 0) { - qint64 bytesAvalible = d->device->read(reinterpret_cast<char *>(d->buffer), d->bufferSize); + qint64 bytesAvailable = d->device->read(reinterpret_cast<char *>(d->buffer), d->bufferSize); d->zlibStream.next_in = d->buffer; - d->zlibStream.avail_in = bytesAvalible; + d->zlibStream.avail_in = bytesAvailable; - if (bytesAvalible == -1) { + if (bytesAvailable == -1) { d->state = QtIOCompressorPrivate::Error; setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error reading data from underlying device: ") + d->device->errorString()); return -1; @@ -534,9 +534,9 @@ qint64 QtIOCompressor::readData(char *data, qint64 maxSize) if (d->state != QtIOCompressorPrivate::InStream) { // If we are not in a stream and get 0 bytes, we are probably trying to read from an empty device. - if(bytesAvalible == 0) + if(bytesAvailable == 0) return 0; - else if (bytesAvalible > 0) + else if (bytesAvailable > 0) d->state = QtIOCompressorPrivate::InStream; } } diff --git a/src/zxcvbn/zxcvbn.cpp b/src/zxcvbn/zxcvbn.cpp index 5e5678332..25cbe5440 100644 --- a/src/zxcvbn/zxcvbn.cpp +++ b/src/zxcvbn/zxcvbn.cpp @@ -1,4 +1,4 @@ -/********************************************************************************** +/********************************************************************************** * C implementation of the zxcvbn password strength estimation method. * Copyright (c) 2015, Tony Evans * All rights reserved. @@ -228,12 +228,13 @@ static void AddMatchRepeats(ZxcMatch_t **Result, ZxcMatch_t *Match, const uint8_ while(MaxLen >= (Len * RepeatCount)) { - if (strncmp((const char *)Passwd, (const char *)Rpt, Len) == 0) + if (strncmp(reinterpret_cast<const char *>(Passwd), + reinterpret_cast<const char *>(Rpt), Len) == 0) { /* Found a repeat */ ZxcMatch_t *p = AllocMatch(); p->Entrpy = Match->Entrpy + log(RepeatCount); - p->Type = (ZxcTypeMatch_t)(Match->Type + MULTIPLE_MATCH); + p->Type = static_cast<ZxcTypeMatch_t>(Match->Type + MULTIPLE_MATCH); p->Length = Len * RepeatCount; p->Begin = Match->Begin; AddResult(Result, p, MaxLen); @@ -617,7 +618,7 @@ static void DictionaryEntropy(ZxcMatch_t *m, DictMatchInfo_t *Extra, const uint8 e += d; } /* Add entropy due to word's rank */ - e += log((double)Extra->Rank); + e += log(static_cast<double>(Extra->Rank)); m->Entrpy = e; } @@ -794,7 +795,7 @@ static void UserMatch(ZxcMatch_t **Result, const char *Words[], const uint8_t *P int Caps = 0; int Lowers = 0; int Leets = 0; - const uint8_t *Wrd = (const uint8_t *)(Words[Rank]); + const uint8_t *Wrd = reinterpret_cast<const uint8_t *>(Words[Rank]); const uint8_t *Pwd = Passwd; memset(Extra.Leeted, 0, sizeof Extra.Leeted); memset(Extra.UnLeet, 0, sizeof Extra.UnLeet); @@ -1169,7 +1170,7 @@ static void SpatialMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, int i, j, s; double Degree, Entropy; ZxcMatch_t *p; - Degree = (k->NumNear-1) - (double)k->NumBlank / (double)k->NumKeys; + Degree = (k->NumNear-1) - static_cast<double>(k->NumBlank) / static_cast<double>(k->NumKeys); s = k->NumKeys; if (k->Shifts) s *= 2; @@ -1405,13 +1406,13 @@ static void RepeatMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, i int RepeatCount = 2; while(MaxLen >= (Len * RepeatCount)) { - if (strncmp((const char *)Passwd, (const char *)Rpt, Len) == 0) + if (strncmp(reinterpret_cast<const char *>(Passwd), reinterpret_cast<const char *>(Rpt), Len) == 0) { /* Found a repeat */ int c = Cardinality(Passwd, Len); ZxcMatch_t *p = AllocMatch(); - p->Entrpy = log((double)c) * Len + log(RepeatCount); - p->Type = (ZxcTypeMatch_t)(BRUTE_MATCH + MULTIPLE_MATCH); + p->Entrpy = log(static_cast<double>(c)) * Len + log(RepeatCount); + p->Type = static_cast<ZxcTypeMatch_t>(BRUTE_MATCH + MULTIPLE_MATCH); p->Length = Len * RepeatCount; p->Begin = Start; AddResult(Result, p, MaxLen); @@ -1527,7 +1528,7 @@ static void SequenceMatch(ZxcMatch_t **Result, const uint8_t *Passwd, int Start, p->Type = SEQUENCE_MATCH; p->Begin = Start; p->Length = i; - p->Entrpy = e + log((double)i); + p->Entrpy = e + log(static_cast<double>(i)); AddMatchRepeats(Result, p, Pwd, MaxLen); AddResult(Result, p, MaxLen); } @@ -1578,13 +1579,13 @@ double ZxcvbnMatch(const char *Pwd, const char *UserDict[], ZxcMatch_t **Info) Node_t *Np; double e; int Len = strlen(Pwd); - const uint8_t *Passwd = (const uint8_t *)Pwd; + const uint8_t *Passwd = reinterpret_cast<const uint8_t *>(Pwd); uint8_t *RevPwd; /* Create the paths */ Node_t *Nodes = MallocFn(Node_t, Len+1); memset(Nodes, 0, (Len+1) * sizeof *Nodes); i = Cardinality(Passwd, Len); - e = log((double)i); + e = log(static_cast<double>(i)); /* Do matching for all parts of the password */ for(i = 0; i < Len; ++i) diff --git a/src/zxcvbn/zxcvbn.h b/src/zxcvbn/zxcvbn.h index 796d6b47b..2d3ec52c1 100644 --- a/src/zxcvbn/zxcvbn.h +++ b/src/zxcvbn/zxcvbn.h @@ -1,4 +1,4 @@ -#ifndef ZXCVBN_H_F98183CE2A01_INCLUDED +#ifndef ZXCVBN_H_F98183CE2A01_INCLUDED #define ZXCVBN_H_F98183CE2A01_INCLUDED /********************************************************************************** * C implementation of the zxcvbn password strength estimation method. diff --git a/tests/TestEntryModel.cpp b/tests/TestEntryModel.cpp index 3f956d7f3..d5a16ebab 100644 --- a/tests/TestEntryModel.cpp +++ b/tests/TestEntryModel.cpp @@ -236,15 +236,15 @@ void TestEntryModel::testAutoTypeAssociationsModel() QCOMPARE(model->rowCount(), 0); - AutoTypeAssociations* assocications = new AutoTypeAssociations(this); - model->setAutoTypeAssociations(assocications); + AutoTypeAssociations* associations = new AutoTypeAssociations(this); + model->setAutoTypeAssociations(associations); QCOMPARE(model->rowCount(), 0); AutoTypeAssociations::Association assoc; assoc.window = "1"; assoc.sequence = "2"; - assocications->add(assoc); + associations->add(assoc); QCOMPARE(model->rowCount(), 1); QCOMPARE(model->data(model->index(0, 0)).toString(), QString("1")); @@ -252,17 +252,17 @@ void TestEntryModel::testAutoTypeAssociationsModel() assoc.window = "3"; assoc.sequence = "4"; - assocications->update(0, assoc); + associations->update(0, assoc); QCOMPARE(model->data(model->index(0, 0)).toString(), QString("3")); QCOMPARE(model->data(model->index(0, 1)).toString(), QString("4")); - assocications->add(assoc); - assocications->remove(0); + associations->add(assoc); + associations->remove(0); QCOMPARE(model->rowCount(), 1); delete modelTest; delete model; - delete assocications; + delete associations; } void TestEntryModel::testProxyModel() diff --git a/tests/TestKeys.cpp b/tests/TestKeys.cpp index 6c1953faf..d5b35b1fb 100644 --- a/tests/TestKeys.cpp +++ b/tests/TestKeys.cpp @@ -83,6 +83,22 @@ void TestKeys::testComposite() delete compositeKey4; } +void TestKeys::testCompositeKeyReadFromLine() +{ + + QString keyFilename = QString("%1/FileKeyXml.key").arg(QString(KEEPASSX_TEST_DATA_DIR)); + + CompositeKey compositeFileKey = CompositeKey::readFromLine(keyFilename); + FileKey fileKey; + fileKey.load(keyFilename); + QCOMPARE(compositeFileKey.rawKey().size(), fileKey.rawKey().size()); + + CompositeKey compositePasswordKey = CompositeKey::readFromLine(QString("password")); + PasswordKey passwordKey(QString("password")); + QCOMPARE(compositePasswordKey.rawKey().size(), passwordKey.rawKey().size()); + +} + void TestKeys::testFileKey() { QFETCH(QString, type); diff --git a/tests/TestKeys.h b/tests/TestKeys.h index 0f14117fd..a6d0b7e1a 100644 --- a/tests/TestKeys.h +++ b/tests/TestKeys.h @@ -27,6 +27,7 @@ class TestKeys : public QObject private Q_SLOTS: void initTestCase(); void testComposite(); + void testCompositeKeyReadFromLine(); void testFileKey(); void testFileKey_data(); void testCreateFileKey(); diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 01f165243..babab8ab7 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -328,7 +328,7 @@ void TestGui::testEntryEntropy() QTest::keyClicks(titleEdit, "test"); // Open the password generator - QToolButton* generatorButton = editEntryWidget->findChild<QToolButton*>("tooglePasswordGeneratorButton"); + QToolButton* generatorButton = editEntryWidget->findChild<QToolButton*>("togglePasswordGeneratorButton"); QTest::mouseClick(generatorButton, Qt::LeftButton); // Type in some password diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index d0cfb5a31..846e39230 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -18,10 +18,14 @@ include_directories(../src) add_executable(kdbx-extract kdbx-extract.cpp) target_link_libraries(kdbx-extract keepassx_core - ${MHD_LIBRARIES} Qt5::Core - Qt5::Concurrent - Qt5::Widgets + ${GCRYPT_LIBRARIES} + ${ZLIB_LIBRARIES}) + +add_executable(kdbx-merge kdbx-merge.cpp) +target_link_libraries(kdbx-merge + keepassx_core + Qt5::Core ${GCRYPT_LIBRARIES} ${ZLIB_LIBRARIES}) diff --git a/utils/kdbx-extract.cpp b/utils/kdbx-extract.cpp index f5d2a19a6..255f5d003 100644 --- a/utils/kdbx-extract.cpp +++ b/utils/kdbx-extract.cpp @@ -17,6 +17,7 @@ #include <stdio.h> +#include <QCommandLineParser> #include <QCoreApplication> #include <QFile> #include <QStringList> @@ -33,8 +34,16 @@ int main(int argc, char **argv) { QCoreApplication app(argc, argv); - if (app.arguments().size() != 3) { - qCritical("Usage: kdbx-extract <password/key file> <kdbx file>"); + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", + "Extract and print a KeePassXC database file.")); + parser.addPositionalArgument("database", QCoreApplication::translate("main", "path of the database to extract.")); + parser.addHelpOption(); + parser.process(app); + + const QStringList args = parser.positionalArguments(); + if (args.size() != 1) { + parser.showHelp(); return 1; } @@ -42,25 +51,18 @@ int main(int argc, char **argv) qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); } - CompositeKey key; - if (QFile::exists(app.arguments().at(1))) { - FileKey fileKey; - fileKey.load(app.arguments().at(1)); - key.addKey(fileKey); - } - else { - PasswordKey password; - password.setPassword(app.arguments().at(1)); - key.addKey(password); - } + static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); + QString line = inputTextStream.readLine(); + CompositeKey key = CompositeKey::readFromLine(line); - QFile dbFile(app.arguments().at(2)); + QString databaseFilename = args.at(0); + QFile dbFile(databaseFilename); if (!dbFile.exists()) { - qCritical("File does not exist."); + qCritical("File %s does not exist.", qPrintable(databaseFilename)); return 1; } if (!dbFile.open(QIODevice::ReadOnly)) { - qCritical("Unable to open file."); + qCritical("Unable to open file %s.", qPrintable(databaseFilename)); return 1; } diff --git a/utils/kdbx-merge.cpp b/utils/kdbx-merge.cpp new file mode 100644 index 000000000..da780ea1b --- /dev/null +++ b/utils/kdbx-merge.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2010 Felix Geyer <debfx@fobos.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +#include <QCommandLineParser> +#include <QCoreApplication> +#include <QFile> +#include <QSaveFile> +#include <QStringList> +#include <QTextStream> + +#include "core/Database.h" +#include "crypto/Crypto.h" +#include "format/KeePass2Reader.h" +#include "format/KeePass2Writer.h" +#include "keys/CompositeKey.h" + +int main(int argc, char **argv) +{ + + QCoreApplication app(argc, argv); + + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", "Merge 2 KeePassXC database files.")); + parser.addPositionalArgument("database1", QCoreApplication::translate("main", "path of the database to merge into.")); + parser.addPositionalArgument("database2", QCoreApplication::translate("main", "path of the database to merge from.")); + + QCommandLineOption samePasswordOption(QStringList() << "s" << "same-password", + QCoreApplication::translate("main", "use the same password for both database files.")); + + parser.addHelpOption(); + parser.addOption(samePasswordOption); + parser.process(app); + + const QStringList args = parser.positionalArguments(); + if (args.size() != 2) { + parser.showHelp(); + return 1; + } + + if (!Crypto::init()) { + qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); + } + + static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); + + QString line1 = inputTextStream.readLine(); + CompositeKey key1 = CompositeKey::readFromLine(line1); + + CompositeKey key2; + if (parser.isSet("same-password")) { + key2 = *key1.clone(); + } + else { + QString line2 = inputTextStream.readLine(); + key2 = CompositeKey::readFromLine(line2); + } + + + QString databaseFilename1 = args.at(0); + QFile dbFile1(databaseFilename1); + if (!dbFile1.exists()) { + qCritical("File %s does not exist.", qPrintable(databaseFilename1)); + return 1; + } + if (!dbFile1.open(QIODevice::ReadOnly)) { + qCritical("Unable to open file %s.", qPrintable(databaseFilename1)); + return 1; + } + + KeePass2Reader reader1; + Database* db1 = reader1.readDatabase(&dbFile1, key1); + + if (reader1.hasError()) { + qCritical("Error while parsing the database:\n%s\n", qPrintable(reader1.errorString())); + return 1; + } + + + QString databaseFilename2 = args.at(1); + QFile dbFile2(databaseFilename2); + if (!dbFile2.exists()) { + qCritical("File %s does not exist.", qPrintable(databaseFilename2)); + return 1; + } + if (!dbFile2.open(QIODevice::ReadOnly)) { + qCritical("Unable to open file %s.", qPrintable(databaseFilename2)); + return 1; + } + + KeePass2Reader reader2; + Database* db2 = reader2.readDatabase(&dbFile2, key2); + + if (reader2.hasError()) { + qCritical("Error while parsing the database:\n%s\n", qPrintable(reader2.errorString())); + return 1; + } + + db1->merge(db2); + + QSaveFile saveFile(databaseFilename1); + if (!saveFile.open(QIODevice::WriteOnly)) { + qCritical("Unable to open file %s for writing.", qPrintable(databaseFilename1)); + return 1; + } + + KeePass2Writer writer; + writer.writeDatabase(&saveFile, db1); + + if (writer.hasError()) { + qCritical("Error while updating the database:\n%s\n", qPrintable(writer.errorString())); + return 1; + } + + if (!saveFile.commit()) { + qCritical("Error while updating the database:\n%s\n", qPrintable(writer.errorString())); + return 0; + } + + qDebug("Successfully merged the database files.\n"); + return 1; + +} |