Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
@@ -200,10 +200,6 @@ add_feature_info(KeePassXC-Browser WITH_XC_BROWSER "Browser integration with Kee
|
||||
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
|
||||
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
||||
|
||||
if(WITH_XC_NETWORKING)
|
||||
find_package(CURL REQUIRED)
|
||||
endif()
|
||||
|
||||
set(BROWSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/browser)
|
||||
add_subdirectory(browser)
|
||||
add_subdirectory(proxy)
|
||||
@@ -276,7 +272,7 @@ if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(keepassx_core Qt5::DBus)
|
||||
endif()
|
||||
if(MINGW)
|
||||
target_link_libraries(keepassx_core Wtsapi32.lib)
|
||||
target_link_libraries(keepassx_core Wtsapi32.lib Ws2_32.lib)
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
|
||||
@@ -220,7 +220,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
#endif
|
||||
}
|
||||
|
||||
Tools::wait(m_plugin->initialTimeout());
|
||||
Tools::wait(qMax(100, config()->get("AutoTypeStartDelay", 500).toInt()));
|
||||
|
||||
if (!window) {
|
||||
window = m_plugin->activeWindow();
|
||||
@@ -364,7 +364,7 @@ bool AutoType::parseActions(const QString& actionSequence, const Entry* entry, Q
|
||||
{
|
||||
QString tmpl;
|
||||
bool inTmpl = false;
|
||||
m_autoTypeDelay = config()->get("AutoTypeDelay").toInt();
|
||||
m_autoTypeDelay = qMax(config()->get("AutoTypeDelay").toInt(), 0);
|
||||
|
||||
QString sequence = actionSequence;
|
||||
sequence.replace("{{}", "{LEFTBRACE}");
|
||||
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
virtual bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) = 0;
|
||||
virtual void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) = 0;
|
||||
virtual int platformEventFilter(void* event) = 0;
|
||||
virtual int initialTimeout() = 0;
|
||||
virtual bool raiseWindow(WId window) = 0;
|
||||
virtual void unload()
|
||||
{
|
||||
|
||||
@@ -154,11 +154,6 @@ AutoTypeExecutor* AutoTypePlatformMac::createExecutor()
|
||||
return new AutoTypeExecutorMac(this);
|
||||
}
|
||||
|
||||
int AutoTypePlatformMac::initialTimeout()
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
|
||||
//
|
||||
// Activate window by process id
|
||||
//
|
||||
|
||||
@@ -42,7 +42,6 @@ public:
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
int initialTimeout() override;
|
||||
bool raiseWindow(WId pid) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
|
||||
@@ -103,11 +103,6 @@ void AutoTypePlatformTest::addActionKey(AutoTypeKey* action)
|
||||
m_actionChars.append(keyToString(action->key));
|
||||
}
|
||||
|
||||
int AutoTypePlatformTest::initialTimeout()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AutoTypePlatformTest::raiseWindow(WId window)
|
||||
{
|
||||
Q_UNUSED(window);
|
||||
|
||||
@@ -40,7 +40,6 @@ public:
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
int initialTimeout() override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
|
||||
@@ -109,11 +109,6 @@ AutoTypeExecutor* AutoTypePlatformWin::createExecutor()
|
||||
return new AutoTypeExecutorWin(this);
|
||||
}
|
||||
|
||||
int AutoTypePlatformWin::initialTimeout()
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
|
||||
//
|
||||
// Set foreground window
|
||||
//
|
||||
|
||||
@@ -39,7 +39,6 @@ public:
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
int initialTimeout() override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
|
||||
@@ -830,11 +830,6 @@ void AutoTypeExecutorX11::execClearField(AutoTypeClearField* action = nullptr)
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
|
||||
int AutoTypePlatformX11::initialTimeout()
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
|
||||
bool AutoTypePlatformX11::raiseWindow(WId window)
|
||||
{
|
||||
if (m_atomNetActiveWindow == None) {
|
||||
|
||||
@@ -51,7 +51,6 @@ public:
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
int initialTimeout() override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "BrowserAction.h"
|
||||
#include "BrowserSettings.h"
|
||||
#include "NativeMessagingBase.h"
|
||||
#include "config-keepassx.h"
|
||||
#include "sodium.h"
|
||||
#include "sodium/crypto_box.h"
|
||||
@@ -237,9 +238,17 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
|
||||
return getErrorReply(action, ERROR_KEEPASS_NO_URL_PROVIDED);
|
||||
}
|
||||
|
||||
const QJsonArray keys = decrypted.value("keys").toArray();
|
||||
|
||||
StringPairList keyList;
|
||||
for (const QJsonValue val : keys) {
|
||||
const QJsonObject keyObject = val.toObject();
|
||||
keyList.push_back(qMakePair(keyObject.value("id").toString(), keyObject.value("key").toString()));
|
||||
}
|
||||
|
||||
const QString id = decrypted.value("id").toString();
|
||||
const QString submit = decrypted.value("submitUrl").toString();
|
||||
const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "");
|
||||
const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "", keyList);
|
||||
|
||||
if (users.isEmpty()) {
|
||||
return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND);
|
||||
@@ -472,7 +481,7 @@ QString BrowserAction::encrypt(const QString plaintext, const QString nonce)
|
||||
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||
|
||||
std::vector<unsigned char> e;
|
||||
e.resize(max_length);
|
||||
e.resize(NATIVE_MSG_MAX_LENGTH);
|
||||
|
||||
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||
return QString();
|
||||
@@ -500,7 +509,7 @@ QByteArray BrowserAction::decrypt(const QString encrypted, const QString nonce)
|
||||
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||
|
||||
std::vector<unsigned char> d;
|
||||
d.resize(max_length);
|
||||
d.resize(NATIVE_MSG_MAX_LENGTH);
|
||||
|
||||
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||
return QByteArray();
|
||||
|
||||
@@ -68,7 +68,6 @@ void BrowserOptionDialog::loadSettings()
|
||||
// hide unimplemented options
|
||||
// TODO: fix this
|
||||
m_ui->showNotification->hide();
|
||||
m_ui->bestMatchOnly->hide();
|
||||
|
||||
if (settings.sortByUsername()) {
|
||||
m_ui->sortByUsername->setChecked(true);
|
||||
|
||||
@@ -47,6 +47,7 @@ static int KEEPASSXCBROWSER_DEFAULT_ICON = 1;
|
||||
BrowserService::BrowserService(DatabaseTabWidget* parent)
|
||||
: m_dbTabWidget(parent)
|
||||
, m_dialogActive(false)
|
||||
, m_bringToFrontRequested(false)
|
||||
{
|
||||
connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*)));
|
||||
connect(m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*)));
|
||||
@@ -83,6 +84,7 @@ bool BrowserService::openDatabase(bool triggerUnlock)
|
||||
|
||||
if (triggerUnlock) {
|
||||
KEEPASSXC_MAIN_WINDOW->bringToFront();
|
||||
m_bringToFrontRequested = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -228,7 +230,8 @@ QString BrowserService::getKey(const QString& id)
|
||||
QJsonArray BrowserService::findMatchingEntries(const QString& id,
|
||||
const QString& url,
|
||||
const QString& submitUrl,
|
||||
const QString& realm)
|
||||
const QString& realm,
|
||||
const StringPairList& keyList)
|
||||
{
|
||||
QJsonArray result;
|
||||
if (thread() != QThread::currentThread()) {
|
||||
@@ -239,7 +242,8 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
|
||||
Q_ARG(const QString&, id),
|
||||
Q_ARG(const QString&, url),
|
||||
Q_ARG(const QString&, submitUrl),
|
||||
Q_ARG(const QString&, realm));
|
||||
Q_ARG(const QString&, realm),
|
||||
Q_ARG(const StringPairList&, keyList));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -250,7 +254,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
|
||||
// Check entries for authorization
|
||||
QList<Entry*> pwEntriesToConfirm;
|
||||
QList<Entry*> pwEntries;
|
||||
for (Entry* entry : searchEntries(url)) {
|
||||
for (Entry* entry : searchEntries(url, keyList)) {
|
||||
switch (checkAccess(entry, host, submitHost, realm)) {
|
||||
case Denied:
|
||||
continue;
|
||||
@@ -356,15 +360,19 @@ void BrowserService::updateEntry(const QString& id,
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.compare(login, Qt::CaseSensitive) != 0
|
||||
|| entry->password().compare(password, Qt::CaseSensitive) != 0) {
|
||||
QMessageBox::StandardButton dialogResult = QMessageBox::No;
|
||||
if (username.compare(login, Qt::CaseSensitive) != 0 || entry->password().compare(password, Qt::CaseSensitive) != 0) {
|
||||
int dialogResult = QMessageBox::No;
|
||||
if (!BrowserSettings::alwaysAllowUpdate()) {
|
||||
dialogResult = QMessageBox::warning(
|
||||
0,
|
||||
tr("KeePassXC: Update Entry"),
|
||||
tr("Do you want to update the information in %1 - %2?").arg(QUrl(url).host()).arg(username),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("KeePassXC: Update Entry"));
|
||||
msgBox.setText(tr("Do you want to update the information in %1 - %2?").arg(QUrl(url).host()).arg(username));
|
||||
msgBox.setStandardButtons(QMessageBox::Yes);
|
||||
msgBox.addButton(QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::No);
|
||||
msgBox.setWindowFlags(Qt::WindowStaysOnTopHint);
|
||||
msgBox.activateWindow();
|
||||
msgBox.raise();
|
||||
dialogResult = msgBox.exec();
|
||||
}
|
||||
|
||||
if (BrowserSettings::alwaysAllowUpdate() || dialogResult == QMessageBox::Yes) {
|
||||
@@ -399,7 +407,7 @@ QList<Entry*> BrowserService::searchEntries(Database* db, const QString& hostnam
|
||||
return entries;
|
||||
}
|
||||
|
||||
QList<Entry*> BrowserService::searchEntries(const QString& text)
|
||||
QList<Entry*> BrowserService::searchEntries(const QString& text, const StringPairList& keyList)
|
||||
{
|
||||
// Get the list of databases to search
|
||||
QList<Database*> databases;
|
||||
@@ -408,7 +416,16 @@ QList<Entry*> BrowserService::searchEntries(const QString& text)
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (DatabaseWidget* dbWidget = qobject_cast<DatabaseWidget*>(m_dbTabWidget->widget(i))) {
|
||||
if (Database* db = dbWidget->database()) {
|
||||
databases << db;
|
||||
// Check if database is connected with KeePassXC-Browser
|
||||
for (const StringPair keyPair : keyList) {
|
||||
Entry* entry = db->resolveEntry(KEEPASSXCBROWSER_UUID);
|
||||
if (entry) {
|
||||
QString key = entry->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first);
|
||||
if (!key.isEmpty() && keyPair.second == key) {
|
||||
databases << db;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,21 +554,30 @@ QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QStrin
|
||||
const QString baseSubmitUrl =
|
||||
url.toString(QUrl::StripTrailingSlash | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment);
|
||||
|
||||
QMultiMap<int, const Entry*> priorities;
|
||||
for (const Entry* entry : pwEntries) {
|
||||
// Build map of prioritized entries
|
||||
QMultiMap<int, Entry*> priorities;
|
||||
for (Entry* entry : pwEntries) {
|
||||
priorities.insert(sortPriority(entry, host, submitUrl, baseSubmitUrl), entry);
|
||||
}
|
||||
|
||||
QList<Entry*> results;
|
||||
QString field = BrowserSettings::sortByTitle() ? "Title" : "UserName";
|
||||
std::sort(pwEntries.begin(), pwEntries.end(), [&priorities, &field](const Entry* left, const Entry* right) {
|
||||
int res = priorities.key(left) - priorities.key(right);
|
||||
if (res == 0) {
|
||||
return QString::localeAwareCompare(left->attributes()->value(field), right->attributes()->value(field)) < 0;
|
||||
for (int i = 100; i >= 0; i -= 5) {
|
||||
if (priorities.count(i) > 0) {
|
||||
// Sort same priority entries by Title or UserName
|
||||
auto entries = priorities.values(i);
|
||||
std::sort(entries.begin(), entries.end(), [&priorities, &field](Entry* left, Entry* right) {
|
||||
return QString::localeAwareCompare(left->attributes()->value(field), right->attributes()->value(field)) < 0;
|
||||
});
|
||||
results << entries;
|
||||
if (BrowserSettings::bestMatchOnly() && !pwEntries.isEmpty()) {
|
||||
// Early out once we find the highest batch of matches
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res < 0;
|
||||
});
|
||||
}
|
||||
|
||||
return pwEntries;
|
||||
return results;
|
||||
}
|
||||
|
||||
bool BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
||||
@@ -607,6 +633,10 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
|
||||
res["name"] = entry->resolveMultiplePlaceholders(entry->title());
|
||||
res["uuid"] = entry->resolveMultiplePlaceholders(entry->uuid().toHex());
|
||||
|
||||
if (entry->hasTotp()) {
|
||||
res["totp"] = entry->totp();
|
||||
}
|
||||
|
||||
if (BrowserSettings::supportKphFields()) {
|
||||
const EntryAttributes* attr = entry->attributes();
|
||||
QJsonArray stringFields;
|
||||
@@ -762,6 +792,10 @@ void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
|
||||
void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget)
|
||||
{
|
||||
if (dbWidget) {
|
||||
if (m_bringToFrontRequested) {
|
||||
KEEPASSXC_MAIN_WINDOW->lower();
|
||||
m_bringToFrontRequested = false;
|
||||
}
|
||||
emit databaseUnlocked();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include <QObject>
|
||||
#include <QtCore>
|
||||
|
||||
typedef QPair<QString, QString> StringPair;
|
||||
typedef QList<StringPair> StringPairList;
|
||||
|
||||
enum
|
||||
{
|
||||
max_length = 16 * 1024
|
||||
@@ -50,13 +53,16 @@ public:
|
||||
const QString& submitUrl,
|
||||
const QString& realm);
|
||||
QList<Entry*> searchEntries(Database* db, const QString& hostname);
|
||||
QList<Entry*> searchEntries(const QString& text);
|
||||
QList<Entry*> searchEntries(const QString& text, const StringPairList& keyList);
|
||||
void removeSharedEncryptionKeys();
|
||||
void removeStoredPermissions();
|
||||
|
||||
public slots:
|
||||
QJsonArray
|
||||
findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm);
|
||||
QJsonArray findMatchingEntries(const QString& id,
|
||||
const QString& url,
|
||||
const QString& submitUrl,
|
||||
const QString& realm,
|
||||
const StringPairList& keyList);
|
||||
QString storeKey(const QString& key);
|
||||
void updateEntry(const QString& id,
|
||||
const QString& uuid,
|
||||
@@ -100,6 +106,7 @@ private:
|
||||
private:
|
||||
DatabaseTabWidget* const m_dbTabWidget;
|
||||
bool m_dialogActive;
|
||||
bool m_bringToFrontRequested;
|
||||
};
|
||||
|
||||
#endif // BROWSERSERVICE_H
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
|
||||
static bool showNotification(); // TODO!!
|
||||
static void setShowNotification(bool showNotification);
|
||||
static bool bestMatchOnly(); // TODO!!
|
||||
static bool bestMatchOnly();
|
||||
static void setBestMatchOnly(bool bestMatchOnly);
|
||||
static bool unlockDatabase();
|
||||
static void setUnlockDatabase(bool unlockDatabase);
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
static void setAlwaysAllowAccess(bool alwaysAllowAccess);
|
||||
static bool alwaysAllowUpdate();
|
||||
static void setAlwaysAllowUpdate(bool alwaysAllowUpdate);
|
||||
static bool searchInAllDatabases(); // TODO!!
|
||||
static bool searchInAllDatabases();
|
||||
static void setSearchInAllDatabases(bool searchInAllDatabases);
|
||||
static bool supportKphFields();
|
||||
static void setSupportKphFields(bool supportKphFields);
|
||||
|
||||
@@ -36,14 +36,17 @@
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
NativeMessagingBase::NativeMessagingBase()
|
||||
NativeMessagingBase::NativeMessagingBase(const bool enabled)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
Q_UNUSED(enabled);
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#else
|
||||
m_notifier.reset(new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this));
|
||||
connect(m_notifier.data(), SIGNAL(activated(int)), this, SLOT(newNativeMessage()));
|
||||
if (enabled) {
|
||||
m_notifier.reset(new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this));
|
||||
connect(m_notifier.data(), SIGNAL(activated(int)), this, SLOT(newNativeMessage()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,19 @@
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
static const int NATIVE_MSG_MAX_LENGTH = 1024*1024;
|
||||
|
||||
class NativeMessagingBase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NativeMessagingBase();
|
||||
explicit NativeMessagingBase(const bool enabled);
|
||||
~NativeMessagingBase() = default;
|
||||
|
||||
protected slots:
|
||||
|
||||
@@ -23,8 +23,12 @@
|
||||
#include <QtNetwork>
|
||||
#include <iostream>
|
||||
|
||||
NativeMessagingHost::NativeMessagingHost(DatabaseTabWidget* parent)
|
||||
: NativeMessagingBase()
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Winsock2.h>
|
||||
#endif
|
||||
|
||||
NativeMessagingHost::NativeMessagingHost(DatabaseTabWidget* parent, const bool enabled)
|
||||
: NativeMessagingBase(enabled)
|
||||
, m_mutex(QMutex::Recursive)
|
||||
, m_browserClients(m_browserService)
|
||||
, m_browserService(parent)
|
||||
@@ -75,6 +79,11 @@ void NativeMessagingHost::run()
|
||||
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();
|
||||
}
|
||||
@@ -144,11 +153,17 @@ void NativeMessagingHost::newLocalConnection()
|
||||
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;
|
||||
|
||||
@@ -31,7 +31,7 @@ class NativeMessagingHost : public NativeMessagingBase
|
||||
typedef QList<QLocalSocket*> SocketList;
|
||||
|
||||
public:
|
||||
explicit NativeMessagingHost(DatabaseTabWidget* parent = 0);
|
||||
explicit NativeMessagingHost(DatabaseTabWidget* parent = 0, const bool enabled = false);
|
||||
~NativeMessagingHost();
|
||||
int init();
|
||||
void run();
|
||||
|
||||
@@ -135,6 +135,7 @@ void Config::init(const QString& fileName)
|
||||
m_defaults.insert("AutoTypeEntryTitleMatch", true);
|
||||
m_defaults.insert("AutoTypeEntryURLMatch", true);
|
||||
m_defaults.insert("AutoTypeDelay", 25);
|
||||
m_defaults.insert("AutoTypeStartDelay", 500);
|
||||
m_defaults.insert("UseGroupIconOnEntryCreation", true);
|
||||
m_defaults.insert("IgnoreGroupExpansion", true);
|
||||
m_defaults.insert("security/clearclipboard", true);
|
||||
|
||||
@@ -197,7 +197,7 @@ void DetailsWidget::updateEntryNotesTab()
|
||||
Q_ASSERT(m_currentEntry);
|
||||
const QString notes = m_currentEntry->notes();
|
||||
setTabEnabled(m_ui->entryTabWidget, m_ui->entryNotesTab, !notes.isEmpty());
|
||||
m_ui->entryNotesEdit->setText(m_currentEntry->resolveMultiplePlaceholders(notes));
|
||||
m_ui->entryNotesEdit->setText(notes);
|
||||
}
|
||||
|
||||
void DetailsWidget::updateEntryAttributesTab()
|
||||
|
||||
@@ -31,9 +31,7 @@
|
||||
#include "gui/MessageBox.h"
|
||||
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
#include "core/AsyncTask.h"
|
||||
#include <curl/curl.h>
|
||||
#undef MessageBox
|
||||
#include <QtNetwork>
|
||||
#endif
|
||||
|
||||
IconStruct::IconStruct()
|
||||
@@ -42,10 +40,31 @@ IconStruct::IconStruct()
|
||||
{
|
||||
}
|
||||
|
||||
UrlFetchProgressDialog::UrlFetchProgressDialog(const QUrl &url, QWidget *parent)
|
||||
: QProgressDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Download Progress"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setLabelText(tr("Downloading %1.").arg(url.toDisplayString()));
|
||||
setMinimum(0);
|
||||
setValue(0);
|
||||
setMinimumDuration(0);
|
||||
setMinimumSize(QSize(400, 75));
|
||||
}
|
||||
|
||||
void UrlFetchProgressDialog::networkReplyProgress(qint64 bytesRead, qint64 totalBytes)
|
||||
{
|
||||
setMaximum(totalBytes);
|
||||
setValue(bytesRead);
|
||||
}
|
||||
|
||||
EditWidgetIcons::EditWidgetIcons(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::EditWidgetIcons())
|
||||
, m_database(nullptr)
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
, m_reply(nullptr)
|
||||
#endif
|
||||
, m_defaultIconModel(new DefaultIconModel(this))
|
||||
, m_customIconModel(new CustomIconModel(this))
|
||||
{
|
||||
@@ -146,7 +165,7 @@ void EditWidgetIcons::load(const Uuid& currentUuid,
|
||||
void EditWidgetIcons::setUrl(const QString& url)
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
m_url = url;
|
||||
m_url = QUrl(url);
|
||||
m_ui->faviconButton->setVisible(!url.isEmpty());
|
||||
#else
|
||||
Q_UNUSED(url);
|
||||
@@ -154,87 +173,152 @@ void EditWidgetIcons::setUrl(const QString& url)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
namespace {
|
||||
// Try to get the 2nd level domain of the host part of a QUrl. For example,
|
||||
// "foo.bar.example.com" would become "example.com", and "foo.bar.example.co.uk"
|
||||
// would become "example.co.uk".
|
||||
QString getSecondLevelDomain(QUrl url)
|
||||
{
|
||||
QString fqdn = url.host();
|
||||
fqdn.truncate(fqdn.length() - url.topLevelDomain().length());
|
||||
QStringList parts = fqdn.split('.');
|
||||
QString newdom = parts.takeLast() + url.topLevelDomain();
|
||||
return newdom;
|
||||
}
|
||||
|
||||
QUrl convertVariantToUrl(QVariant var)
|
||||
{
|
||||
QUrl url;
|
||||
if (var.canConvert<QUrl>())
|
||||
url = var.value<QUrl>();
|
||||
return url;
|
||||
}
|
||||
|
||||
QUrl getRedirectTarget(QNetworkReply *reply)
|
||||
{
|
||||
QVariant var = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
QUrl url = convertVariantToUrl(var);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void EditWidgetIcons::downloadFavicon()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
m_ui->faviconButton->setDisabled(true);
|
||||
|
||||
QUrl url = QUrl(m_url);
|
||||
url.setPath("/favicon.ico");
|
||||
m_redirects = 0;
|
||||
m_urlsToTry.clear();
|
||||
|
||||
QString fullyQualifiedDomain = m_url.host();
|
||||
QString secondLevelDomain = getSecondLevelDomain(m_url);
|
||||
|
||||
// Attempt to simply load the favicon.ico file
|
||||
QImage image = fetchFavicon(url);
|
||||
if (fullyQualifiedDomain != secondLevelDomain) {
|
||||
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + fullyQualifiedDomain + "/favicon.ico"));
|
||||
}
|
||||
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + secondLevelDomain + "/favicon.ico"));
|
||||
|
||||
// Try to use Google fallback, if enabled
|
||||
if (config()->get("security/IconDownloadFallbackToGoogle", false).toBool()) {
|
||||
QUrl urlGoogle = QUrl("https://www.google.com/s2/favicons");
|
||||
|
||||
urlGoogle.setQuery("domain=" + QUrl::toPercentEncoding(secondLevelDomain));
|
||||
m_urlsToTry.append(urlGoogle);
|
||||
}
|
||||
|
||||
startFetchFavicon(m_urlsToTry.takeFirst());
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditWidgetIcons::fetchReadyRead()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
m_bytesReceived += m_reply->readAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditWidgetIcons::fetchFinished()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
QImage image;
|
||||
bool googleFallbackEnabled = config()->get("security/IconDownloadFallbackToGoogle", false).toBool();
|
||||
bool error = (m_reply->error() != QNetworkReply::NoError);
|
||||
QUrl redirectTarget = getRedirectTarget(m_reply);
|
||||
|
||||
m_reply->deleteLater();
|
||||
m_reply = nullptr;
|
||||
|
||||
if (!error) {
|
||||
if (redirectTarget.isValid()) {
|
||||
// Redirected, we need to follow it, or fall through if we have
|
||||
// done too many redirects already.
|
||||
if (m_redirects < 5) {
|
||||
m_redirects++;
|
||||
if (redirectTarget.isRelative())
|
||||
redirectTarget = m_fetchUrl.resolved(redirectTarget);
|
||||
startFetchFavicon(redirectTarget);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// No redirect, and we theoretically have some icon data now.
|
||||
image.loadFromData(m_bytesReceived);
|
||||
}
|
||||
}
|
||||
|
||||
if (!image.isNull()) {
|
||||
addCustomIcon(image);
|
||||
} else if (config()->get("security/IconDownloadFallbackToGoogle", false).toBool()) {
|
||||
QUrl faviconUrl = QUrl("https://www.google.com/s2/favicons");
|
||||
faviconUrl.setQuery("domain=" + QUrl::toPercentEncoding(url.host()));
|
||||
// Attempt to load favicon from Google
|
||||
image = fetchFavicon(faviconUrl);
|
||||
if (!image.isNull()) {
|
||||
addCustomIcon(image);
|
||||
} else if (!m_urlsToTry.empty()) {
|
||||
m_redirects = 0;
|
||||
startFetchFavicon(m_urlsToTry.takeFirst());
|
||||
return;
|
||||
} else {
|
||||
if (!googleFallbackEnabled) {
|
||||
emit messageEditEntry(tr("Unable to fetch favicon.") + "\n" +
|
||||
tr("Hint: You can enable Google as a fallback under Tools>Settings>Security"),
|
||||
MessageWidget::Error);
|
||||
} else {
|
||||
emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error);
|
||||
}
|
||||
} else {
|
||||
emit messageEditEntry(tr("Unable to fetch favicon.") + "\n"
|
||||
+ tr("Hint: You can enable Google as a fallback under Tools>Settings>Security"),
|
||||
MessageWidget::Error);
|
||||
}
|
||||
|
||||
m_ui->faviconButton->setDisabled(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditWidgetIcons::fetchCanceled()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
namespace
|
||||
{
|
||||
std::size_t writeCurlResponse(char* ptr, std::size_t size, std::size_t nmemb, void* data)
|
||||
{
|
||||
QByteArray* response = static_cast<QByteArray*>(data);
|
||||
std::size_t realsize = size * nmemb;
|
||||
response->append(ptr, realsize);
|
||||
return realsize;
|
||||
}
|
||||
m_reply->abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
QImage EditWidgetIcons::fetchFavicon(const QUrl& url)
|
||||
void EditWidgetIcons::startFetchFavicon(const QUrl& url)
|
||||
{
|
||||
QImage image;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
QByteArray imagedata;
|
||||
QByteArray baUrl = url.url().toLatin1();
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
m_bytesReceived.clear();
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, baUrl.data());
|
||||
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
|
||||
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl");
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &imagedata);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCurlResponse);
|
||||
#ifdef Q_OS_WIN
|
||||
const QDir appDir = QFileInfo(QCoreApplication::applicationFilePath()).absoluteDir();
|
||||
if (appDir.exists("ssl\\certs")) {
|
||||
curl_easy_setopt(
|
||||
curl, CURLOPT_CAINFO, (appDir.absolutePath() + "\\ssl\\certs\\ca-bundle.crt").toLatin1().data());
|
||||
}
|
||||
m_fetchUrl = url;
|
||||
|
||||
QNetworkRequest request(url);
|
||||
|
||||
m_reply = m_netMgr.get(request);
|
||||
connect(m_reply, &QNetworkReply::finished, this, &EditWidgetIcons::fetchFinished);
|
||||
connect(m_reply, &QIODevice::readyRead, this, &EditWidgetIcons::fetchReadyRead);
|
||||
|
||||
UrlFetchProgressDialog *progress = new UrlFetchProgressDialog(url, this);
|
||||
progress->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(m_reply, &QNetworkReply::finished, progress, &QProgressDialog::hide);
|
||||
connect(m_reply, &QNetworkReply::downloadProgress, progress, &UrlFetchProgressDialog::networkReplyProgress);
|
||||
connect(progress, &QProgressDialog::canceled, this, &EditWidgetIcons::fetchCanceled);
|
||||
|
||||
progress->show();
|
||||
#else
|
||||
Q_UNUSED(url);
|
||||
#endif
|
||||
|
||||
// Perform the request in another thread
|
||||
CURLcode result = AsyncTask::runAndWaitForFuture([curl]() { return curl_easy_perform(curl); });
|
||||
|
||||
if (result == CURLE_OK) {
|
||||
image.loadFromData(imagedata);
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
#endif
|
||||
|
||||
void EditWidgetIcons::addCustomIconFromFile()
|
||||
{
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
#define KEEPASSX_EDITWIDGETICONS_H
|
||||
|
||||
#include <QSet>
|
||||
#include <QProgressDialog>
|
||||
#include <QUrl>
|
||||
#include <QWidget>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Global.h"
|
||||
@@ -31,6 +33,9 @@
|
||||
class Database;
|
||||
class DefaultIconModel;
|
||||
class CustomIconModel;
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
class QNetworkReply;
|
||||
#endif
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -45,6 +50,17 @@ struct IconStruct
|
||||
int number;
|
||||
};
|
||||
|
||||
class UrlFetchProgressDialog : public QProgressDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit UrlFetchProgressDialog(const QUrl &url, QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void networkReplyProgress(qint64 bytesRead, qint64 totalBytes);
|
||||
};
|
||||
|
||||
class EditWidgetIcons : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -67,9 +83,10 @@ signals:
|
||||
|
||||
private slots:
|
||||
void downloadFavicon();
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
QImage fetchFavicon(const QUrl& url);
|
||||
#endif
|
||||
void startFetchFavicon(const QUrl& url);
|
||||
void fetchFinished();
|
||||
void fetchReadyRead();
|
||||
void fetchCanceled();
|
||||
void addCustomIconFromFile();
|
||||
void addCustomIcon(const QImage& icon);
|
||||
void removeCustomIcon();
|
||||
@@ -82,7 +99,15 @@ private:
|
||||
const QScopedPointer<Ui::EditWidgetIcons> m_ui;
|
||||
Database* m_database;
|
||||
Uuid m_currentUuid;
|
||||
QString m_url;
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
QUrl m_url;
|
||||
QUrl m_fetchUrl;
|
||||
QList<QUrl> m_urlsToTry;
|
||||
QByteArray m_bytesReceived;
|
||||
QNetworkAccessManager m_netMgr;
|
||||
QNetworkReply *m_reply;
|
||||
int m_redirects;
|
||||
#endif
|
||||
DefaultIconModel* const m_defaultIconModel;
|
||||
CustomIconModel* const m_customIconModel;
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class BrowserPlugin : public ISettingsPage
|
||||
public:
|
||||
BrowserPlugin(DatabaseTabWidget* tabWidget)
|
||||
{
|
||||
m_nativeMessagingHost = QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget));
|
||||
m_nativeMessagingHost = QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget, BrowserSettings::isEnabled()));
|
||||
}
|
||||
|
||||
~BrowserPlugin()
|
||||
@@ -686,7 +686,7 @@ void MainWindow::closeEvent(QCloseEvent* event)
|
||||
|
||||
bool minimizeOnClose = isTrayIconEnabled() && config()->get("GUI/MinimizeOnClose").toBool();
|
||||
if (minimizeOnClose && !m_appExitCalled) {
|
||||
event->ignore();
|
||||
event->accept();
|
||||
hideWindow();
|
||||
|
||||
if (config()->get("security/lockdatabaseminimize").toBool()) {
|
||||
|
||||
@@ -157,6 +157,7 @@ void SettingsWidget::loadSettings()
|
||||
}
|
||||
m_generalUi->autoTypeShortcutWidget->setAttribute(Qt::WA_MacShowFocusRect, true);
|
||||
m_generalUi->autoTypeDelaySpinBox->setValue(config()->get("AutoTypeDelay").toInt());
|
||||
m_generalUi->autoTypeStartDelaySpinBox->setValue(config()->get("AutoTypeStartDelay").toInt());
|
||||
}
|
||||
|
||||
m_secUi->clearClipboardCheckBox->setChecked(config()->get("security/clearclipboard").toBool());
|
||||
@@ -223,6 +224,7 @@ void SettingsWidget::saveSettings()
|
||||
config()->set("GlobalAutoTypeKey", m_generalUi->autoTypeShortcutWidget->key());
|
||||
config()->set("GlobalAutoTypeModifiers", static_cast<int>(m_generalUi->autoTypeShortcutWidget->modifiers()));
|
||||
config()->set("AutoTypeDelay", m_generalUi->autoTypeDelaySpinBox->value());
|
||||
config()->set("AutoTypeStartDelay", m_generalUi->autoTypeStartDelaySpinBox->value());
|
||||
}
|
||||
config()->set("security/clearclipboard", m_secUi->clearClipboardCheckBox->isChecked());
|
||||
config()->set("security/clearclipboardtimeout", m_secUi->clearClipboardSpinBox->value());
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<item>
|
||||
<widget class="QTabWidget" name="generalSettingsTabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabGeneral">
|
||||
<attribute name="title">
|
||||
@@ -411,7 +411,7 @@
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="autoTypeShortcutLabel_2">
|
||||
<widget class="QLabel" name="autoTypeShortcutLabel">
|
||||
<property name="text">
|
||||
<string>Global Auto-Type shortcut</string>
|
||||
</property>
|
||||
@@ -427,14 +427,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="autoTypeDelayLabel_2">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="autoTypeDelayLabel">
|
||||
<property name="text">
|
||||
<string>Auto-Type delay</string>
|
||||
<string>Auto-Type typing delay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="autoTypeDelaySpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
@@ -449,13 +449,48 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999</number>
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>25</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="autoTypeStartDelayLabel">
|
||||
<property name="text">
|
||||
<string>Auto-Type start delay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="autoTypeStartDelaySpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string comment="Milliseconds"> ms</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>500</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -339,7 +339,7 @@ void EditEntryWidget::setupSSHAgent()
|
||||
connect(m_sshAgentUi->decryptButton, SIGNAL(clicked()), SLOT(decryptPrivateKey()));
|
||||
connect(m_sshAgentUi->copyToClipboardButton, SIGNAL(clicked()), SLOT(copyPublicKey()));
|
||||
|
||||
connect(m_advancedUi->attachmentsWidget->entryAttachments(), SIGNAL(modified()), SLOT(updateAttachments()));
|
||||
connect(m_advancedUi->attachmentsWidget->entryAttachments(), SIGNAL(modified()), SLOT(updateSSHAgentAttachments()));
|
||||
|
||||
addPage(tr("SSH Agent"), FilePath::instance()->icon("apps", "utilities-terminal"), m_sshAgentWidget);
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
|
||||
return result;
|
||||
case Notes:
|
||||
// Display only first line of notes in simplified format
|
||||
result = entry->resolveMultiplePlaceholders(entry->notes().section("\n", 0, 0).simplified());
|
||||
result = entry->notes().section("\n", 0, 0).simplified();
|
||||
if (attr->isReference(EntryAttributes::NotesKey)) {
|
||||
result.prepend(tr("Ref: ", "Reference abbreviation"));
|
||||
}
|
||||
|
||||
17
src/main.cpp
17
src/main.cpp
@@ -45,6 +45,21 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static inline void earlyQNetworkAccessManagerWorkaround()
|
||||
{
|
||||
// When QNetworkAccessManager is instantiated it regularly starts polling
|
||||
// all network interfaces to see if anything changes and if so, what. This
|
||||
// creates a latency spike every 10 seconds on Mac OS 10.12+ and Windows 7 >=
|
||||
// when on a wifi connection.
|
||||
// So here we disable it for lack of better measure.
|
||||
// This will also cause this message: QObject::startTimer: Timers cannot
|
||||
// have negative intervals
|
||||
// For more info see:
|
||||
// - https://bugreports.qt.io/browse/QTBUG-40332
|
||||
// - https://bugreports.qt.io/browse/QTBUG-46015
|
||||
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#ifdef QT_NO_DEBUG
|
||||
@@ -52,6 +67,8 @@ int main(int argc, char** argv)
|
||||
#endif
|
||||
Tools::setupSearchPaths();
|
||||
|
||||
earlyQNetworkAccessManagerWorkaround();
|
||||
|
||||
Application app(argc, argv);
|
||||
Application::setApplicationName("keepassxc");
|
||||
Application::setApplicationVersion(KEEPASSX_VERSION);
|
||||
|
||||
@@ -55,4 +55,7 @@ if(WITH_XC_BROWSER)
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
||||
COMMENT "Changing linking of keepassxc-proxy")
|
||||
endif()
|
||||
if(MINGW)
|
||||
target_link_libraries(keepassxc-proxy Wtsapi32.lib Ws2_32.lib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -18,11 +18,21 @@
|
||||
#include "NativeMessagingHost.h"
|
||||
#include <QCoreApplication>
|
||||
|
||||
NativeMessagingHost::NativeMessagingHost()
|
||||
: NativeMessagingBase()
|
||||
#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(true);
|
||||
m_future =
|
||||
|
||||
@@ -317,9 +317,9 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase)
|
||||
|
||||
if (m_cipherName.compare("aes-128-cbc", Qt::CaseInsensitive) == 0) {
|
||||
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||
} else if (m_cipherName == "aes256-cbc") {
|
||||
} else if (m_cipherName == "aes256-cbc" || m_cipherName.compare("aes-256-cbc", Qt::CaseInsensitive) == 0) {
|
||||
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||
} else if (m_cipherName == "aes256-ctr") {
|
||||
} else if (m_cipherName == "aes256-ctr" || m_cipherName.compare("aes-256-ctr", Qt::CaseInsensitive) == 0) {
|
||||
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Ctr, SymmetricCipher::Decrypt));
|
||||
} else if (m_cipherName != "none") {
|
||||
m_error = tr("Unknown cipher: %1").arg(m_cipherName);
|
||||
@@ -370,10 +370,22 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase)
|
||||
return false;
|
||||
}
|
||||
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
hash.addData(passphrase.toUtf8());
|
||||
hash.addData(m_cipherIV.data(), 8);
|
||||
QByteArray keyData = hash.result();
|
||||
QByteArray keyData;
|
||||
QByteArray mdBuf;
|
||||
do {
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
hash.addData(mdBuf);
|
||||
hash.addData(passphrase.toUtf8());
|
||||
hash.addData(m_cipherIV.data(), 8);
|
||||
mdBuf = hash.result();
|
||||
keyData.append(mdBuf);
|
||||
} while(keyData.size() < cipher->keySize());
|
||||
|
||||
if (keyData.size() > cipher->keySize()) {
|
||||
// If our key size isn't a multiple of 16 (e.g. AES-192 or something),
|
||||
// then we will need to truncate it.
|
||||
keyData.resize(cipher->keySize());
|
||||
}
|
||||
|
||||
if (!cipher->init(keyData, m_cipherIV)) {
|
||||
m_error = cipher->errorString();
|
||||
|
||||
Reference in New Issue
Block a user