From 88bffa38795541cfa496213b5e83a32e67fb98b0 Mon Sep 17 00:00:00 2001 From: Aetf Date: Thu, 21 Feb 2019 00:51:23 -0500 Subject: [PATCH 1/2] Improve existing code prior to implementing FDO Secrets * DatabaseTabWidget::newDatabase returns the created DatabaseWidget * Emit DatabaseTabWidget::databaseOpened signal before a new tab is added * EntrySearcher can now search attribute values including custom ones * Add Group::applyGroupIconTo to set the group icon on the supplied entry * Implement desktop notifications through the system tray icon * Add DatabaseWidget::deleteEntries to delete a list of entries * Add Aes128 in SymmetricCipher::algorithmIvSize * Add DatabaseWidget::databaseReplaced signal * Add a helper class to override the message box's parent (prevent bugs) --- src/core/EntrySearcher.cpp | 22 +++++++++++++++++---- src/core/EntrySearcher.h | 5 +++-- src/core/Group.cpp | 17 ++++++++++++++++ src/core/Group.h | 2 ++ src/crypto/SymmetricCipher.cpp | 2 ++ src/gui/DatabaseTabWidget.cpp | 12 ++++++++---- src/gui/DatabaseTabWidget.h | 3 ++- src/gui/DatabaseWidget.cpp | 27 +++++++++---------------- src/gui/DatabaseWidget.h | 5 ++++- src/gui/MainWindow.cpp | 17 ++++++++++++++++ src/gui/MainWindow.h | 1 + src/gui/MessageBox.cpp | 23 ++++++++++++++++++++++ src/gui/MessageBox.h | 11 +++++++++++ tests/TestEntrySearcher.cpp | 36 ++++++++++++++++++++++++++++++++++ tests/TestEntrySearcher.h | 1 + 15 files changed, 154 insertions(+), 30 deletions(-) diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index f6c67ad502..cf6a4b5f3f 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -140,12 +140,20 @@ bool EntrySearcher::searchEntryImpl(Entry* entry) case Field::Notes: found = term->regex.match(entry->notes()).hasMatch(); break; - case Field::Attribute: + case Field::AttributeKey: found = !attributes.filter(term->regex).empty(); break; case Field::Attachment: found = !attachments.filter(term->regex).empty(); break; + case Field::AttributeValue: + // skip protected attributes + if (entry->attributes()->isProtected(term->word)) { + continue; + } + found = entry->attributes()->contains(term->word) + && term->regex.match(entry->attributes()->value(term->word)).hasMatch(); + break; default: // Terms without a specific field try to match title, username, url, and notes found = term->regex.match(entry->resolvePlaceholder(entry->title())).hasMatch() @@ -207,12 +215,18 @@ void EntrySearcher::parseSearchTerms(const QString& searchString) } else if (field.compare("notes", cs) == 0) { term->field = Field::Notes; } else if (field.startsWith("attr", cs)) { - term->field = Field::Attribute; + term->field = Field::AttributeKey; } else if (field.startsWith("attach", cs)) { term->field = Field::Attachment; - } else { - term->field = Field::Undefined; + } else if (field.startsWith("_", cs)) { + term->field = Field::AttributeValue; + // searching a custom attribute + // in this case term->word is the attribute key (removing the leading "_") + // and term->regex is used to match attribute value + term->word = field.mid(1); } + } else { + term->field = Field::Undefined; } m_searchTerms.append(term); diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h index 153a0612ee..4a33949244 100644 --- a/src/core/EntrySearcher.h +++ b/src/core/EntrySearcher.h @@ -48,8 +48,9 @@ class EntrySearcher Password, Url, Notes, - Attribute, - Attachment + AttributeKey, + Attachment, + AttributeValue }; struct SearchTerm diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 87741ee0cd..814ac2f3b5 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -1057,6 +1057,23 @@ Entry* Group::addEntryWithPath(const QString& entryPath) return entry; } +void Group::applyGroupIconTo(Entry* entry) +{ + if (!config()->get("UseGroupIconOnEntryCreation").toBool()) { + return; + } + + if (iconNumber() == Group::DefaultIconNumber && iconUuid().isNull()) { + return; + } + + if (iconUuid().isNull()) { + entry->setIcon(iconNumber()); + } else { + entry->setIcon(iconUuid()); + } +} + bool Group::GroupData::operator==(const Group::GroupData& other) const { return equals(other, CompareItemDefault); diff --git a/src/core/Group.h b/src/core/Group.h index 4b85692074..59e455ac07 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -167,6 +167,8 @@ class Group : public QObject void addEntry(Entry* entry); void removeEntry(Entry* entry); + void applyGroupIconTo(Entry* entry); + signals: void groupDataChanged(Group* group); void groupAboutToAdd(Group* group, int index); diff --git a/src/crypto/SymmetricCipher.cpp b/src/crypto/SymmetricCipher.cpp index cdb0d1f562..ee4295eec5 100644 --- a/src/crypto/SymmetricCipher.cpp +++ b/src/crypto/SymmetricCipher.cpp @@ -126,6 +126,8 @@ int SymmetricCipher::algorithmIvSize(Algorithm algo) switch (algo) { case ChaCha20: return 12; + case Aes128: + return 16; case Aes256: return 16; case Twofish: diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index fb234795cc..61051e3f0b 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -116,15 +116,17 @@ QSharedPointer DatabaseTabWidget::execNewDatabaseWizard() return db; } -void DatabaseTabWidget::newDatabase() +DatabaseWidget* DatabaseTabWidget::newDatabase() { auto db = execNewDatabaseWizard(); if (!db) { - return; + return nullptr; } - addDatabaseTab(new DatabaseWidget(db, this)); + auto dbWidget = new DatabaseWidget(db, this); + addDatabaseTab(dbWidget); db->markAsModified(); + return dbWidget; } void DatabaseTabWidget::openDatabase() @@ -187,10 +189,12 @@ void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget, bool inBackgrou { Q_ASSERT(dbWidget->database()); + // emit before index change + emit databaseOpened(dbWidget); + int index = addTab(dbWidget, ""); updateTabName(index); toggleTabbar(); - if (!inBackground) { setCurrentIndex(index); } diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index bafbfa37af..af84c0a1e1 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -60,7 +60,7 @@ public slots: bool closeDatabaseTabFromSender(); void updateTabName(int index = -1); - void newDatabase(); + DatabaseWidget* newDatabase(); void openDatabase(); void mergeDatabase(); void importCsv(); @@ -80,6 +80,7 @@ public slots: void performGlobalAutoType(); signals: + void databaseOpened(DatabaseWidget* dbWidget); void databaseClosed(const QString& filePath); void databaseUnlocked(DatabaseWidget* dbWidget); void databaseLocked(DatabaseWidget* dbWidget); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index e4f175bf26..178af80051 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -362,27 +362,10 @@ void DatabaseWidget::createEntry() m_newEntry->setUuid(QUuid::createUuid()); m_newEntry->setUsername(m_db->metadata()->defaultUserName()); m_newParent = m_groupView->currentGroup(); - setIconFromParent(); + m_newParent->applyGroupIconTo(m_newEntry.data()); switchToEntryEdit(m_newEntry.data(), true); } -void DatabaseWidget::setIconFromParent() -{ - if (!config()->get("UseGroupIconOnEntryCreation").toBool()) { - return; - } - - if (m_newParent->iconNumber() == Group::DefaultIconNumber && m_newParent->iconUuid().isNull()) { - return; - } - - if (m_newParent->iconUuid().isNull()) { - m_newEntry->setIcon(m_newParent->iconNumber()); - } else { - m_newEntry->setIcon(m_newParent->iconUuid()); - } -} - void DatabaseWidget::replaceDatabase(QSharedPointer db) { // TODO: instead of increasing the ref count temporarily, there should be a clean @@ -393,6 +376,9 @@ void DatabaseWidget::replaceDatabase(QSharedPointer db) connectDatabaseSignals(); m_groupView->changeDatabase(m_db); processAutoOpen(); + + emit databaseReplaced(oldDb, m_db); + #if defined(WITH_XC_KEESHARE) KeeShare::instance()->connectDatabase(m_db, oldDb); #else @@ -461,6 +447,11 @@ void DatabaseWidget::deleteSelectedEntries() selectedEntries.append(m_entryView->entryFromIndex(index)); } + deleteEntries(std::move(selectedEntries)); +} + +void DatabaseWidget::deleteEntries(QList selectedEntries) +{ // Confirm entry removal before moving forward auto* recycleBin = m_db->metadata()->recycleBin(); bool permanent = (recycleBin && recycleBin->findEntryByUuid(selectedEntries.first()->uuid())) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index fb9cf817e4..7e012d2c35 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -125,6 +125,9 @@ class DatabaseWidget : public QStackedWidget void databaseUnlocked(); void databaseLocked(); + // Emitted in replaceDatabase, may be caused by lock, reload, unlock, load. + void databaseReplaced(const QSharedPointer& oldDb, const QSharedPointer& newDb); + void closeRequest(); void currentModeChanged(DatabaseWidget::Mode mode); void groupChanged(); @@ -151,6 +154,7 @@ public slots: void createEntry(); void cloneEntry(); void deleteSelectedEntries(); + void deleteEntries(QList entries); void setFocus(); void copyTitle(); void copyUsername(); @@ -223,7 +227,6 @@ private slots: private: int addChildWidget(QWidget* w); void setClipboardTextAndMinimize(const QString& text); - void setIconFromParent(); void processAutoOpen(); bool confirmDeleteEntries(QList entries, bool permanent); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index e5f5ea6132..52aed3b7cf 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1261,3 +1261,20 @@ void MainWindow::lockAllDatabases() { lockDatabasesAfterInactivity(); } + +void MainWindow::displayDesktopNotification(const QString& msg, QString title, int msTimeoutHint) +{ + if (!m_trayIcon || !QSystemTrayIcon::supportsMessages()) { + return; + } + + if (title.isEmpty()) { + title = BaseWindowTitle; + } + +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + m_trayIcon->showMessage(title, msg, filePath()->applicationIcon(), msTimeoutHint); +#else + m_trayIcon->showMessage(title, msg, QSystemTrayIcon::Information, msTimeoutHint); +#endif +} diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index f1e543468f..7c10727f6b 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -74,6 +74,7 @@ public slots: void bringToFront(); void closeAllDatabases(); void lockAllDatabases(); + void displayDesktopNotification(const QString& msg, QString title = "", int msTimeoutHint = 10000); protected: void closeEvent(QCloseEvent* event) override; diff --git a/src/gui/MessageBox.cpp b/src/gui/MessageBox.cpp index 582baa5ccb..9382287318 100644 --- a/src/gui/MessageBox.cpp +++ b/src/gui/MessageBox.cpp @@ -18,6 +18,10 @@ #include "MessageBox.h" +#include + +QWindow* MessageBox::m_overrideParent(nullptr); + MessageBox::Button MessageBox::m_nextAnswer(MessageBox::NoButton); QHash MessageBox::m_addedButtonLookup = @@ -81,6 +85,14 @@ MessageBox::Button MessageBox::messageBox(QWidget* parent, msgBox.setWindowTitle(title); msgBox.setText(text); + if (m_overrideParent) { + // Force the creation of the QWindow, without this windowHandle() will return nullptr + msgBox.winId(); + auto msgBoxWindow = msgBox.windowHandle(); + Q_ASSERT(msgBoxWindow); + msgBoxWindow->setTransientParent(m_overrideParent); + } + for (uint64_t b = First; b <= Last; b <<= 1) { if (b & buttons) { QString text = m_buttonDefs[static_cast