From 66ff42c78bfec3ab6cb07f1d4f9e5144b648a1b3 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sat, 20 Sep 2025 23:42:51 -0400 Subject: [PATCH] Final fixes --- share/translations/keepassxc_en.ts | 8 ++++---- src/gui/DatabaseOpenWidget.cpp | 21 ++++++++++++++------ src/gui/DatabaseOpenWidget.h | 1 + src/gui/DatabaseTabWidget.cpp | 31 +++++++++++++++++------------- src/gui/MainWindow.cpp | 2 +- tests/gui/TestGui.cpp | 16 +++++++-------- 6 files changed, 47 insertions(+), 32 deletions(-) diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index f974db170..6bba8c8f9 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -1799,6 +1799,10 @@ Are you sure you want to continue with this file?. Press ESC again to close this database + + The database file does not exist or is not accessible. + + DatabaseSettingWidgetMetaData @@ -2608,10 +2612,6 @@ This is definitely a bug, please report it to the developers. Open database - - Failed to open %1. It either does not exist or is not accessible. - - CSV file diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 167ee32f5..35fc34800 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -38,6 +38,7 @@ namespace { constexpr int clearFormsDelay = 30000; + constexpr int fileExistsCheckInterval = 5000; bool isQuickUnlockAvailable() { @@ -68,6 +69,16 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) m_ui->editPassword->setShowPassword(false); }); + m_fileExistsTimer.setInterval(fileExistsCheckInterval); + m_fileExistsTimer.setSingleShot(false); + connect(&m_fileExistsTimer, &QTimer::timeout, this, [this] { + if (!QFile::exists(m_filename)) { + m_ui->messageWidget->showMessage(tr("The database file does not exist or is not accessible."), + MessageWidget::Warning, + fileExistsCheckInterval + 500); + } + }); + QFont font; font.setPointSize(font.pointSize() + 4); font.setBold(true); @@ -215,6 +226,7 @@ bool DatabaseOpenWidget::event(QEvent* event) } if (isVisible()) { + m_fileExistsTimer.start(); m_hideTimer.stop(); pollHardwareKey(); } @@ -226,6 +238,8 @@ bool DatabaseOpenWidget::event(QEvent* event) m_hideTimer.start(); } + m_fileExistsTimer.stop(); + #ifdef WITH_XC_YUBIKEY if (type == QEvent::Hide) { m_deviceListener->deregisterAllHotplugCallbacks(); @@ -257,12 +271,7 @@ void DatabaseOpenWidget::load(const QString& filename) // Read public headers QString error; m_db.reset(new Database()); - bool openSuccess = m_db->open(m_filename, nullptr, &error); - - // If opening failed (e.g., file doesn't exist), show an informative message - if (!openSuccess && !error.isEmpty()) { - m_ui->messageWidget->showMessage(error, MessageWidget::MessageType::Warning); - } + m_db->open(m_filename, nullptr, &error); m_ui->fileNameLabel->setRawText(m_filename); diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index d730634b3..14e15e711 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -97,6 +97,7 @@ private: bool m_triedToQuit = false; QTimer m_hideTimer; QTimer m_hideNoHardwareKeysFoundTimer; + QTimer m_fileExistsTimer; Q_DISABLE_COPY(DatabaseOpenWidget) }; diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 825bceb1e..70712028f 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -163,24 +163,29 @@ void DatabaseTabWidget::addDatabaseTab(const QString& filePath, QString canonicalFilePath = fileInfo.canonicalFilePath(); if (canonicalFilePath.isEmpty()) { - // Don't return early - continue to show unlock dialog even if file is missing - // This allows user to retry when file becomes available (e.g., cloud storage mounting) - emit messageGlobal(tr("Failed to open %1. It either does not exist or is not accessible.").arg(cleanFilePath), - MessageWidget::Error); - canonicalFilePath = cleanFilePath; // Use the original path for comparison + // The file does not exist, revert back to the cleaned path for comparison + canonicalFilePath = cleanFilePath; } + // Try to find an existing tab with the same file path for (int i = 0, c = count(); i < c; ++i) { auto* dbWidget = databaseWidgetFromIndex(i); - Q_ASSERT(dbWidget); - if (dbWidget - && dbWidget->database()->canonicalFilePath().compare(canonicalFilePath, FILE_CASE_SENSITIVE) == 0) { - dbWidget->performUnlockDatabase(password, keyfile); - if (!inBackground) { - // switch to existing tab if file is already open - setCurrentIndex(indexOf(dbWidget)); + if (dbWidget) { + auto dbFilePath = dbWidget->database()->canonicalFilePath(); + if (dbFilePath.isEmpty()) { + // The file does not exist, revert back to the cleaned path for comparison + dbFilePath = QDir::toNativeSeparators(dbWidget->database()->filePath()); + } + if (dbFilePath.compare(canonicalFilePath, FILE_CASE_SENSITIVE) == 0) { + // Attempt to unlock the database if password and/or keyfile is provided + dbWidget->performUnlockDatabase(password, keyfile); + if (!inBackground) { + // switch to existing tab if file is already open + setCurrentIndex(indexOf(dbWidget)); + } + // Prevent opening a new tab for this file + return; } - return; } } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index d51588ac5..47ef32eac 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -714,7 +714,7 @@ void MainWindow::restoreConfigState() if (config()->get(Config::OpenPreviousDatabasesOnStartup).toBool()) { const QStringList fileNames = config()->get(Config::LastOpenedDatabases).toStringList(); for (const QString& filename : fileNames) { - if (!filename.isEmpty() && QFile::exists(filename)) { + if (!filename.isEmpty()) { openDatabase(filename); } } diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 9a0f8c1d0..798e621f9 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -2469,31 +2469,31 @@ void TestGui::testOpenMissingDatabaseFile() // Test that when trying to open a non-existent database file, // the unlock dialog is still shown (instead of auto-closing) // This allows user to retry when the file becomes available (e.g., cloud storage mounting) - + const QString nonExistentPath = "/tmp/does_not_exist.kdbx"; - + // Ensure the file doesn't exist QFile::remove(nonExistentPath); QVERIFY(!QFile::exists(nonExistentPath)); - + // Record initial tab count int initialTabCount = m_tabWidget->count(); - + // Try to add database tab with non-existent file // This should NOT fail but should create a tab and show unlock dialog m_tabWidget->addDatabaseTab(nonExistentPath); - + // Verify that a tab was created (unlock dialog shown) QCOMPARE(m_tabWidget->count(), initialTabCount + 1); - + // Get the database widget for the new tab auto* dbWidget = m_tabWidget->currentDatabaseWidget(); QVERIFY(dbWidget); - + // Verify the database is in a state where it can be unlocked // (not closed/rejected due to missing file) QVERIFY(dbWidget->isLocked()); - + // Close the tab to clean up m_tabWidget->closeDatabaseTab(m_tabWidget->currentIndex()); QCOMPARE(m_tabWidget->count(), initialTabCount);