Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan White <support@dmapps.us>2020-05-11 04:20:00 +0300
committerJonathan White <support@dmapps.us>2020-05-15 00:14:17 +0300
commita145bf91191f0a4630a7e31654aff8a8dfd09bf0 (patch)
tree70cb911c31cb1cf3362129a85fce64f80b898324
parent3b4057a78ca07d4a3424137b0f17e9a14301da3c (diff)
Complete refactor of Browser Integration classes
* Removed option to attach KeePassXC to the browser extension. Users must use the proxy application to communicate with KeePassXC. * Significantly streamlined proxy code. Used same implementation of stdin/stdout interface across all platforms. * Moved browser service entry point to BrowserService class instead of NativeMessagingHost. BrowserService now coordinates the communication to/from clients. * Moved settings page definition out of MainWindow * Decoupled BrowserService from DatabaseTabWidget * Reduced complexity of various functions and cleaned the ABI (public vs private). * Eliminated BrowserClients class, moved functionality into the BrowserService * Renamed HostInstaller to NativeMessageInstaller and renamed NativeMessageHost to BrowserHost. * Recognize XDG_CONFIG_HOME when installing native message file on Linux. Fix #4121 and fix #4123.
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/browser/BrowserAction.cpp123
-rw-r--r--src/browser/BrowserAction.h44
-rw-r--r--src/browser/BrowserClients.cpp77
-rw-r--r--src/browser/BrowserClients.h61
-rw-r--r--src/browser/BrowserHost.cpp107
-rw-r--r--src/browser/BrowserHost.h (renamed from src/proxy/NativeMessagingHost.h)40
-rw-r--r--src/browser/BrowserService.cpp286
-rw-r--r--src/browser/BrowserService.h105
-rw-r--r--src/browser/BrowserSettings.cpp103
-rw-r--r--src/browser/BrowserSettings.h28
-rw-r--r--src/browser/BrowserSettingsPage.cpp49
-rw-r--r--src/browser/BrowserSettingsPage.h36
-rw-r--r--src/browser/BrowserSettingsWidget.cpp (renamed from src/browser/BrowserOptionDialog.cpp)98
-rw-r--r--src/browser/BrowserSettingsWidget.h (renamed from src/browser/BrowserOptionDialog.h)21
-rw-r--r--[-rwxr-xr-x]src/browser/BrowserSettingsWidget.ui (renamed from src/browser/BrowserOptionDialog.ui)24
-rw-r--r--src/browser/BrowserShared.cpp46
-rw-r--r--src/browser/BrowserShared.h42
-rwxr-xr-xsrc/browser/CMakeLists.txt10
-rw-r--r--src/browser/HostInstaller.cpp359
-rw-r--r--src/browser/HostInstaller.h75
-rw-r--r--src/browser/NativeMessageInstaller.cpp313
-rw-r--r--src/browser/NativeMessageInstaller.h46
-rw-r--r--src/browser/NativeMessagingBase.cpp152
-rw-r--r--src/browser/NativeMessagingBase.h68
-rw-r--r--src/browser/NativeMessagingHost.cpp222
-rw-r--r--src/browser/NativeMessagingHost.h63
-rw-r--r--src/gui/MainWindow.cpp84
-rw-r--r--src/gui/MainWindow.h2
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp3
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h1
-rwxr-xr-xsrc/proxy/CMakeLists.txt22
-rw-r--r--src/proxy/NativeMessagingHost.cpp133
-rw-r--r--src/proxy/NativeMessagingProxy.cpp110
-rw-r--r--src/proxy/NativeMessagingProxy.h53
-rw-r--r--src/proxy/keepassxc-proxy.cpp5
-rw-r--r--src/sshagent/AgentSettingsPage.cpp9
-rw-r--r--src/sshagent/AgentSettingsPage.h4
-rw-r--r--tests/TestBrowser.cpp66
-rw-r--r--tests/TestBrowser.h5
-rw-r--r--tests/data/NewDatabaseBrowser.kdbxbin16743 -> 17463 bytes
-rw-r--r--tests/gui/TestGuiBrowser.cpp38
-rw-r--r--tests/gui/TestGuiBrowser.h4
43 files changed, 1220 insertions, 1918 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e57973ae8..c5fa3a1ac 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -233,7 +233,6 @@ if(APPLE)
add_feature_info(TouchID WITH_XC_TOUCHID "TouchID integration")
endif()
-set(BROWSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/browser)
add_subdirectory(browser)
add_subdirectory(proxy)
if(WITH_XC_BROWSER)
diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp
index fec5b985a..361dc2a9c 100644
--- a/src/browser/BrowserAction.cpp
+++ b/src/browser/BrowserAction.cpp
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,9 +16,11 @@
*/
#include "BrowserAction.h"
+#include "BrowserService.h"
#include "BrowserSettings.h"
-#include "NativeMessagingBase.h"
+#include "BrowserShared.h"
#include "config-keepassx.h"
+#include "core/Global.h"
#include <QJsonDocument>
#include <QJsonParseError>
@@ -27,14 +28,31 @@
#include <sodium/crypto_box.h>
#include <sodium/randombytes.h>
-BrowserAction::BrowserAction(BrowserService& browserService)
- : m_mutex(QMutex::Recursive)
- , m_browserService(browserService)
- , m_associated(false)
+namespace
{
+ enum
+ {
+ ERROR_KEEPASS_DATABASE_NOT_OPENED = 1,
+ ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED = 2,
+ ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED = 3,
+ ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE = 4,
+ ERROR_KEEPASS_TIMEOUT_OR_NOT_CONNECTED = 5,
+ ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED = 6,
+ ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE = 7,
+ ERROR_KEEPASS_ASSOCIATION_FAILED = 8,
+ ERROR_KEEPASS_KEY_CHANGE_FAILED = 9,
+ ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED = 10,
+ ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND = 11,
+ 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_GROUPS_FOUND = 16,
+ ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP = 17
+ };
}
-QJsonObject BrowserAction::readResponse(const QJsonObject& json)
+QJsonObject BrowserAction::processClientMessage(const QJsonObject& json)
{
if (json.isEmpty()) {
return getErrorReply("", ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED);
@@ -51,11 +69,10 @@ QJsonObject BrowserAction::readResponse(const QJsonObject& json)
return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION);
}
- QMutexLocker locker(&m_mutex);
- if (action.compare("change-public-keys", Qt::CaseSensitive) != 0 && !m_browserService.isDatabaseOpened()) {
+ if (action.compare("change-public-keys", Qt::CaseSensitive) != 0 && !browserService()->isDatabaseOpened()) {
if (m_clientPublicKey.isEmpty()) {
return getErrorReply(action, ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED);
- } else if (!m_browserService.openDatabase(triggerUnlock)) {
+ } else if (!browserService()->openDatabase(triggerUnlock)) {
return getErrorReply(action, ERROR_KEEPASS_DATABASE_NOT_OPENED);
}
}
@@ -98,7 +115,6 @@ QJsonObject BrowserAction::handleAction(const QJsonObject& json)
QJsonObject BrowserAction::handleChangePublicKeys(const QJsonObject& json, const QString& action)
{
- QMutexLocker locker(&m_mutex);
const QString nonce = json.value("nonce").toString();
const QString clientPublicKey = json.value("publicKey").toString();
@@ -130,7 +146,7 @@ QJsonObject BrowserAction::handleChangePublicKeys(const QJsonObject& json, const
QJsonObject BrowserAction::handleGetDatabaseHash(const QJsonObject& json, const QString& action)
{
- const QString hash = getDatabaseHash();
+ const QString hash = browserService()->getDatabaseHash();
const QString nonce = json.value("nonce").toString();
const QString encrypted = json.value("message").toString();
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
@@ -153,7 +169,7 @@ QJsonObject BrowserAction::handleGetDatabaseHash(const QJsonObject& json, const
// Update a legacy database hash if found
const QJsonArray hashes = decrypted.value("connectedKeys").toArray();
if (!hashes.isEmpty()) {
- const QString legacyHash = getLegacyDatabaseHash();
+ const QString legacyHash = browserService()->getDatabaseHash(true);
if (hashes.contains(legacyHash)) {
message["oldHash"] = legacyHash;
}
@@ -167,7 +183,7 @@ QJsonObject BrowserAction::handleGetDatabaseHash(const QJsonObject& json, const
QJsonObject BrowserAction::handleAssociate(const QJsonObject& json, const QString& action)
{
- const QString hash = getDatabaseHash();
+ const QString hash = browserService()->getDatabaseHash();
const QString nonce = json.value("nonce").toString();
const QString encrypted = json.value("message").toString();
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
@@ -181,12 +197,11 @@ QJsonObject BrowserAction::handleAssociate(const QJsonObject& json, const QStrin
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
}
- QMutexLocker locker(&m_mutex);
if (key.compare(m_clientPublicKey, Qt::CaseSensitive) == 0) {
// Check for identification key. If it's not found, ensure backwards compatibility and use the current public
// key
const QString idKey = decrypted.value("idKey").toString();
- const QString id = m_browserService.storeKey((idKey.isEmpty() ? key : idKey));
+ const QString id = browserService()->storeKey((idKey.isEmpty() ? key : idKey));
if (id.isEmpty()) {
return getErrorReply(action, ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED);
}
@@ -205,7 +220,7 @@ QJsonObject BrowserAction::handleAssociate(const QJsonObject& json, const QStrin
QJsonObject BrowserAction::handleTestAssociate(const QJsonObject& json, const QString& action)
{
- const QString hash = getDatabaseHash();
+ const QString hash = browserService()->getDatabaseHash();
const QString nonce = json.value("nonce").toString();
const QString encrypted = json.value("message").toString();
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
@@ -220,8 +235,7 @@ QJsonObject BrowserAction::handleTestAssociate(const QJsonObject& json, const QS
return getErrorReply(action, ERROR_KEEPASS_DATABASE_NOT_OPENED);
}
- QMutexLocker locker(&m_mutex);
- const QString key = m_browserService.getKey(id);
+ const QString key = browserService()->getKey(id);
if (key.isEmpty() || key.compare(responseKey, Qt::CaseSensitive) != 0) {
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
}
@@ -238,11 +252,10 @@ QJsonObject BrowserAction::handleTestAssociate(const QJsonObject& json, const QS
QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QString& action)
{
- const QString hash = getDatabaseHash();
+ const QString hash = browserService()->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);
}
@@ -269,7 +282,7 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
const QString submit = decrypted.value("submitUrl").toString();
const QString auth = decrypted.value("httpAuth").toString();
const bool httpAuth = auth.compare(TRUE_STR, Qt::CaseSensitive) == 0 ? true : false;
- const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "", keyList, httpAuth);
+ const QJsonArray users = browserService()->findMatchingEntries(id, url, submit, "", keyList, httpAuth);
if (users.isEmpty()) {
return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND);
@@ -311,11 +324,10 @@ QJsonObject BrowserAction::handleGeneratePassword(const QJsonObject& json, const
QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString& action)
{
- const QString hash = getDatabaseHash();
+ const QString hash = browserService()->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);
}
@@ -339,11 +351,11 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString
const QString groupUuid = decrypted.value("groupUuid").toString();
const QString realm;
- BrowserService::ReturnValue result = BrowserService::ReturnValue::Success;
+ bool result = true;
if (uuid.isEmpty()) {
- m_browserService.addEntry(id, login, password, url, submitUrl, realm, group, groupUuid);
+ browserService()->addEntry(id, login, password, url, submitUrl, realm, group, groupUuid);
} else {
- result = m_browserService.updateEntry(id, uuid, login, password, url, submitUrl);
+ result = browserService()->updateEntry(id, uuid, login, password, url, submitUrl);
}
const QString newNonce = incrementNonce(nonce);
@@ -351,7 +363,7 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString
QJsonObject message = buildMessage(newNonce);
message["count"] = QJsonValue::Null;
message["entries"] = QJsonValue::Null;
- message["error"] = getReturnValue(result);
+ message["error"] = result ? QStringLiteral("success") : QStringLiteral("error");
message["hash"] = hash;
return buildResponse(action, message, newNonce);
@@ -359,7 +371,7 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString
QJsonObject BrowserAction::handleLockDatabase(const QJsonObject& json, const QString& action)
{
- const QString hash = getDatabaseHash();
+ const QString hash = browserService()->getDatabaseHash();
const QString nonce = json.value("nonce").toString();
const QString encrypted = json.value("message").toString();
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
@@ -374,8 +386,7 @@ QJsonObject BrowserAction::handleLockDatabase(const QJsonObject& json, const QSt
QString command = decrypted.value("action").toString();
if (!command.isEmpty() && command.compare("lock-database", Qt::CaseSensitive) == 0) {
- QMutexLocker locker(&m_mutex);
- m_browserService.lockDatabase();
+ browserService()->lockDatabase();
const QString newNonce = incrementNonce(nonce);
QJsonObject message = buildMessage(newNonce);
@@ -388,11 +399,10 @@ QJsonObject BrowserAction::handleLockDatabase(const QJsonObject& json, const QSt
QJsonObject BrowserAction::handleGetDatabaseGroups(const QJsonObject& json, const QString& action)
{
- const QString hash = getDatabaseHash();
+ const QString hash = browserService()->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);
}
@@ -407,7 +417,7 @@ QJsonObject BrowserAction::handleGetDatabaseGroups(const QJsonObject& json, cons
return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION);
}
- const QJsonObject groups = m_browserService.getDatabaseGroups();
+ const QJsonObject groups = browserService()->getDatabaseGroups();
if (groups.isEmpty()) {
return getErrorReply(action, ERROR_KEEPASS_NO_GROUPS_FOUND);
}
@@ -422,11 +432,10 @@ QJsonObject BrowserAction::handleGetDatabaseGroups(const QJsonObject& json, cons
QJsonObject BrowserAction::handleCreateNewGroup(const QJsonObject& json, const QString& action)
{
- const QString hash = getDatabaseHash();
+ const QString hash = browserService()->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);
}
@@ -442,7 +451,7 @@ QJsonObject BrowserAction::handleCreateNewGroup(const QJsonObject& json, const Q
}
QString group = decrypted.value("groupName").toString();
- const QJsonObject newGroup = m_browserService.createNewGroup(group);
+ const QJsonObject newGroup = browserService()->createNewGroup(group);
if (newGroup.isEmpty() || newGroup["name"].toString().isEmpty() || newGroup["uuid"].toString().isEmpty()) {
return getErrorReply(action, ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP);
}
@@ -524,38 +533,6 @@ QString BrowserAction::getErrorMessage(const int errorCode) const
}
}
-QString BrowserAction::getReturnValue(const BrowserService::ReturnValue returnValue) const
-{
- switch (returnValue) {
- case BrowserService::ReturnValue::Success:
- return QString("success");
- case BrowserService::ReturnValue::Error:
- return QString("error");
- case BrowserService::ReturnValue::Canceled:
- return QString("canceled");
- }
- return QString("error");
-}
-
-QString BrowserAction::getDatabaseHash()
-{
- QMutexLocker locker(&m_mutex);
- QByteArray hash =
- QCryptographicHash::hash(m_browserService.getDatabaseRootUuid().toUtf8(), QCryptographicHash::Sha256).toHex();
- return QString(hash);
-}
-
-QString BrowserAction::getLegacyDatabaseHash()
-{
- QMutexLocker locker(&m_mutex);
- QByteArray hash =
- QCryptographicHash::hash(
- (m_browserService.getDatabaseRootUuid() + m_browserService.getDatabaseRecycleBinUuid()).toUtf8(),
- QCryptographicHash::Sha256)
- .toHex();
- return QString(hash);
-}
-
QString BrowserAction::encryptMessage(const QJsonObject& message, const QString& nonce)
{
if (message.isEmpty() || nonce.isEmpty()) {
@@ -586,7 +563,6 @@ QJsonObject BrowserAction::decryptMessage(const QString& message, const QString&
QString BrowserAction::encrypt(const QString& plaintext, const QString& nonce)
{
- QMutexLocker locker(&m_mutex);
const QByteArray ma = plaintext.toUtf8();
const QByteArray na = base64Decode(nonce);
const QByteArray ca = base64Decode(m_clientPublicKey);
@@ -598,7 +574,7 @@ QString BrowserAction::encrypt(const QString& plaintext, const QString& nonce)
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
std::vector<unsigned char> e;
- e.resize(NATIVE_MSG_MAX_LENGTH);
+ e.resize(BrowserShared::NATIVEMSG_MAX_LENGTH);
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
return QString();
@@ -614,7 +590,6 @@ QString BrowserAction::encrypt(const QString& plaintext, const QString& nonce)
QByteArray BrowserAction::decrypt(const QString& encrypted, const QString& nonce)
{
- QMutexLocker locker(&m_mutex);
const QByteArray ma = base64Decode(encrypted);
const QByteArray na = base64Decode(nonce);
const QByteArray ca = base64Decode(m_clientPublicKey);
@@ -626,7 +601,7 @@ QByteArray BrowserAction::decrypt(const QString& encrypted, const QString& nonce
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
std::vector<unsigned char> d;
- d.resize(NATIVE_MSG_MAX_LENGTH);
+ d.resize(BrowserShared::NATIVEMSG_MAX_LENGTH);
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
return QByteArray();
diff --git a/src/browser/BrowserAction.h b/src/browser/BrowserAction.h
index a8af0915e..c65409dd8 100644
--- a/src/browser/BrowserAction.h
+++ b/src/browser/BrowserAction.h
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,42 +18,16 @@
#ifndef BROWSERACTION_H
#define BROWSERACTION_H
-#include "BrowserService.h"
#include <QJsonObject>
-#include <QMutex>
-#include <QObject>
-#include <QtCore>
+#include <QString>
-class BrowserAction : public QObject
+class BrowserAction
{
- Q_OBJECT
-
- enum
- {
- ERROR_KEEPASS_DATABASE_NOT_OPENED = 1,
- ERROR_KEEPASS_DATABASE_HASH_NOT_RECEIVED = 2,
- ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED = 3,
- ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE = 4,
- ERROR_KEEPASS_TIMEOUT_OR_NOT_CONNECTED = 5,
- ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED = 6,
- ERROR_KEEPASS_CANNOT_ENCRYPT_MESSAGE = 7,
- ERROR_KEEPASS_ASSOCIATION_FAILED = 8,
- ERROR_KEEPASS_KEY_CHANGE_FAILED = 9,
- ERROR_KEEPASS_ENCRYPTION_KEY_UNRECOGNIZED = 10,
- ERROR_KEEPASS_NO_SAVED_DATABASES_FOUND = 11,
- 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_GROUPS_FOUND = 16,
- ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP = 17
- };
-
public:
- BrowserAction(BrowserService& browserService);
+ explicit BrowserAction() = default;
~BrowserAction() = default;
- QJsonObject readResponse(const QJsonObject& json);
+ QJsonObject processClientMessage(const QJsonObject& json);
private:
QJsonObject handleAction(const QJsonObject& json);
@@ -73,9 +46,6 @@ private:
QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce);
QJsonObject getErrorReply(const QString& action, const int errorCode) const;
QString getErrorMessage(const int errorCode) const;
- QString getReturnValue(const BrowserService::ReturnValue returnValue) const;
- QString getDatabaseHash();
- QString getLegacyDatabaseHash();
QString encryptMessage(const QJsonObject& message, const QString& nonce);
QJsonObject decryptMessage(const QString& message, const QString& nonce);
@@ -90,12 +60,10 @@ private:
QString incrementNonce(const QString& nonce);
private:
- QMutex m_mutex;
- BrowserService& m_browserService;
QString m_clientPublicKey;
QString m_publicKey;
QString m_secretKey;
- bool m_associated;
+ bool m_associated = false;
friend class TestBrowser;
};
diff --git a/src/browser/BrowserClients.cpp b/src/browser/BrowserClients.cpp
deleted file mode 100644
index 083df3945..000000000
--- a/src/browser/BrowserClients.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "BrowserClients.h"
-#include <QJsonParseError>
-#include <QJsonValue>
-
-BrowserClients::BrowserClients(BrowserService& browserService)
- : m_mutex(QMutex::Recursive)
- , m_browserService(browserService)
-{
- m_clients.reserve(1000);
-}
-
-QJsonObject BrowserClients::readResponse(const QByteArray& arr)
-{
- QJsonObject json;
- const QJsonObject message = byteArrayToJson(arr);
- const QString clientID = getClientID(message);
-
- if (!clientID.isEmpty()) {
- const ClientPtr client = getClient(clientID);
- if (client->browserAction) {
- json = client->browserAction->readResponse(message);
- }
- }
-
- return json;
-}
-
-QJsonObject BrowserClients::byteArrayToJson(const QByteArray& arr) const
-{
- QJsonObject json;
- QJsonParseError err;
- QJsonDocument doc(QJsonDocument::fromJson(arr, &err));
- if (doc.isObject()) {
- json = doc.object();
- }
-
- return json;
-}
-
-QString BrowserClients::getClientID(const QJsonObject& json) const
-{
- return json["clientID"].toString();
-}
-
-BrowserClients::ClientPtr BrowserClients::getClient(const QString& clientID)
-{
- QMutexLocker locker(&m_mutex);
- for (const auto& i : m_clients) {
- if (i->clientID.compare(clientID, Qt::CaseSensitive) == 0) {
- return i;
- }
- }
-
- // clientID not found, create a new client
- QSharedPointer<BrowserAction> ba = QSharedPointer<BrowserAction>::create(m_browserService);
- ClientPtr client = ClientPtr::create(clientID, ba);
- m_clients.push_back(client);
- return m_clients.back();
-}
diff --git a/src/browser/BrowserClients.h b/src/browser/BrowserClients.h
deleted file mode 100644
index 1fa3dfe17..000000000
--- a/src/browser/BrowserClients.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef BROWSERCLIENTS_H
-#define BROWSERCLIENTS_H
-
-#include "BrowserAction.h"
-#include <QJsonObject>
-#include <QLocalSocket>
-#include <QMutex>
-#include <QSharedPointer>
-#include <QVector>
-
-class BrowserClients
-{
- struct Client
- {
- Client(const QString& id, QSharedPointer<BrowserAction> ba)
- : clientID(id)
- , browserAction(ba)
- {
- }
- QString clientID;
- QSharedPointer<BrowserAction> browserAction;
- };
-
- typedef QSharedPointer<Client> ClientPtr;
-
-public:
- BrowserClients(BrowserService& browserService);
- ~BrowserClients() = default;
-
- QJsonObject readResponse(const QByteArray& arr);
-
-private:
- QJsonObject byteArrayToJson(const QByteArray& arr) const;
- QString getClientID(const QJsonObject& json) const;
- ClientPtr getClient(const QString& clientID);
-
-private:
- QMutex m_mutex;
- QVector<ClientPtr> m_clients;
- BrowserService& m_browserService;
-};
-
-#endif // BROWSERCLIENTS_H
diff --git a/src/browser/BrowserHost.cpp b/src/browser/BrowserHost.cpp
new file mode 100644
index 000000000..62c3e9cd8
--- /dev/null
+++ b/src/browser/BrowserHost.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "BrowserHost.h"
+#include "BrowserSettings.h"
+#include "BrowserShared.h"
+
+#include <QJsonDocument>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QMutexLocker>
+#include <QtNetwork>
+
+#include "sodium.h"
+#include <iostream>
+
+BrowserHost::BrowserHost(QObject* parent)
+ : QObject(parent)
+{
+ m_localServer = new QLocalServer(this);
+ m_localServer->setSocketOptions(QLocalServer::UserAccessOption);
+ connect(m_localServer.data(), SIGNAL(newConnection()), this, SLOT(proxyConnected()));
+}
+
+BrowserHost::~BrowserHost()
+{
+ stop();
+}
+
+void BrowserHost::start()
+{
+ if (sodium_init() == -1) {
+ qWarning() << "Failed to start browser service: libsodium failed to initialize!";
+ return;
+ }
+
+ if (!m_localServer->isListening()) {
+ m_localServer->listen(BrowserShared::localServerPath());
+ }
+}
+
+void BrowserHost::stop()
+{
+ m_socketList.clear();
+ m_localServer->close();
+}
+
+void BrowserHost::proxyConnected()
+{
+ auto socket = m_localServer->nextPendingConnection();
+ if (socket) {
+ m_socketList.append(socket);
+ connect(socket, SIGNAL(readyRead()), this, SLOT(readProxyMessage()));
+ connect(socket, SIGNAL(disconnected()), this, SLOT(proxyDisconnected()));
+ }
+}
+
+void BrowserHost::readProxyMessage()
+{
+ QLocalSocket* socket = qobject_cast<QLocalSocket*>(QObject::sender());
+ if (!socket || socket->bytesAvailable() <= 0) {
+ return;
+ }
+
+ socket->setReadBufferSize(BrowserShared::NATIVEMSG_MAX_LENGTH);
+
+ QJsonParseError error;
+ auto json = QJsonDocument::fromJson(socket->readAll(), &error);
+ if (json.isNull()) {
+ qWarning() << "Failed to read proxy message: " << error.errorString();
+ return;
+ }
+
+ emit clientMessageReceived(json.object());
+}
+
+void BrowserHost::sendClientMessage(const QJsonObject& json)
+{
+ QString reply(QJsonDocument(json).toJson(QJsonDocument::Compact));
+ for (const auto socket : m_socketList) {
+ if (socket && socket->isValid() && socket->state() == QLocalSocket::ConnectedState) {
+ QByteArray arr = reply.toUtf8();
+ socket->write(arr.constData(), arr.length());
+ socket->flush();
+ }
+ }
+}
+
+void BrowserHost::proxyDisconnected()
+{
+ auto socket = qobject_cast<QLocalSocket*>(QObject::sender());
+ m_socketList.removeOne(socket);
+}
diff --git a/src/proxy/NativeMessagingHost.h b/src/browser/BrowserHost.h
index 5bedd9de5..ea8e07409 100644
--- a/src/proxy/NativeMessagingHost.h
+++ b/src/browser/BrowserHost.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,29 +18,37 @@
#ifndef NATIVEMESSAGINGHOST_H
#define NATIVEMESSAGINGHOST_H
-#include "NativeMessagingBase.h"
+#include <QJsonObject>
+#include <QObject>
+#include <QPointer>
-class NativeMessagingHost : public NativeMessagingBase
+class QLocalServer;
+class QLocalSocket;
+
+class BrowserHost : public QObject
{
Q_OBJECT
+
public:
- NativeMessagingHost();
- ~NativeMessagingHost() override;
+ explicit BrowserHost(QObject* parent = nullptr);
+ ~BrowserHost() override;
-public slots:
- void newLocalMessage();
- void deleteSocket();
- void socketStateChanged(QLocalSocket::LocalSocketState socketState);
+ void start();
+ void stop();
-private:
- void readNativeMessages() override;
- void readLength() override;
- bool readStdIn(const quint32 length) override;
+ void sendClientMessage(const QJsonObject& json);
-private:
- QLocalSocket* m_localSocket;
+signals:
+ void clientMessageReceived(const QJsonObject& json);
+
+private slots:
+ void proxyConnected();
+ void readProxyMessage();
+ void proxyDisconnected();
- Q_DISABLE_COPY(NativeMessagingHost)
+private:
+ QPointer<QLocalServer> m_localServer;
+ QList<QLocalSocket*> m_socketList;
};
#endif // NATIVEMESSAGINGHOST_H
diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp
index 5aa5e77ed..b83af627a 100644
--- a/src/browser/BrowserService.cpp
+++ b/src/browser/BrowserService.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2013 Francois Ferrand
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,8 +26,10 @@
#include <QUuid>
#include "BrowserAccessControlDialog.h"
+#include "BrowserAction.h"
#include "BrowserEntryConfig.h"
#include "BrowserEntrySaveDialog.h"
+#include "BrowserHost.h"
#include "BrowserService.h"
#include "BrowserSettings.h"
#include "core/Database.h"
@@ -58,34 +60,45 @@ const QString BrowserService::OPTION_ONLY_HTTP_AUTH = QStringLiteral("BrowserOnl
// Multiple URL's
const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL");
-BrowserService::BrowserService(DatabaseTabWidget* parent)
- : m_dbTabWidget(parent)
+Q_GLOBAL_STATIC(BrowserService, s_browserService);
+
+BrowserService::BrowserService()
+ : QObject()
+ , m_browserHost(new BrowserHost)
, m_dialogActive(false)
, m_bringToFrontRequested(false)
, m_prevWindowState(WindowState::Normal)
, m_keepassBrowserUUID(Tools::hexToUuid("de887cc3036343b8974b5911b8816224"))
{
- // Don't connect the signals when used from DatabaseSettingsWidgetBrowser (parent is nullptr)
- if (m_dbTabWidget) {
- connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*)));
- connect(
- m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*)));
- connect(m_dbTabWidget,
- SIGNAL(activateDatabaseChanged(DatabaseWidget*)),
- this,
- SLOT(activateDatabaseChanged(DatabaseWidget*)));
+ connect(m_browserHost, &BrowserHost::clientMessageReceived, this, &BrowserService::processClientMessage);
+ setEnabled(browserSettings()->isEnabled());
+}
+
+BrowserService* BrowserService::instance()
+{
+ return s_browserService;
+}
+
+void BrowserService::setEnabled(bool enabled)
+{
+ if (enabled) {
+ // Update KeePassXC/keepassxc-proxy binary paths to Native Messaging scripts
+ if (browserSettings()->updateBinaryPath()) {
+ browserSettings()->updateBinaryPaths();
+ }
+
+ m_browserHost->start();
+ } else {
+ m_browserHost->stop();
}
}
bool BrowserService::isDatabaseOpened() const
{
- DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
- if (!dbWidget) {
- return false;
+ if (m_currentDatabaseWidget) {
+ return !m_currentDatabaseWidget->isLocked();
}
-
- return dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
- || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode;
+ return false;
}
bool BrowserService::openDatabase(bool triggerUnlock)
@@ -94,13 +107,7 @@ bool BrowserService::openDatabase(bool triggerUnlock)
return false;
}
- DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
- if (!dbWidget) {
- return false;
- }
-
- if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
- || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
+ if (m_currentDatabaseWidget && !m_currentDatabaseWidget->isLocked()) {
return true;
}
@@ -114,19 +121,20 @@ bool BrowserService::openDatabase(bool triggerUnlock)
void BrowserService::lockDatabase()
{
- if (thread() != QThread::currentThread()) {
- QMetaObject::invokeMethod(this, "lockDatabase", Qt::BlockingQueuedConnection);
- }
-
- DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
- if (!dbWidget) {
- return;
+ if (m_currentDatabaseWidget) {
+ m_currentDatabaseWidget->lock();
}
+}
- if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
- || dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
- dbWidget->lock();
+QString BrowserService::getDatabaseHash(bool legacy)
+{
+ if (legacy) {
+ return QCryptographicHash::hash(
+ (browserService()->getDatabaseRootUuid() + browserService()->getDatabaseRecycleBinUuid()).toUtf8(),
+ QCryptographicHash::Sha256)
+ .toHex();
}
+ return QCryptographicHash::hash(getDatabaseRootUuid().toUtf8(), QCryptographicHash::Sha256).toHex();
}
QString BrowserService::getDatabaseRootUuid()
@@ -180,9 +188,9 @@ QJsonArray BrowserService::getChildrenFromGroup(Group* group)
return groupList;
}
-QJsonObject BrowserService::getDatabaseGroups(const QSharedPointer<Database>& selectedDb)
+QJsonObject BrowserService::getDatabaseGroups()
{
- auto db = selectedDb ? selectedDb : getDatabase();
+ auto db = getDatabase();
if (!db) {
return {};
}
@@ -208,15 +216,6 @@ QJsonObject BrowserService::getDatabaseGroups(const QSharedPointer<Database>& se
QJsonObject BrowserService::createNewGroup(const QString& groupName)
{
- QJsonObject result;
- if (thread() != QThread::currentThread()) {
- QMetaObject::invokeMethod(this,
- "createNewGroup",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(QJsonObject, result),
- Q_ARG(QString, groupName));
- return result;
- }
auto db = getDatabase();
if (!db) {
@@ -232,6 +231,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName)
// Group already exists
if (group) {
+ QJsonObject result;
result["name"] = group->name();
result["uuid"] = Tools::uuidToHex(group->uuid());
return result;
@@ -245,7 +245,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName)
MessageBox::Yes | MessageBox::No);
if (dialogResult != MessageBox::Yes) {
- return result;
+ return {};
}
QString name, uuid;
@@ -279,6 +279,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName)
previousGroup = tempGroup;
}
+ QJsonObject result;
result["name"] = name;
result["uuid"] = uuid;
return result;
@@ -286,25 +287,18 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName)
QString BrowserService::storeKey(const QString& key)
{
- QString id;
-
- if (thread() != QThread::currentThread()) {
- QMetaObject::invokeMethod(
- this, "storeKey", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, id), Q_ARG(QString, key));
- return id;
- }
-
auto db = getDatabase();
if (!db) {
return {};
}
bool contains;
- MessageBox::Button dialogResult = MessageBox::Cancel;
+ auto dialogResult = MessageBox::Cancel;
+ QString id;
do {
QInputDialog keyDialog;
- connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), &keyDialog, SLOT(reject()));
+ connect(m_currentDatabaseWidget, SIGNAL(databaseLocked()), &keyDialog, SLOT(reject()));
keyDialog.setWindowTitle(tr("KeePassXC: New key association request"));
keyDialog.setLabelText(tr("You have received an association request for the following database:\n%1\n\n"
"Give the connection a unique name or ID, for example:\nchrome-laptop.")
@@ -353,28 +347,14 @@ QString BrowserService::getKey(const QString& id)
return db->metadata()->customData()->value(ASSOCIATE_KEY_PREFIX + id);
}
-QJsonArray BrowserService::findMatchingEntries(const QString& id,
+QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
const QString& url,
const QString& submitUrl,
const QString& realm,
const StringPairList& keyList,
const bool httpAuth)
{
- QJsonArray result;
- if (thread() != QThread::currentThread()) {
- QMetaObject::invokeMethod(this,
- "findMatchingEntries",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(QJsonArray, result),
- Q_ARG(QString, id),
- Q_ARG(QString, url),
- Q_ARG(QString, submitUrl),
- Q_ARG(QString, realm),
- Q_ARG(StringPairList, keyList),
- Q_ARG(bool, httpAuth));
- return result;
- }
-
+ Q_UNUSED(dbid);
const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess();
const bool ignoreHttpAuth = browserSettings()->httpAuthPermission();
const QString host = QUrl(url).host();
@@ -425,18 +405,19 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
}
if (pwEntries.isEmpty()) {
- return QJsonArray();
+ return {};
}
// Ensure that database is not locked when the popup was visible
if (!isDatabaseOpened()) {
- return QJsonArray();
+ return {};
}
// Sort results
pwEntries = sortEntries(pwEntries, host, submitUrl);
// Fill the list
+ QJsonArray result;
for (auto* entry : pwEntries) {
result.append(prepareEntry(entry));
}
@@ -444,7 +425,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
return result;
}
-void BrowserService::addEntry(const QString& id,
+void BrowserService::addEntry(const QString& dbid,
const QString& login,
const QString& password,
const QString& url,
@@ -454,21 +435,8 @@ void BrowserService::addEntry(const QString& id,
const QString& groupUuid,
const QSharedPointer<Database>& selectedDb)
{
- if (thread() != QThread::currentThread()) {
- QMetaObject::invokeMethod(this,
- "addEntry",
- Qt::BlockingQueuedConnection,
- Q_ARG(QString, id),
- Q_ARG(QString, login),
- Q_ARG(QString, password),
- Q_ARG(QString, url),
- Q_ARG(QString, submitUrl),
- Q_ARG(QString, realm),
- Q_ARG(QString, group),
- Q_ARG(QString, groupUuid),
- Q_ARG(QSharedPointer<Database>, selectedDb));
- }
-
+ // TODO: select database based on this key id
+ Q_UNUSED(dbid);
auto db = selectedDb ? selectedDb : selectedDatabase();
if (!db) {
return;
@@ -510,37 +478,25 @@ void BrowserService::addEntry(const QString& id,
config.save(entry);
}
-BrowserService::ReturnValue BrowserService::updateEntry(const QString& id,
- const QString& uuid,
- const QString& login,
- const QString& password,
- const QString& url,
- const QString& submitUrl)
+bool BrowserService::updateEntry(const QString& dbid,
+ const QString& uuid,
+ const QString& login,
+ const QString& password,
+ const QString& url,
+ const QString& submitUrl)
{
- ReturnValue result = ReturnValue::Error;
- if (thread() != QThread::currentThread()) {
- QMetaObject::invokeMethod(this,
- "updateEntry",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(ReturnValue, result),
- Q_ARG(QString, id),
- Q_ARG(QString, uuid),
- Q_ARG(QString, login),
- Q_ARG(QString, password),
- Q_ARG(QString, url),
- Q_ARG(QString, submitUrl));
- }
-
+ // TODO: select database based on this key id
+ Q_UNUSED(dbid);
auto db = selectedDatabase();
if (!db) {
- return ReturnValue::Error;
+ return false;
}
Entry* entry = db->rootGroup()->findEntryByUuid(Tools::hexToUuid(uuid));
if (!entry) {
// If entry is not found for update, add a new one to the selected database
- addEntry(id, login, password, url, submitUrl, "", "", "", db);
- return ReturnValue::Success;
+ addEntry(dbid, login, password, url, submitUrl, "", "", "", db);
+ return true;
}
// Check if the entry password is a reference. If so, update the original entry instead
@@ -549,16 +505,17 @@ BrowserService::ReturnValue BrowserService::updateEntry(const QString& id,
if (!referenceUuid.isNull()) {
entry = db->rootGroup()->findEntryByUuid(referenceUuid);
if (!entry) {
- return ReturnValue::Error;
+ return false;
}
}
}
QString username = entry->username();
if (username.isEmpty()) {
- return ReturnValue::Error;
+ return false;
}
+ bool result = false;
if (username.compare(login, Qt::CaseSensitive) != 0
|| entry->password().compare(password, Qt::CaseSensitive) != 0) {
MessageBox::Button dialogResult = MessageBox::No;
@@ -580,9 +537,7 @@ BrowserService::ReturnValue BrowserService::updateEntry(const QString& id,
}
entry->setPassword(password);
entry->endUpdate();
- result = ReturnValue::Success;
- } else {
- result = ReturnValue::Canceled;
+ result = true;
}
hideWindow();
@@ -646,17 +601,14 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const QString& s
// Get the list of databases to search
QList<QSharedPointer<Database>> databases;
if (browserSettings()->searchInAllDatabases()) {
- const int count = m_dbTabWidget->count();
- for (int i = 0; i < count; ++i) {
- if (auto* dbWidget = qobject_cast<DatabaseWidget*>(m_dbTabWidget->widget(i))) {
- if (const auto& db = dbWidget->database()) {
- if (databaseConnected(db)) {
- databases << db;
- }
- }
+ for (auto dbWidget : getMainWindow()->getOpenDatabases()) {
+ auto db = dbWidget->database();
+ if (db && databaseConnected(dbWidget->database())) {
+ databases << db;
}
}
- } else if (const auto& db = getDatabase()) {
+ } else {
+ const auto& db = getDatabase();
if (databaseConnected(db)) {
databases << db;
}
@@ -674,9 +626,8 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const QString& s
return entries;
}
-void BrowserService::convertAttributesToCustomData(const QSharedPointer<Database>& currentDb)
+void BrowserService::convertAttributesToCustomData(QSharedPointer<Database> db)
{
- auto db = currentDb ? currentDb : getDatabase();
if (!db) {
return;
}
@@ -806,7 +757,7 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
m_dialogActive = true;
BrowserAccessControlDialog accessControlDialog;
- connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), &accessControlDialog, SLOT(reject()));
+ connect(m_currentDatabaseWidget, SIGNAL(databaseLocked()), &accessControlDialog, SLOT(reject()));
connect(&accessControlDialog, &BrowserAccessControlDialog::disableAccess, [&](QTableWidgetItem* item) {
auto entry = pwEntriesToConfirm[item->row()];
BrowserEntryConfig config;
@@ -1103,10 +1054,8 @@ QString BrowserService::baseDomain(const QString& hostname) const
QSharedPointer<Database> BrowserService::getDatabase()
{
- if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) {
- if (const auto& db = dbWidget->database()) {
- return db;
- }
+ if (m_currentDatabaseWidget) {
+ return m_currentDatabaseWidget->database();
}
return {};
}
@@ -1114,20 +1063,15 @@ QSharedPointer<Database> BrowserService::getDatabase()
QSharedPointer<Database> BrowserService::selectedDatabase()
{
QList<DatabaseWidget*> databaseWidgets;
- for (int i = 0;; ++i) {
- auto* dbWidget = m_dbTabWidget->databaseWidgetFromIndex(i);
+ for (auto dbWidget : getMainWindow()->getOpenDatabases()) {
// Add only open databases
- if (dbWidget && !dbWidget->isLocked()) {
- databaseWidgets.push_back(dbWidget);
- continue;
+ if (!dbWidget->isLocked()) {
+ databaseWidgets << dbWidget;
}
-
- // Break out if dbStruct.dbWidget is nullptr
- break;
}
BrowserEntrySaveDialog browserEntrySaveDialog;
- int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_dbTabWidget->currentDatabaseWidget());
+ int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_currentDatabaseWidget);
if (openDatabaseCount > 1) {
int res = browserEntrySaveDialog.exec();
if (res == QDialog::Accepted) {
@@ -1145,7 +1089,7 @@ QSharedPointer<Database> BrowserService::selectedDatabase()
return getDatabase();
}
-bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name) const
+bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name)
{
if (entry->attributes()->contains(name)) {
QString attr = entry->attributes()->value(name);
@@ -1160,7 +1104,7 @@ bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name)
return false;
}
-int BrowserService::moveKeysToCustomData(Entry* entry, const QSharedPointer<Database>& db) const
+int BrowserService::moveKeysToCustomData(Entry* entry, QSharedPointer<Database> db)
{
int keyCounter = 0;
for (const auto& key : entry->attributes()->keys()) {
@@ -1179,14 +1123,9 @@ int BrowserService::moveKeysToCustomData(Entry* entry, const QSharedPointer<Data
return keyCounter;
}
-bool BrowserService::checkLegacySettings()
+bool BrowserService::checkLegacySettings(QSharedPointer<Database> db)
{
- if (!browserSettings()->isEnabled() || browserSettings()->noMigrationPrompt()) {
- return false;
- }
-
- auto db = getDatabase();
- if (!db) {
+ if (!db || !browserSettings()->isEnabled() || browserSettings()->noMigrationPrompt()) {
return false;
}
@@ -1272,7 +1211,9 @@ void BrowserService::raiseWindow(const bool force)
void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
{
if (dbWidget) {
- emit databaseLocked();
+ QJsonObject msg;
+ msg["action"] = QString("database-locked");
+ m_browserHost->sendClientMessage(msg);
}
}
@@ -1283,22 +1224,43 @@ void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget)
hideWindow();
m_bringToFrontRequested = false;
}
- emit databaseUnlocked();
- if (checkLegacySettings()) {
- convertAttributesToCustomData();
+ QJsonObject msg;
+ msg["action"] = QString("database-unlocked");
+ m_browserHost->sendClientMessage(msg);
+
+ auto db = dbWidget->database();
+ if (checkLegacySettings(db)) {
+ convertAttributesToCustomData(db);
}
}
}
-void BrowserService::activateDatabaseChanged(DatabaseWidget* dbWidget)
+void BrowserService::activeDatabaseChanged(DatabaseWidget* dbWidget)
{
+ m_currentDatabaseWidget = dbWidget;
if (dbWidget) {
- auto currentMode = dbWidget->currentMode();
- if (currentMode == DatabaseWidget::Mode::ViewMode || currentMode == DatabaseWidget::Mode::EditMode) {
- emit databaseUnlocked();
+ if (dbWidget->isLocked()) {
+ databaseLocked(dbWidget);
} else {
- emit databaseLocked();
+ databaseUnlocked(dbWidget);
}
}
}
+
+void BrowserService::processClientMessage(const QJsonObject& message)
+{
+ auto clientID = message["clientID"].toString();
+ if (clientID.isEmpty()) {
+ return;
+ }
+
+ // Create a new client action if we haven't seen this id yet
+ if (!m_browserClients.contains(clientID)) {
+ m_browserClients.insert(clientID, QSharedPointer<BrowserAction>::create());
+ }
+
+ auto& action = m_browserClients.value(clientID);
+ auto response = action->processClientMessage(message);
+ m_browserHost->sendClientMessage(response);
+}
diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h
index 3157df61f..6de5e49bf 100644
--- a/src/browser/BrowserService.h
+++ b/src/browser/BrowserService.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2013 Francois Ferrand
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,8 +21,9 @@
#define BROWSERSERVICE_H
#include "core/Entry.h"
-#include "gui/DatabaseTabWidget.h"
#include <QObject>
+#include <QPointer>
+#include <QSharedPointer>
#include <QtCore>
typedef QPair<QString, QString> StringPair;
@@ -33,28 +34,33 @@ enum
max_length = 16 * 1024
};
+class DatabaseTabWidget;
+class DatabaseWidget;
+class BrowserHost;
+class BrowserAction;
+
class BrowserService : public QObject
{
Q_OBJECT
public:
- enum ReturnValue
- {
- Success,
- Error,
- Canceled
- };
+ explicit BrowserService();
+ static BrowserService* instance();
- explicit BrowserService(DatabaseTabWidget* parent);
+ void setEnabled(bool enabled);
+
+ QString getKey(const QString& id);
+ QString storeKey(const QString& key);
+ QString getDatabaseHash(bool legacy = false);
bool isDatabaseOpened() const;
bool openDatabase(bool triggerUnlock);
- QString getDatabaseRootUuid();
- QString getDatabaseRecycleBinUuid();
- QJsonObject getDatabaseGroups(const QSharedPointer<Database>& selectedDb = {});
+ void lockDatabase();
+
+ QJsonObject getDatabaseGroups();
QJsonObject createNewGroup(const QString& groupName);
- QString getKey(const QString& id);
- void addEntry(const QString& id,
+
+ void addEntry(const QString& dbid,
const QString& login,
const QString& password,
const QString& url,
@@ -63,11 +69,22 @@ public:
const QString& group,
const QString& groupUuid,
const QSharedPointer<Database>& selectedDb = {});
- QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& url, const QString& submitUrl);
- QList<Entry*> searchEntries(const QString& url, const QString& submitUrl, const StringPairList& keyList);
- void convertAttributesToCustomData(const QSharedPointer<Database>& currentDb = {});
+ bool updateEntry(const QString& dbid,
+ const QString& uuid,
+ const QString& login,
+ const QString& password,
+ const QString& url,
+ const QString& submitUrl);
+
+ QJsonArray findMatchingEntries(const QString& dbid,
+ const QString& url,
+ const QString& submitUrl,
+ const QString& realm,
+ const StringPairList& keyList,
+ const bool httpAuth = false);
+
+ static void convertAttributesToCustomData(QSharedPointer<Database> db);
-public:
static const QString KEEPASSXCBROWSER_NAME;
static const QString KEEPASSXCBROWSER_OLD_NAME;
static const QString ASSOCIATE_KEY_PREFIX;
@@ -78,28 +95,12 @@ public:
static const QString ADDITIONAL_URL;
public slots:
- QJsonArray findMatchingEntries(const QString& id,
- const QString& url,
- const QString& submitUrl,
- const QString& realm,
- const StringPairList& keyList,
- const bool httpAuth = false);
- QString storeKey(const QString& key);
- ReturnValue updateEntry(const QString& id,
- const QString& uuid,
- const QString& login,
- const QString& password,
- const QString& url,
- const QString& submitUrl);
void databaseLocked(DatabaseWidget* dbWidget);
void databaseUnlocked(DatabaseWidget* dbWidget);
- void activateDatabaseChanged(DatabaseWidget* dbWidget);
- void lockDatabase();
+ void activeDatabaseChanged(DatabaseWidget* dbWidget);
-signals:
- void databaseLocked();
- void databaseUnlocked();
- void databaseChanged();
+private slots:
+ void processClientMessage(const QJsonObject& message);
private:
enum Access
@@ -116,7 +117,8 @@ private:
Hidden
};
-private:
+ QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& url, const QString& submitUrl);
+ QList<Entry*> searchEntries(const QString& url, const QString& submitUrl, const StringPairList& keyList);
QList<Entry*> sortEntries(QList<Entry*>& pwEntries, const QString& host, const QString& submitUrl);
QList<Entry*> confirmEntries(QList<Entry*>& pwEntriesToConfirm,
const QString& url,
@@ -125,6 +127,7 @@ private:
const QString& realm,
const bool httpAuth);
QJsonObject prepareEntry(const Entry* entry);
+ QJsonArray getChildrenFromGroup(Group* group);
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
int
@@ -135,21 +138,35 @@ private:
QString baseDomain(const QString& hostname) const;
QSharedPointer<Database> getDatabase();
QSharedPointer<Database> selectedDatabase();
- QJsonArray getChildrenFromGroup(Group* group);
- bool moveSettingsToCustomData(Entry* entry, const QString& name) const;
- int moveKeysToCustomData(Entry* entry, const QSharedPointer<Database>& db) const;
- bool checkLegacySettings();
+ QString getDatabaseRootUuid();
+ QString getDatabaseRecycleBinUuid();
+
+ bool checkLegacySettings(QSharedPointer<Database> db);
+
void hideWindow() const;
void raiseWindow(const bool force = false);
-private:
- DatabaseTabWidget* const m_dbTabWidget;
+ static bool moveSettingsToCustomData(Entry* entry, const QString& name);
+ static int moveKeysToCustomData(Entry* entry, QSharedPointer<Database> db);
+
+ QPointer<BrowserHost> m_browserHost;
+ QHash<QString, QSharedPointer<BrowserAction>> m_browserClients;
+
bool m_dialogActive;
bool m_bringToFrontRequested;
WindowState m_prevWindowState;
QUuid m_keepassBrowserUUID;
+ QPointer<DatabaseWidget> m_currentDatabaseWidget;
+
+ Q_DISABLE_COPY(BrowserService);
+
friend class TestBrowser;
};
+static inline BrowserService* browserService()
+{
+ return BrowserService::instance();
+}
+
#endif // BROWSERSERVICE_H
diff --git a/src/browser/BrowserSettings.cpp b/src/browser/BrowserSettings.cpp
index 7fa80ea3b..5d6514cae 100644
--- a/src/browser/BrowserSettings.cpp
+++ b/src/browser/BrowserSettings.cpp
@@ -162,16 +162,6 @@ void BrowserSettings::setNoMigrationPrompt(bool prompt)
config()->set(Config::Browser_NoMigrationPrompt, prompt);
}
-bool BrowserSettings::supportBrowserProxy()
-{
- return config()->get(Config::Browser_SupportBrowserProxy).toBool();
-}
-
-void BrowserSettings::setSupportBrowserProxy(bool enabled)
-{
- config()->set(Config::Browser_SupportBrowserProxy, enabled);
-}
-
bool BrowserSettings::useCustomProxy()
{
return config()->get(Config::Browser_UseCustomProxy).toBool();
@@ -184,9 +174,6 @@ void BrowserSettings::setUseCustomProxy(bool enabled)
QString BrowserSettings::customProxyLocation()
{
- if (!useCustomProxy()) {
- return QString();
- }
return config()->get(Config::Browser_CustomProxyLocation).toString();
}
@@ -195,6 +182,11 @@ void BrowserSettings::setCustomProxyLocation(const QString& location)
config()->set(Config::Browser_CustomProxyLocation, location);
}
+QString BrowserSettings::proxyLocation()
+{
+ return m_nativeMessageInstaller.getProxyPath();
+}
+
bool BrowserSettings::updateBinaryPath()
{
return config()->get(Config::Browser_UpdateBinaryPath).toBool();
@@ -215,81 +207,14 @@ void BrowserSettings::setAllowExpiredCredentials(bool enabled)
config()->set(Config::Browser_AllowExpiredCredentials, enabled);
}
-bool BrowserSettings::chromeSupport()
-{
- return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROME);
-}
-
-void BrowserSettings::setChromeSupport(bool enabled)
-{
- m_hostInstaller.installBrowser(
- HostInstaller::SupportedBrowsers::CHROME, enabled, supportBrowserProxy(), customProxyLocation());
-}
-
-bool BrowserSettings::chromiumSupport()
-{
- return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROMIUM);
-}
-
-void BrowserSettings::setChromiumSupport(bool enabled)
-{
- m_hostInstaller.installBrowser(
- HostInstaller::SupportedBrowsers::CHROMIUM, enabled, supportBrowserProxy(), customProxyLocation());
-}
-
-bool BrowserSettings::firefoxSupport()
-{
- return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::FIREFOX);
-}
-
-void BrowserSettings::setFirefoxSupport(bool enabled)
+bool BrowserSettings::browserSupport(BrowserShared::SupportedBrowsers browser)
{
- m_hostInstaller.installBrowser(
- HostInstaller::SupportedBrowsers::FIREFOX, enabled, supportBrowserProxy(), customProxyLocation());
+ return m_nativeMessageInstaller.isBrowserEnabled(browser);
}
-bool BrowserSettings::vivaldiSupport()
+void BrowserSettings::setBrowserSupport(BrowserShared::SupportedBrowsers browser, bool enabled)
{
- return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::VIVALDI);
-}
-
-void BrowserSettings::setVivaldiSupport(bool enabled)
-{
- m_hostInstaller.installBrowser(
- HostInstaller::SupportedBrowsers::VIVALDI, enabled, supportBrowserProxy(), customProxyLocation());
-}
-
-bool BrowserSettings::braveSupport()
-{
- return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::BRAVE);
-}
-
-void BrowserSettings::setBraveSupport(bool enabled)
-{
- m_hostInstaller.installBrowser(
- HostInstaller::SupportedBrowsers::BRAVE, enabled, supportBrowserProxy(), customProxyLocation());
-}
-
-bool BrowserSettings::torBrowserSupport()
-{
- return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::TOR_BROWSER);
-}
-
-void BrowserSettings::setTorBrowserSupport(bool enabled)
-{
- m_hostInstaller.installBrowser(
- HostInstaller::SupportedBrowsers::TOR_BROWSER, enabled, supportBrowserProxy(), customProxyLocation());
-}
-
-bool BrowserSettings::edgeSupport()
-{
- return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::EDGE);
-}
-
-void BrowserSettings::setEdgeSupport(bool enabled)
-{
- m_hostInstaller.installBrowser(
- HostInstaller::SupportedBrowsers::EDGE, enabled, supportBrowserProxy(), customProxyLocation());
+ m_nativeMessageInstaller.setBrowserEnabled(browser, enabled);
}
bool BrowserSettings::passwordUseNumbers()
@@ -563,13 +488,7 @@ QJsonObject BrowserSettings::generatePassword()
return password;
}
-void BrowserSettings::updateBinaryPaths(const QString& customProxyLocation)
-{
- bool isProxy = supportBrowserProxy();
- m_hostInstaller.updateBinaryPaths(isProxy, customProxyLocation);
-}
-
-bool BrowserSettings::checkIfProxyExists(QString& path)
+void BrowserSettings::updateBinaryPaths()
{
- return m_hostInstaller.checkIfProxyExists(supportBrowserProxy(), customProxyLocation(), path);
+ m_nativeMessageInstaller.updateBinaryPaths();
}
diff --git a/src/browser/BrowserSettings.h b/src/browser/BrowserSettings.h
index 9340cd0a3..3f5cceea7 100644
--- a/src/browser/BrowserSettings.h
+++ b/src/browser/BrowserSettings.h
@@ -20,7 +20,8 @@
#ifndef BROWSERSETTINGS_H
#define BROWSERSETTINGS_H
-#include "HostInstaller.h"
+#include "BrowserShared.h"
+#include "NativeMessageInstaller.h"
#include "core/PassphraseGenerator.h"
#include "core/PasswordGenerator.h"
@@ -58,30 +59,18 @@ public:
bool noMigrationPrompt();
void setNoMigrationPrompt(bool prompt);
- bool supportBrowserProxy();
- void setSupportBrowserProxy(bool enabled);
bool useCustomProxy();
void setUseCustomProxy(bool enabled);
QString customProxyLocation();
void setCustomProxyLocation(const QString& location);
+ QString proxyLocation();
bool updateBinaryPath();
void setUpdateBinaryPath(bool enabled);
bool allowExpiredCredentials();
void setAllowExpiredCredentials(bool enabled);
- bool chromeSupport();
- void setChromeSupport(bool enabled);
- bool chromiumSupport();
- void setChromiumSupport(bool enabled);
- bool firefoxSupport();
- void setFirefoxSupport(bool enabled);
- bool vivaldiSupport();
- void setVivaldiSupport(bool enabled);
- bool braveSupport();
- void setBraveSupport(bool enabled);
- bool torBrowserSupport();
- void setTorBrowserSupport(bool enabled);
- bool edgeSupport();
- void setEdgeSupport(bool enabled);
+
+ bool browserSupport(BrowserShared::SupportedBrowsers browser);
+ void setBrowserSupport(BrowserShared::SupportedBrowsers browser, bool enabled);
bool passwordUseNumbers();
void setPasswordUseNumbers(bool useNumbers);
@@ -126,15 +115,14 @@ public:
PasswordGenerator::CharClasses passwordCharClasses();
PasswordGenerator::GeneratorFlags passwordGeneratorFlags();
QJsonObject generatePassword();
- void updateBinaryPaths(const QString& customProxyLocation = QString());
- bool checkIfProxyExists(QString& path);
+ void updateBinaryPaths();
private:
static BrowserSettings* m_instance;
PasswordGenerator m_passwordGenerator;
PassphraseGenerator m_passPhraseGenerator;
- HostInstaller m_hostInstaller;
+ NativeMessageInstaller m_nativeMessageInstaller;
};
inline BrowserSettings* browserSettings()
diff --git a/src/browser/BrowserSettingsPage.cpp b/src/browser/BrowserSettingsPage.cpp
new file mode 100644
index 000000000..692854bf8
--- /dev/null
+++ b/src/browser/BrowserSettingsPage.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "BrowserSettingsPage.h"
+
+#include "BrowserService.h"
+#include "BrowserSettings.h"
+#include "BrowserSettingsWidget.h"
+#include "core/Resources.h"
+
+QString BrowserSettingsPage::name()
+{
+ return QObject::tr("Browser Integration");
+}
+
+QIcon BrowserSettingsPage::icon()
+{
+ return Resources::instance()->icon("internet-web-browser");
+}
+
+QWidget* BrowserSettingsPage::createWidget()
+{
+ return new BrowserSettingsWidget();
+}
+
+void BrowserSettingsPage::loadSettings(QWidget* widget)
+{
+ qobject_cast<BrowserSettingsWidget*>(widget)->loadSettings();
+}
+
+void BrowserSettingsPage::saveSettings(QWidget* widget)
+{
+ qobject_cast<BrowserSettingsWidget*>(widget)->saveSettings();
+ browserService()->setEnabled(browserSettings()->isEnabled());
+}
diff --git a/src/browser/BrowserSettingsPage.h b/src/browser/BrowserSettingsPage.h
new file mode 100644
index 000000000..9e669b194
--- /dev/null
+++ b/src/browser/BrowserSettingsPage.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEEPASSXC_BROWSERSETTINGSPAGE_H
+#define KEEPASSXC_BROWSERSETTINGSPAGE_H
+
+#include "gui/ApplicationSettingsWidget.h"
+
+class BrowserSettingsPage : public ISettingsPage
+{
+public:
+ explicit BrowserSettingsPage() = default;
+ ~BrowserSettingsPage() override = default;
+
+ QString name() override;
+ QIcon icon() override;
+ QWidget* createWidget() override;
+ void loadSettings(QWidget* widget) override;
+ void saveSettings(QWidget* widget) override;
+};
+
+#endif // KEEPASSXC_BROWSERSETTINGSPAGE_H
diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserSettingsWidget.cpp
index 8a67d62da..15b20b19a 100644
--- a/src/browser/BrowserOptionDialog.cpp
+++ b/src/browser/BrowserSettingsWidget.cpp
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2013 Francois Ferrand
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,8 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "BrowserOptionDialog.h"
-#include "ui_BrowserOptionDialog.h"
+#include "BrowserSettingsWidget.h"
+#include "ui_BrowserSettingsWidget.h"
#include "BrowserSettings.h"
#include "config-keepassx.h"
@@ -26,9 +24,9 @@
#include <QFileDialog>
-BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
+BrowserSettingsWidget::BrowserSettingsWidget(QWidget* parent)
: QWidget(parent)
- , m_ui(new Ui::BrowserOptionDialog())
+ , m_ui(new Ui::BrowserSettingsWidget())
{
m_ui->setupUi(this);
@@ -52,13 +50,9 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
snapInstructions));
// clang-format on
- m_ui->scriptWarningWidget->setVisible(false);
- m_ui->scriptWarningWidget->setAutoHideTimeout(-1);
-
- m_ui->warningWidget->showMessage(tr("<b>Warning:</b> The following options can be dangerous!"),
- MessageWidget::Warning);
m_ui->warningWidget->setCloseButtonVisible(false);
m_ui->warningWidget->setAutoHideTimeout(-1);
+ m_ui->warningWidget->setAnimate(false);
m_ui->tabWidget->setEnabled(m_ui->enableBrowserSupport->isChecked());
connect(m_ui->enableBrowserSupport, SIGNAL(toggled(bool)), m_ui->tabWidget, SLOT(setEnabled(bool)));
@@ -67,6 +61,8 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
m_ui->customProxyLocationBrowseButton->setEnabled(m_ui->useCustomProxy->isChecked());
connect(m_ui->useCustomProxy, SIGNAL(toggled(bool)), m_ui->customProxyLocation, SLOT(setEnabled(bool)));
connect(m_ui->useCustomProxy, SIGNAL(toggled(bool)), m_ui->customProxyLocationBrowseButton, SLOT(setEnabled(bool)));
+ connect(m_ui->useCustomProxy, SIGNAL(toggled(bool)), SLOT(validateCustomProxyLocation()));
+ connect(m_ui->customProxyLocation, SIGNAL(editingFinished()), SLOT(validateCustomProxyLocation()));
connect(m_ui->customProxyLocationBrowseButton, SIGNAL(clicked()), this, SLOT(showProxyLocationFileDialog()));
#ifndef Q_OS_LINUX
@@ -86,11 +82,11 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
m_ui->browserGlobalWarningWidget->setVisible(false);
}
-BrowserOptionDialog::~BrowserOptionDialog()
+BrowserSettingsWidget::~BrowserSettingsWidget()
{
}
-void BrowserOptionDialog::loadSettings()
+void BrowserSettingsWidget::loadSettings()
{
auto settings = browserSettings();
m_ui->enableBrowserSupport->setChecked(settings->isEnabled());
@@ -116,43 +112,39 @@ void BrowserOptionDialog::loadSettings()
m_ui->searchInAllDatabases->setChecked(settings->searchInAllDatabases());
m_ui->supportKphFields->setChecked(settings->supportKphFields());
m_ui->noMigrationPrompt->setChecked(settings->noMigrationPrompt());
- m_ui->supportBrowserProxy->setChecked(settings->supportBrowserProxy());
m_ui->useCustomProxy->setChecked(settings->useCustomProxy());
m_ui->customProxyLocation->setText(settings->customProxyLocation());
m_ui->updateBinaryPath->setChecked(settings->updateBinaryPath());
m_ui->allowExpiredCredentials->setChecked(settings->allowExpiredCredentials());
- m_ui->chromeSupport->setChecked(settings->chromeSupport());
- m_ui->chromiumSupport->setChecked(settings->chromiumSupport());
- m_ui->firefoxSupport->setChecked(settings->firefoxSupport());
- m_ui->edgeSupport->setChecked(settings->edgeSupport());
+ m_ui->chromeSupport->setChecked(settings->browserSupport(BrowserShared::CHROME));
+ m_ui->chromiumSupport->setChecked(settings->browserSupport(BrowserShared::CHROMIUM));
+ m_ui->firefoxSupport->setChecked(settings->browserSupport(BrowserShared::FIREFOX));
+ m_ui->edgeSupport->setChecked(settings->browserSupport(BrowserShared::EDGE));
#ifndef Q_OS_WIN
- m_ui->braveSupport->setChecked(settings->braveSupport());
- m_ui->vivaldiSupport->setChecked(settings->vivaldiSupport());
- m_ui->torBrowserSupport->setChecked(settings->torBrowserSupport());
+ m_ui->braveSupport->setChecked(settings->browserSupport(BrowserShared::BRAVE));
+ m_ui->vivaldiSupport->setChecked(settings->browserSupport(BrowserShared::VIVALDI));
+ m_ui->torBrowserSupport->setChecked(settings->browserSupport(BrowserShared::TOR_BROWSER));
#endif
#ifndef Q_OS_LINUX
m_ui->snapWarningLabel->setVisible(false);
#endif
-// TODO: Enable when Linux version is released
+// TODO: Enable Edge support when Linux version is released
#ifdef Q_OS_LINUX
m_ui->edgeSupport->setChecked(false);
m_ui->edgeSupport->setEnabled(false);
#endif
-#if defined(KEEPASSXC_DIST_APPIMAGE)
- m_ui->supportBrowserProxy->setChecked(true);
- m_ui->supportBrowserProxy->setEnabled(false);
-#elif defined(KEEPASSXC_DIST_SNAP)
+#ifdef KEEPASSXC_DIST_SNAP
// Disable settings that will not work
- m_ui->supportBrowserProxy->setChecked(true);
- m_ui->supportBrowserProxy->setEnabled(false);
m_ui->useCustomProxy->setChecked(false);
- m_ui->useCustomProxy->setEnabled(false);
+ m_ui->useCustomProxy->setVisible(false);
+ m_ui->customProxyLocation->setVisible(false);
+ m_ui->customProxyLocationBrowseButton->setVisible(false);
m_ui->browsersGroupBox->setVisible(false);
m_ui->browsersGroupBox->setEnabled(false);
m_ui->updateBinaryPath->setChecked(false);
- m_ui->updateBinaryPath->setEnabled(false);
+ m_ui->updateBinaryPath->setVisible(false);
// Show notice to user
m_ui->browserGlobalWarningWidget->showMessage(tr("Please see special instructions for browser extension use below"),
MessageWidget::Warning);
@@ -160,23 +152,23 @@ void BrowserOptionDialog::loadSettings()
m_ui->browserGlobalWarningWidget->setAutoHideTimeout(-1);
#endif
- // Check for native messaging host location errors
- QString path;
- if (!settings->checkIfProxyExists(path)) {
- auto text =
- tr("<b>Warning</b>, the keepassxc-proxy application was not found!"
- "<br />Please check the KeePassXC installation directory or confirm the custom path in advanced options."
- "<br />Browser integration WILL NOT WORK without the proxy application."
- "<br />Expected Path: %1")
- .arg(path);
- m_ui->scriptWarningWidget->showMessage(text, MessageWidget::Warning);
- m_ui->scriptWarningWidget->setVisible(true);
+ validateCustomProxyLocation();
+}
+
+void BrowserSettingsWidget::validateCustomProxyLocation()
+{
+ auto path = m_ui->customProxyLocation->text();
+ if (m_ui->useCustomProxy->isChecked() && !QFile::exists(path)) {
+ m_ui->warningWidget->showMessage(tr("<b>Error:</b> The custom proxy location cannot be found!"
+ "<br/>Browser integration WILL NOT WORK without the proxy application."),
+ MessageWidget::Error);
} else {
- m_ui->scriptWarningWidget->setVisible(false);
+ m_ui->warningWidget->showMessage(tr("<b>Warning:</b> The following options can be dangerous!"),
+ MessageWidget::Warning);
}
}
-void BrowserOptionDialog::saveSettings()
+void BrowserSettingsWidget::saveSettings()
{
auto settings = browserSettings();
settings->setEnabled(m_ui->enableBrowserSupport->isChecked());
@@ -186,7 +178,6 @@ void BrowserOptionDialog::saveSettings()
settings->setMatchUrlScheme(m_ui->matchUrlScheme->isChecked());
settings->setSortByUsername(m_ui->sortByUsername->isChecked());
- settings->setSupportBrowserProxy(m_ui->supportBrowserProxy->isChecked());
settings->setUseCustomProxy(m_ui->useCustomProxy->isChecked());
settings->setCustomProxyLocation(m_ui->customProxyLocation->text());
@@ -199,18 +190,18 @@ void BrowserOptionDialog::saveSettings()
settings->setSupportKphFields(m_ui->supportKphFields->isChecked());
settings->setNoMigrationPrompt(m_ui->noMigrationPrompt->isChecked());
- settings->setChromeSupport(m_ui->chromeSupport->isChecked());
- settings->setChromiumSupport(m_ui->chromiumSupport->isChecked());
- settings->setFirefoxSupport(m_ui->firefoxSupport->isChecked());
- settings->setEdgeSupport(m_ui->edgeSupport->isChecked());
+ settings->setBrowserSupport(BrowserShared::CHROME, m_ui->chromeSupport->isChecked());
+ settings->setBrowserSupport(BrowserShared::CHROMIUM, m_ui->chromiumSupport->isChecked());
+ settings->setBrowserSupport(BrowserShared::FIREFOX, m_ui->firefoxSupport->isChecked());
+ settings->setBrowserSupport(BrowserShared::EDGE, m_ui->edgeSupport->isChecked());
#ifndef Q_OS_WIN
- settings->setBraveSupport(m_ui->braveSupport->isChecked());
- settings->setVivaldiSupport(m_ui->vivaldiSupport->isChecked());
- settings->setTorBrowserSupport(m_ui->torBrowserSupport->isChecked());
+ settings->setBrowserSupport(BrowserShared::BRAVE, m_ui->braveSupport->isChecked());
+ settings->setBrowserSupport(BrowserShared::VIVALDI, m_ui->vivaldiSupport->isChecked());
+ settings->setBrowserSupport(BrowserShared::TOR_BROWSER, m_ui->torBrowserSupport->isChecked());
#endif
}
-void BrowserOptionDialog::showProxyLocationFileDialog()
+void BrowserSettingsWidget::showProxyLocationFileDialog()
{
#ifdef Q_OS_WIN
QString fileTypeFilter(QString("%1 (*.exe);;%2 (*.*)").arg(tr("Executable Files"), tr("All Files")));
@@ -222,4 +213,5 @@ void BrowserOptionDialog::showProxyLocationFileDialog()
QFileInfo(QCoreApplication::applicationDirPath()).filePath(),
fileTypeFilter);
m_ui->customProxyLocation->setText(proxyLocation);
+ validateCustomProxyLocation();
}
diff --git a/src/browser/BrowserOptionDialog.h b/src/browser/BrowserSettingsWidget.h
index 5efb808e5..8f9dea62f 100644
--- a/src/browser/BrowserOptionDialog.h
+++ b/src/browser/BrowserSettingsWidget.h
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2013 Francois Ferrand
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,8 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef BROWSEROPTIONDIALOG_H
-#define BROWSEROPTIONDIALOG_H
+#ifndef BROWSERSETTINGSWIDGET_H
+#define BROWSERSETTINGSWIDGET_H
#include <QPointer>
#include <QScopedPointer>
@@ -26,16 +24,16 @@
namespace Ui
{
- class BrowserOptionDialog;
+ class BrowserSettingsWidget;
}
-class BrowserOptionDialog : public QWidget
+class BrowserSettingsWidget : public QWidget
{
Q_OBJECT
public:
- explicit BrowserOptionDialog(QWidget* parent = nullptr);
- ~BrowserOptionDialog();
+ explicit BrowserSettingsWidget(QWidget* parent = nullptr);
+ ~BrowserSettingsWidget();
public slots:
void loadSettings();
@@ -43,9 +41,10 @@ public slots:
private slots:
void showProxyLocationFileDialog();
+ void validateCustomProxyLocation();
private:
- QScopedPointer<Ui::BrowserOptionDialog> m_ui;
+ QScopedPointer<Ui::BrowserSettingsWidget> m_ui;
};
-#endif // BROWSEROPTIONDIALOG_H
+#endif // BROWSERSETTINGSWIDGET_H
diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserSettingsWidget.ui
index 9dabde948..b3458fdcd 100755..100644
--- a/src/browser/BrowserOptionDialog.ui
+++ b/src/browser/BrowserSettingsWidget.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>BrowserOptionDialog</class>
- <widget class="QWidget" name="BrowserOptionDialog">
+ <class>BrowserSettingsWidget</class>
+ <widget class="QWidget" name="BrowserSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
@@ -50,16 +50,6 @@
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="MessageWidget" name="scriptWarningWidget" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- <item>
<widget class="QLabel" name="snapWarningLabel">
<property name="text">
<string>Browsers installed as snaps are currently not supported.</string>
@@ -352,16 +342,6 @@
</widget>
</item>
<item>
- <widget class="QCheckBox" name="supportBrowserProxy">
- <property name="toolTip">
- <string>Support a proxy application between KeePassXC and browser extension.</string>
- </property>
- <property name="text">
- <string>Use a proxy application between KeePassXC and browser extension</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QCheckBox" name="useCustomProxy">
<property name="toolTip">
<string>Use a custom proxy location if you installed a proxy manually.</string>
diff --git a/src/browser/BrowserShared.cpp b/src/browser/BrowserShared.cpp
new file mode 100644
index 000000000..654201705
--- /dev/null
+++ b/src/browser/BrowserShared.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "BrowserShared.h"
+#include "config-keepassx.h"
+
+#include <QCoreApplication>
+#include <QProcessEnvironment>
+#include <QStandardPaths>
+#include <QVariant>
+
+namespace BrowserShared
+{
+ QString localServerPath()
+ {
+ const auto appName = qApp->property("KPXC_QUALIFIED_APPNAME").toString();
+ const auto serverName = QStringLiteral("/%1.BrowserServer").arg(appName);
+#if defined(KEEPASSXC_DIST_SNAP)
+ return QProcessEnvironment::systemEnvironment().value("SNAP_USER_COMMON") + serverName;
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
+ // Use XDG_RUNTIME_DIR instead of /tmp if it's available
+ QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
+ return path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverName
+ : path + serverName;
+#elif defined(Q_OS_WIN)
+ // Windows uses named pipes
+ return serverName;
+#else // Q_OS_MACOS and others
+ return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverName;
+#endif
+ }
+} // namespace BrowserShared
diff --git a/src/browser/BrowserShared.h b/src/browser/BrowserShared.h
new file mode 100644
index 000000000..02bee9c44
--- /dev/null
+++ b/src/browser/BrowserShared.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEEPASSXC_BROWSERSHARED_H
+#define KEEPASSXC_BROWSERSHARED_H
+
+#include <QString>
+
+namespace BrowserShared
+{
+ constexpr int NATIVEMSG_MAX_LENGTH = 1024 * 1024;
+
+ enum SupportedBrowsers : int
+ {
+ CHROME = 0,
+ CHROMIUM,
+ FIREFOX,
+ VIVALDI,
+ TOR_BROWSER,
+ BRAVE,
+ EDGE,
+ MAX_SUPPORTED
+ };
+
+ QString localServerPath();
+} // namespace BrowserShared
+
+#endif // KEEPASSXC_BROWSERSHARED_H
diff --git a/src/browser/CMakeLists.txt b/src/browser/CMakeLists.txt
index 7e813eb5b..bb92511bc 100755
--- a/src/browser/CMakeLists.txt
+++ b/src/browser/CMakeLists.txt
@@ -20,15 +20,15 @@ if(WITH_XC_BROWSER)
set(keepassxcbrowser_SOURCES
BrowserAccessControlDialog.cpp
BrowserAction.cpp
- BrowserClients.cpp
BrowserEntryConfig.cpp
BrowserEntrySaveDialog.cpp
- BrowserOptionDialog.cpp
+ BrowserHost.cpp
+ BrowserSettingsPage.cpp
+ BrowserSettingsWidget.cpp
BrowserService.cpp
BrowserSettings.cpp
- HostInstaller.cpp
- NativeMessagingBase.cpp
- NativeMessagingHost.cpp
+ BrowserShared.cpp
+ NativeMessageInstaller.cpp
Variant.cpp)
add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES})
diff --git a/src/browser/HostInstaller.cpp b/src/browser/HostInstaller.cpp
deleted file mode 100644
index f4ffae3b7..000000000
--- a/src/browser/HostInstaller.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "HostInstaller.h"
-#include "config-keepassx.h"
-
-#include <QCoreApplication>
-#include <QDir>
-#include <QFile>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QMessageBox>
-#include <QProcessEnvironment>
-#include <QStandardPaths>
-
-HostInstaller::HostInstaller()
- : HOST_NAME("org.keepassxc.keepassxc_browser")
- , ALLOWED_EXTENSIONS(QStringList() << "keepassxc-browser@keepassxc.org")
- , ALLOWED_ORIGINS(QStringList() << "chrome-extension://pdffhmdngciaglkoonimfcmckehcpafo/"
- << "chrome-extension://oboonakemofpalcgghocfoadofidjkkk/")
-#if defined(Q_OS_MACOS)
- , TARGET_DIR_CHROME("/Library/Application Support/Google/Chrome/NativeMessagingHosts")
- , TARGET_DIR_CHROMIUM("/Library/Application Support/Chromium/NativeMessagingHosts")
- , TARGET_DIR_FIREFOX("/Library/Application Support/Mozilla/NativeMessagingHosts")
- , TARGET_DIR_VIVALDI("/Library/Application Support/Vivaldi/NativeMessagingHosts")
- , TARGET_DIR_TOR_BROWSER("/Library/Application Support/TorBrowser-Data/Browser/Mozilla/NativeMessagingHosts")
- , TARGET_DIR_BRAVE("/Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts")
- , TARGET_DIR_EDGE("/Library/Application Support/Microsoft Edge/NativeMessagingHosts")
-#elif defined(Q_OS_WIN)
- // clang-format off
- , TARGET_DIR_CHROME("HKEY_CURRENT_USER\\Software\\Google\\Chrome\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser")
- , TARGET_DIR_CHROMIUM("HKEY_CURRENT_USER\\Software\\Chromium\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser")
- // clang-format on
- , TARGET_DIR_FIREFOX("HKEY_CURRENT_USER\\Software\\Mozilla\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser")
- , TARGET_DIR_VIVALDI(TARGET_DIR_CHROME)
- , TARGET_DIR_TOR_BROWSER(TARGET_DIR_FIREFOX)
- , TARGET_DIR_BRAVE(TARGET_DIR_CHROME)
- , TARGET_DIR_EDGE(
- "HKEY_CURRENT_USER\\Software\\Microsoft\\Edge\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser")
-#else
- , TARGET_DIR_CHROME("/.config/google-chrome/NativeMessagingHosts")
- , TARGET_DIR_CHROMIUM("/.config/chromium/NativeMessagingHosts")
- , TARGET_DIR_FIREFOX("/.mozilla/native-messaging-hosts")
- , TARGET_DIR_VIVALDI("/.config/vivaldi/NativeMessagingHosts")
- , TARGET_DIR_TOR_BROWSER("/.tor-browser/app/Browser/TorBrowser/Data/Browser/.mozilla/native-messaging-hosts")
- , TARGET_DIR_BRAVE("/.config/BraveSoftware/Brave-Browser/NativeMessagingHosts")
- , TARGET_DIR_EDGE("/.config/microsoftedge/NativeMessagingHosts")
-#endif
-{
-}
-
-/**
- * Checks if the selected browser has native messaging host properly installed
- *
- * @param browser Selected browser
- * @return bool Script is installed correctly
- */
-bool HostInstaller::checkIfInstalled(SupportedBrowsers browser)
-{
- QString fileName = getPath(browser);
-#ifdef Q_OS_WIN
- QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
- return registryEntryFound(settings);
-#else
- return QFile::exists(fileName);
-#endif
-}
-
-/**
- * Checks if keepassxc-proxy location is found
- *
- * @param proxy Is keepassxc-proxy enabled
- * @param location Custom proxy location
- * @param path The path is set here and returned to the caller
- * @return bool
- */
-bool HostInstaller::checkIfProxyExists(const bool& proxy, const QString& location, QString& path) const
-{
- QString fileName = getProxyPath(proxy, location);
- path = fileName;
- return QFile::exists(fileName);
-}
-
-/**
- * Installs native messaging JSON script for the selected browser
- *
- * @param browser Selected browser
- * @param enabled Is browser integration enabled
- * @param proxy Is keepassxc-proxy enabled
- * @param location Custom proxy location
- */
-void HostInstaller::installBrowser(SupportedBrowsers browser,
- const bool& enabled,
- const bool& proxy,
- const QString& location)
-{
- if (enabled) {
-#ifdef Q_OS_WIN
- // Create a registry key
- QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
- settings.setValue("Default", getPath(browser));
-#endif
- // Always create the script file
- QJsonObject script = constructFile(browser, proxy, location);
- if (!saveFile(browser, script)) {
- QMessageBox::critical(nullptr,
- tr("KeePassXC: Cannot save file!"),
- tr("Cannot save the native messaging script file."),
- QMessageBox::Ok);
- }
- } else {
- // Remove the script file
- QString fileName = getPath(browser);
- QFile::remove(fileName);
-#ifdef Q_OS_WIN
- // Remove the registry entry
- QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
- settings.remove("Default");
-#endif
- }
-}
-
-/**
- * Updates the paths to native messaging host for each browser that has been enabled
- *
- * @param proxy Is keepassxc-proxy enabled
- * @param location Custom proxy location
- */
-void HostInstaller::updateBinaryPaths(const bool& proxy, const QString& location)
-{
- for (int i = 0; i <= SupportedBrowsers::EDGE; ++i) {
- if (checkIfInstalled(static_cast<SupportedBrowsers>(i))) {
- installBrowser(static_cast<SupportedBrowsers>(i), true, proxy, location);
- }
- }
-}
-
-/**
- * Returns the target path for each browser. Windows uses a registry path instead of a file path
- *
- * @param browser Selected browser
- * @return QString Current target path for the selected browser
- */
-QString HostInstaller::getTargetPath(SupportedBrowsers browser) const
-{
- switch (browser) {
- case SupportedBrowsers::CHROME:
- return TARGET_DIR_CHROME;
- case SupportedBrowsers::CHROMIUM:
- return TARGET_DIR_CHROMIUM;
- case SupportedBrowsers::FIREFOX:
- return TARGET_DIR_FIREFOX;
- case SupportedBrowsers::VIVALDI:
- return TARGET_DIR_VIVALDI;
- case SupportedBrowsers::TOR_BROWSER:
- return TARGET_DIR_TOR_BROWSER;
- case SupportedBrowsers::BRAVE:
- return TARGET_DIR_BRAVE;
- case SupportedBrowsers::EDGE:
- return TARGET_DIR_EDGE;
- default:
- return QString();
- }
-}
-
-/**
- * Returns the browser name
- * Needed for Windows to separate Chromium- or Firefox-based scripts
- *
- * @param browser Selected browser
- * @return QString Name of the selected browser
- */
-QString HostInstaller::getBrowserName(SupportedBrowsers browser) const
-{
- switch (browser) {
- case SupportedBrowsers::CHROME:
- return "chrome";
- case SupportedBrowsers::CHROMIUM:
- return "chromium";
- case SupportedBrowsers::FIREFOX:
- return "firefox";
- case SupportedBrowsers::VIVALDI:
- return "vivaldi";
- case SupportedBrowsers::TOR_BROWSER:
- return "tor-browser";
- case SupportedBrowsers::BRAVE:
- return "brave";
- case SupportedBrowsers::EDGE:
- return "edge";
- default:
- return QString();
- }
-}
-
-/**
- * Returns the path of native messaging JSON script for the selected browser
- *
- * @param browser Selected browser
- * @return QString JSON script path for the selected browser
- */
-QString HostInstaller::getPath(SupportedBrowsers browser) const
-{
-#ifdef Q_OS_WIN
- // If portable settings file exists save the JSON scripts to application folder
- QString userPath;
- QString portablePath = QCoreApplication::applicationDirPath() + "/keepassxc.ini";
- if (QFile::exists(portablePath)) {
- userPath = QCoreApplication::applicationDirPath();
- } else {
- userPath = QDir::fromNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
- }
-
- QString winPath = QString("%1/%2_%3.json").arg(userPath, HOST_NAME, getBrowserName(browser));
- winPath.replace("/", "\\");
- return winPath;
-#else
- QString path = getTargetPath(browser);
- return QString("%1%2/%3.json").arg(QDir::homePath(), path, HOST_NAME);
-#endif
-}
-
-/**
- * Gets the installation directory for JSON script file (application install path)
- *
- * @param browser Selected browser
- * @return QString Install path
- */
-QString HostInstaller::getInstallDir(SupportedBrowsers browser) const
-{
- QString path = getTargetPath(browser);
-#ifdef Q_OS_WIN
- return QCoreApplication::applicationDirPath();
-#else
- return QString("%1%2").arg(QDir::homePath(), path);
-#endif
-}
-
-/**
- * Gets the path to keepassxc-proxy binary
- *
- * @param proxy Is keepassxc-proxy used with KeePassXC
- * @param location Custom proxy path
- * @return path Path to keepassxc-proxy
- */
-QString HostInstaller::getProxyPath(const bool& proxy, const QString& location) const
-{
- QString path;
-#ifdef KEEPASSXC_DIST_APPIMAGE
- if (proxy && !location.isEmpty()) {
- path = location;
- } else {
- path = QProcessEnvironment::systemEnvironment().value("APPIMAGE");
- }
-#else
- if (proxy) {
- if (!location.isEmpty()) {
- path = location;
- } else {
- path = QFileInfo(QCoreApplication::applicationFilePath()).absolutePath();
- path.append("/keepassxc-proxy");
-#ifdef Q_OS_WIN
- path.append(".exe");
-#endif
- }
- } else {
- path = QFileInfo(QCoreApplication::applicationFilePath()).absoluteFilePath();
- }
-#ifdef Q_OS_WIN
- path.replace("/", "\\");
-#endif
-
-#endif // #ifdef KEEPASSXC_DIST_APPIMAGE
- return path;
-}
-
-/**
- * Constructs the JSON script file used with native messaging
- *
- * @param browser Browser (Chromium- and Firefox-based browsers need a different parameters for the script)
- * @param proxy Is keepassxc-proxy used with KeePassXC
- * @param location Custom proxy location
- * @return script The JSON script file
- */
-QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& proxy, const QString& location)
-{
- QString path = getProxyPath(proxy, location);
-
- QJsonObject script;
- script["name"] = HOST_NAME;
- script["description"] = QString("KeePassXC integration with native messaging support");
- script["path"] = path;
- script["type"] = QString("stdio");
-
- QJsonArray arr;
- if (browser == SupportedBrowsers::FIREFOX || browser == SupportedBrowsers::TOR_BROWSER) {
- for (const QString& extension : ALLOWED_EXTENSIONS) {
- arr.append(extension);
- }
- script["allowed_extensions"] = arr;
- } else {
- for (const QString& origin : ALLOWED_ORIGINS) {
- arr.append(origin);
- }
- script["allowed_origins"] = arr;
- }
-
- return script;
-}
-
-/**
- * Checks if a registry setting is found with default value
- *
- * @param settings Registry path
- * @return bool Is the registry value found
- */
-bool HostInstaller::registryEntryFound(const QSettings& settings)
-{
- return !settings.value("Default").isNull();
-}
-
-/**
- * Saves a JSON script file
- *
- * @param browser Selected browser
- * @param script JSON native messaging script object
- * @return bool Write succeeds
- */
-bool HostInstaller::saveFile(SupportedBrowsers browser, const QJsonObject& script)
-{
- QString path = getPath(browser);
- QString installDir = getInstallDir(browser);
- QDir dir(installDir);
- if (!dir.exists()) {
- QDir().mkpath(installDir);
- }
-
- QFile scriptFile(path);
- if (!scriptFile.open(QIODevice::WriteOnly)) {
- return false;
- }
-
- QJsonDocument doc(script);
- return scriptFile.write(doc.toJson()) >= 0;
-}
diff --git a/src/browser/HostInstaller.h b/src/browser/HostInstaller.h
deleted file mode 100644
index 2136d1c34..000000000
--- a/src/browser/HostInstaller.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef HOSTINSTALLER_H
-#define HOSTINSTALLER_H
-
-#include <QJsonObject>
-#include <QObject>
-#include <QSettings>
-
-class HostInstaller : public QObject
-{
- Q_OBJECT
-
-public:
- enum SupportedBrowsers : int
- {
- CHROME = 0,
- CHROMIUM = 1,
- FIREFOX = 2,
- VIVALDI = 3,
- TOR_BROWSER = 4,
- BRAVE = 5,
- EDGE = 6
- };
-
-public:
- HostInstaller();
- bool checkIfInstalled(SupportedBrowsers browser);
- bool checkIfProxyExists(const bool& proxy, const QString& location, QString& path) const;
- void installBrowser(SupportedBrowsers browser,
- const bool& enabled,
- const bool& proxy = false,
- const QString& location = "");
- void updateBinaryPaths(const bool& proxy, const QString& location = "");
-
-private:
- QString getTargetPath(SupportedBrowsers browser) const;
- QString getBrowserName(SupportedBrowsers browser) const;
- QString getPath(SupportedBrowsers browser) const;
- QString getInstallDir(SupportedBrowsers browser) const;
- QString getProxyPath(const bool& proxy, const QString& location) const;
- QJsonObject constructFile(SupportedBrowsers browser, const bool& proxy, const QString& location);
- bool registryEntryFound(const QSettings& settings);
- bool saveFile(SupportedBrowsers browser, const QJsonObject& script);
-
-private:
- const QString HOST_NAME;
- const QStringList ALLOWED_EXTENSIONS;
- const QStringList ALLOWED_ORIGINS;
- const QString TARGET_DIR_CHROME;
- const QString TARGET_DIR_CHROMIUM;
- const QString TARGET_DIR_FIREFOX;
- const QString TARGET_DIR_VIVALDI;
- const QString TARGET_DIR_TOR_BROWSER;
- const QString TARGET_DIR_BRAVE;
- const QString TARGET_DIR_EDGE;
-};
-
-#endif // HOSTINSTALLER_H
diff --git a/src/browser/NativeMessageInstaller.cpp b/src/browser/NativeMessageInstaller.cpp
new file mode 100644
index 000000000..d3b3daf32
--- /dev/null
+++ b/src/browser/NativeMessageInstaller.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
+ * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "NativeMessageInstaller.h"
+#include "BrowserSettings.h"
+#include "config-keepassx.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QMessageBox>
+#include <QProcessEnvironment>
+#include <QSettings>
+#include <QStandardPaths>
+
+using namespace BrowserShared;
+
+namespace
+{
+ const QString HOST_NAME = QStringLiteral("org.keepassxc.keepassxc_browser");
+ const QStringList ALLOWED_EXTENSIONS = QStringList() << QStringLiteral("keepassxc-browser@keepassxc.org");
+ const QStringList ALLOWED_ORIGINS = QStringList()
+ << QStringLiteral("chrome-extension://pdffhmdngciaglkoonimfcmckehcpafo/")
+ << QStringLiteral("chrome-extension://oboonakemofpalcgghocfoadofidjkkk/");
+#if defined(Q_OS_MACOS)
+ const QString TARGET_DIR_CHROME = QStringLiteral("/Library/Application Support/Google/Chrome/NativeMessagingHosts");
+ const QString TARGET_DIR_CHROMIUM = QStringLiteral("/Library/Application Support/Chromium/NativeMessagingHosts");
+ const QString TARGET_DIR_FIREFOX = QStringLiteral("/Library/Application Support/Mozilla/NativeMessagingHosts");
+ const QString TARGET_DIR_VIVALDI = QStringLiteral("/Library/Application Support/Vivaldi/NativeMessagingHosts");
+ const QString TARGET_DIR_TOR_BROWSER =
+ QStringLiteral("/Library/Application Support/TorBrowser-Data/Browser/Mozilla/NativeMessagingHosts");
+ const QString TARGET_DIR_BRAVE =
+ QStringLiteral("/Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts");
+ const QString TARGET_DIR_EDGE = QStringLiteral("/Library/Application Support/Microsoft Edge/NativeMessagingHosts");
+#elif defined(Q_OS_WIN)
+ const QString TARGET_DIR_CHROME = QStringLiteral(
+ "HKEY_CURRENT_USER\\Software\\Google\\Chrome\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser");
+ const QString TARGET_DIR_CHROMIUM =
+ QStringLiteral("HKEY_CURRENT_USER\\Software\\Chromium\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser");
+ const QString TARGET_DIR_FIREFOX =
+ QStringLiteral("HKEY_CURRENT_USER\\Software\\Mozilla\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser");
+ const QString TARGET_DIR_VIVALDI = TARGET_DIR_CHROME;
+ const QString TARGET_DIR_TOR_BROWSER = TARGET_DIR_FIREFOX;
+ const QString TARGET_DIR_BRAVE = TARGET_DIR_CHROME;
+ const QString TARGET_DIR_EDGE = QStringLiteral(
+ "HKEY_CURRENT_USER\\Software\\Microsoft\\Edge\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser");
+#else
+ const QString TARGET_DIR_CHROME = QStringLiteral("/google-chrome/NativeMessagingHosts");
+ const QString TARGET_DIR_CHROMIUM = QStringLiteral("/chromium/NativeMessagingHosts");
+ const QString TARGET_DIR_FIREFOX = QStringLiteral("/.mozilla/native-messaging-hosts");
+ const QString TARGET_DIR_VIVALDI = QStringLiteral("/vivaldi/NativeMessagingHosts");
+ const QString TARGET_DIR_TOR_BROWSER = QStringLiteral(
+ "/torbrowser/tbb/x86_64/tor-browser_en-US/Browser/TorBrowser/Data/Browser/.mozilla/native-messaging-hosts");
+ const QString TARGET_DIR_BRAVE = QStringLiteral("/BraveSoftware/Brave-Browser/NativeMessagingHosts");
+ const QString TARGET_DIR_EDGE = QStringLiteral("/microsoftedge/NativeMessagingHosts");
+#endif
+} // namespace
+
+/**
+ * Checks if the selected browser has native messaging host properly installed
+ *
+ * @param browser Selected browser
+ * @return bool Script is installed correctly
+ */
+bool NativeMessageInstaller::isBrowserEnabled(SupportedBrowsers browser)
+{
+#ifdef Q_OS_WIN
+ QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
+ return !settings.value("Default").isNull();
+#else
+ return QFile::exists(getNativeMessagePath(browser));
+#endif
+}
+
+/**
+ * Installs native messaging JSON script for the selected browser
+ *
+ * @param browser Selected browser
+ * @param enabled Is browser integration enabled
+ */
+void NativeMessageInstaller::setBrowserEnabled(SupportedBrowsers browser, bool enabled)
+{
+ if (enabled) {
+#ifdef Q_OS_WIN
+ // Create a registry key
+ QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
+ settings.setValue("Default", getNativeMessagePath(browser));
+#endif
+ // Always create the script file
+ if (!createNativeMessageFile(browser)) {
+ QMessageBox::critical(
+ nullptr,
+ QObject::tr("Browser Plugin Failure"),
+ QObject::tr("Could not save the native messaging script file for %1.").arg(getBrowserName(browser)),
+ QMessageBox::Ok);
+ }
+ } else {
+ // Remove the script file
+ QString fileName = getNativeMessagePath(browser);
+ QFile::remove(fileName);
+#ifdef Q_OS_WIN
+ // Remove the registry entry
+ QSettings settings(getTargetPath(browser), QSettings::NativeFormat);
+ settings.remove("Default");
+#endif
+ }
+}
+
+/**
+ * Updates the paths to native messaging host for each browser that has been enabled
+ */
+void NativeMessageInstaller::updateBinaryPaths()
+{
+ for (int i = 0; i < SupportedBrowsers::MAX_SUPPORTED; ++i) {
+ if (isBrowserEnabled(static_cast<SupportedBrowsers>(i))) {
+ setBrowserEnabled(static_cast<SupportedBrowsers>(i), true);
+ }
+ }
+}
+
+/**
+ * Returns the target path for each browser. Windows uses a registry path instead of a file path
+ *
+ * @param browser Selected browser
+ * @return QString Current target path for the selected browser
+ */
+QString NativeMessageInstaller::getTargetPath(SupportedBrowsers browser) const
+{
+ switch (browser) {
+ case SupportedBrowsers::CHROME:
+ return TARGET_DIR_CHROME;
+ case SupportedBrowsers::CHROMIUM:
+ return TARGET_DIR_CHROMIUM;
+ case SupportedBrowsers::FIREFOX:
+ return TARGET_DIR_FIREFOX;
+ case SupportedBrowsers::VIVALDI:
+ return TARGET_DIR_VIVALDI;
+ case SupportedBrowsers::TOR_BROWSER:
+ return TARGET_DIR_TOR_BROWSER;
+ case SupportedBrowsers::BRAVE:
+ return TARGET_DIR_BRAVE;
+ case SupportedBrowsers::EDGE:
+ return TARGET_DIR_EDGE;
+ default:
+ return {};
+ }
+}
+
+/**
+ * Returns the browser name
+ * Needed for Windows to separate Chromium- or Firefox-based scripts
+ *
+ * @param browser Selected browser
+ * @return QString Name of the selected browser
+ */
+QString NativeMessageInstaller::getBrowserName(SupportedBrowsers browser) const
+{
+ switch (browser) {
+ case SupportedBrowsers::CHROME:
+ return QStringLiteral("chrome");
+ case SupportedBrowsers::CHROMIUM:
+ return QStringLiteral("chromium");
+ case SupportedBrowsers::FIREFOX:
+ return QStringLiteral("firefox");
+ case SupportedBrowsers::VIVALDI:
+ return QStringLiteral("vivaldi");
+ case SupportedBrowsers::TOR_BROWSER:
+ return QStringLiteral("tor-browser");
+ case SupportedBrowsers::BRAVE:
+ return QStringLiteral("brave");
+ case SupportedBrowsers::EDGE:
+ return QStringLiteral("edge");
+ default:
+ return {};
+ }
+}
+
+/**
+ * Returns the path of native messaging JSON script for the selected browser
+ *
+ * @param browser Selected browser
+ * @return QString JSON script path for the selected browser
+ */
+QString NativeMessageInstaller::getNativeMessagePath(SupportedBrowsers browser) const
+{
+ QString basePath;
+#if defined(Q_OS_WIN)
+ // If portable settings file exists save the JSON scripts to the application folder
+ if (QFile::exists(QCoreApplication::applicationDirPath() + QStringLiteral("/keepassxc.ini"))) {
+ basePath = QCoreApplication::applicationDirPath();
+ } else {
+ basePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
+ }
+ return QStringLiteral("%1/%2_%3.json").arg(basePath, HOST_NAME, getBrowserName(browser));
+#elif defined(Q_OS_LINUX)
+ if (browser == SupportedBrowsers::TOR_BROWSER) {
+ // Tor Browser launcher stores its config in ~/.local/share/...
+ basePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
+ } else if (browser == SupportedBrowsers::FIREFOX) {
+ // Firefox stores its config in ~/
+ basePath = QDir::homePath();
+ } else {
+ basePath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
+ }
+#else
+ basePath = QDir::homePath();
+#endif
+ return QStringLiteral("%1%2/%3.json").arg(basePath, getTargetPath(browser), HOST_NAME);
+}
+
+/**
+ * Gets the path to keepassxc-proxy binary
+ *
+ * @param location Custom proxy path
+ * @return path Path to keepassxc-proxy
+ */
+QString NativeMessageInstaller::getProxyPath() const
+{
+ if (browserSettings()->useCustomProxy()) {
+ return browserSettings()->customProxyLocation();
+ }
+
+ QString path;
+#ifdef KEEPASSXC_DIST_APPIMAGE
+ path = QProcessEnvironment::systemEnvironment().value("APPIMAGE");
+#else
+ path = QCoreApplication::applicationDirPath() + QStringLiteral("/keepassxc-proxy");
+#ifdef Q_OS_WIN
+ path.append(QStringLiteral(".exe"));
+#endif // #ifdef Q_OS_WIN
+
+#endif // #ifdef KEEPASSXC_DIST_APPIMAGE
+ return QDir::toNativeSeparators(path);
+}
+
+/**
+ * Constructs the JSON script file used with native messaging
+ *
+ * @param browser Browser (Chromium- and Firefox-based browsers need a different parameters for the script)
+ * @param location Custom proxy location
+ * @return script The JSON script file
+ */
+QJsonObject NativeMessageInstaller::constructFile(SupportedBrowsers browser)
+{
+ QJsonObject script;
+ script["name"] = HOST_NAME;
+ script["description"] = QStringLiteral("KeePassXC integration with native messaging support");
+ script["path"] = getProxyPath();
+ script["type"] = QStringLiteral("stdio");
+
+ QJsonArray arr;
+ if (browser == SupportedBrowsers::FIREFOX || browser == SupportedBrowsers::TOR_BROWSER) {
+ for (const QString& extension : ALLOWED_EXTENSIONS) {
+ arr.append(extension);
+ }
+ script["allowed_extensions"] = arr;
+ } else {
+ for (const QString& origin : ALLOWED_ORIGINS) {
+ arr.append(origin);
+ }
+ script["allowed_origins"] = arr;
+ }
+
+ return script;
+}
+
+/**
+ * Saves a JSON script file
+ *
+ * @param browser Selected browser
+ * @param script JSON native messaging script object
+ * @return bool Write succeeds
+ */
+bool NativeMessageInstaller::createNativeMessageFile(SupportedBrowsers browser)
+{
+ auto path = getNativeMessagePath(browser);
+
+ // Make the parent directory path if necessary
+ QDir().mkpath(QFileInfo(path).absolutePath());
+
+ QFile scriptFile(path);
+ if (!scriptFile.open(QIODevice::WriteOnly)) {
+ qWarning() << "Browser Plugin: Failed to open native message file for writing at " << scriptFile.fileName();
+ qWarning() << scriptFile.errorString();
+ return false;
+ }
+
+ QJsonDocument doc(constructFile(browser));
+ if (scriptFile.write(doc.toJson()) < 0) {
+ qWarning() << "Browser Plugin: Failed to write native message file at " << scriptFile.fileName();
+ qWarning() << scriptFile.errorString();
+ return false;
+ }
+ return true;
+}
diff --git a/src/browser/NativeMessageInstaller.h b/src/browser/NativeMessageInstaller.h
new file mode 100644
index 000000000..4c0e339ee
--- /dev/null
+++ b/src/browser/NativeMessageInstaller.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
+ * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NATIVEMESSAGEINSTALLER_H
+#define NATIVEMESSAGEINSTALLER_H
+
+#include "BrowserShared.h"
+#include <QJsonObject>
+
+class NativeMessageInstaller
+{
+public:
+ NativeMessageInstaller() = default;
+
+ void setBrowserEnabled(BrowserShared::SupportedBrowsers browser, bool enabled);
+ bool isBrowserEnabled(BrowserShared::SupportedBrowsers browser);
+
+ QString getProxyPath() const;
+ void updateBinaryPaths();
+
+private:
+ QString getTargetPath(BrowserShared::SupportedBrowsers browser) const;
+ QString getBrowserName(BrowserShared::SupportedBrowsers browser) const;
+ QString getNativeMessagePath(BrowserShared::SupportedBrowsers browser) const;
+ QJsonObject constructFile(BrowserShared::SupportedBrowsers browser);
+ bool createNativeMessageFile(BrowserShared::SupportedBrowsers browser);
+
+ Q_DISABLE_COPY(NativeMessageInstaller);
+};
+
+#endif // NATIVEMESSAGEINSTALLER_H
diff --git a/src/browser/NativeMessagingBase.cpp b/src/browser/NativeMessagingBase.cpp
deleted file mode 100644
index 208d28a1e..000000000
--- a/src/browser/NativeMessagingBase.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "NativeMessagingBase.h"
-#include <QStandardPaths>
-
-#include "config-keepassx.h"
-
-#if defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
-#include <sys/event.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-#endif
-
-#ifdef Q_OS_LINUX
-#include <sys/epoll.h>
-#include <unistd.h>
-#endif
-
-#ifdef Q_OS_WIN
-#include <fcntl.h>
-#include <io.h>
-#endif
-
-NativeMessagingBase::NativeMessagingBase(const bool enabled)
-{
-#ifdef Q_OS_WIN
- Q_UNUSED(enabled);
- _setmode(_fileno(stdin), _O_BINARY);
- _setmode(_fileno(stdout), _O_BINARY);
-#else
- if (enabled) {
- m_notifier.reset(new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this));
- connect(m_notifier.data(), SIGNAL(activated(int)), this, SLOT(newNativeMessage()));
- }
-#endif
-}
-
-void NativeMessagingBase::newNativeMessage()
-{
-#if defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
- struct kevent ev[1];
- struct timespec ts = {5, 0};
-
- int fd = kqueue();
- if (fd == -1) {
- m_notifier->setEnabled(false);
- return;
- }
-
- EV_SET(ev, fileno(stdin), EVFILT_READ, EV_ADD, 0, 0, nullptr);
- if (kevent(fd, ev, 1, nullptr, 0, &ts) == -1) {
- m_notifier->setEnabled(false);
- ::close(fd);
- return;
- }
-
- int ret = kevent(fd, NULL, 0, ev, 1, &ts);
- if (ret < 1) {
- m_notifier->setEnabled(false);
- ::close(fd);
- return;
- }
-#elif defined(Q_OS_LINUX)
- int fd = epoll_create(5);
- struct epoll_event event;
- event.events = EPOLLIN;
- event.data.fd = 0;
- if (epoll_ctl(fd, EPOLL_CTL_ADD, 0, &event) != 0) {
- m_notifier->setEnabled(false);
- ::close(fd);
- return;
- }
-
- if (epoll_wait(fd, &event, 1, 5000) < 1) {
- m_notifier->setEnabled(false);
- ::close(fd);
- return;
- }
-#endif
- readLength();
-#ifndef Q_OS_WIN
- ::close(fd);
-#endif
-}
-
-void NativeMessagingBase::readNativeMessages()
-{
-#ifdef Q_OS_WIN
- quint32 length = 0;
- while (m_running.load() != 0 && !std::cin.eof()) {
- length = 0;
- std::cin.readsome(reinterpret_cast<char*>(&length), 4);
- readStdIn(length);
- QThread::msleep(100);
- }
-#endif
-}
-
-QString NativeMessagingBase::jsonToString(const QJsonObject& json) const
-{
- return QString(QJsonDocument(json).toJson(QJsonDocument::Compact));
-}
-
-void NativeMessagingBase::sendReply(const QJsonObject& json)
-{
- if (!json.isEmpty()) {
- sendReply(jsonToString(json));
- }
-}
-
-void NativeMessagingBase::sendReply(const QString& reply)
-{
- if (!reply.isEmpty()) {
- QByteArray bytes = reply.toUtf8();
- uint len = bytes.size();
- std::cout << char(((len >> 0) & 0xFF)) << char(((len >> 8) & 0xFF)) << char(((len >> 16) & 0xFF))
- << char(((len >> 24) & 0xFF));
- std::cout << reply.toStdString() << std::flush;
- }
-}
-
-QString NativeMessagingBase::getLocalServerPath() const
-{
- const QString serverPath = "/kpxc_server";
-#if defined(KEEPASSXC_DIST_SNAP)
- return QProcessEnvironment::systemEnvironment().value("SNAP_USER_COMMON") + serverPath;
-#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
- // Use XDG_RUNTIME_DIR instead of /tmp if it's available
- QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
- return path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverPath
- : path + serverPath;
-#else // Q_OS_MACOS, Q_OS_WIN and others
- return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + serverPath;
-#endif
-}
diff --git a/src/browser/NativeMessagingBase.h b/src/browser/NativeMessagingBase.h
deleted file mode 100644
index b68208c68..000000000
--- a/src/browser/NativeMessagingBase.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef NATIVEMESSAGINGBASE_H
-#define NATIVEMESSAGINGBASE_H
-
-#include <QAtomicInt>
-#include <QFuture>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QLocalServer>
-#include <QLocalSocket>
-#include <QMutex>
-#include <QObject>
-#include <QSocketNotifier>
-#include <QtConcurrent/QtConcurrent>
-#include <iostream>
-#include <unistd.h>
-
-#ifndef Q_OS_WIN
-#include <sys/socket.h>
-#include <sys/types.h>
-#endif
-
-static const int NATIVE_MSG_MAX_LENGTH = 1024 * 1024;
-
-class NativeMessagingBase : public QObject
-{
- Q_OBJECT
-
-public:
- explicit NativeMessagingBase(const bool enabled);
- ~NativeMessagingBase() = default;
-
-protected slots:
- void newNativeMessage();
-
-protected:
- virtual void readLength() = 0;
- virtual bool readStdIn(const quint32 length) = 0;
- virtual void readNativeMessages();
- QString jsonToString(const QJsonObject& json) const;
- void sendReply(const QJsonObject& json);
- void sendReply(const QString& reply);
- QString getLocalServerPath() const;
-
-protected:
- QAtomicInt m_running;
- QSharedPointer<QSocketNotifier> m_notifier;
- QFuture<void> m_future;
-};
-
-#endif // NATIVEMESSAGINGBASE_H
diff --git a/src/browser/NativeMessagingHost.cpp b/src/browser/NativeMessagingHost.cpp
deleted file mode 100644
index a6c321215..000000000
--- a/src/browser/NativeMessagingHost.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "NativeMessagingHost.h"
-#include "BrowserSettings.h"
-#include "sodium.h"
-#include <QMutexLocker>
-#include <QtNetwork>
-#include <iostream>
-
-#ifdef Q_OS_WIN
-#include <Winsock2.h>
-#endif
-
-NativeMessagingHost::NativeMessagingHost(DatabaseTabWidget* parent, const bool enabled)
- : NativeMessagingBase(enabled)
- , m_mutex(QMutex::Recursive)
- , m_browserService(parent)
- , m_browserClients(m_browserService)
-{
- m_localServer.reset(new QLocalServer(this));
- m_localServer->setSocketOptions(QLocalServer::UserAccessOption);
- m_running.store(0);
-
- if (browserSettings()->isEnabled() && m_running.load() == 0) {
- run();
- }
-
- connect(&m_browserService, SIGNAL(databaseLocked()), this, SLOT(databaseLocked()));
- connect(&m_browserService, SIGNAL(databaseUnlocked()), this, SLOT(databaseUnlocked()));
-}
-
-NativeMessagingHost::~NativeMessagingHost()
-{
- stop();
-}
-
-int NativeMessagingHost::init()
-{
- QMutexLocker locker(&m_mutex);
- return sodium_init();
-}
-
-void NativeMessagingHost::run()
-{
- QMutexLocker locker(&m_mutex);
- if (m_running.load() == 0 && init() == -1) {
- return;
- }
-
- // Update KeePassXC/keepassxc-proxy binary paths to Native Messaging scripts
- if (browserSettings()->updateBinaryPath()) {
- browserSettings()->updateBinaryPaths(
- browserSettings()->useCustomProxy() ? browserSettings()->customProxyLocation() : "");
- }
-
- m_running.store(1);
-#ifdef Q_OS_WIN
- m_future =
- QtConcurrent::run(this, static_cast<void (NativeMessagingHost::*)()>(&NativeMessagingHost::readNativeMessages));
-#endif
-
- if (browserSettings()->supportBrowserProxy()) {
- QString serverPath = getLocalServerPath();
- QFile::remove(serverPath);
-
- // Ensure that STDIN is not being listened when proxy is used
- if (m_notifier && m_notifier->isEnabled()) {
- m_notifier->setEnabled(false);
- }
-
- if (m_localServer->isListening()) {
- m_localServer->close();
- }
-
- m_localServer->listen(serverPath);
- connect(m_localServer.data(), SIGNAL(newConnection()), this, SLOT(newLocalConnection()));
- } else {
- m_localServer->close();
- }
-}
-
-void NativeMessagingHost::stop()
-{
- databaseLocked();
- QMutexLocker locker(&m_mutex);
- m_socketList.clear();
- m_running.testAndSetOrdered(1, 0);
- m_future.waitForFinished();
- m_localServer->close();
-}
-
-void NativeMessagingHost::readLength()
-{
- quint32 length = 0;
- std::cin.read(reinterpret_cast<char*>(&length), 4);
- if (!std::cin.eof() && length > 0) {
- readStdIn(length);
- } else {
- m_notifier->setEnabled(false);
- }
-}
-
-bool NativeMessagingHost::readStdIn(const quint32 length)
-{
- if (length <= 0) {
- return false;
- }
-
- QByteArray arr;
- arr.reserve(length);
-
- QMutexLocker locker(&m_mutex);
-
- for (quint32 i = 0; i < length; ++i) {
- int c = std::getchar();
- if (c == EOF) {
- // message ended prematurely, ignore it and return
- return false;
- }
- arr.append(static_cast<char>(c));
- }
-
- if (arr.length() > 0) {
- sendReply(m_browserClients.readResponse(arr));
- }
- return true;
-}
-
-void NativeMessagingHost::newLocalConnection()
-{
- QLocalSocket* socket = m_localServer->nextPendingConnection();
- if (socket) {
- connect(socket, SIGNAL(readyRead()), this, SLOT(newLocalMessage()));
- connect(socket, SIGNAL(disconnected()), this, SLOT(disconnectSocket()));
- }
-}
-
-void NativeMessagingHost::newLocalMessage()
-{
- QLocalSocket* socket = qobject_cast<QLocalSocket*>(QObject::sender());
- if (!socket || socket->bytesAvailable() <= 0) {
- return;
- }
-
- socket->setReadBufferSize(NATIVE_MSG_MAX_LENGTH);
- int socketDesc = socket->socketDescriptor();
- if (socketDesc) {
- int max = NATIVE_MSG_MAX_LENGTH;
- setsockopt(socketDesc, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&max), sizeof(max));
- }
-
- QByteArray arr = socket->readAll();
- if (arr.isEmpty()) {
- return;
- }
-
- QMutexLocker locker(&m_mutex);
- if (!m_socketList.contains(socket)) {
- m_socketList.push_back(socket);
- }
-
- QString reply = jsonToString(m_browserClients.readResponse(arr));
- if (socket && socket->isValid() && socket->state() == QLocalSocket::ConnectedState) {
- QByteArray arr = reply.toUtf8();
- socket->write(arr.constData(), arr.length());
- socket->flush();
- }
-}
-
-void NativeMessagingHost::sendReplyToAllClients(const QJsonObject& json)
-{
- QString reply = jsonToString(json);
- QMutexLocker locker(&m_mutex);
- for (const auto socket : m_socketList) {
- if (socket && socket->isValid() && socket->state() == QLocalSocket::ConnectedState) {
- QByteArray arr = reply.toUtf8();
- socket->write(arr.constData(), arr.length());
- socket->flush();
- }
- }
-}
-
-void NativeMessagingHost::disconnectSocket()
-{
- QLocalSocket* socket(qobject_cast<QLocalSocket*>(QObject::sender()));
- QMutexLocker locker(&m_mutex);
- for (auto s : m_socketList) {
- if (s == socket) {
- m_socketList.removeOne(s);
- }
- }
-}
-
-void NativeMessagingHost::databaseLocked()
-{
- QJsonObject response;
- response["action"] = QString("database-locked");
- sendReplyToAllClients(response);
-}
-
-void NativeMessagingHost::databaseUnlocked()
-{
- QJsonObject response;
- response["action"] = QString("database-unlocked");
- sendReplyToAllClients(response);
-}
diff --git a/src/browser/NativeMessagingHost.h b/src/browser/NativeMessagingHost.h
deleted file mode 100644
index 9ce1dab60..000000000
--- a/src/browser/NativeMessagingHost.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef NATIVEMESSAGINGHOST_H
-#define NATIVEMESSAGINGHOST_H
-
-#include "BrowserClients.h"
-#include "BrowserService.h"
-#include "NativeMessagingBase.h"
-#include "gui/DatabaseTabWidget.h"
-
-class NativeMessagingHost : public NativeMessagingBase
-{
- Q_OBJECT
-
- typedef QList<QLocalSocket*> SocketList;
-
-public:
- explicit NativeMessagingHost(DatabaseTabWidget* parent = nullptr, const bool enabled = false);
- ~NativeMessagingHost() override;
- int init();
- void run();
- void stop();
-
-signals:
- void quit();
-
-private:
- void readLength() override;
- bool readStdIn(const quint32 length) override;
- void sendReplyToAllClients(const QJsonObject& json);
-
-private slots:
- void databaseLocked();
- void databaseUnlocked();
- void newLocalConnection();
- void newLocalMessage();
- void disconnectSocket();
-
-private:
- QMutex m_mutex;
- BrowserService m_browserService;
- BrowserClients m_browserClients;
- QSharedPointer<QLocalServer> m_localServer;
- SocketList m_socketList;
-};
-
-#endif // NATIVEMESSAGINGHOST_H
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index f20f3b9d1..a94229376 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -44,6 +44,9 @@
#ifdef Q_OS_MACOS
#include "gui/osutils/macutils/MacUtils.h"
+#ifdef WITH_XC_TOUCHID
+#include "touchid/TouchID.h"
+#endif
#endif
#ifdef WITH_XC_UPDATECHECK
@@ -56,7 +59,7 @@
#include "sshagent/AgentSettingsPage.h"
#include "sshagent/SSHAgent.h"
#endif
-#if defined(WITH_XC_KEESHARE)
+#ifdef WITH_XC_KEESHARE
#include "keeshare/KeeShare.h"
#include "keeshare/SettingsPageKeeShare.h"
#endif
@@ -66,9 +69,8 @@
#endif
#ifdef WITH_XC_BROWSER
-#include "browser/BrowserOptionDialog.h"
-#include "browser/BrowserSettings.h"
-#include "browser/NativeMessagingHost.h"
+#include "browser/BrowserService.h"
+#include "browser/BrowserSettingsPage.h"
#endif
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
@@ -77,61 +79,6 @@
#include <QtDBus/QtDBus>
#endif
-#include "gui/ApplicationSettingsWidget.h"
-#include "gui/PasswordGeneratorWidget.h"
-
-#include "touchid/TouchID.h"
-
-#ifdef WITH_XC_BROWSER
-class BrowserPlugin : public ISettingsPage
-{
-public:
- explicit BrowserPlugin(DatabaseTabWidget* tabWidget)
- {
- m_nativeMessagingHost =
- QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget, browserSettings()->isEnabled()));
- }
-
- ~BrowserPlugin()
- {
- }
-
- QString name() override
- {
- return QObject::tr("Browser Integration");
- }
-
- QIcon icon() override
- {
- return Resources::instance()->icon("internet-web-browser");
- }
-
- QWidget* createWidget() override
- {
- BrowserOptionDialog* dlg = new BrowserOptionDialog();
- return dlg;
- }
-
- void loadSettings(QWidget* widget) override
- {
- qobject_cast<BrowserOptionDialog*>(widget)->loadSettings();
- }
-
- void saveSettings(QWidget* widget) override
- {
- qobject_cast<BrowserOptionDialog*>(widget)->saveSettings();
- if (browserSettings()->isEnabled()) {
- m_nativeMessagingHost->run();
- } else {
- m_nativeMessagingHost->stop();
- }
- }
-
-private:
- QSharedPointer<NativeMessagingHost> m_nativeMessagingHost;
-};
-#endif
-
const QString MainWindow::BaseWindowTitle = "KeePassXC";
MainWindow* g_MainWindow = nullptr;
@@ -186,13 +133,19 @@ MainWindow::MainWindow()
restoreGeometry(config()->get(Config::GUI_MainWindowGeometry).toByteArray());
restoreState(config()->get(Config::GUI_MainWindowState).toByteArray());
#ifdef WITH_XC_BROWSER
- m_ui->settingsWidget->addSettingsPage(new BrowserPlugin(m_ui->tabWidget));
+ m_ui->settingsWidget->addSettingsPage(new BrowserSettingsPage());
+ connect(m_ui->tabWidget, &DatabaseTabWidget::databaseLocked, browserService(), &BrowserService::databaseLocked);
+ connect(m_ui->tabWidget, &DatabaseTabWidget::databaseUnlocked, browserService(), &BrowserService::databaseUnlocked);
+ connect(m_ui->tabWidget,
+ &DatabaseTabWidget::activateDatabaseChanged,
+ browserService(),
+ &BrowserService::activeDatabaseChanged);
#endif
#ifdef WITH_XC_SSHAGENT
connect(sshAgent(), SIGNAL(error(QString)), this, SLOT(showErrorMessage(QString)));
connect(sshAgent(), SIGNAL(enabledChanged(bool)), this, SLOT(agentEnabled(bool)));
- m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage(m_ui->tabWidget));
+ m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage());
m_entryContextMenu->addSeparator();
m_entryContextMenu->addAction(m_ui->actionEntryAddToAgent);
@@ -565,6 +518,15 @@ MainWindow::~MainWindow()
{
}
+QList<DatabaseWidget*> MainWindow::getOpenDatabases()
+{
+ QList<DatabaseWidget*> dbWidgets;
+ for (int i = 0; i < m_ui->tabWidget->count(); ++i) {
+ dbWidgets << m_ui->tabWidget->databaseWidgetFromIndex(i);
+ }
+ return dbWidgets;
+}
+
void MainWindow::showErrorMessage(const QString& message)
{
m_ui->globalMessageWidget->showMessage(message, MessageWidget::Error);
diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h
index a038b7c29..81d8212af 100644
--- a/src/gui/MainWindow.h
+++ b/src/gui/MainWindow.h
@@ -48,6 +48,8 @@ public:
MainWindow();
~MainWindow();
+ QList<DatabaseWidget*> getOpenDatabases();
+
enum StackedWidgetIndex
{
DatabaseTabScreen = 0,
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp
index 31fe34eef..a37f1f742 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp
@@ -34,7 +34,6 @@ DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent)
, m_ui(new Ui::DatabaseSettingsWidgetBrowser())
, m_customData(new CustomData(this))
, m_customDataModel(new QStandardItemModel(this))
- , m_browserService(nullptr)
{
m_ui->setupUi(this);
m_ui->removeCustomDataButton->setEnabled(false);
@@ -254,7 +253,7 @@ void DatabaseSettingsWidgetBrowser::convertAttributesToCustomData()
return;
}
- m_browserService.convertAttributesToCustomData(m_db);
+ BrowserService::convertAttributesToCustomData(m_db);
}
void DatabaseSettingsWidgetBrowser::refreshDatabaseID()
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h
index c3cc0b122..51abf7f39 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.h
@@ -77,7 +77,6 @@ protected:
private:
QPointer<CustomData> m_customData;
QPointer<QStandardItemModel> m_customDataModel;
- BrowserService m_browserService;
};
#endif // KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H
diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt
index 61dfd1b25..b7bec6deb 100755
--- a/src/proxy/CMakeLists.txt
+++ b/src/proxy/CMakeLists.txt
@@ -1,5 +1,4 @@
-# Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
-# Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
+# Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,19 +14,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if(WITH_XC_BROWSER)
- include_directories(${BROWSER_SOURCE_DIR})
-
set(proxy_SOURCES
- ../core/Alloc.cpp
+ ../browser/BrowserShared.cpp
keepassxc-proxy.cpp
- ${BROWSER_SOURCE_DIR}/NativeMessagingBase.cpp
- NativeMessagingHost.cpp)
+ NativeMessagingProxy.cpp)
- add_library(proxy STATIC ${proxy_SOURCES})
- target_link_libraries(proxy Qt5::Core Qt5::Network ${sodium_LIBRARY_RELEASE})
- add_executable(keepassxc-proxy keepassxc-proxy.cpp)
- target_link_libraries(keepassxc-proxy proxy)
+ # Alloc must be defined in a static library to prevent clashing with clang ASAN definitions
+ add_library(proxy_alloc STATIC ../core/Alloc.cpp)
+ target_link_libraries(proxy_alloc PRIVATE Qt5::Core ${sodium_LIBRARY_RELEASE})
+ add_executable(keepassxc-proxy ${proxy_SOURCES})
+ target_link_libraries(keepassxc-proxy proxy_alloc Qt5::Core Qt5::Network)
install(TARGETS keepassxc-proxy
BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION ${PROXY_INSTALL_DIR} COMPONENT Runtime)
@@ -56,7 +53,4 @@ if(WITH_XC_BROWSER)
COMMAND ${CMAKE_COMMAND} -E copy keepassxc-proxy ${PROXY_APP_DIR}/keepassxc-proxy
COMMENT "Copying keepassxc-proxy inside the application")
endif()
- if(MINGW)
- target_link_libraries(keepassxc-proxy Wtsapi32.lib Ws2_32.lib)
- endif()
endif()
diff --git a/src/proxy/NativeMessagingHost.cpp b/src/proxy/NativeMessagingHost.cpp
deleted file mode 100644
index 44b3ab7ef..000000000
--- a/src/proxy/NativeMessagingHost.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "NativeMessagingHost.h"
-#include <QCoreApplication>
-
-#ifdef Q_OS_WIN
-#include <winsock2.h>
-#endif
-
-NativeMessagingHost::NativeMessagingHost()
- : NativeMessagingBase(true)
-{
- m_localSocket = new QLocalSocket();
- m_localSocket->connectToServer(getLocalServerPath());
- m_localSocket->setReadBufferSize(NATIVE_MSG_MAX_LENGTH);
-
- int socketDesc = m_localSocket->socketDescriptor();
- if (socketDesc) {
- int max = NATIVE_MSG_MAX_LENGTH;
- setsockopt(socketDesc, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&max), sizeof(max));
- }
-#ifdef Q_OS_WIN
- m_running.store(1);
- m_future = QtConcurrent::run(this, &NativeMessagingHost::readNativeMessages);
-#endif
- connect(m_localSocket, SIGNAL(readyRead()), this, SLOT(newLocalMessage()));
- connect(m_localSocket, SIGNAL(disconnected()), this, SLOT(deleteSocket()));
- connect(m_localSocket,
- SIGNAL(stateChanged(QLocalSocket::LocalSocketState)),
- SLOT(socketStateChanged(QLocalSocket::LocalSocketState)));
-}
-
-NativeMessagingHost::~NativeMessagingHost()
-{
-#ifdef Q_OS_WIN
- m_future.waitForFinished();
-#endif
-}
-
-void NativeMessagingHost::readNativeMessages()
-{
-#ifdef Q_OS_WIN
- quint32 length = 0;
- while (m_running.load() == 1 && !std::cin.eof()) {
- length = 0;
- std::cin.read(reinterpret_cast<char*>(&length), 4);
- if (!readStdIn(length)) {
- QCoreApplication::quit();
- }
- QThread::msleep(1);
- }
-#endif
-}
-
-void NativeMessagingHost::readLength()
-{
- quint32 length = 0;
- std::cin.read(reinterpret_cast<char*>(&length), 4);
- if (!std::cin.eof() && length > 0) {
- readStdIn(length);
- } else {
- QCoreApplication::quit();
- }
-}
-
-bool NativeMessagingHost::readStdIn(const quint32 length)
-{
- if (length <= 0) {
- return false;
- }
-
- QByteArray arr;
- arr.reserve(length);
-
- for (quint32 i = 0; i < length; ++i) {
- int c = std::getchar();
- if (c == EOF) {
- // message ended prematurely, ignore it and return
- return false;
- }
- arr.append(static_cast<char>(c));
- }
-
- if (arr.length() > 0 && m_localSocket && m_localSocket->state() == QLocalSocket::ConnectedState) {
- m_localSocket->write(arr.constData(), arr.length());
- m_localSocket->flush();
- }
-
- return true;
-}
-
-void NativeMessagingHost::newLocalMessage()
-{
- if (!m_localSocket || m_localSocket->bytesAvailable() <= 0) {
- return;
- }
-
- QByteArray arr = m_localSocket->readAll();
- if (!arr.isEmpty()) {
- sendReply(arr);
- }
-}
-
-void NativeMessagingHost::deleteSocket()
-{
- if (m_notifier) {
- m_notifier->setEnabled(false);
- }
- m_localSocket->deleteLater();
- QCoreApplication::quit();
-}
-
-void NativeMessagingHost::socketStateChanged(QLocalSocket::LocalSocketState socketState)
-{
- if (socketState == QLocalSocket::UnconnectedState || socketState == QLocalSocket::ClosingState) {
- m_running.testAndSetOrdered(1, 0);
- }
-}
diff --git a/src/proxy/NativeMessagingProxy.cpp b/src/proxy/NativeMessagingProxy.cpp
new file mode 100644
index 000000000..a7bd1c0f3
--- /dev/null
+++ b/src/proxy/NativeMessagingProxy.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "NativeMessagingProxy.h"
+#include "browser/BrowserShared.h"
+
+#include <QCoreApplication>
+#include <QtConcurrent/QtConcurrent>
+
+#include <iostream>
+
+#ifdef Q_OS_WIN
+#include <fcntl.h>
+#include <windows.h>
+#endif
+
+NativeMessagingProxy::NativeMessagingProxy()
+ : QObject()
+{
+ connect(this,
+ &NativeMessagingProxy::stdinMessage,
+ this,
+ &NativeMessagingProxy::transferStdinMessage,
+ Qt::QueuedConnection);
+
+ setupStandardInput();
+ setupLocalSocket();
+}
+
+void NativeMessagingProxy::setupStandardInput()
+{
+#ifdef Q_OS_WIN
+ setmode(fileno(stdin), _O_BINARY);
+ setmode(fileno(stdout), _O_BINARY);
+#endif
+
+ QtConcurrent::run([this] {
+ while (std::cin.good()) {
+ if (std::cin.peek() != EOF) {
+ uint length = 0;
+ for (uint i = 0; i < sizeof(uint); ++i) {
+ length |= getchar() << (i * 8);
+ }
+
+ QString msg;
+ msg.reserve(length);
+ for (uint i = 0; i < length; ++i) {
+ msg.append(getchar());
+ }
+
+ if (msg.length() > 0) {
+ emit stdinMessage(msg);
+ }
+ }
+ QThread::msleep(100);
+ }
+ QCoreApplication::quit();
+ });
+}
+
+void NativeMessagingProxy::transferStdinMessage(const QString& msg)
+{
+ if (m_localSocket && m_localSocket->state() == QLocalSocket::ConnectedState) {
+ m_localSocket->write(msg.toUtf8(), msg.length());
+ m_localSocket->flush();
+ }
+}
+
+void NativeMessagingProxy::setupLocalSocket()
+{
+ m_localSocket.reset(new QLocalSocket());
+ m_localSocket->connectToServer(BrowserShared::localServerPath());
+ m_localSocket->setReadBufferSize(BrowserShared::NATIVEMSG_MAX_LENGTH);
+
+ connect(m_localSocket.data(), SIGNAL(readyRead()), this, SLOT(transferSocketMessage()));
+ connect(m_localSocket.data(), SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
+}
+
+void NativeMessagingProxy::transferSocketMessage()
+{
+ auto msg = m_localSocket->readAll();
+ if (!msg.isEmpty()) {
+ // Explicitly write the message length as 1 byte chunks
+ uint len = msg.size();
+ std::cout.write(reinterpret_cast<char*>(&len), sizeof(len));
+
+ // Write the message and flush the stream
+ std::cout << msg.toStdString() << std::flush;
+ }
+}
+
+void NativeMessagingProxy::socketDisconnected()
+{
+ // Shutdown the proxy when disconnected from the application
+ QCoreApplication::quit();
+}
diff --git a/src/proxy/NativeMessagingProxy.h b/src/proxy/NativeMessagingProxy.h
new file mode 100644
index 000000000..75e6f03ac
--- /dev/null
+++ b/src/proxy/NativeMessagingProxy.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NATIVEMESSAGINGPROXY_H
+#define NATIVEMESSAGINGPROXY_H
+
+#include <QLocalSocket>
+#include <QObject>
+#include <QScopedPointer>
+
+class QWinEventNotifier;
+class QSocketNotifier;
+
+class NativeMessagingProxy : public QObject
+{
+ Q_OBJECT
+public:
+ NativeMessagingProxy();
+ ~NativeMessagingProxy() override = default;
+
+signals:
+ void stdinMessage(QString msg);
+
+public slots:
+ void transferSocketMessage();
+ void transferStdinMessage(const QString& msg);
+ void socketDisconnected();
+
+private:
+ void setupStandardInput();
+ void setupLocalSocket();
+
+private:
+ QScopedPointer<QLocalSocket> m_localSocket;
+
+ Q_DISABLE_COPY(NativeMessagingProxy)
+};
+
+#endif // NATIVEMESSAGINGPROXY_H
diff --git a/src/proxy/keepassxc-proxy.cpp b/src/proxy/keepassxc-proxy.cpp
index ea472b2c3..b2a2b1458 100644
--- a/src/proxy/keepassxc-proxy.cpp
+++ b/src/proxy/keepassxc-proxy.cpp
@@ -16,8 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "NativeMessagingHost.h"
+#include "NativeMessagingProxy.h"
#include <QCoreApplication>
+
#include <iostream>
#ifndef Q_OS_WIN
@@ -79,6 +80,6 @@ int main(int argc, char* argv[])
#else
SetConsoleCtrlHandler(static_cast<PHANDLER_ROUTINE>(ConsoleHandler), TRUE);
#endif
- NativeMessagingHost host;
+ NativeMessagingProxy proxy;
return a.exec();
}
diff --git a/src/sshagent/AgentSettingsPage.cpp b/src/sshagent/AgentSettingsPage.cpp
index eb86f3fce..efadfbab8 100644
--- a/src/sshagent/AgentSettingsPage.cpp
+++ b/src/sshagent/AgentSettingsPage.cpp
@@ -20,15 +20,6 @@
#include "AgentSettingsWidget.h"
#include "core/Resources.h"
-AgentSettingsPage::AgentSettingsPage(DatabaseTabWidget* tabWidget)
-{
- Q_UNUSED(tabWidget);
-}
-
-AgentSettingsPage::~AgentSettingsPage()
-{
-}
-
QString AgentSettingsPage::name()
{
return QObject::tr("SSH Agent");
diff --git a/src/sshagent/AgentSettingsPage.h b/src/sshagent/AgentSettingsPage.h
index 015dfb9ac..33f29b055 100644
--- a/src/sshagent/AgentSettingsPage.h
+++ b/src/sshagent/AgentSettingsPage.h
@@ -25,8 +25,8 @@
class AgentSettingsPage : public ISettingsPage
{
public:
- AgentSettingsPage(DatabaseTabWidget* tabWidget);
- ~AgentSettingsPage() override;
+ AgentSettingsPage() = default;
+ ~AgentSettingsPage() override = default;
QString name() override;
QIcon icon() override;
diff --git a/tests/TestBrowser.cpp b/tests/TestBrowser.cpp
index 5ddb5e898..5b2f61178 100644
--- a/tests/TestBrowser.cpp
+++ b/tests/TestBrowser.cpp
@@ -16,11 +16,13 @@
*/
#include "TestBrowser.h"
+
#include "TestGlobal.h"
#include "browser/BrowserSettings.h"
#include "core/Tools.h"
#include "crypto/Crypto.h"
#include "sodium/crypto_box.h"
+
#include <QString>
QTEST_GUILESS_MAIN(TestBrowser)
@@ -35,12 +37,12 @@ const QString CLIENTID = "testClient";
void TestBrowser::initTestCase()
{
QVERIFY(Crypto::init());
- m_browserService.reset(new BrowserService(nullptr));
- m_browserAction.reset(new BrowserAction(*m_browserService.data()));
+ m_browserService = browserService();
}
-void TestBrowser::cleanupTestCase()
+void TestBrowser::init()
{
+ m_browserAction.reset(new BrowserAction());
}
/**
@@ -54,7 +56,7 @@ void TestBrowser::testChangePublicKeys()
json["publicKey"] = PUBLICKEY;
json["nonce"] = NONCE;
- auto response = m_browserAction->handleAction(json);
+ auto response = m_browserAction->processClientMessage(json);
QCOMPARE(response["action"].toString(), QString("change-public-keys"));
QCOMPARE(response["publicKey"].toString() == PUBLICKEY, false);
QCOMPARE(response["success"].toString(), TRUE_STR);
@@ -393,62 +395,6 @@ void TestBrowser::testSortEntries()
QCOMPARE(result[3]->url(), QString("github.com/login"));
}
-void TestBrowser::testGetDatabaseGroups()
-{
- auto db = QSharedPointer<Database>::create();
- auto* root = db->rootGroup();
-
- QScopedPointer<Group> group1(new Group());
- group1->setParent(root);
- group1->setName("group1");
-
- QScopedPointer<Group> group2(new Group());
- group2->setParent(root);
- group2->setName("group2");
-
- QScopedPointer<Group> group3(new Group());
- group3->setParent(root);
- group3->setName("group3");
-
- QScopedPointer<Group> group2_1(new Group());
- group2_1->setParent(group2.data());
- group2_1->setName("group2_1");
-
- QScopedPointer<Group> group2_2(new Group());
- group2_2->setParent(group2.data());
- group2_2->setName("group2_2");
-
- QScopedPointer<Group> group2_1_1(new Group());
- group2_1_1->setParent(group2_1.data());
- group2_1_1->setName("group2_1_1");
-
- auto result = m_browserService->getDatabaseGroups(db);
- QCOMPARE(result.length(), 1);
-
- auto groups = result["groups"].toArray();
- auto first = groups.at(0);
- auto children = first.toObject()["children"].toArray();
- QCOMPARE(first.toObject()["name"].toString(), QString("Root"));
- QCOMPARE(children.size(), 3);
-
- auto firstChild = children.at(0);
- auto secondChild = children.at(1);
- auto thirdChild = children.at(2);
- QCOMPARE(firstChild.toObject()["name"].toString(), QString("group1"));
- QCOMPARE(secondChild.toObject()["name"].toString(), QString("group2"));
- QCOMPARE(thirdChild.toObject()["name"].toString(), QString("group3"));
-
- auto childrenOfSecond = secondChild.toObject()["children"].toArray();
- auto firstOfCOS = childrenOfSecond.at(0);
- auto secondOfCOS = childrenOfSecond.at(1);
- QCOMPARE(firstOfCOS.toObject()["name"].toString(), QString("group2_1"));
- QCOMPARE(secondOfCOS.toObject()["name"].toString(), QString("group2_2"));
-
- auto lastChildren = firstOfCOS.toObject()["children"].toArray();
- auto lastChild = lastChildren.at(0);
- QCOMPARE(lastChild.toObject()["name"].toString(), QString("group2_1_1"));
-}
-
QList<Entry*> TestBrowser::createEntries(QStringList& urls, Group* root) const
{
QList<Entry*> entries;
diff --git a/tests/TestBrowser.h b/tests/TestBrowser.h
index 69ba69309..00f9d7528 100644
--- a/tests/TestBrowser.h
+++ b/tests/TestBrowser.h
@@ -30,7 +30,7 @@ class TestBrowser : public QObject
private slots:
void initTestCase();
- void cleanupTestCase();
+ void init();
void testChangePublicKeys();
void testEncryptMessage();
@@ -46,14 +46,13 @@ private slots:
void testInvalidEntries();
void testSubdomainsAndPaths();
void testSortEntries();
- void testGetDatabaseGroups();
void testValidURLs();
private:
QList<Entry*> createEntries(QStringList& urls, Group* root) const;
QScopedPointer<BrowserAction> m_browserAction;
- QScopedPointer<BrowserService> m_browserService;
+ QPointer<BrowserService> m_browserService;
};
#endif // KEEPASSXC_TESTBROWSER_H
diff --git a/tests/data/NewDatabaseBrowser.kdbx b/tests/data/NewDatabaseBrowser.kdbx
index 97599fccf..fb327943c 100644
--- a/tests/data/NewDatabaseBrowser.kdbx
+++ b/tests/data/NewDatabaseBrowser.kdbx
Binary files differ
diff --git a/tests/gui/TestGuiBrowser.cpp b/tests/gui/TestGuiBrowser.cpp
index 7e5d89df4..6eac62798 100644
--- a/tests/gui/TestGuiBrowser.cpp
+++ b/tests/gui/TestGuiBrowser.cpp
@@ -31,6 +31,7 @@
#include <QTableView>
#include <QToolBar>
+#include "browser/BrowserService.h"
#include "config-keepassx-tests.h"
#include "core/Bootstrap.h"
#include "core/Config.h"
@@ -82,30 +83,19 @@ void TestGuiBrowser::initTestCase()
Bootstrap::restoreMainWindowState(*m_mainWindow);
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
m_mainWindow->show();
-
- // Load the NewDatabase.kdbx file into temporary storage
- QFile sourceDbFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabaseBrowser.kdbx"));
- QVERIFY(sourceDbFile.open(QIODevice::ReadOnly));
- QVERIFY(Tools::readAllFromDevice(&sourceDbFile, m_dbData));
- sourceDbFile.close();
}
// Every test starts with opening the temp database
void TestGuiBrowser::init()
{
m_dbFile.reset(new TemporaryFile());
- // Write the temp storage to a temp database file for use in our tests
- QVERIFY(m_dbFile->open());
- QCOMPARE(m_dbFile->write(m_dbData), static_cast<qint64>((m_dbData.size())));
- m_dbFileName = QFileInfo(m_dbFile->fileName()).fileName();
- m_dbFilePath = m_dbFile->fileName();
- m_dbFile->close();
+ m_dbFile->copyFromFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabaseBrowser.kdbx"));
// make sure window is activated or focus tests may fail
m_mainWindow->activateWindow();
QApplication::processEvents();
- fileDialog()->setNextFileName(m_dbFilePath);
+ fileDialog()->setNextFileName(m_dbFile->fileName());
triggerAction("actionDatabaseOpen");
auto* databaseOpenWidget = m_tabWidget->currentDatabaseWidget()->findChild<QWidget*>("databaseOpenWidget");
@@ -241,6 +231,28 @@ void TestGuiBrowser::testAdditionalURLs()
}
}
+void TestGuiBrowser::testGetDatabaseGroups()
+{
+ auto result = browserService()->getDatabaseGroups();
+ QCOMPARE(result.length(), 1);
+
+ auto groups = result["groups"].toArray();
+ auto first = groups.at(0);
+ auto children = first.toObject()["children"].toArray();
+ QCOMPARE(first.toObject()["name"].toString(), QString("NewDatabase"));
+ QCOMPARE(children.size(), 6);
+
+ auto firstChild = children.at(0).toObject();
+ auto secondChild = children.at(1).toObject();
+ QCOMPARE(firstChild["name"].toString(), QString("General"));
+ QCOMPARE(secondChild["name"].toString(), QString("Windows"));
+
+ auto subGroups = firstChild["children"].toArray();
+ QCOMPARE(subGroups.count(), 1);
+ auto subGroupObj = subGroups.at(0).toObject();
+ QCOMPARE(subGroupObj["name"].toString(), QString("SubGroup"));
+}
+
void TestGuiBrowser::triggerAction(const QString& name)
{
auto* action = m_mainWindow->findChild<QAction*>(name);
diff --git a/tests/gui/TestGuiBrowser.h b/tests/gui/TestGuiBrowser.h
index 53a9c73c4..818a36952 100644
--- a/tests/gui/TestGuiBrowser.h
+++ b/tests/gui/TestGuiBrowser.h
@@ -45,6 +45,7 @@ private slots:
void testEntrySettings();
void testAdditionalURLs();
+ void testGetDatabaseGroups();
private:
void triggerAction(const QString& name);
@@ -57,10 +58,7 @@ private:
QPointer<DatabaseTabWidget> m_tabWidget;
QPointer<DatabaseWidget> m_dbWidget;
QSharedPointer<Database> m_db;
- QByteArray m_dbData;
QScopedPointer<TemporaryFile> m_dbFile;
- QString m_dbFileName;
- QString m_dbFilePath;
};
#endif // KEEPASSXC_TESTGUIBROWSER_H