mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
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)
This commit is contained in:
parent
bc891761b6
commit
d93f33f514
@ -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);
|
||||
|
@ -48,8 +48,9 @@ private:
|
||||
Password,
|
||||
Url,
|
||||
Notes,
|
||||
Attribute,
|
||||
Attachment
|
||||
AttributeKey,
|
||||
Attachment,
|
||||
AttributeValue
|
||||
};
|
||||
|
||||
struct SearchTerm
|
||||
|
@ -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);
|
||||
|
@ -167,6 +167,8 @@ public:
|
||||
void addEntry(Entry* entry);
|
||||
void removeEntry(Entry* entry);
|
||||
|
||||
void applyGroupIconTo(Entry* entry);
|
||||
|
||||
signals:
|
||||
void groupDataChanged(Group* group);
|
||||
void groupAboutToAdd(Group* group, int index);
|
||||
|
@ -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:
|
||||
|
@ -116,15 +116,17 @@ QSharedPointer<Database> 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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<Database> db)
|
||||
{
|
||||
// TODO: instead of increasing the ref count temporarily, there should be a clean
|
||||
@ -393,6 +376,9 @@ void DatabaseWidget::replaceDatabase(QSharedPointer<Database> 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<Entry*> selectedEntries)
|
||||
{
|
||||
// Confirm entry removal before moving forward
|
||||
auto* recycleBin = m_db->metadata()->recycleBin();
|
||||
bool permanent = (recycleBin && recycleBin->findEntryByUuid(selectedEntries.first()->uuid()))
|
||||
|
@ -125,6 +125,9 @@ signals:
|
||||
void databaseUnlocked();
|
||||
void databaseLocked();
|
||||
|
||||
// Emitted in replaceDatabase, may be caused by lock, reload, unlock, load.
|
||||
void databaseReplaced(const QSharedPointer<Database>& oldDb, const QSharedPointer<Database>& 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<Entry*> 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<Entry*> entries, bool permanent);
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -18,6 +18,10 @@
|
||||
|
||||
#include "MessageBox.h"
|
||||
|
||||
#include <QWindow>
|
||||
|
||||
QWindow* MessageBox::m_overrideParent(nullptr);
|
||||
|
||||
MessageBox::Button MessageBox::m_nextAnswer(MessageBox::NoButton);
|
||||
|
||||
QHash<QAbstractButton*, MessageBox::Button> 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<Button>(b)].first;
|
||||
@ -160,3 +172,14 @@ void MessageBox::setNextAnswer(MessageBox::Button button)
|
||||
{
|
||||
m_nextAnswer = button;
|
||||
}
|
||||
|
||||
MessageBox::OverrideParent::OverrideParent(QWindow* newParent)
|
||||
: m_oldParent(MessageBox::m_overrideParent)
|
||||
{
|
||||
MessageBox::m_overrideParent = newParent;
|
||||
}
|
||||
|
||||
MessageBox::OverrideParent::~OverrideParent()
|
||||
{
|
||||
MessageBox::m_overrideParent = m_oldParent;
|
||||
}
|
||||
|
@ -101,7 +101,18 @@ public:
|
||||
Button defaultButton = MessageBox::NoButton,
|
||||
Action action = MessageBox::None);
|
||||
|
||||
class OverrideParent
|
||||
{
|
||||
public:
|
||||
explicit OverrideParent(QWindow* newParent);
|
||||
~OverrideParent();
|
||||
|
||||
private:
|
||||
QWindow* m_oldParent;
|
||||
};
|
||||
|
||||
private:
|
||||
static QWindow* m_overrideParent;
|
||||
static Button m_nextAnswer;
|
||||
static QHash<QAbstractButton*, Button> m_addedButtonLookup;
|
||||
static QMap<Button, std::pair<QString, QMessageBox::ButtonRole>> m_buttonDefs;
|
||||
|
@ -210,4 +210,40 @@ void TestEntrySearcher::testSearchTermParser()
|
||||
|
||||
QCOMPARE(terms[1]->field, EntrySearcher::Field::Username);
|
||||
QCOMPARE(terms[1]->regex.pattern(), QString("\\d+\\w{2}"));
|
||||
|
||||
// Test custom attribute search terms
|
||||
m_entrySearcher.parseSearchTerms("+_abc:efg _def:\"ddd\"");
|
||||
terms = m_entrySearcher.m_searchTerms;
|
||||
|
||||
QCOMPARE(terms.length(), 2);
|
||||
|
||||
QCOMPARE(terms[0]->field, EntrySearcher::Field::AttributeValue);
|
||||
QCOMPARE(terms[0]->word, QString("abc"));
|
||||
QCOMPARE(terms[0]->regex.pattern(), QString("^efg$"));
|
||||
|
||||
QCOMPARE(terms[1]->field, EntrySearcher::Field::AttributeValue);
|
||||
QCOMPARE(terms[1]->word, QString("def"));
|
||||
QCOMPARE(terms[1]->regex.pattern(), QString("ddd"));
|
||||
}
|
||||
|
||||
void TestEntrySearcher::testCustomAttributesAreSearched()
|
||||
{
|
||||
QScopedPointer<Entry> e1(new Entry());
|
||||
e1->setGroup(m_rootGroup);
|
||||
|
||||
e1->attributes()->set("testAttribute", "testE1");
|
||||
e1->attributes()->set("testProtected", "testP", true);
|
||||
|
||||
QScopedPointer<Entry> e2(new Entry());
|
||||
e2->setGroup(m_rootGroup);
|
||||
e2->attributes()->set("testAttribute", "testE2");
|
||||
e2->attributes()->set("testProtected", "testP2", true);
|
||||
|
||||
// search for custom entries
|
||||
m_searchResult = m_entrySearcher.search("_testAttribute:test", m_rootGroup);
|
||||
QCOMPARE(m_searchResult.count(), 2);
|
||||
|
||||
// protected attributes are ignored
|
||||
m_searchResult = m_entrySearcher.search("_testAttribute:test _testProtected:testP2", m_rootGroup);
|
||||
QCOMPARE(m_searchResult.count(), 2);
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ private slots:
|
||||
void testSearch();
|
||||
void testAllAttributesAreSearched();
|
||||
void testSearchTermParser();
|
||||
void testCustomAttributesAreSearched();
|
||||
|
||||
private:
|
||||
Group* m_rootGroup;
|
||||
|
Loading…
Reference in New Issue
Block a user