mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-25 08:51:21 -05:00
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.
This commit is contained in:
parent
3b4057a78c
commit
a145bf9119
@ -233,7 +233,6 @@ if(APPLE)
|
|||||||
add_feature_info(TouchID WITH_XC_TOUCHID "TouchID integration")
|
add_feature_info(TouchID WITH_XC_TOUCHID "TouchID integration")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(BROWSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/browser)
|
|
||||||
add_subdirectory(browser)
|
add_subdirectory(browser)
|
||||||
add_subdirectory(proxy)
|
add_subdirectory(proxy)
|
||||||
if(WITH_XC_BROWSER)
|
if(WITH_XC_BROWSER)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -17,9 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "BrowserAction.h"
|
#include "BrowserAction.h"
|
||||||
|
#include "BrowserService.h"
|
||||||
#include "BrowserSettings.h"
|
#include "BrowserSettings.h"
|
||||||
#include "NativeMessagingBase.h"
|
#include "BrowserShared.h"
|
||||||
#include "config-keepassx.h"
|
#include "config-keepassx.h"
|
||||||
|
#include "core/Global.h"
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonParseError>
|
#include <QJsonParseError>
|
||||||
@ -27,14 +28,31 @@
|
|||||||
#include <sodium/crypto_box.h>
|
#include <sodium/crypto_box.h>
|
||||||
#include <sodium/randombytes.h>
|
#include <sodium/randombytes.h>
|
||||||
|
|
||||||
BrowserAction::BrowserAction(BrowserService& browserService)
|
namespace
|
||||||
: m_mutex(QMutex::Recursive)
|
|
||||||
, m_browserService(browserService)
|
|
||||||
, m_associated(false)
|
|
||||||
{
|
{
|
||||||
|
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()) {
|
if (json.isEmpty()) {
|
||||||
return getErrorReply("", ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED);
|
return getErrorReply("", ERROR_KEEPASS_EMPTY_MESSAGE_RECEIVED);
|
||||||
@ -51,11 +69,10 @@ QJsonObject BrowserAction::readResponse(const QJsonObject& json)
|
|||||||
return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION);
|
return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(&m_mutex);
|
if (action.compare("change-public-keys", Qt::CaseSensitive) != 0 && !browserService()->isDatabaseOpened()) {
|
||||||
if (action.compare("change-public-keys", Qt::CaseSensitive) != 0 && !m_browserService.isDatabaseOpened()) {
|
|
||||||
if (m_clientPublicKey.isEmpty()) {
|
if (m_clientPublicKey.isEmpty()) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_CLIENT_PUBLIC_KEY_NOT_RECEIVED);
|
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);
|
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)
|
QJsonObject BrowserAction::handleChangePublicKeys(const QJsonObject& json, const QString& action)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
const QString nonce = json.value("nonce").toString();
|
const QString nonce = json.value("nonce").toString();
|
||||||
const QString clientPublicKey = json.value("publicKey").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)
|
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 nonce = json.value("nonce").toString();
|
||||||
const QString encrypted = json.value("message").toString();
|
const QString encrypted = json.value("message").toString();
|
||||||
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
|
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
|
||||||
@ -153,7 +169,7 @@ QJsonObject BrowserAction::handleGetDatabaseHash(const QJsonObject& json, const
|
|||||||
// Update a legacy database hash if found
|
// Update a legacy database hash if found
|
||||||
const QJsonArray hashes = decrypted.value("connectedKeys").toArray();
|
const QJsonArray hashes = decrypted.value("connectedKeys").toArray();
|
||||||
if (!hashes.isEmpty()) {
|
if (!hashes.isEmpty()) {
|
||||||
const QString legacyHash = getLegacyDatabaseHash();
|
const QString legacyHash = browserService()->getDatabaseHash(true);
|
||||||
if (hashes.contains(legacyHash)) {
|
if (hashes.contains(legacyHash)) {
|
||||||
message["oldHash"] = legacyHash;
|
message["oldHash"] = legacyHash;
|
||||||
}
|
}
|
||||||
@ -167,7 +183,7 @@ QJsonObject BrowserAction::handleGetDatabaseHash(const QJsonObject& json, const
|
|||||||
|
|
||||||
QJsonObject BrowserAction::handleAssociate(const QJsonObject& json, const QString& action)
|
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 nonce = json.value("nonce").toString();
|
||||||
const QString encrypted = json.value("message").toString();
|
const QString encrypted = json.value("message").toString();
|
||||||
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
|
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);
|
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
if (key.compare(m_clientPublicKey, Qt::CaseSensitive) == 0) {
|
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
|
// Check for identification key. If it's not found, ensure backwards compatibility and use the current public
|
||||||
// key
|
// key
|
||||||
const QString idKey = decrypted.value("idKey").toString();
|
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()) {
|
if (id.isEmpty()) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED);
|
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)
|
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 nonce = json.value("nonce").toString();
|
||||||
const QString encrypted = json.value("message").toString();
|
const QString encrypted = json.value("message").toString();
|
||||||
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
|
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);
|
return getErrorReply(action, ERROR_KEEPASS_DATABASE_NOT_OPENED);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(&m_mutex);
|
const QString key = browserService()->getKey(id);
|
||||||
const QString key = m_browserService.getKey(id);
|
|
||||||
if (key.isEmpty() || key.compare(responseKey, Qt::CaseSensitive) != 0) {
|
if (key.isEmpty() || key.compare(responseKey, Qt::CaseSensitive) != 0) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
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)
|
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 nonce = json.value("nonce").toString();
|
||||||
const QString encrypted = json.value("message").toString();
|
const QString encrypted = json.value("message").toString();
|
||||||
|
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
if (!m_associated) {
|
if (!m_associated) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
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 submit = decrypted.value("submitUrl").toString();
|
||||||
const QString auth = decrypted.value("httpAuth").toString();
|
const QString auth = decrypted.value("httpAuth").toString();
|
||||||
const bool httpAuth = auth.compare(TRUE_STR, Qt::CaseSensitive) == 0 ? true : false;
|
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()) {
|
if (users.isEmpty()) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND);
|
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)
|
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 nonce = json.value("nonce").toString();
|
||||||
const QString encrypted = json.value("message").toString();
|
const QString encrypted = json.value("message").toString();
|
||||||
|
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
if (!m_associated) {
|
if (!m_associated) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
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 groupUuid = decrypted.value("groupUuid").toString();
|
||||||
const QString realm;
|
const QString realm;
|
||||||
|
|
||||||
BrowserService::ReturnValue result = BrowserService::ReturnValue::Success;
|
bool result = true;
|
||||||
if (uuid.isEmpty()) {
|
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 {
|
} 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);
|
const QString newNonce = incrementNonce(nonce);
|
||||||
@ -351,7 +363,7 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString
|
|||||||
QJsonObject message = buildMessage(newNonce);
|
QJsonObject message = buildMessage(newNonce);
|
||||||
message["count"] = QJsonValue::Null;
|
message["count"] = QJsonValue::Null;
|
||||||
message["entries"] = QJsonValue::Null;
|
message["entries"] = QJsonValue::Null;
|
||||||
message["error"] = getReturnValue(result);
|
message["error"] = result ? QStringLiteral("success") : QStringLiteral("error");
|
||||||
message["hash"] = hash;
|
message["hash"] = hash;
|
||||||
|
|
||||||
return buildResponse(action, message, newNonce);
|
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)
|
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 nonce = json.value("nonce").toString();
|
||||||
const QString encrypted = json.value("message").toString();
|
const QString encrypted = json.value("message").toString();
|
||||||
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
|
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
|
||||||
@ -374,8 +386,7 @@ QJsonObject BrowserAction::handleLockDatabase(const QJsonObject& json, const QSt
|
|||||||
|
|
||||||
QString command = decrypted.value("action").toString();
|
QString command = decrypted.value("action").toString();
|
||||||
if (!command.isEmpty() && command.compare("lock-database", Qt::CaseSensitive) == 0) {
|
if (!command.isEmpty() && command.compare("lock-database", Qt::CaseSensitive) == 0) {
|
||||||
QMutexLocker locker(&m_mutex);
|
browserService()->lockDatabase();
|
||||||
m_browserService.lockDatabase();
|
|
||||||
|
|
||||||
const QString newNonce = incrementNonce(nonce);
|
const QString newNonce = incrementNonce(nonce);
|
||||||
QJsonObject message = buildMessage(newNonce);
|
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)
|
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 nonce = json.value("nonce").toString();
|
||||||
const QString encrypted = json.value("message").toString();
|
const QString encrypted = json.value("message").toString();
|
||||||
|
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
if (!m_associated) {
|
if (!m_associated) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
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);
|
return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QJsonObject groups = m_browserService.getDatabaseGroups();
|
const QJsonObject groups = browserService()->getDatabaseGroups();
|
||||||
if (groups.isEmpty()) {
|
if (groups.isEmpty()) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_NO_GROUPS_FOUND);
|
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)
|
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 nonce = json.value("nonce").toString();
|
||||||
const QString encrypted = json.value("message").toString();
|
const QString encrypted = json.value("message").toString();
|
||||||
|
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
if (!m_associated) {
|
if (!m_associated) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
|
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();
|
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()) {
|
if (newGroup.isEmpty() || newGroup["name"].toString().isEmpty() || newGroup["uuid"].toString().isEmpty()) {
|
||||||
return getErrorReply(action, ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP);
|
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)
|
QString BrowserAction::encryptMessage(const QJsonObject& message, const QString& nonce)
|
||||||
{
|
{
|
||||||
if (message.isEmpty() || nonce.isEmpty()) {
|
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)
|
QString BrowserAction::encrypt(const QString& plaintext, const QString& nonce)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
const QByteArray ma = plaintext.toUtf8();
|
const QByteArray ma = plaintext.toUtf8();
|
||||||
const QByteArray na = base64Decode(nonce);
|
const QByteArray na = base64Decode(nonce);
|
||||||
const QByteArray ca = base64Decode(m_clientPublicKey);
|
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> sk(sa.cbegin(), sa.cend());
|
||||||
|
|
||||||
std::vector<unsigned char> e;
|
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()) {
|
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||||
return QString();
|
return QString();
|
||||||
@ -614,7 +590,6 @@ QString BrowserAction::encrypt(const QString& plaintext, const QString& nonce)
|
|||||||
|
|
||||||
QByteArray BrowserAction::decrypt(const QString& encrypted, const QString& nonce)
|
QByteArray BrowserAction::decrypt(const QString& encrypted, const QString& nonce)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
const QByteArray ma = base64Decode(encrypted);
|
const QByteArray ma = base64Decode(encrypted);
|
||||||
const QByteArray na = base64Decode(nonce);
|
const QByteArray na = base64Decode(nonce);
|
||||||
const QByteArray ca = base64Decode(m_clientPublicKey);
|
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> sk(sa.cbegin(), sa.cend());
|
||||||
|
|
||||||
std::vector<unsigned char> d;
|
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()) {
|
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -19,42 +18,16 @@
|
|||||||
#ifndef BROWSERACTION_H
|
#ifndef BROWSERACTION_H
|
||||||
#define BROWSERACTION_H
|
#define BROWSERACTION_H
|
||||||
|
|
||||||
#include "BrowserService.h"
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QMutex>
|
#include <QString>
|
||||||
#include <QObject>
|
|
||||||
#include <QtCore>
|
|
||||||
|
|
||||||
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:
|
public:
|
||||||
BrowserAction(BrowserService& browserService);
|
explicit BrowserAction() = default;
|
||||||
~BrowserAction() = default;
|
~BrowserAction() = default;
|
||||||
|
|
||||||
QJsonObject readResponse(const QJsonObject& json);
|
QJsonObject processClientMessage(const QJsonObject& json);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QJsonObject handleAction(const QJsonObject& json);
|
QJsonObject handleAction(const QJsonObject& json);
|
||||||
@ -73,9 +46,6 @@ private:
|
|||||||
QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce);
|
QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce);
|
||||||
QJsonObject getErrorReply(const QString& action, const int errorCode) const;
|
QJsonObject getErrorReply(const QString& action, const int errorCode) const;
|
||||||
QString getErrorMessage(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);
|
QString encryptMessage(const QJsonObject& message, const QString& nonce);
|
||||||
QJsonObject decryptMessage(const QString& message, const QString& nonce);
|
QJsonObject decryptMessage(const QString& message, const QString& nonce);
|
||||||
@ -90,12 +60,10 @@ private:
|
|||||||
QString incrementNonce(const QString& nonce);
|
QString incrementNonce(const QString& nonce);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMutex m_mutex;
|
|
||||||
BrowserService& m_browserService;
|
|
||||||
QString m_clientPublicKey;
|
QString m_clientPublicKey;
|
||||||
QString m_publicKey;
|
QString m_publicKey;
|
||||||
QString m_secretKey;
|
QString m_secretKey;
|
||||||
bool m_associated;
|
bool m_associated = false;
|
||||||
|
|
||||||
friend class TestBrowser;
|
friend class TestBrowser;
|
||||||
};
|
};
|
||||||
|
@ -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();
|
|
||||||
}
|
|
@ -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
|
|
107
src/browser/BrowserHost.cpp
Normal file
107
src/browser/BrowserHost.cpp
Normal file
@ -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);
|
||||||
|
}
|
@ -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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -18,29 +18,37 @@
|
|||||||
#ifndef NATIVEMESSAGINGHOST_H
|
#ifndef NATIVEMESSAGINGHOST_H
|
||||||
#define 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
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NativeMessagingHost();
|
explicit BrowserHost(QObject* parent = nullptr);
|
||||||
~NativeMessagingHost() override;
|
~BrowserHost() override;
|
||||||
|
|
||||||
public slots:
|
void start();
|
||||||
void newLocalMessage();
|
void stop();
|
||||||
void deleteSocket();
|
|
||||||
void socketStateChanged(QLocalSocket::LocalSocketState socketState);
|
void sendClientMessage(const QJsonObject& json);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clientMessageReceived(const QJsonObject& json);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void proxyConnected();
|
||||||
|
void readProxyMessage();
|
||||||
|
void proxyDisconnected();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readNativeMessages() override;
|
QPointer<QLocalServer> m_localServer;
|
||||||
void readLength() override;
|
QList<QLocalSocket*> m_socketList;
|
||||||
bool readStdIn(const quint32 length) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QLocalSocket* m_localSocket;
|
|
||||||
|
|
||||||
Q_DISABLE_COPY(NativeMessagingHost)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NATIVEMESSAGINGHOST_H
|
#endif // NATIVEMESSAGINGHOST_H
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Francois Ferrand
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -26,8 +26,10 @@
|
|||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
#include "BrowserAccessControlDialog.h"
|
#include "BrowserAccessControlDialog.h"
|
||||||
|
#include "BrowserAction.h"
|
||||||
#include "BrowserEntryConfig.h"
|
#include "BrowserEntryConfig.h"
|
||||||
#include "BrowserEntrySaveDialog.h"
|
#include "BrowserEntrySaveDialog.h"
|
||||||
|
#include "BrowserHost.h"
|
||||||
#include "BrowserService.h"
|
#include "BrowserService.h"
|
||||||
#include "BrowserSettings.h"
|
#include "BrowserSettings.h"
|
||||||
#include "core/Database.h"
|
#include "core/Database.h"
|
||||||
@ -58,34 +60,45 @@ const QString BrowserService::OPTION_ONLY_HTTP_AUTH = QStringLiteral("BrowserOnl
|
|||||||
// Multiple URL's
|
// Multiple URL's
|
||||||
const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL");
|
const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL");
|
||||||
|
|
||||||
BrowserService::BrowserService(DatabaseTabWidget* parent)
|
Q_GLOBAL_STATIC(BrowserService, s_browserService);
|
||||||
: m_dbTabWidget(parent)
|
|
||||||
|
BrowserService::BrowserService()
|
||||||
|
: QObject()
|
||||||
|
, m_browserHost(new BrowserHost)
|
||||||
, m_dialogActive(false)
|
, m_dialogActive(false)
|
||||||
, m_bringToFrontRequested(false)
|
, m_bringToFrontRequested(false)
|
||||||
, m_prevWindowState(WindowState::Normal)
|
, m_prevWindowState(WindowState::Normal)
|
||||||
, m_keepassBrowserUUID(Tools::hexToUuid("de887cc3036343b8974b5911b8816224"))
|
, m_keepassBrowserUUID(Tools::hexToUuid("de887cc3036343b8974b5911b8816224"))
|
||||||
{
|
{
|
||||||
// Don't connect the signals when used from DatabaseSettingsWidgetBrowser (parent is nullptr)
|
connect(m_browserHost, &BrowserHost::clientMessageReceived, this, &BrowserService::processClientMessage);
|
||||||
if (m_dbTabWidget) {
|
setEnabled(browserSettings()->isEnabled());
|
||||||
connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*)));
|
}
|
||||||
connect(
|
|
||||||
m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*)));
|
BrowserService* BrowserService::instance()
|
||||||
connect(m_dbTabWidget,
|
{
|
||||||
SIGNAL(activateDatabaseChanged(DatabaseWidget*)),
|
return s_browserService;
|
||||||
this,
|
}
|
||||||
SLOT(activateDatabaseChanged(DatabaseWidget*)));
|
|
||||||
|
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
|
bool BrowserService::isDatabaseOpened() const
|
||||||
{
|
{
|
||||||
DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
|
if (m_currentDatabaseWidget) {
|
||||||
if (!dbWidget) {
|
return !m_currentDatabaseWidget->isLocked();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
|
|
||||||
|| dbWidget->currentMode() == DatabaseWidget::Mode::EditMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserService::openDatabase(bool triggerUnlock)
|
bool BrowserService::openDatabase(bool triggerUnlock)
|
||||||
@ -94,13 +107,7 @@ bool BrowserService::openDatabase(bool triggerUnlock)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
|
if (m_currentDatabaseWidget && !m_currentDatabaseWidget->isLocked()) {
|
||||||
if (!dbWidget) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
|
|
||||||
|| dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,19 +121,20 @@ bool BrowserService::openDatabase(bool triggerUnlock)
|
|||||||
|
|
||||||
void BrowserService::lockDatabase()
|
void BrowserService::lockDatabase()
|
||||||
{
|
{
|
||||||
if (thread() != QThread::currentThread()) {
|
if (m_currentDatabaseWidget) {
|
||||||
QMetaObject::invokeMethod(this, "lockDatabase", Qt::BlockingQueuedConnection);
|
m_currentDatabaseWidget->lock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget();
|
QString BrowserService::getDatabaseHash(bool legacy)
|
||||||
if (!dbWidget) {
|
{
|
||||||
return;
|
if (legacy) {
|
||||||
}
|
return QCryptographicHash::hash(
|
||||||
|
(browserService()->getDatabaseRootUuid() + browserService()->getDatabaseRecycleBinUuid()).toUtf8(),
|
||||||
if (dbWidget->currentMode() == DatabaseWidget::Mode::ViewMode
|
QCryptographicHash::Sha256)
|
||||||
|| dbWidget->currentMode() == DatabaseWidget::Mode::EditMode) {
|
.toHex();
|
||||||
dbWidget->lock();
|
|
||||||
}
|
}
|
||||||
|
return QCryptographicHash::hash(getDatabaseRootUuid().toUtf8(), QCryptographicHash::Sha256).toHex();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BrowserService::getDatabaseRootUuid()
|
QString BrowserService::getDatabaseRootUuid()
|
||||||
@ -180,9 +188,9 @@ QJsonArray BrowserService::getChildrenFromGroup(Group* group)
|
|||||||
return groupList;
|
return groupList;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject BrowserService::getDatabaseGroups(const QSharedPointer<Database>& selectedDb)
|
QJsonObject BrowserService::getDatabaseGroups()
|
||||||
{
|
{
|
||||||
auto db = selectedDb ? selectedDb : getDatabase();
|
auto db = getDatabase();
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -208,15 +216,6 @@ QJsonObject BrowserService::getDatabaseGroups(const QSharedPointer<Database>& se
|
|||||||
|
|
||||||
QJsonObject BrowserService::createNewGroup(const QString& groupName)
|
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();
|
auto db = getDatabase();
|
||||||
if (!db) {
|
if (!db) {
|
||||||
@ -232,6 +231,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName)
|
|||||||
|
|
||||||
// Group already exists
|
// Group already exists
|
||||||
if (group) {
|
if (group) {
|
||||||
|
QJsonObject result;
|
||||||
result["name"] = group->name();
|
result["name"] = group->name();
|
||||||
result["uuid"] = Tools::uuidToHex(group->uuid());
|
result["uuid"] = Tools::uuidToHex(group->uuid());
|
||||||
return result;
|
return result;
|
||||||
@ -245,7 +245,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName)
|
|||||||
MessageBox::Yes | MessageBox::No);
|
MessageBox::Yes | MessageBox::No);
|
||||||
|
|
||||||
if (dialogResult != MessageBox::Yes) {
|
if (dialogResult != MessageBox::Yes) {
|
||||||
return result;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString name, uuid;
|
QString name, uuid;
|
||||||
@ -279,6 +279,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName)
|
|||||||
previousGroup = tempGroup;
|
previousGroup = tempGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonObject result;
|
||||||
result["name"] = name;
|
result["name"] = name;
|
||||||
result["uuid"] = uuid;
|
result["uuid"] = uuid;
|
||||||
return result;
|
return result;
|
||||||
@ -286,25 +287,18 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName)
|
|||||||
|
|
||||||
QString BrowserService::storeKey(const QString& key)
|
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();
|
auto db = getDatabase();
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains;
|
bool contains;
|
||||||
MessageBox::Button dialogResult = MessageBox::Cancel;
|
auto dialogResult = MessageBox::Cancel;
|
||||||
|
QString id;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
QInputDialog keyDialog;
|
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.setWindowTitle(tr("KeePassXC: New key association request"));
|
||||||
keyDialog.setLabelText(tr("You have received an association request for the following database:\n%1\n\n"
|
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.")
|
"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);
|
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& url,
|
||||||
const QString& submitUrl,
|
const QString& submitUrl,
|
||||||
const QString& realm,
|
const QString& realm,
|
||||||
const StringPairList& keyList,
|
const StringPairList& keyList,
|
||||||
const bool httpAuth)
|
const bool httpAuth)
|
||||||
{
|
{
|
||||||
QJsonArray result;
|
Q_UNUSED(dbid);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess();
|
const bool alwaysAllowAccess = browserSettings()->alwaysAllowAccess();
|
||||||
const bool ignoreHttpAuth = browserSettings()->httpAuthPermission();
|
const bool ignoreHttpAuth = browserSettings()->httpAuthPermission();
|
||||||
const QString host = QUrl(url).host();
|
const QString host = QUrl(url).host();
|
||||||
@ -425,18 +405,19 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pwEntries.isEmpty()) {
|
if (pwEntries.isEmpty()) {
|
||||||
return QJsonArray();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that database is not locked when the popup was visible
|
// Ensure that database is not locked when the popup was visible
|
||||||
if (!isDatabaseOpened()) {
|
if (!isDatabaseOpened()) {
|
||||||
return QJsonArray();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort results
|
// Sort results
|
||||||
pwEntries = sortEntries(pwEntries, host, submitUrl);
|
pwEntries = sortEntries(pwEntries, host, submitUrl);
|
||||||
|
|
||||||
// Fill the list
|
// Fill the list
|
||||||
|
QJsonArray result;
|
||||||
for (auto* entry : pwEntries) {
|
for (auto* entry : pwEntries) {
|
||||||
result.append(prepareEntry(entry));
|
result.append(prepareEntry(entry));
|
||||||
}
|
}
|
||||||
@ -444,7 +425,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserService::addEntry(const QString& id,
|
void BrowserService::addEntry(const QString& dbid,
|
||||||
const QString& login,
|
const QString& login,
|
||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& url,
|
const QString& url,
|
||||||
@ -454,21 +435,8 @@ void BrowserService::addEntry(const QString& id,
|
|||||||
const QString& groupUuid,
|
const QString& groupUuid,
|
||||||
const QSharedPointer<Database>& selectedDb)
|
const QSharedPointer<Database>& selectedDb)
|
||||||
{
|
{
|
||||||
if (thread() != QThread::currentThread()) {
|
// TODO: select database based on this key id
|
||||||
QMetaObject::invokeMethod(this,
|
Q_UNUSED(dbid);
|
||||||
"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));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto db = selectedDb ? selectedDb : selectedDatabase();
|
auto db = selectedDb ? selectedDb : selectedDatabase();
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return;
|
return;
|
||||||
@ -510,37 +478,25 @@ void BrowserService::addEntry(const QString& id,
|
|||||||
config.save(entry);
|
config.save(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserService::ReturnValue BrowserService::updateEntry(const QString& id,
|
bool BrowserService::updateEntry(const QString& dbid,
|
||||||
const QString& uuid,
|
const QString& uuid,
|
||||||
const QString& login,
|
const QString& login,
|
||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& url,
|
const QString& url,
|
||||||
const QString& submitUrl)
|
const QString& submitUrl)
|
||||||
{
|
{
|
||||||
ReturnValue result = ReturnValue::Error;
|
// TODO: select database based on this key id
|
||||||
if (thread() != QThread::currentThread()) {
|
Q_UNUSED(dbid);
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto db = selectedDatabase();
|
auto db = selectedDatabase();
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return ReturnValue::Error;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* entry = db->rootGroup()->findEntryByUuid(Tools::hexToUuid(uuid));
|
Entry* entry = db->rootGroup()->findEntryByUuid(Tools::hexToUuid(uuid));
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
// If entry is not found for update, add a new one to the selected database
|
// If entry is not found for update, add a new one to the selected database
|
||||||
addEntry(id, login, password, url, submitUrl, "", "", "", db);
|
addEntry(dbid, login, password, url, submitUrl, "", "", "", db);
|
||||||
return ReturnValue::Success;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the entry password is a reference. If so, update the original entry instead
|
// 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()) {
|
if (!referenceUuid.isNull()) {
|
||||||
entry = db->rootGroup()->findEntryByUuid(referenceUuid);
|
entry = db->rootGroup()->findEntryByUuid(referenceUuid);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
return ReturnValue::Error;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString username = entry->username();
|
QString username = entry->username();
|
||||||
if (username.isEmpty()) {
|
if (username.isEmpty()) {
|
||||||
return ReturnValue::Error;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
if (username.compare(login, Qt::CaseSensitive) != 0
|
if (username.compare(login, Qt::CaseSensitive) != 0
|
||||||
|| entry->password().compare(password, Qt::CaseSensitive) != 0) {
|
|| entry->password().compare(password, Qt::CaseSensitive) != 0) {
|
||||||
MessageBox::Button dialogResult = MessageBox::No;
|
MessageBox::Button dialogResult = MessageBox::No;
|
||||||
@ -580,9 +537,7 @@ BrowserService::ReturnValue BrowserService::updateEntry(const QString& id,
|
|||||||
}
|
}
|
||||||
entry->setPassword(password);
|
entry->setPassword(password);
|
||||||
entry->endUpdate();
|
entry->endUpdate();
|
||||||
result = ReturnValue::Success;
|
result = true;
|
||||||
} else {
|
|
||||||
result = ReturnValue::Canceled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hideWindow();
|
hideWindow();
|
||||||
@ -646,17 +601,14 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const QString& s
|
|||||||
// Get the list of databases to search
|
// Get the list of databases to search
|
||||||
QList<QSharedPointer<Database>> databases;
|
QList<QSharedPointer<Database>> databases;
|
||||||
if (browserSettings()->searchInAllDatabases()) {
|
if (browserSettings()->searchInAllDatabases()) {
|
||||||
const int count = m_dbTabWidget->count();
|
for (auto dbWidget : getMainWindow()->getOpenDatabases()) {
|
||||||
for (int i = 0; i < count; ++i) {
|
auto db = dbWidget->database();
|
||||||
if (auto* dbWidget = qobject_cast<DatabaseWidget*>(m_dbTabWidget->widget(i))) {
|
if (db && databaseConnected(dbWidget->database())) {
|
||||||
if (const auto& db = dbWidget->database()) {
|
|
||||||
if (databaseConnected(db)) {
|
|
||||||
databases << db;
|
databases << db;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
}
|
const auto& db = getDatabase();
|
||||||
} else if (const auto& db = getDatabase()) {
|
|
||||||
if (databaseConnected(db)) {
|
if (databaseConnected(db)) {
|
||||||
databases << db;
|
databases << db;
|
||||||
}
|
}
|
||||||
@ -674,9 +626,8 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const QString& s
|
|||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserService::convertAttributesToCustomData(const QSharedPointer<Database>& currentDb)
|
void BrowserService::convertAttributesToCustomData(QSharedPointer<Database> db)
|
||||||
{
|
{
|
||||||
auto db = currentDb ? currentDb : getDatabase();
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -806,7 +757,7 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
|||||||
m_dialogActive = true;
|
m_dialogActive = true;
|
||||||
BrowserAccessControlDialog accessControlDialog;
|
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) {
|
connect(&accessControlDialog, &BrowserAccessControlDialog::disableAccess, [&](QTableWidgetItem* item) {
|
||||||
auto entry = pwEntriesToConfirm[item->row()];
|
auto entry = pwEntriesToConfirm[item->row()];
|
||||||
BrowserEntryConfig config;
|
BrowserEntryConfig config;
|
||||||
@ -1103,10 +1054,8 @@ QString BrowserService::baseDomain(const QString& hostname) const
|
|||||||
|
|
||||||
QSharedPointer<Database> BrowserService::getDatabase()
|
QSharedPointer<Database> BrowserService::getDatabase()
|
||||||
{
|
{
|
||||||
if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) {
|
if (m_currentDatabaseWidget) {
|
||||||
if (const auto& db = dbWidget->database()) {
|
return m_currentDatabaseWidget->database();
|
||||||
return db;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -1114,20 +1063,15 @@ QSharedPointer<Database> BrowserService::getDatabase()
|
|||||||
QSharedPointer<Database> BrowserService::selectedDatabase()
|
QSharedPointer<Database> BrowserService::selectedDatabase()
|
||||||
{
|
{
|
||||||
QList<DatabaseWidget*> databaseWidgets;
|
QList<DatabaseWidget*> databaseWidgets;
|
||||||
for (int i = 0;; ++i) {
|
for (auto dbWidget : getMainWindow()->getOpenDatabases()) {
|
||||||
auto* dbWidget = m_dbTabWidget->databaseWidgetFromIndex(i);
|
|
||||||
// Add only open databases
|
// Add only open databases
|
||||||
if (dbWidget && !dbWidget->isLocked()) {
|
if (!dbWidget->isLocked()) {
|
||||||
databaseWidgets.push_back(dbWidget);
|
databaseWidgets << dbWidget;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Break out if dbStruct.dbWidget is nullptr
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserEntrySaveDialog browserEntrySaveDialog;
|
BrowserEntrySaveDialog browserEntrySaveDialog;
|
||||||
int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_dbTabWidget->currentDatabaseWidget());
|
int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_currentDatabaseWidget);
|
||||||
if (openDatabaseCount > 1) {
|
if (openDatabaseCount > 1) {
|
||||||
int res = browserEntrySaveDialog.exec();
|
int res = browserEntrySaveDialog.exec();
|
||||||
if (res == QDialog::Accepted) {
|
if (res == QDialog::Accepted) {
|
||||||
@ -1145,7 +1089,7 @@ QSharedPointer<Database> BrowserService::selectedDatabase()
|
|||||||
return getDatabase();
|
return getDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name) const
|
bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name)
|
||||||
{
|
{
|
||||||
if (entry->attributes()->contains(name)) {
|
if (entry->attributes()->contains(name)) {
|
||||||
QString attr = entry->attributes()->value(name);
|
QString attr = entry->attributes()->value(name);
|
||||||
@ -1160,7 +1104,7 @@ bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int BrowserService::moveKeysToCustomData(Entry* entry, const QSharedPointer<Database>& db) const
|
int BrowserService::moveKeysToCustomData(Entry* entry, QSharedPointer<Database> db)
|
||||||
{
|
{
|
||||||
int keyCounter = 0;
|
int keyCounter = 0;
|
||||||
for (const auto& key : entry->attributes()->keys()) {
|
for (const auto& key : entry->attributes()->keys()) {
|
||||||
@ -1179,14 +1123,9 @@ int BrowserService::moveKeysToCustomData(Entry* entry, const QSharedPointer<Data
|
|||||||
return keyCounter;
|
return keyCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserService::checkLegacySettings()
|
bool BrowserService::checkLegacySettings(QSharedPointer<Database> db)
|
||||||
{
|
{
|
||||||
if (!browserSettings()->isEnabled() || browserSettings()->noMigrationPrompt()) {
|
if (!db || !browserSettings()->isEnabled() || browserSettings()->noMigrationPrompt()) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto db = getDatabase();
|
|
||||||
if (!db) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1272,7 +1211,9 @@ void BrowserService::raiseWindow(const bool force)
|
|||||||
void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
|
void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
|
||||||
{
|
{
|
||||||
if (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();
|
hideWindow();
|
||||||
m_bringToFrontRequested = false;
|
m_bringToFrontRequested = false;
|
||||||
}
|
}
|
||||||
emit databaseUnlocked();
|
|
||||||
|
|
||||||
if (checkLegacySettings()) {
|
QJsonObject msg;
|
||||||
convertAttributesToCustomData();
|
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) {
|
if (dbWidget) {
|
||||||
auto currentMode = dbWidget->currentMode();
|
if (dbWidget->isLocked()) {
|
||||||
if (currentMode == DatabaseWidget::Mode::ViewMode || currentMode == DatabaseWidget::Mode::EditMode) {
|
databaseLocked(dbWidget);
|
||||||
emit databaseUnlocked();
|
|
||||||
} else {
|
} 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);
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Francois Ferrand
|
* Copyright (C) 2013 Francois Ferrand
|
||||||
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
* 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
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -21,8 +21,9 @@
|
|||||||
#define BROWSERSERVICE_H
|
#define BROWSERSERVICE_H
|
||||||
|
|
||||||
#include "core/Entry.h"
|
#include "core/Entry.h"
|
||||||
#include "gui/DatabaseTabWidget.h"
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QSharedPointer>
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
|
|
||||||
typedef QPair<QString, QString> StringPair;
|
typedef QPair<QString, QString> StringPair;
|
||||||
@ -33,28 +34,33 @@ enum
|
|||||||
max_length = 16 * 1024
|
max_length = 16 * 1024
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DatabaseTabWidget;
|
||||||
|
class DatabaseWidget;
|
||||||
|
class BrowserHost;
|
||||||
|
class BrowserAction;
|
||||||
|
|
||||||
class BrowserService : public QObject
|
class BrowserService : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum ReturnValue
|
explicit BrowserService();
|
||||||
{
|
static BrowserService* instance();
|
||||||
Success,
|
|
||||||
Error,
|
|
||||||
Canceled
|
|
||||||
};
|
|
||||||
|
|
||||||
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 isDatabaseOpened() const;
|
||||||
bool openDatabase(bool triggerUnlock);
|
bool openDatabase(bool triggerUnlock);
|
||||||
QString getDatabaseRootUuid();
|
void lockDatabase();
|
||||||
QString getDatabaseRecycleBinUuid();
|
|
||||||
QJsonObject getDatabaseGroups(const QSharedPointer<Database>& selectedDb = {});
|
QJsonObject getDatabaseGroups();
|
||||||
QJsonObject createNewGroup(const QString& groupName);
|
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& login,
|
||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& url,
|
const QString& url,
|
||||||
@ -63,11 +69,22 @@ public:
|
|||||||
const QString& group,
|
const QString& group,
|
||||||
const QString& groupUuid,
|
const QString& groupUuid,
|
||||||
const QSharedPointer<Database>& selectedDb = {});
|
const QSharedPointer<Database>& selectedDb = {});
|
||||||
QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& url, const QString& submitUrl);
|
bool updateEntry(const QString& dbid,
|
||||||
QList<Entry*> searchEntries(const QString& url, const QString& submitUrl, const StringPairList& keyList);
|
const QString& uuid,
|
||||||
void convertAttributesToCustomData(const QSharedPointer<Database>& currentDb = {});
|
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_NAME;
|
||||||
static const QString KEEPASSXCBROWSER_OLD_NAME;
|
static const QString KEEPASSXCBROWSER_OLD_NAME;
|
||||||
static const QString ASSOCIATE_KEY_PREFIX;
|
static const QString ASSOCIATE_KEY_PREFIX;
|
||||||
@ -78,28 +95,12 @@ public:
|
|||||||
static const QString ADDITIONAL_URL;
|
static const QString ADDITIONAL_URL;
|
||||||
|
|
||||||
public slots:
|
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 databaseLocked(DatabaseWidget* dbWidget);
|
||||||
void databaseUnlocked(DatabaseWidget* dbWidget);
|
void databaseUnlocked(DatabaseWidget* dbWidget);
|
||||||
void activateDatabaseChanged(DatabaseWidget* dbWidget);
|
void activeDatabaseChanged(DatabaseWidget* dbWidget);
|
||||||
void lockDatabase();
|
|
||||||
|
|
||||||
signals:
|
private slots:
|
||||||
void databaseLocked();
|
void processClientMessage(const QJsonObject& message);
|
||||||
void databaseUnlocked();
|
|
||||||
void databaseChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Access
|
enum Access
|
||||||
@ -116,7 +117,8 @@ private:
|
|||||||
Hidden
|
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*> sortEntries(QList<Entry*>& pwEntries, const QString& host, const QString& submitUrl);
|
||||||
QList<Entry*> confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
QList<Entry*> confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
||||||
const QString& url,
|
const QString& url,
|
||||||
@ -125,6 +127,7 @@ private:
|
|||||||
const QString& realm,
|
const QString& realm,
|
||||||
const bool httpAuth);
|
const bool httpAuth);
|
||||||
QJsonObject prepareEntry(const Entry* entry);
|
QJsonObject prepareEntry(const Entry* entry);
|
||||||
|
QJsonArray getChildrenFromGroup(Group* group);
|
||||||
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
|
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
|
||||||
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
|
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
|
||||||
int
|
int
|
||||||
@ -135,21 +138,35 @@ private:
|
|||||||
QString baseDomain(const QString& hostname) const;
|
QString baseDomain(const QString& hostname) const;
|
||||||
QSharedPointer<Database> getDatabase();
|
QSharedPointer<Database> getDatabase();
|
||||||
QSharedPointer<Database> selectedDatabase();
|
QSharedPointer<Database> selectedDatabase();
|
||||||
QJsonArray getChildrenFromGroup(Group* group);
|
QString getDatabaseRootUuid();
|
||||||
bool moveSettingsToCustomData(Entry* entry, const QString& name) const;
|
QString getDatabaseRecycleBinUuid();
|
||||||
int moveKeysToCustomData(Entry* entry, const QSharedPointer<Database>& db) const;
|
|
||||||
bool checkLegacySettings();
|
bool checkLegacySettings(QSharedPointer<Database> db);
|
||||||
|
|
||||||
void hideWindow() const;
|
void hideWindow() const;
|
||||||
void raiseWindow(const bool force = false);
|
void raiseWindow(const bool force = false);
|
||||||
|
|
||||||
private:
|
static bool moveSettingsToCustomData(Entry* entry, const QString& name);
|
||||||
DatabaseTabWidget* const m_dbTabWidget;
|
static int moveKeysToCustomData(Entry* entry, QSharedPointer<Database> db);
|
||||||
|
|
||||||
|
QPointer<BrowserHost> m_browserHost;
|
||||||
|
QHash<QString, QSharedPointer<BrowserAction>> m_browserClients;
|
||||||
|
|
||||||
bool m_dialogActive;
|
bool m_dialogActive;
|
||||||
bool m_bringToFrontRequested;
|
bool m_bringToFrontRequested;
|
||||||
WindowState m_prevWindowState;
|
WindowState m_prevWindowState;
|
||||||
QUuid m_keepassBrowserUUID;
|
QUuid m_keepassBrowserUUID;
|
||||||
|
|
||||||
|
QPointer<DatabaseWidget> m_currentDatabaseWidget;
|
||||||
|
|
||||||
|
Q_DISABLE_COPY(BrowserService);
|
||||||
|
|
||||||
friend class TestBrowser;
|
friend class TestBrowser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline BrowserService* browserService()
|
||||||
|
{
|
||||||
|
return BrowserService::instance();
|
||||||
|
}
|
||||||
|
|
||||||
#endif // BROWSERSERVICE_H
|
#endif // BROWSERSERVICE_H
|
||||||
|
@ -162,16 +162,6 @@ void BrowserSettings::setNoMigrationPrompt(bool prompt)
|
|||||||
config()->set(Config::Browser_NoMigrationPrompt, 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()
|
bool BrowserSettings::useCustomProxy()
|
||||||
{
|
{
|
||||||
return config()->get(Config::Browser_UseCustomProxy).toBool();
|
return config()->get(Config::Browser_UseCustomProxy).toBool();
|
||||||
@ -184,9 +174,6 @@ void BrowserSettings::setUseCustomProxy(bool enabled)
|
|||||||
|
|
||||||
QString BrowserSettings::customProxyLocation()
|
QString BrowserSettings::customProxyLocation()
|
||||||
{
|
{
|
||||||
if (!useCustomProxy()) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return config()->get(Config::Browser_CustomProxyLocation).toString();
|
return config()->get(Config::Browser_CustomProxyLocation).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +182,11 @@ void BrowserSettings::setCustomProxyLocation(const QString& location)
|
|||||||
config()->set(Config::Browser_CustomProxyLocation, location);
|
config()->set(Config::Browser_CustomProxyLocation, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString BrowserSettings::proxyLocation()
|
||||||
|
{
|
||||||
|
return m_nativeMessageInstaller.getProxyPath();
|
||||||
|
}
|
||||||
|
|
||||||
bool BrowserSettings::updateBinaryPath()
|
bool BrowserSettings::updateBinaryPath()
|
||||||
{
|
{
|
||||||
return config()->get(Config::Browser_UpdateBinaryPath).toBool();
|
return config()->get(Config::Browser_UpdateBinaryPath).toBool();
|
||||||
@ -215,81 +207,14 @@ void BrowserSettings::setAllowExpiredCredentials(bool enabled)
|
|||||||
config()->set(Config::Browser_AllowExpiredCredentials, enabled);
|
config()->set(Config::Browser_AllowExpiredCredentials, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserSettings::chromeSupport()
|
bool BrowserSettings::browserSupport(BrowserShared::SupportedBrowsers browser)
|
||||||
{
|
{
|
||||||
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROME);
|
return m_nativeMessageInstaller.isBrowserEnabled(browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::setChromeSupport(bool enabled)
|
void BrowserSettings::setBrowserSupport(BrowserShared::SupportedBrowsers browser, bool enabled)
|
||||||
{
|
{
|
||||||
m_hostInstaller.installBrowser(
|
m_nativeMessageInstaller.setBrowserEnabled(browser, enabled);
|
||||||
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)
|
|
||||||
{
|
|
||||||
m_hostInstaller.installBrowser(
|
|
||||||
HostInstaller::SupportedBrowsers::FIREFOX, enabled, supportBrowserProxy(), customProxyLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BrowserSettings::vivaldiSupport()
|
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserSettings::passwordUseNumbers()
|
bool BrowserSettings::passwordUseNumbers()
|
||||||
@ -563,13 +488,7 @@ QJsonObject BrowserSettings::generatePassword()
|
|||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserSettings::updateBinaryPaths(const QString& customProxyLocation)
|
void BrowserSettings::updateBinaryPaths()
|
||||||
{
|
{
|
||||||
bool isProxy = supportBrowserProxy();
|
m_nativeMessageInstaller.updateBinaryPaths();
|
||||||
m_hostInstaller.updateBinaryPaths(isProxy, customProxyLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BrowserSettings::checkIfProxyExists(QString& path)
|
|
||||||
{
|
|
||||||
return m_hostInstaller.checkIfProxyExists(supportBrowserProxy(), customProxyLocation(), path);
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
#ifndef BROWSERSETTINGS_H
|
#ifndef BROWSERSETTINGS_H
|
||||||
#define BROWSERSETTINGS_H
|
#define BROWSERSETTINGS_H
|
||||||
|
|
||||||
#include "HostInstaller.h"
|
#include "BrowserShared.h"
|
||||||
|
#include "NativeMessageInstaller.h"
|
||||||
#include "core/PassphraseGenerator.h"
|
#include "core/PassphraseGenerator.h"
|
||||||
#include "core/PasswordGenerator.h"
|
#include "core/PasswordGenerator.h"
|
||||||
|
|
||||||
@ -58,30 +59,18 @@ public:
|
|||||||
bool noMigrationPrompt();
|
bool noMigrationPrompt();
|
||||||
void setNoMigrationPrompt(bool prompt);
|
void setNoMigrationPrompt(bool prompt);
|
||||||
|
|
||||||
bool supportBrowserProxy();
|
|
||||||
void setSupportBrowserProxy(bool enabled);
|
|
||||||
bool useCustomProxy();
|
bool useCustomProxy();
|
||||||
void setUseCustomProxy(bool enabled);
|
void setUseCustomProxy(bool enabled);
|
||||||
QString customProxyLocation();
|
QString customProxyLocation();
|
||||||
void setCustomProxyLocation(const QString& location);
|
void setCustomProxyLocation(const QString& location);
|
||||||
|
QString proxyLocation();
|
||||||
bool updateBinaryPath();
|
bool updateBinaryPath();
|
||||||
void setUpdateBinaryPath(bool enabled);
|
void setUpdateBinaryPath(bool enabled);
|
||||||
bool allowExpiredCredentials();
|
bool allowExpiredCredentials();
|
||||||
void setAllowExpiredCredentials(bool enabled);
|
void setAllowExpiredCredentials(bool enabled);
|
||||||
bool chromeSupport();
|
|
||||||
void setChromeSupport(bool enabled);
|
bool browserSupport(BrowserShared::SupportedBrowsers browser);
|
||||||
bool chromiumSupport();
|
void setBrowserSupport(BrowserShared::SupportedBrowsers browser, bool enabled);
|
||||||
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 passwordUseNumbers();
|
bool passwordUseNumbers();
|
||||||
void setPasswordUseNumbers(bool useNumbers);
|
void setPasswordUseNumbers(bool useNumbers);
|
||||||
@ -126,15 +115,14 @@ public:
|
|||||||
PasswordGenerator::CharClasses passwordCharClasses();
|
PasswordGenerator::CharClasses passwordCharClasses();
|
||||||
PasswordGenerator::GeneratorFlags passwordGeneratorFlags();
|
PasswordGenerator::GeneratorFlags passwordGeneratorFlags();
|
||||||
QJsonObject generatePassword();
|
QJsonObject generatePassword();
|
||||||
void updateBinaryPaths(const QString& customProxyLocation = QString());
|
void updateBinaryPaths();
|
||||||
bool checkIfProxyExists(QString& path);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static BrowserSettings* m_instance;
|
static BrowserSettings* m_instance;
|
||||||
|
|
||||||
PasswordGenerator m_passwordGenerator;
|
PasswordGenerator m_passwordGenerator;
|
||||||
PassphraseGenerator m_passPhraseGenerator;
|
PassphraseGenerator m_passPhraseGenerator;
|
||||||
HostInstaller m_hostInstaller;
|
NativeMessageInstaller m_nativeMessageInstaller;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline BrowserSettings* browserSettings()
|
inline BrowserSettings* browserSettings()
|
||||||
|
49
src/browser/BrowserSettingsPage.cpp
Normal file
49
src/browser/BrowserSettingsPage.cpp
Normal file
@ -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());
|
||||||
|
}
|
36
src/browser/BrowserSettingsPage.h
Normal file
36
src/browser/BrowserSettingsPage.h
Normal file
@ -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
|
@ -1,7 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Francois Ferrand
|
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
* 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
|
* 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
|
* 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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "BrowserOptionDialog.h"
|
#include "BrowserSettingsWidget.h"
|
||||||
#include "ui_BrowserOptionDialog.h"
|
#include "ui_BrowserSettingsWidget.h"
|
||||||
|
|
||||||
#include "BrowserSettings.h"
|
#include "BrowserSettings.h"
|
||||||
#include "config-keepassx.h"
|
#include "config-keepassx.h"
|
||||||
@ -26,9 +24,9 @@
|
|||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
|
||||||
BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
|
BrowserSettingsWidget::BrowserSettingsWidget(QWidget* parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_ui(new Ui::BrowserOptionDialog())
|
, m_ui(new Ui::BrowserSettingsWidget())
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
@ -52,13 +50,9 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
|
|||||||
snapInstructions));
|
snapInstructions));
|
||||||
// clang-format on
|
// 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->setCloseButtonVisible(false);
|
||||||
m_ui->warningWidget->setAutoHideTimeout(-1);
|
m_ui->warningWidget->setAutoHideTimeout(-1);
|
||||||
|
m_ui->warningWidget->setAnimate(false);
|
||||||
|
|
||||||
m_ui->tabWidget->setEnabled(m_ui->enableBrowserSupport->isChecked());
|
m_ui->tabWidget->setEnabled(m_ui->enableBrowserSupport->isChecked());
|
||||||
connect(m_ui->enableBrowserSupport, SIGNAL(toggled(bool)), m_ui->tabWidget, SLOT(setEnabled(bool)));
|
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());
|
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->customProxyLocation, SLOT(setEnabled(bool)));
|
||||||
connect(m_ui->useCustomProxy, SIGNAL(toggled(bool)), m_ui->customProxyLocationBrowseButton, 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()));
|
connect(m_ui->customProxyLocationBrowseButton, SIGNAL(clicked()), this, SLOT(showProxyLocationFileDialog()));
|
||||||
|
|
||||||
#ifndef Q_OS_LINUX
|
#ifndef Q_OS_LINUX
|
||||||
@ -86,11 +82,11 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
|
|||||||
m_ui->browserGlobalWarningWidget->setVisible(false);
|
m_ui->browserGlobalWarningWidget->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserOptionDialog::~BrowserOptionDialog()
|
BrowserSettingsWidget::~BrowserSettingsWidget()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserOptionDialog::loadSettings()
|
void BrowserSettingsWidget::loadSettings()
|
||||||
{
|
{
|
||||||
auto settings = browserSettings();
|
auto settings = browserSettings();
|
||||||
m_ui->enableBrowserSupport->setChecked(settings->isEnabled());
|
m_ui->enableBrowserSupport->setChecked(settings->isEnabled());
|
||||||
@ -116,43 +112,39 @@ void BrowserOptionDialog::loadSettings()
|
|||||||
m_ui->searchInAllDatabases->setChecked(settings->searchInAllDatabases());
|
m_ui->searchInAllDatabases->setChecked(settings->searchInAllDatabases());
|
||||||
m_ui->supportKphFields->setChecked(settings->supportKphFields());
|
m_ui->supportKphFields->setChecked(settings->supportKphFields());
|
||||||
m_ui->noMigrationPrompt->setChecked(settings->noMigrationPrompt());
|
m_ui->noMigrationPrompt->setChecked(settings->noMigrationPrompt());
|
||||||
m_ui->supportBrowserProxy->setChecked(settings->supportBrowserProxy());
|
|
||||||
m_ui->useCustomProxy->setChecked(settings->useCustomProxy());
|
m_ui->useCustomProxy->setChecked(settings->useCustomProxy());
|
||||||
m_ui->customProxyLocation->setText(settings->customProxyLocation());
|
m_ui->customProxyLocation->setText(settings->customProxyLocation());
|
||||||
m_ui->updateBinaryPath->setChecked(settings->updateBinaryPath());
|
m_ui->updateBinaryPath->setChecked(settings->updateBinaryPath());
|
||||||
m_ui->allowExpiredCredentials->setChecked(settings->allowExpiredCredentials());
|
m_ui->allowExpiredCredentials->setChecked(settings->allowExpiredCredentials());
|
||||||
m_ui->chromeSupport->setChecked(settings->chromeSupport());
|
m_ui->chromeSupport->setChecked(settings->browserSupport(BrowserShared::CHROME));
|
||||||
m_ui->chromiumSupport->setChecked(settings->chromiumSupport());
|
m_ui->chromiumSupport->setChecked(settings->browserSupport(BrowserShared::CHROMIUM));
|
||||||
m_ui->firefoxSupport->setChecked(settings->firefoxSupport());
|
m_ui->firefoxSupport->setChecked(settings->browserSupport(BrowserShared::FIREFOX));
|
||||||
m_ui->edgeSupport->setChecked(settings->edgeSupport());
|
m_ui->edgeSupport->setChecked(settings->browserSupport(BrowserShared::EDGE));
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
m_ui->braveSupport->setChecked(settings->braveSupport());
|
m_ui->braveSupport->setChecked(settings->browserSupport(BrowserShared::BRAVE));
|
||||||
m_ui->vivaldiSupport->setChecked(settings->vivaldiSupport());
|
m_ui->vivaldiSupport->setChecked(settings->browserSupport(BrowserShared::VIVALDI));
|
||||||
m_ui->torBrowserSupport->setChecked(settings->torBrowserSupport());
|
m_ui->torBrowserSupport->setChecked(settings->browserSupport(BrowserShared::TOR_BROWSER));
|
||||||
#endif
|
#endif
|
||||||
#ifndef Q_OS_LINUX
|
#ifndef Q_OS_LINUX
|
||||||
m_ui->snapWarningLabel->setVisible(false);
|
m_ui->snapWarningLabel->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: Enable when Linux version is released
|
// TODO: Enable Edge support when Linux version is released
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
m_ui->edgeSupport->setChecked(false);
|
m_ui->edgeSupport->setChecked(false);
|
||||||
m_ui->edgeSupport->setEnabled(false);
|
m_ui->edgeSupport->setEnabled(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(KEEPASSXC_DIST_APPIMAGE)
|
#ifdef KEEPASSXC_DIST_SNAP
|
||||||
m_ui->supportBrowserProxy->setChecked(true);
|
|
||||||
m_ui->supportBrowserProxy->setEnabled(false);
|
|
||||||
#elif defined(KEEPASSXC_DIST_SNAP)
|
|
||||||
// Disable settings that will not work
|
// Disable settings that will not work
|
||||||
m_ui->supportBrowserProxy->setChecked(true);
|
|
||||||
m_ui->supportBrowserProxy->setEnabled(false);
|
|
||||||
m_ui->useCustomProxy->setChecked(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->setVisible(false);
|
||||||
m_ui->browsersGroupBox->setEnabled(false);
|
m_ui->browsersGroupBox->setEnabled(false);
|
||||||
m_ui->updateBinaryPath->setChecked(false);
|
m_ui->updateBinaryPath->setChecked(false);
|
||||||
m_ui->updateBinaryPath->setEnabled(false);
|
m_ui->updateBinaryPath->setVisible(false);
|
||||||
// Show notice to user
|
// Show notice to user
|
||||||
m_ui->browserGlobalWarningWidget->showMessage(tr("Please see special instructions for browser extension use below"),
|
m_ui->browserGlobalWarningWidget->showMessage(tr("Please see special instructions for browser extension use below"),
|
||||||
MessageWidget::Warning);
|
MessageWidget::Warning);
|
||||||
@ -160,23 +152,23 @@ void BrowserOptionDialog::loadSettings()
|
|||||||
m_ui->browserGlobalWarningWidget->setAutoHideTimeout(-1);
|
m_ui->browserGlobalWarningWidget->setAutoHideTimeout(-1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Check for native messaging host location errors
|
validateCustomProxyLocation();
|
||||||
QString path;
|
}
|
||||||
if (!settings->checkIfProxyExists(path)) {
|
|
||||||
auto text =
|
void BrowserSettingsWidget::validateCustomProxyLocation()
|
||||||
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."
|
auto path = m_ui->customProxyLocation->text();
|
||||||
"<br />Browser integration WILL NOT WORK without the proxy application."
|
if (m_ui->useCustomProxy->isChecked() && !QFile::exists(path)) {
|
||||||
"<br />Expected Path: %1")
|
m_ui->warningWidget->showMessage(tr("<b>Error:</b> The custom proxy location cannot be found!"
|
||||||
.arg(path);
|
"<br/>Browser integration WILL NOT WORK without the proxy application."),
|
||||||
m_ui->scriptWarningWidget->showMessage(text, MessageWidget::Warning);
|
MessageWidget::Error);
|
||||||
m_ui->scriptWarningWidget->setVisible(true);
|
|
||||||
} else {
|
} 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();
|
auto settings = browserSettings();
|
||||||
settings->setEnabled(m_ui->enableBrowserSupport->isChecked());
|
settings->setEnabled(m_ui->enableBrowserSupport->isChecked());
|
||||||
@ -186,7 +178,6 @@ void BrowserOptionDialog::saveSettings()
|
|||||||
settings->setMatchUrlScheme(m_ui->matchUrlScheme->isChecked());
|
settings->setMatchUrlScheme(m_ui->matchUrlScheme->isChecked());
|
||||||
settings->setSortByUsername(m_ui->sortByUsername->isChecked());
|
settings->setSortByUsername(m_ui->sortByUsername->isChecked());
|
||||||
|
|
||||||
settings->setSupportBrowserProxy(m_ui->supportBrowserProxy->isChecked());
|
|
||||||
settings->setUseCustomProxy(m_ui->useCustomProxy->isChecked());
|
settings->setUseCustomProxy(m_ui->useCustomProxy->isChecked());
|
||||||
settings->setCustomProxyLocation(m_ui->customProxyLocation->text());
|
settings->setCustomProxyLocation(m_ui->customProxyLocation->text());
|
||||||
|
|
||||||
@ -199,18 +190,18 @@ void BrowserOptionDialog::saveSettings()
|
|||||||
settings->setSupportKphFields(m_ui->supportKphFields->isChecked());
|
settings->setSupportKphFields(m_ui->supportKphFields->isChecked());
|
||||||
settings->setNoMigrationPrompt(m_ui->noMigrationPrompt->isChecked());
|
settings->setNoMigrationPrompt(m_ui->noMigrationPrompt->isChecked());
|
||||||
|
|
||||||
settings->setChromeSupport(m_ui->chromeSupport->isChecked());
|
settings->setBrowserSupport(BrowserShared::CHROME, m_ui->chromeSupport->isChecked());
|
||||||
settings->setChromiumSupport(m_ui->chromiumSupport->isChecked());
|
settings->setBrowserSupport(BrowserShared::CHROMIUM, m_ui->chromiumSupport->isChecked());
|
||||||
settings->setFirefoxSupport(m_ui->firefoxSupport->isChecked());
|
settings->setBrowserSupport(BrowserShared::FIREFOX, m_ui->firefoxSupport->isChecked());
|
||||||
settings->setEdgeSupport(m_ui->edgeSupport->isChecked());
|
settings->setBrowserSupport(BrowserShared::EDGE, m_ui->edgeSupport->isChecked());
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
settings->setBraveSupport(m_ui->braveSupport->isChecked());
|
settings->setBrowserSupport(BrowserShared::BRAVE, m_ui->braveSupport->isChecked());
|
||||||
settings->setVivaldiSupport(m_ui->vivaldiSupport->isChecked());
|
settings->setBrowserSupport(BrowserShared::VIVALDI, m_ui->vivaldiSupport->isChecked());
|
||||||
settings->setTorBrowserSupport(m_ui->torBrowserSupport->isChecked());
|
settings->setBrowserSupport(BrowserShared::TOR_BROWSER, m_ui->torBrowserSupport->isChecked());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserOptionDialog::showProxyLocationFileDialog()
|
void BrowserSettingsWidget::showProxyLocationFileDialog()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QString fileTypeFilter(QString("%1 (*.exe);;%2 (*.*)").arg(tr("Executable Files"), tr("All Files")));
|
QString fileTypeFilter(QString("%1 (*.exe);;%2 (*.*)").arg(tr("Executable Files"), tr("All Files")));
|
||||||
@ -222,4 +213,5 @@ void BrowserOptionDialog::showProxyLocationFileDialog()
|
|||||||
QFileInfo(QCoreApplication::applicationDirPath()).filePath(),
|
QFileInfo(QCoreApplication::applicationDirPath()).filePath(),
|
||||||
fileTypeFilter);
|
fileTypeFilter);
|
||||||
m_ui->customProxyLocation->setText(proxyLocation);
|
m_ui->customProxyLocation->setText(proxyLocation);
|
||||||
|
validateCustomProxyLocation();
|
||||||
}
|
}
|
@ -1,7 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Francois Ferrand
|
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
* 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
|
* 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
|
* 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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef BROWSEROPTIONDIALOG_H
|
#ifndef BROWSERSETTINGSWIDGET_H
|
||||||
#define BROWSEROPTIONDIALOG_H
|
#define BROWSERSETTINGSWIDGET_H
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
@ -26,16 +24,16 @@
|
|||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
class BrowserOptionDialog;
|
class BrowserSettingsWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BrowserOptionDialog : public QWidget
|
class BrowserSettingsWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BrowserOptionDialog(QWidget* parent = nullptr);
|
explicit BrowserSettingsWidget(QWidget* parent = nullptr);
|
||||||
~BrowserOptionDialog();
|
~BrowserSettingsWidget();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
@ -43,9 +41,10 @@ public slots:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showProxyLocationFileDialog();
|
void showProxyLocationFileDialog();
|
||||||
|
void validateCustomProxyLocation();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QScopedPointer<Ui::BrowserOptionDialog> m_ui;
|
QScopedPointer<Ui::BrowserSettingsWidget> m_ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BROWSEROPTIONDIALOG_H
|
#endif // BROWSERSETTINGSWIDGET_H
|
24
src/browser/BrowserOptionDialog.ui → src/browser/BrowserSettingsWidget.ui
Executable file → Normal file
24
src/browser/BrowserOptionDialog.ui → src/browser/BrowserSettingsWidget.ui
Executable file → Normal file
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>BrowserOptionDialog</class>
|
<class>BrowserSettingsWidget</class>
|
||||||
<widget class="QWidget" name="BrowserOptionDialog">
|
<widget class="QWidget" name="BrowserSettingsWidget">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
@ -49,16 +49,6 @@
|
|||||||
<string>General</string>
|
<string>General</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<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>
|
<item>
|
||||||
<widget class="QLabel" name="snapWarningLabel">
|
<widget class="QLabel" name="snapWarningLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -351,16 +341,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<widget class="QCheckBox" name="useCustomProxy">
|
<widget class="QCheckBox" name="useCustomProxy">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
46
src/browser/BrowserShared.cpp
Normal file
46
src/browser/BrowserShared.cpp
Normal file
@ -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
|
42
src/browser/BrowserShared.h
Normal file
42
src/browser/BrowserShared.h
Normal file
@ -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
|
@ -20,15 +20,15 @@ if(WITH_XC_BROWSER)
|
|||||||
set(keepassxcbrowser_SOURCES
|
set(keepassxcbrowser_SOURCES
|
||||||
BrowserAccessControlDialog.cpp
|
BrowserAccessControlDialog.cpp
|
||||||
BrowserAction.cpp
|
BrowserAction.cpp
|
||||||
BrowserClients.cpp
|
|
||||||
BrowserEntryConfig.cpp
|
BrowserEntryConfig.cpp
|
||||||
BrowserEntrySaveDialog.cpp
|
BrowserEntrySaveDialog.cpp
|
||||||
BrowserOptionDialog.cpp
|
BrowserHost.cpp
|
||||||
|
BrowserSettingsPage.cpp
|
||||||
|
BrowserSettingsWidget.cpp
|
||||||
BrowserService.cpp
|
BrowserService.cpp
|
||||||
BrowserSettings.cpp
|
BrowserSettings.cpp
|
||||||
HostInstaller.cpp
|
BrowserShared.cpp
|
||||||
NativeMessagingBase.cpp
|
NativeMessageInstaller.cpp
|
||||||
NativeMessagingHost.cpp
|
|
||||||
Variant.cpp)
|
Variant.cpp)
|
||||||
|
|
||||||
add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES})
|
add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES})
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
313
src/browser/NativeMessageInstaller.cpp
Normal file
313
src/browser/NativeMessageInstaller.cpp
Normal file
@ -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;
|
||||||
|
}
|
46
src/browser/NativeMessageInstaller.h
Normal file
46
src/browser/NativeMessageInstaller.h
Normal file
@ -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
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -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);
|
|
||||||
}
|
|
@ -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
|
|
@ -44,6 +44,9 @@
|
|||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
#include "gui/osutils/macutils/MacUtils.h"
|
#include "gui/osutils/macutils/MacUtils.h"
|
||||||
|
#ifdef WITH_XC_TOUCHID
|
||||||
|
#include "touchid/TouchID.h"
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_XC_UPDATECHECK
|
#ifdef WITH_XC_UPDATECHECK
|
||||||
@ -56,7 +59,7 @@
|
|||||||
#include "sshagent/AgentSettingsPage.h"
|
#include "sshagent/AgentSettingsPage.h"
|
||||||
#include "sshagent/SSHAgent.h"
|
#include "sshagent/SSHAgent.h"
|
||||||
#endif
|
#endif
|
||||||
#if defined(WITH_XC_KEESHARE)
|
#ifdef WITH_XC_KEESHARE
|
||||||
#include "keeshare/KeeShare.h"
|
#include "keeshare/KeeShare.h"
|
||||||
#include "keeshare/SettingsPageKeeShare.h"
|
#include "keeshare/SettingsPageKeeShare.h"
|
||||||
#endif
|
#endif
|
||||||
@ -66,9 +69,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_XC_BROWSER
|
#ifdef WITH_XC_BROWSER
|
||||||
#include "browser/BrowserOptionDialog.h"
|
#include "browser/BrowserService.h"
|
||||||
#include "browser/BrowserSettings.h"
|
#include "browser/BrowserSettingsPage.h"
|
||||||
#include "browser/NativeMessagingHost.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
|
||||||
@ -77,61 +79,6 @@
|
|||||||
#include <QtDBus/QtDBus>
|
#include <QtDBus/QtDBus>
|
||||||
#endif
|
#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";
|
const QString MainWindow::BaseWindowTitle = "KeePassXC";
|
||||||
|
|
||||||
MainWindow* g_MainWindow = nullptr;
|
MainWindow* g_MainWindow = nullptr;
|
||||||
@ -186,13 +133,19 @@ MainWindow::MainWindow()
|
|||||||
restoreGeometry(config()->get(Config::GUI_MainWindowGeometry).toByteArray());
|
restoreGeometry(config()->get(Config::GUI_MainWindowGeometry).toByteArray());
|
||||||
restoreState(config()->get(Config::GUI_MainWindowState).toByteArray());
|
restoreState(config()->get(Config::GUI_MainWindowState).toByteArray());
|
||||||
#ifdef WITH_XC_BROWSER
|
#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
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_XC_SSHAGENT
|
#ifdef WITH_XC_SSHAGENT
|
||||||
connect(sshAgent(), SIGNAL(error(QString)), this, SLOT(showErrorMessage(QString)));
|
connect(sshAgent(), SIGNAL(error(QString)), this, SLOT(showErrorMessage(QString)));
|
||||||
connect(sshAgent(), SIGNAL(enabledChanged(bool)), this, SLOT(agentEnabled(bool)));
|
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->addSeparator();
|
||||||
m_entryContextMenu->addAction(m_ui->actionEntryAddToAgent);
|
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)
|
void MainWindow::showErrorMessage(const QString& message)
|
||||||
{
|
{
|
||||||
m_ui->globalMessageWidget->showMessage(message, MessageWidget::Error);
|
m_ui->globalMessageWidget->showMessage(message, MessageWidget::Error);
|
||||||
|
@ -48,6 +48,8 @@ public:
|
|||||||
MainWindow();
|
MainWindow();
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
|
QList<DatabaseWidget*> getOpenDatabases();
|
||||||
|
|
||||||
enum StackedWidgetIndex
|
enum StackedWidgetIndex
|
||||||
{
|
{
|
||||||
DatabaseTabScreen = 0,
|
DatabaseTabScreen = 0,
|
||||||
|
@ -34,7 +34,6 @@ DatabaseSettingsWidgetBrowser::DatabaseSettingsWidgetBrowser(QWidget* parent)
|
|||||||
, m_ui(new Ui::DatabaseSettingsWidgetBrowser())
|
, m_ui(new Ui::DatabaseSettingsWidgetBrowser())
|
||||||
, m_customData(new CustomData(this))
|
, m_customData(new CustomData(this))
|
||||||
, m_customDataModel(new QStandardItemModel(this))
|
, m_customDataModel(new QStandardItemModel(this))
|
||||||
, m_browserService(nullptr)
|
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
m_ui->removeCustomDataButton->setEnabled(false);
|
m_ui->removeCustomDataButton->setEnabled(false);
|
||||||
@ -254,7 +253,7 @@ void DatabaseSettingsWidgetBrowser::convertAttributesToCustomData()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_browserService.convertAttributesToCustomData(m_db);
|
BrowserService::convertAttributesToCustomData(m_db);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseSettingsWidgetBrowser::refreshDatabaseID()
|
void DatabaseSettingsWidgetBrowser::refreshDatabaseID()
|
||||||
|
@ -77,7 +77,6 @@ protected:
|
|||||||
private:
|
private:
|
||||||
QPointer<CustomData> m_customData;
|
QPointer<CustomData> m_customData;
|
||||||
QPointer<QStandardItemModel> m_customDataModel;
|
QPointer<QStandardItemModel> m_customDataModel;
|
||||||
BrowserService m_browserService;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H
|
#endif // KEEPASSXC_DATABASESETTINGSWIDGETBROWSER_H
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
|
# Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
# Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
if(WITH_XC_BROWSER)
|
if(WITH_XC_BROWSER)
|
||||||
include_directories(${BROWSER_SOURCE_DIR})
|
|
||||||
|
|
||||||
set(proxy_SOURCES
|
set(proxy_SOURCES
|
||||||
../core/Alloc.cpp
|
../browser/BrowserShared.cpp
|
||||||
keepassxc-proxy.cpp
|
keepassxc-proxy.cpp
|
||||||
${BROWSER_SOURCE_DIR}/NativeMessagingBase.cpp
|
NativeMessagingProxy.cpp)
|
||||||
NativeMessagingHost.cpp)
|
|
||||||
|
|
||||||
add_library(proxy STATIC ${proxy_SOURCES})
|
# Alloc must be defined in a static library to prevent clashing with clang ASAN definitions
|
||||||
target_link_libraries(proxy Qt5::Core Qt5::Network ${sodium_LIBRARY_RELEASE})
|
add_library(proxy_alloc STATIC ../core/Alloc.cpp)
|
||||||
add_executable(keepassxc-proxy keepassxc-proxy.cpp)
|
target_link_libraries(proxy_alloc PRIVATE Qt5::Core ${sodium_LIBRARY_RELEASE})
|
||||||
target_link_libraries(keepassxc-proxy proxy)
|
|
||||||
|
|
||||||
|
add_executable(keepassxc-proxy ${proxy_SOURCES})
|
||||||
|
target_link_libraries(keepassxc-proxy proxy_alloc Qt5::Core Qt5::Network)
|
||||||
install(TARGETS keepassxc-proxy
|
install(TARGETS keepassxc-proxy
|
||||||
BUNDLE DESTINATION . COMPONENT Runtime
|
BUNDLE DESTINATION . COMPONENT Runtime
|
||||||
RUNTIME DESTINATION ${PROXY_INSTALL_DIR} 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
|
COMMAND ${CMAKE_COMMAND} -E copy keepassxc-proxy ${PROXY_APP_DIR}/keepassxc-proxy
|
||||||
COMMENT "Copying keepassxc-proxy inside the application")
|
COMMENT "Copying keepassxc-proxy inside the application")
|
||||||
endif()
|
endif()
|
||||||
if(MINGW)
|
|
||||||
target_link_libraries(keepassxc-proxy Wtsapi32.lib Ws2_32.lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
110
src/proxy/NativeMessagingProxy.cpp
Normal file
110
src/proxy/NativeMessagingProxy.cpp
Normal file
@ -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();
|
||||||
|
}
|
53
src/proxy/NativeMessagingProxy.h
Normal file
53
src/proxy/NativeMessagingProxy.h
Normal file
@ -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
|
@ -16,8 +16,9 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "NativeMessagingHost.h"
|
#include "NativeMessagingProxy.h"
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
@ -79,6 +80,6 @@ int main(int argc, char* argv[])
|
|||||||
#else
|
#else
|
||||||
SetConsoleCtrlHandler(static_cast<PHANDLER_ROUTINE>(ConsoleHandler), TRUE);
|
SetConsoleCtrlHandler(static_cast<PHANDLER_ROUTINE>(ConsoleHandler), TRUE);
|
||||||
#endif
|
#endif
|
||||||
NativeMessagingHost host;
|
NativeMessagingProxy proxy;
|
||||||
return a.exec();
|
return a.exec();
|
||||||
}
|
}
|
||||||
|
@ -20,15 +20,6 @@
|
|||||||
#include "AgentSettingsWidget.h"
|
#include "AgentSettingsWidget.h"
|
||||||
#include "core/Resources.h"
|
#include "core/Resources.h"
|
||||||
|
|
||||||
AgentSettingsPage::AgentSettingsPage(DatabaseTabWidget* tabWidget)
|
|
||||||
{
|
|
||||||
Q_UNUSED(tabWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
AgentSettingsPage::~AgentSettingsPage()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AgentSettingsPage::name()
|
QString AgentSettingsPage::name()
|
||||||
{
|
{
|
||||||
return QObject::tr("SSH Agent");
|
return QObject::tr("SSH Agent");
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
class AgentSettingsPage : public ISettingsPage
|
class AgentSettingsPage : public ISettingsPage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AgentSettingsPage(DatabaseTabWidget* tabWidget);
|
AgentSettingsPage() = default;
|
||||||
~AgentSettingsPage() override;
|
~AgentSettingsPage() override = default;
|
||||||
|
|
||||||
QString name() override;
|
QString name() override;
|
||||||
QIcon icon() override;
|
QIcon icon() override;
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "TestBrowser.h"
|
#include "TestBrowser.h"
|
||||||
|
|
||||||
#include "TestGlobal.h"
|
#include "TestGlobal.h"
|
||||||
#include "browser/BrowserSettings.h"
|
#include "browser/BrowserSettings.h"
|
||||||
#include "core/Tools.h"
|
#include "core/Tools.h"
|
||||||
#include "crypto/Crypto.h"
|
#include "crypto/Crypto.h"
|
||||||
#include "sodium/crypto_box.h"
|
#include "sodium/crypto_box.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(TestBrowser)
|
QTEST_GUILESS_MAIN(TestBrowser)
|
||||||
@ -35,12 +37,12 @@ const QString CLIENTID = "testClient";
|
|||||||
void TestBrowser::initTestCase()
|
void TestBrowser::initTestCase()
|
||||||
{
|
{
|
||||||
QVERIFY(Crypto::init());
|
QVERIFY(Crypto::init());
|
||||||
m_browserService.reset(new BrowserService(nullptr));
|
m_browserService = browserService();
|
||||||
m_browserAction.reset(new BrowserAction(*m_browserService.data()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestBrowser::cleanupTestCase()
|
void TestBrowser::init()
|
||||||
{
|
{
|
||||||
|
m_browserAction.reset(new BrowserAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +56,7 @@ void TestBrowser::testChangePublicKeys()
|
|||||||
json["publicKey"] = PUBLICKEY;
|
json["publicKey"] = PUBLICKEY;
|
||||||
json["nonce"] = NONCE;
|
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["action"].toString(), QString("change-public-keys"));
|
||||||
QCOMPARE(response["publicKey"].toString() == PUBLICKEY, false);
|
QCOMPARE(response["publicKey"].toString() == PUBLICKEY, false);
|
||||||
QCOMPARE(response["success"].toString(), TRUE_STR);
|
QCOMPARE(response["success"].toString(), TRUE_STR);
|
||||||
@ -393,62 +395,6 @@ void TestBrowser::testSortEntries()
|
|||||||
QCOMPARE(result[3]->url(), QString("github.com/login"));
|
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*> TestBrowser::createEntries(QStringList& urls, Group* root) const
|
||||||
{
|
{
|
||||||
QList<Entry*> entries;
|
QList<Entry*> entries;
|
||||||
|
@ -30,7 +30,7 @@ class TestBrowser : public QObject
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void cleanupTestCase();
|
void init();
|
||||||
|
|
||||||
void testChangePublicKeys();
|
void testChangePublicKeys();
|
||||||
void testEncryptMessage();
|
void testEncryptMessage();
|
||||||
@ -46,14 +46,13 @@ private slots:
|
|||||||
void testInvalidEntries();
|
void testInvalidEntries();
|
||||||
void testSubdomainsAndPaths();
|
void testSubdomainsAndPaths();
|
||||||
void testSortEntries();
|
void testSortEntries();
|
||||||
void testGetDatabaseGroups();
|
|
||||||
void testValidURLs();
|
void testValidURLs();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<Entry*> createEntries(QStringList& urls, Group* root) const;
|
QList<Entry*> createEntries(QStringList& urls, Group* root) const;
|
||||||
|
|
||||||
QScopedPointer<BrowserAction> m_browserAction;
|
QScopedPointer<BrowserAction> m_browserAction;
|
||||||
QScopedPointer<BrowserService> m_browserService;
|
QPointer<BrowserService> m_browserService;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_TESTBROWSER_H
|
#endif // KEEPASSXC_TESTBROWSER_H
|
||||||
|
Binary file not shown.
@ -31,6 +31,7 @@
|
|||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
|
|
||||||
|
#include "browser/BrowserService.h"
|
||||||
#include "config-keepassx-tests.h"
|
#include "config-keepassx-tests.h"
|
||||||
#include "core/Bootstrap.h"
|
#include "core/Bootstrap.h"
|
||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
@ -82,30 +83,19 @@ void TestGuiBrowser::initTestCase()
|
|||||||
Bootstrap::restoreMainWindowState(*m_mainWindow);
|
Bootstrap::restoreMainWindowState(*m_mainWindow);
|
||||||
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
|
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
|
||||||
m_mainWindow->show();
|
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
|
// Every test starts with opening the temp database
|
||||||
void TestGuiBrowser::init()
|
void TestGuiBrowser::init()
|
||||||
{
|
{
|
||||||
m_dbFile.reset(new TemporaryFile());
|
m_dbFile.reset(new TemporaryFile());
|
||||||
// Write the temp storage to a temp database file for use in our tests
|
m_dbFile->copyFromFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabaseBrowser.kdbx"));
|
||||||
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();
|
|
||||||
|
|
||||||
// make sure window is activated or focus tests may fail
|
// make sure window is activated or focus tests may fail
|
||||||
m_mainWindow->activateWindow();
|
m_mainWindow->activateWindow();
|
||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
fileDialog()->setNextFileName(m_dbFilePath);
|
fileDialog()->setNextFileName(m_dbFile->fileName());
|
||||||
triggerAction("actionDatabaseOpen");
|
triggerAction("actionDatabaseOpen");
|
||||||
|
|
||||||
auto* databaseOpenWidget = m_tabWidget->currentDatabaseWidget()->findChild<QWidget*>("databaseOpenWidget");
|
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)
|
void TestGuiBrowser::triggerAction(const QString& name)
|
||||||
{
|
{
|
||||||
auto* action = m_mainWindow->findChild<QAction*>(name);
|
auto* action = m_mainWindow->findChild<QAction*>(name);
|
||||||
|
@ -45,6 +45,7 @@ private slots:
|
|||||||
|
|
||||||
void testEntrySettings();
|
void testEntrySettings();
|
||||||
void testAdditionalURLs();
|
void testAdditionalURLs();
|
||||||
|
void testGetDatabaseGroups();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void triggerAction(const QString& name);
|
void triggerAction(const QString& name);
|
||||||
@ -57,10 +58,7 @@ private:
|
|||||||
QPointer<DatabaseTabWidget> m_tabWidget;
|
QPointer<DatabaseTabWidget> m_tabWidget;
|
||||||
QPointer<DatabaseWidget> m_dbWidget;
|
QPointer<DatabaseWidget> m_dbWidget;
|
||||||
QSharedPointer<Database> m_db;
|
QSharedPointer<Database> m_db;
|
||||||
QByteArray m_dbData;
|
|
||||||
QScopedPointer<TemporaryFile> m_dbFile;
|
QScopedPointer<TemporaryFile> m_dbFile;
|
||||||
QString m_dbFileName;
|
|
||||||
QString m_dbFilePath;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSXC_TESTGUIBROWSER_H
|
#endif // KEEPASSXC_TESTGUIBROWSER_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user