diff --git a/AppImage-Recipe.sh b/AppImage-Recipe.sh index 4cb5ceda1..f8d7e105f 100755 --- a/AppImage-Recipe.sh +++ b/AppImage-Recipe.sh @@ -87,14 +87,15 @@ else fi EOF chmod +x ./usr/bin/keepassxc_env -sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' org.${LOWERAPP}.desktop -get_desktopintegration "org.${LOWERAPP}" - -GLIBC_NEEDED=$(glibc_needed) +sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' org.${LOWERAPP}.${APP}.desktop +get_desktopintegration "org.${LOWERAPP}.${APP}" cd .. -generate_type2_appimage +GLIBC_NEEDED=$(glibc_needed) +NO_GLIBC_VERSION=true -mv ../out/*.AppImage ../KeePassXC-${VERSION}-${ARCH}.AppImage -rmdir ../out > /dev/null 2>&1 +generate_type2_appimage -u "gh-releases-zsync|keepassxreboot|keepassxc|latest|KeePassXC-*-${ARCH}.AppImage.zsync" + +mv ../out/*.AppImage* ../ +rm -rf ../out diff --git a/Dockerfile b/Dockerfile index ad9a2f442..a5966be39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,8 @@ RUN set -x \ && apt-get -y install software-properties-common RUN set -x \ - && add-apt-repository ppa:beineri/opt-qt${QT5_PPA_VERSION}-trusty + && add-apt-repository ppa:beineri/opt-qt${QT5_PPA_VERSION}-trusty \ + && add-apt-repository ppa:phoerious/keepassxc RUN set -x \ && apt-get update -y \ @@ -42,7 +43,9 @@ RUN set -x \ zlib1g-dev \ libxi-dev \ libxtst-dev \ - mesa-common-dev + mesa-common-dev \ + libyubikey-dev \ + libykpers-1-dev ENV CMAKE_PREFIX_PATH=/opt/qt${QT5_VERSION}/lib/cmake ENV LD_LIBRARY_PATH=/opt/qt${QT5_VERSION}/lib @@ -52,34 +55,8 @@ RUN set -x \ # AppImage dependencies RUN set -x \ && apt-get install -y \ - wget \ - libfuse2 - -# build libyubikey -ENV YUBIKEY_VERSION=1.13 -RUN set -x \ - && wget "https://developers.yubico.com/yubico-c/Releases/libyubikey-${YUBIKEY_VERSION}.tar.gz" \ - && tar xf libyubikey-${YUBIKEY_VERSION}.tar.gz \ - && cd libyubikey-${YUBIKEY_VERSION} \ - && ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu \ - && make \ - && make install \ - && cd .. \ - && rm -Rf libyubikey-${YUBIKEY_VERSION}* - -# build libykpers-1 -ENV YKPERS_VERSION=1.18.0 -RUN set -x \ - && apt-get install -y libusb-dev -RUN set -x \ - && wget "https://developers.yubico.com/yubikey-personalization/Releases/ykpers-${YKPERS_VERSION}.tar.gz" \ - && tar xf ykpers-${YKPERS_VERSION}.tar.gz \ - && cd ykpers-${YKPERS_VERSION} \ - && ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu \ - && make \ - && make install \ - && cd .. \ - && rm -Rf ykpers-${YKPERS_VERSION}* + libfuse2 \ + wget VOLUME /keepassxc/src VOLUME /keepassxc/out diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 8510279b5..ce3e04c1a 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -298,8 +298,7 @@ bool DatabaseTabWidget::closeDatabase(Database* db) if (!saveDatabase(db)) { return false; } - } - else { + } else if (dbStruct.dbWidget->currentMode() != DatabaseWidget::LockedMode) { QMessageBox::StandardButton result = MessageBox::question( this, tr("Save changes?"), @@ -307,10 +306,9 @@ bool DatabaseTabWidget::closeDatabase(Database* db) QMessageBox::Yes | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Yes); if (result == QMessageBox::Yes) { if (!saveDatabase(db)) { - return false; + return false; } - } - else if (result == QMessageBox::Cancel) { + } else if (result == QMessageBox::Cancel) { return false; } } @@ -355,8 +353,13 @@ bool DatabaseTabWidget::saveDatabase(Database* db) { DatabaseManagerStruct& dbStruct = m_dbList[db]; - if (dbStruct.saveToFilename) { + if (dbStruct.dbWidget->currentMode() == DatabaseWidget::LockedMode) { + // Never allow saving a locked database; it causes corruption + // We return true since a save is not required + return true; + } + if (dbStruct.saveToFilename) { dbStruct.dbWidget->blockAutoReload(true); QString errorMessage = db->saveToFile(dbStruct.canonicalFilePath); dbStruct.dbWidget->blockAutoReload(false); @@ -375,7 +378,6 @@ bool DatabaseTabWidget::saveDatabase(Database* db) MessageWidget::Error); return false; } - } else { return saveDatabaseAs(db); } diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index a0671af98..5dd7c5ed0 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1206,7 +1206,7 @@ void DatabaseWidget::onWatchedFileChanged() void DatabaseWidget::reloadDatabaseFile() { - if (m_db == nullptr) { + if (!m_db || currentMode() == DatabaseWidget::LockedMode) { return; } diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 54b0ca288..ad736bf20 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -31,9 +31,18 @@ PasswordEdit::PasswordEdit(QWidget* parent) { setEchoMode(QLineEdit::Password); updateStylesheet(); - - // set font to system monospace font and increase letter spacing + + // use a monospace font for the password field QFont passwordFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); +#ifdef Q_OS_WIN + // try to use Consolas on Windows, because the default Courier New has too many similar characters + QFont consolasFont = QFontDatabase().font("Consolas", passwordFont.styleName(), passwordFont.pointSize()); + const QFont defaultFont; + if (passwordFont != defaultFont) { + passwordFont = consolasFont; + } +#endif + passwordFont.setLetterSpacing(QFont::PercentageSpacing, 110); setFont(passwordFont); } diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index f1fe37502..dda0ab1d0 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -536,6 +536,7 @@ void EditEntryWidget::loadEntry(Entry* entry, bool create, bool history, const Q m_database = database; m_create = create; m_history = history; + m_saved = false; if (history) { setHeadline(QString("%1 > %2").arg(parentName, tr("Entry history"))); @@ -712,6 +713,7 @@ void EditEntryWidget::saveEntry() } updateEntryData(m_entry); + m_saved = true; if (!m_create) { m_entry->endUpdate(); @@ -790,7 +792,7 @@ void EditEntryWidget::cancel() clear(); - emit editFinished(false); + emit editFinished(m_saved); } void EditEntryWidget::clear() diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index a151f1b5e..883e7e7fb 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -147,6 +147,7 @@ private: bool m_create; bool m_history; + bool m_saved; #ifdef WITH_XC_SSHAGENT bool m_sshAgentEnabled; KeeAgentSettings m_sshAgentSettings; diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index ffa10b7dd..664bfd654 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -372,15 +372,27 @@ void TestGui::testAddEntry() QTest::keyClicks(passwordRepeatEdit, "something 2"); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); - // Add entry "something 3" + // Add entry "something 3" using the apply button then click ok QTest::mouseClick(entryNewWidget, Qt::LeftButton); QTest::keyClicks(titleEdit, "something 3"); + QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); + // Add entry "something 4" using the apply button then click cancel + QTest::mouseClick(entryNewWidget, Qt::LeftButton); + QTest::keyClicks(titleEdit, "something 4"); + QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton); + QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Cancel), Qt::LeftButton); + + // Add entry "something 5" but click cancel button (does NOT add entry) + QTest::mouseClick(entryNewWidget, Qt::LeftButton); + QTest::keyClicks(titleEdit, "something 5"); + QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Cancel), Qt::LeftButton); + QApplication::processEvents(); - // Confirm that 4 entries now exist - QTRY_COMPARE(entryView->model()->rowCount(), 4); + // Confirm that 5 entries now exist + QTRY_COMPARE(entryView->model()->rowCount(), 5); } void TestGui::testPasswordEntryEntropy() @@ -553,7 +565,7 @@ void TestGui::testTotp() void TestGui::testSearch() { // Add canned entries for consistent testing - testAddEntry(); + Q_UNUSED(addCannedEntries()); QToolBar* toolBar = m_mainWindow->findChild("toolBar"); @@ -669,7 +681,7 @@ void TestGui::testSearch() void TestGui::testDeleteEntry() { // Add canned entries for consistent testing - testAddEntry(); + Q_UNUSED(addCannedEntries()); GroupView* groupView = m_dbWidget->findChild("groupView"); EntryView* entryView = m_dbWidget->findChild("entryView"); @@ -945,6 +957,42 @@ void TestGui::cleanupTestCase() delete m_mainWindow; } +int TestGui::addCannedEntries() +{ + int entries_added = 0; + + // Find buttons + QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + QWidget* entryNewWidget = toolBar->widgetForAction(m_mainWindow->findChild("actionEntryNew")); + EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); + QLineEdit* passwordEdit = editEntryWidget->findChild("passwordEdit"); + QLineEdit* passwordRepeatEdit = editEntryWidget->findChild("passwordRepeatEdit"); + + // Add entry "test" and confirm added + QTest::mouseClick(entryNewWidget, Qt::LeftButton); + QTest::keyClicks(titleEdit, "test"); + QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); + QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); + ++entries_added; + + // Add entry "something 2" + QTest::mouseClick(entryNewWidget, Qt::LeftButton); + QTest::keyClicks(titleEdit, "something 2"); + QTest::keyClicks(passwordEdit, "something 2"); + QTest::keyClicks(passwordRepeatEdit, "something 2"); + QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); + ++entries_added; + + // Add entry "something 3" + QTest::mouseClick(entryNewWidget, Qt::LeftButton); + QTest::keyClicks(titleEdit, "something 3"); + QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); + ++entries_added; + + return entries_added; +} + void TestGui::checkDatabase(QString dbFileName) { if (dbFileName.isEmpty()) diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h index 9929b4e7f..5ec8237b3 100644 --- a/tests/gui/TestGui.h +++ b/tests/gui/TestGui.h @@ -62,6 +62,7 @@ private slots: void testDatabaseLocking(); private: + int addCannedEntries(); void checkDatabase(QString dbFileName = ""); void triggerAction(const QString& name); void dragAndDropGroup(const QModelIndex& sourceIndex, const QModelIndex& targetIndex, int row,