diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index 4357718b36..0759104eb9 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -88,6 +88,8 @@ QJsonObject BrowserAction::handleAction(const QJsonObject& json) return handleSetLogin(json, action); } else if (action.compare("lock-database", Qt::CaseSensitive) == 0) { return handleLockDatabase(json, action); + } else if (action.compare("get-database-groups", Qt::CaseSensitive) == 0) { + return handleGetDatabaseGroups(json, action); } // Action was not recognized @@ -319,10 +321,12 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString const QString password = decrypted.value("password").toString(); const QString submitUrl = decrypted.value("submitUrl").toString(); const QString uuid = decrypted.value("uuid").toString(); + const QString group = decrypted.value("group").toString(); + const QString groupUuid = decrypted.value("groupUuid").toString(); const QString realm; if (uuid.isEmpty()) { - m_browserService.addEntry(id, login, password, url, submitUrl, realm); + m_browserService.addEntry(id, login, password, url, submitUrl, realm, group, groupUuid); } else { m_browserService.updateEntry(id, uuid, login, password, url, submitUrl); } @@ -367,6 +371,40 @@ QJsonObject BrowserAction::handleLockDatabase(const QJsonObject& json, const QSt return getErrorReply(action, ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED); } +QJsonObject BrowserAction::handleGetDatabaseGroups(const QJsonObject& json, const QString& action) +{ + const QString hash = getDatabaseHash(); + const QString nonce = json.value("nonce").toString(); + const QString encrypted = json.value("message").toString(); + + QMutexLocker locker(&m_mutex); + if (!m_associated) { + return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED); + } + + const QJsonObject decrypted = decryptMessage(encrypted, nonce, action); + if (decrypted.isEmpty()) { + return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE); + } + + QString command = decrypted.value("action").toString(); + if (command.isEmpty() || command.compare("get-database-groups", Qt::CaseSensitive) != 0) { + return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION); + } + + const QJsonObject groups = m_browserService.getDatabaseGroups(); + if (groups.isEmpty()) { + return getErrorReply(action, ERROR_KEEPASS_NO_GROUPS_FOUND); + } + + const QString newNonce = incrementNonce(nonce); + + QJsonObject message = buildMessage(newNonce); + message["groups"] = groups; + + return buildResponse(action, message, newNonce); +} + QJsonObject BrowserAction::getErrorReply(const QString& action, const int errorCode) const { QJsonObject response; @@ -427,6 +465,8 @@ QString BrowserAction::getErrorMessage(const int errorCode) const return QObject::tr("No URL provided"); case ERROR_KEEPASS_NO_LOGINS_FOUND: return QObject::tr("No logins found"); + case ERROR_KEEPASS_NO_GROUPS_FOUND: + return QObject::tr("No groups found"); default: return QObject::tr("Unknown error"); } diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h index c82af2c176..d65dfc5bfa 100644 --- a/src/browser/BrowserAction.h +++ b/src/browser/BrowserAction.h @@ -45,7 +45,8 @@ class BrowserAction : public QObject ERROR_KEEPASS_INCORRECT_ACTION = 12, ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED = 13, ERROR_KEEPASS_NO_URL_PROVIDED = 14, - ERROR_KEEPASS_NO_LOGINS_FOUND = 15 + ERROR_KEEPASS_NO_LOGINS_FOUND = 15, + ERROR_KEEPASS_NO_GROUPS_FOUND = 16 }; public: @@ -64,6 +65,7 @@ class BrowserAction : public QObject QJsonObject handleGeneratePassword(const QJsonObject& json, const QString& action); QJsonObject handleSetLogin(const QJsonObject& json, const QString& action); QJsonObject handleLockDatabase(const QJsonObject& json, const QString& action); + QJsonObject handleGetDatabaseGroups(const QJsonObject& json, const QString& action); QJsonObject buildMessage(const QString& nonce) const; QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce); diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index a1ea5d6849..a7a94f67df 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -148,6 +148,54 @@ QString BrowserService::getDatabaseRecycleBinUuid() return recycleBin->uuidToHex(); } +QJsonArray BrowserService::addChildrenToGroup(Group* group) +{ + QJsonArray groupList; + + if (!group) { + return groupList; + } + + for (const auto& c : group->children()) { + if (c == group->database()->metadata()->recycleBin()) { + continue; + } + + QJsonObject jsonGroup; + jsonGroup["name"] = c->name(); + jsonGroup["uuid"] = Tools::uuidToHex(c->uuid()); + jsonGroup["children"] = addChildrenToGroup(c); + groupList.push_back(jsonGroup); + } + return groupList; +} + +QJsonObject BrowserService::getDatabaseGroups() +{ + auto db = getDatabase(); + if (!db) { + return {}; + } + + Group* rootGroup = db->rootGroup(); + if (!rootGroup) { + return {}; + } + + QJsonObject root; + root["name"] = rootGroup->name(); + root["uuid"] = Tools::uuidToHex(rootGroup->uuid()); + root["children"] = addChildrenToGroup(rootGroup); + + QJsonArray groups; + groups.push_back(root); + + QJsonObject result; + result["groups"] = groups; + + return result; +} + QString BrowserService::storeKey(const QString& key) { QString id; @@ -292,6 +340,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, void BrowserService::addEntry(const QString& id, const QString& login, const QString& password, const QString& url, const QString& submitUrl, const QString& realm, + const QString& group, const QString& groupUuid, QSharedPointer selectedDb) { if (thread() != QThread::currentThread()) { @@ -304,6 +353,8 @@ void BrowserService::addEntry(const QString& id, const QString& login, Q_ARG(QString, url), Q_ARG(QString, submitUrl), Q_ARG(QString, realm), + Q_ARG(QString, group), + Q_ARG(QString, groupUuid), Q_ARG(QSharedPointer, selectedDb)); } @@ -312,8 +363,8 @@ void BrowserService::addEntry(const QString& id, const QString& login, return; } - Group* group = findCreateAddEntryGroup(db); - if (!group) { + auto* addEntryGroup = findCreateAddEntryGroup(db); + if (!addEntryGroup) { return; } @@ -324,7 +375,17 @@ void BrowserService::addEntry(const QString& id, const QString& login, entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON); entry->setUsername(login); entry->setPassword(password); - entry->setGroup(group); + entry->setGroup(addEntryGroup); + + // Select a group for the entry + if (!group.isEmpty()) { + if (db->rootGroup()) { + auto selectedGroup = db->rootGroup()->findGroupByUuid(QUuid::fromRfc4122(QByteArray::fromHex(groupUuid.toLatin1()))); + if (selectedGroup && selectedGroup->name() == group) { + entry->setGroup(selectedGroup); + } + } + } const QString host = QUrl(url).host(); const QString submitHost = QUrl(submitUrl).host(); @@ -367,7 +428,7 @@ void BrowserService::updateEntry(const QString& id, Entry* entry = db->rootGroup()->findEntryByUuid(QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1()))); if (!entry) { // If entry is not found for update, add a new one to the selected database - addEntry(id, login, password, url, submitUrl, "", db); + addEntry(id, login, password, url, submitUrl, "", "", "", db); return; } @@ -959,6 +1020,7 @@ void BrowserService::hideWindow() const void BrowserService::raiseWindow(const bool force) { + m_wasMinimized = force; m_wasMinimized = getMainWindow()->isMinimized(); #ifdef Q_OS_MACOS m_wasHidden = macUtils()->isHidden(); diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 8e1c45f4a4..39b00ad536 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -44,6 +44,7 @@ class BrowserService : public QObject bool openDatabase(bool triggerUnlock); QString getDatabaseRootUuid(); QString getDatabaseRecycleBinUuid(); + QJsonObject getDatabaseGroups(); QString getKey(const QString& id); void addEntry(const QString& id, const QString& login, @@ -51,6 +52,8 @@ class BrowserService : public QObject const QString& url, const QString& submitUrl, const QString& realm, + const QString& group, + const QString& groupUuid, QSharedPointer selectedDb = {}); QList searchEntries(QSharedPointer db, const QString& hostname, const QString& url); QList searchEntries(const QString& url, const StringPairList& keyList); @@ -114,6 +117,7 @@ public slots: QString baseDomain(const QString& url) const; QSharedPointer getDatabase(); QSharedPointer selectedDatabase(); + QJsonArray addChildrenToGroup(Group* group); bool moveSettingsToCustomData(Entry* entry, const QString& name) const; int moveKeysToCustomData(Entry* entry, QSharedPointer db) const; bool checkLegacySettings();