Merge branch 'master' into develop

This commit is contained in:
Jonathan White 2019-03-19 19:01:31 -04:00
commit 31bd90a8df
No known key found for this signature in database
GPG key ID: 440FC65F2E0C6E01
99 changed files with 4428 additions and 3872 deletions

View file

@ -21,11 +21,11 @@
#include "NativeMessagingBase.h"
#include "config-keepassx.h"
#include <QJsonDocument>
#include <QJsonParseError>
#include <sodium.h>
#include <sodium/crypto_box.h>
#include <sodium/randombytes.h>
#include <QJsonDocument>
#include <QJsonParseError>
BrowserAction::BrowserAction(BrowserService& browserService)
: m_mutex(QMutex::Recursive)
@ -88,6 +88,8 @@ QJsonObject BrowserAction::handleAction(const QJsonObject& json)
return handleLockDatabase(json, action);
} else if (action.compare("get-database-groups", Qt::CaseSensitive) == 0) {
return handleGetDatabaseGroups(json, action);
} else if (action.compare("create-new-group", Qt::CaseSensitive) == 0) {
return handleCreateNewGroup(json, action);
}
// Action was not recognized
@ -407,6 +409,42 @@ QJsonObject BrowserAction::handleGetDatabaseGroups(const QJsonObject& json, cons
return buildResponse(action, message, newNonce);
}
QJsonObject BrowserAction::handleCreateNewGroup(const QJsonObject& json, const QString& action)
{
const QString hash = getDatabaseHash();
const QString nonce = json.value("nonce").toString();
const QString encrypted = json.value("message").toString();
QMutexLocker locker(&m_mutex);
if (!m_associated) {
return getErrorReply(action, ERROR_KEEPASS_ASSOCIATION_FAILED);
}
const QJsonObject decrypted = decryptMessage(encrypted, nonce);
if (decrypted.isEmpty()) {
return getErrorReply(action, ERROR_KEEPASS_CANNOT_DECRYPT_MESSAGE);
}
QString command = decrypted.value("action").toString();
if (command.isEmpty() || command.compare("create-new-group", Qt::CaseSensitive) != 0) {
return getErrorReply(action, ERROR_KEEPASS_INCORRECT_ACTION);
}
QString group = decrypted.value("groupName").toString();
const QJsonObject newGroup = m_browserService.createNewGroup(group);
if (newGroup.isEmpty() || newGroup["name"].toString().isEmpty() || newGroup["uuid"].toString().isEmpty()) {
return getErrorReply(action, ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP);
}
const QString newNonce = incrementNonce(nonce);
QJsonObject message = buildMessage(newNonce);
message["name"] = newGroup["name"];
message["uuid"] = newGroup["uuid"];
return buildResponse(action, message, newNonce);
}
QJsonObject BrowserAction::getErrorReply(const QString& action, const int errorCode) const
{
QJsonObject response;
@ -468,6 +506,8 @@ QString BrowserAction::getErrorMessage(const int errorCode) const
return QObject::tr("No logins found");
case ERROR_KEEPASS_NO_GROUPS_FOUND:
return QObject::tr("No groups found");
case ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP:
return QObject::tr("Cannot create new group");
default:
return QObject::tr("Unknown error");
}

View file

@ -46,7 +46,8 @@ class BrowserAction : public QObject
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_NO_GROUPS_FOUND = 16,
ERROR_KEEPASS_CANNOT_CREATE_NEW_GROUP = 17
};
public:
@ -66,6 +67,7 @@ private:
QJsonObject handleSetLogin(const QJsonObject& json, const QString& action);
QJsonObject handleLockDatabase(const QJsonObject& json, const QString& action);
QJsonObject handleGetDatabaseGroups(const QJsonObject& json, const QString& action);
QJsonObject handleCreateNewGroup(const QJsonObject& json, const QString& action);
QJsonObject buildMessage(const QString& nonce) const;
QJsonObject buildResponse(const QString& action, const QJsonObject& message, const QString& nonce);

View file

@ -32,13 +32,24 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
{
m_ui->setupUi(this);
// clang-format off
QString snapInstructions;
#if defined(KEEPASSXC_DIST_SNAP)
snapInstructions = "<br /><br />" +
tr("Due to Snap sandboxing, you must run a script to enable browser integration."
"<br />"
"You can obtain this script from %1")
.arg("<a href=\"https://keepassxc.org/download#linux\">https://keepassxc.org</a>");
#endif
m_ui->extensionLabel->setOpenExternalLinks(true);
m_ui->extensionLabel->setText(
tr("KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2.")
tr("KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. %3")
.arg("<a href=\"https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/\">Firefox</a>",
"<a "
"href=\"https://chrome.google.com/webstore/detail/keepassxc-browser/"
"oboonakemofpalcgghocfoadofidjkkk\">Google Chrome / Chromium / Vivaldi</a>"));
"<a href=\"https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk\">"
"Google Chrome / Chromium / Vivaldi</a>",
snapInstructions));
// clang-format on
m_ui->scriptWarningWidget->setVisible(false);
m_ui->scriptWarningWidget->setAutoHideTimeout(-1);
@ -119,11 +130,18 @@ void BrowserOptionDialog::loadSettings()
m_ui->supportBrowserProxy->setChecked(true);
m_ui->supportBrowserProxy->setEnabled(false);
#elif defined(KEEPASSXC_DIST_SNAP)
m_ui->enableBrowserSupport->setChecked(false);
m_ui->enableBrowserSupport->setEnabled(false);
m_ui->browserGlobalWarningWidget->showMessage(
tr("We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment."),
MessageWidget::Warning);
// Disable settings that will not work
m_ui->supportBrowserProxy->setChecked(true);
m_ui->supportBrowserProxy->setEnabled(false);
m_ui->useCustomProxy->setChecked(false);
m_ui->useCustomProxy->setEnabled(false);
m_ui->browsersGroupBox->setVisible(false);
m_ui->browsersGroupBox->setEnabled(false);
m_ui->updateBinaryPath->setChecked(false);
m_ui->updateBinaryPath->setEnabled(false);
// Show notice to user
m_ui->browserGlobalWarningWidget->showMessage(tr("Please see special instructions for browser extension use below"),
MessageWidget::Warning);
m_ui->browserGlobalWarningWidget->setCloseButtonVisible(false);
m_ui->browserGlobalWarningWidget->setAutoHideTimeout(-1);
#endif

View file

@ -150,7 +150,7 @@ QString BrowserService::getDatabaseRecycleBinUuid()
return recycleBin->uuidToHex();
}
QJsonArray BrowserService::addChildrenToGroup(Group* group)
QJsonArray BrowserService::getChildrenFromGroup(Group* group)
{
QJsonArray groupList;
@ -166,7 +166,7 @@ QJsonArray BrowserService::addChildrenToGroup(Group* group)
QJsonObject jsonGroup;
jsonGroup["name"] = c->name();
jsonGroup["uuid"] = Tools::uuidToHex(c->uuid());
jsonGroup["children"] = addChildrenToGroup(c);
jsonGroup["children"] = getChildrenFromGroup(c);
groupList.push_back(jsonGroup);
}
return groupList;
@ -187,7 +187,7 @@ QJsonObject BrowserService::getDatabaseGroups()
QJsonObject root;
root["name"] = rootGroup->name();
root["uuid"] = Tools::uuidToHex(rootGroup->uuid());
root["children"] = addChildrenToGroup(rootGroup);
root["children"] = getChildrenFromGroup(rootGroup);
QJsonArray groups;
groups.push_back(root);
@ -198,6 +198,84 @@ QJsonObject BrowserService::getDatabaseGroups()
return result;
}
QJsonObject BrowserService::createNewGroup(const QString& groupName)
{
QJsonObject result;
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this,
"createNewGroup",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QJsonObject, result),
Q_ARG(QString, groupName));
return result;
}
auto db = getDatabase();
if (!db) {
return {};
}
Group* rootGroup = db->rootGroup();
if (!rootGroup) {
return {};
}
auto group = rootGroup->findGroupByPath(groupName);
// Group already exists
if (group) {
result["name"] = group->name();
result["uuid"] = Tools::uuidToHex(group->uuid());
return result;
}
auto dialogResult = MessageBox::warning(nullptr,
tr("KeePassXC: Create a new group"),
tr("A request for creating a new group \"%1\" has been received.\n"
"Do you want to create this group?\n")
.arg(groupName),
MessageBox::Yes | MessageBox::No);
if (dialogResult != MessageBox::Yes) {
return result;
}
QString name, uuid;
Group* previousGroup = rootGroup;
auto groups = groupName.split("/");
// Returns the group name based on depth
auto getGroupName = [&](int depth) {
QString gName;
for (int i = 0; i < depth + 1; ++i) {
gName.append((i == 0 ? "" : "/") + groups[i]);
}
return gName;
};
// Create new group(s) always when the path is not found
for (int i = 0; i < groups.length(); ++i) {
QString gName = getGroupName(i);
auto tempGroup = rootGroup->findGroupByPath(gName);
if (!tempGroup) {
Group* newGroup = new Group();
newGroup->setName(groups[i]);
newGroup->setUuid(QUuid::createUuid());
newGroup->setParent(previousGroup);
name = newGroup->name();
uuid = Tools::uuidToHex(newGroup->uuid());
previousGroup = newGroup;
continue;
}
previousGroup = tempGroup;
}
result["name"] = name;
result["uuid"] = uuid;
return result;
}
QString BrowserService::storeKey(const QString& key)
{
QString id;
@ -630,7 +708,7 @@ QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QStrin
{
QUrl url(entryUrl);
if (url.scheme().isEmpty()) {
url.setScheme("http");
url.setScheme("https");
}
const QString submitUrl = url.toString(QUrl::StripTrailingSlash);
@ -996,12 +1074,13 @@ bool BrowserService::checkLegacySettings()
return false;
}
auto dialogResult = MessageBox::warning(nullptr,
tr("KeePassXC: Legacy browser integration settings detected"),
tr("Legacy browser integration settings have been detected.\n"
"Do you want to upgrade the settings to the latest standard?\n"
"This is necessary to maintain compatibility with the browser plugin."),
MessageBox::Yes | MessageBox::No);
auto dialogResult =
MessageBox::warning(nullptr,
tr("KeePassXC: Legacy browser integration settings detected"),
tr("Your KeePassXC-Browser settings need to be moved into the database settings.\n"
"This is necessary to maintain your current browser connections.\n"
"Would you like to migrate your existing settings now?"),
MessageBox::Yes | MessageBox::No);
return dialogResult == MessageBox::Yes;
}
@ -1034,6 +1113,8 @@ void BrowserService::raiseWindow(const bool force)
m_prevWindowState = WindowState::Minimized;
}
#ifdef Q_OS_MACOS
Q_UNUSED(force);
if (macUtils()->isHidden()) {
m_prevWindowState = WindowState::Hidden;
}

View file

@ -45,6 +45,7 @@ public:
QString getDatabaseRootUuid();
QString getDatabaseRecycleBinUuid();
QJsonObject getDatabaseGroups();
QJsonObject createNewGroup(const QString& groupName);
QString getKey(const QString& id);
void addEntry(const QString& id,
const QString& login,
@ -121,7 +122,7 @@ private:
QString baseDomain(const QString& url) const;
QSharedPointer<Database> getDatabase();
QSharedPointer<Database> selectedDatabase();
QJsonArray addChildrenToGroup(Group* group);
QJsonArray getChildrenFromGroup(Group* group);
bool moveSettingsToCustomData(Entry* entry, const QString& name) const;
int moveKeysToCustomData(Entry* entry, const QSharedPointer<Database>& db) const;
bool checkLegacySettings();

View file

@ -186,7 +186,7 @@ void BrowserSettings::setCustomProxyLocation(const QString& location)
bool BrowserSettings::updateBinaryPath()
{
return config()->get("Browser/UpdateBinaryPath", false).toBool();
return config()->get("Browser/UpdateBinaryPath", true).toBool();
}
void BrowserSettings::setUpdateBinaryPath(bool enabled)

View file

@ -65,6 +65,7 @@ void NativeMessagingBase::newNativeMessage()
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;
}
@ -81,6 +82,7 @@ void NativeMessagingBase::newNativeMessage()
event.data.fd = 0;
if (epoll_ctl(fd, EPOLL_CTL_ADD, 0, &event) != 0) {
m_notifier->setEnabled(false);
::close(fd);
return;
}
@ -135,7 +137,9 @@ void NativeMessagingBase::sendReply(const QString& reply)
QString NativeMessagingBase::getLocalServerPath() const
{
const QString serverPath = "/kpxc_server";
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
#if defined(KEEPASSXC_DIST_SNAP)
return QProcessEnvironment::systemEnvironment().value("SNAP_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

View file

@ -53,7 +53,7 @@ protected slots:
protected:
virtual void readLength() = 0;
virtual bool readStdIn(const quint32 length) = 0;
void readNativeMessages();
virtual void readNativeMessages();
QString jsonToString(const QJsonObject& json) const;
void sendReply(const QJsonObject& json);
void sendReply(const QString& reply);

View file

@ -32,7 +32,7 @@ class NativeMessagingHost : public NativeMessagingBase
public:
explicit NativeMessagingHost(DatabaseTabWidget* parent = nullptr, const bool enabled = false);
~NativeMessagingHost();
~NativeMessagingHost() override;
int init();
void run();
void stop();

View file

@ -275,6 +275,7 @@ bool Database::writeDatabase(QIODevice* device, QString* error)
return false;
}
QByteArray oldTransformedKey = m_data.transformedMasterKey;
KeePass2Writer writer;
setEmitModified(false);
writer.writeDatabase(device, this);
@ -287,6 +288,15 @@ bool Database::writeDatabase(QIODevice* device, QString* error)
return false;
}
Q_ASSERT(!m_data.transformedMasterKey.isEmpty());
Q_ASSERT(m_data.transformedMasterKey != oldTransformedKey);
if (m_data.transformedMasterKey.isEmpty() || m_data.transformedMasterKey == oldTransformedKey) {
if (error) {
*error = tr("Key not transformed. This is a bug, please report it to the developers!");
}
return false;
}
markAsClean();
return true;
}
@ -307,16 +317,18 @@ bool Database::extract(QByteArray& xmlOutput, QString* error)
/**
* Remove the old backup and replace it with a new one
* backups are named <filename>.old.kdbx
* backups are named <filename>.old.<extension>
*
* @param filePath Path to the file to backup
* @return true on success
*/
bool Database::backupDatabase(const QString& filePath)
{
QString backupFilePath = filePath;
auto re = QRegularExpression("\\.kdbx$|(?<!\\.kdbx)$", QRegularExpression::CaseInsensitiveOption);
backupFilePath.replace(re, ".old.kdbx");
static auto re = QRegularExpression("(\\.[^.]+)$");
auto match = re.match(filePath);
auto backupFilePath = filePath;
backupFilePath = backupFilePath.replace(re, "") + ".old" + match.captured(1);
QFile::remove(backupFilePath);
return QFile::copy(filePath, backupFilePath);
}
@ -512,9 +524,13 @@ void Database::setCompressionAlgorithm(Database::CompressionAlgorithm algo)
* @param key key to set and transform or nullptr to reset the key
* @param updateChangedTime true to update database change time
* @param updateTransformSalt true to update the transform salt
* @param transformKey trigger the KDF after setting the key
* @return true on success
*/
bool Database::setKey(const QSharedPointer<const CompositeKey>& key, bool updateChangedTime, bool updateTransformSalt)
bool Database::setKey(const QSharedPointer<const CompositeKey>& key,
bool updateChangedTime,
bool updateTransformSalt,
bool transformKey)
{
Q_ASSERT(!m_data.isReadOnly);
@ -532,7 +548,9 @@ bool Database::setKey(const QSharedPointer<const CompositeKey>& key, bool update
QByteArray oldTransformedMasterKey = m_data.transformedMasterKey;
QByteArray transformedMasterKey;
if (!key->transform(*m_data.kdf, transformedMasterKey)) {
if (!transformKey) {
transformedMasterKey = oldTransformedMasterKey;
} else if (!key->transform(*m_data.kdf, transformedMasterKey)) {
return false;
}

View file

@ -109,7 +109,8 @@ public:
QSharedPointer<const CompositeKey> key() const;
bool setKey(const QSharedPointer<const CompositeKey>& key,
bool updateChangedTime = true,
bool updateTransformSalt = false);
bool updateTransformSalt = false,
bool transformKey = true);
QByteArray challengeResponseKey() const;
bool challengeMasterSeed(const QByteArray& masterSeed);
bool verifyKey(const QSharedPointer<CompositeKey>& key) const;

View file

@ -926,7 +926,7 @@ QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder,
Q_ASSERT(m_group);
Q_ASSERT(m_group->database());
const Entry* refEntry = m_group->findEntryBySearchTerm(searchText, searchInType);
const Entry* refEntry = m_group->database()->rootGroup()->findEntryBySearchTerm(searchText, searchInType);
if (refEntry) {
const QString wantedField = match.captured(EntryAttributes::WantedFieldGroupName);

View file

@ -67,6 +67,15 @@ QString EntryAttributes::value(const QString& key) const
return m_attributes.value(key);
}
QList<QString> EntryAttributes::values(const QList<QString>& keys) const
{
QList<QString> values;
for (const QString& key : keys) {
values.append(m_attributes.value(key));
}
return values;
}
bool EntryAttributes::contains(const QString& key) const
{
return m_attributes.contains(key);

View file

@ -36,6 +36,7 @@ public:
bool hasKey(const QString& key) const;
QList<QString> customKeys() const;
QString value(const QString& key) const;
QList<QString> values(const QList<QString>& keys) const;
bool contains(const QString& key) const;
bool containsValue(const QString& value) const;
bool isProtected(const QString& key) const;

View file

@ -28,31 +28,83 @@ EntrySearcher::EntrySearcher(bool caseSensitive)
{
}
/**
* Search group, and its children, by parsing the provided search
* string for search terms.
*
* @param searchString search terms
* @param baseGroup group to start search from, cannot be null
* @param forceSearch ignore group search settings
* @return list of entries that match the search terms
*/
QList<Entry*> EntrySearcher::search(const QString& searchString, const Group* baseGroup, bool forceSearch)
{
Q_ASSERT(baseGroup);
parseSearchTerms(searchString);
return repeat(baseGroup, forceSearch);
}
/**
* Repeat the last search starting from the given group
*
* @param baseGroup group to start search from, cannot be null
* @param forceSearch ignore group search settings
* @return list of entries that match the search terms
*/
QList<Entry*> EntrySearcher::repeat(const Group* baseGroup, bool forceSearch)
{
Q_ASSERT(baseGroup);
QList<Entry*> results;
for (const auto group : baseGroup->groupsRecursive(true)) {
if (forceSearch || group->resolveSearchingEnabled()) {
results.append(searchEntries(searchString, group->entries()));
for (auto* entry : group->entries()) {
if (searchEntryImpl(entry)) {
results.append(entry);
}
}
}
}
return results;
}
/**
* Search provided entries by parsing the search string
* for search terms.
*
* @param searchString search terms
* @param entries list of entries to include in the search
* @return list of entries that match the search terms
*/
QList<Entry*> EntrySearcher::searchEntries(const QString& searchString, const QList<Entry*>& entries)
{
parseSearchTerms(searchString);
return repeatEntries(entries);
}
/**
* Repeat the last search on the given entries
*
* @param entries list of entries to include in the search
* @return list of entries that match the search terms
*/
QList<Entry*> EntrySearcher::repeatEntries(const QList<Entry*>& entries)
{
QList<Entry*> results;
for (Entry* entry : entries) {
if (searchEntryImpl(searchString, entry)) {
for (auto* entry : entries) {
if (searchEntryImpl(entry)) {
results.append(entry);
}
}
return results;
}
/**
* Set the next search to be case sensitive or not
*
* @param state
*/
void EntrySearcher::setCaseSensitive(bool state)
{
m_caseSensitive = state;
@ -63,16 +115,15 @@ bool EntrySearcher::isCaseSensitive()
return m_caseSensitive;
}
bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry)
bool EntrySearcher::searchEntryImpl(Entry* entry)
{
// Pre-load in case they are needed
auto attributes = QStringList(entry->attributes()->keys());
auto attributes_keys = entry->attributes()->customKeys();
auto attributes = QStringList(attributes_keys + entry->attributes()->values(attributes_keys));
auto attachments = QStringList(entry->attachments()->keys());
bool found;
auto searchTerms = parseSearchTerms(searchString);
for (const auto& term : searchTerms) {
for (const auto& term : m_searchTerms) {
switch (term->field) {
case Field::Title:
found = term->regex.match(entry->resolvePlaceholder(entry->title())).hasMatch();
@ -112,10 +163,9 @@ bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry)
return true;
}
QList<QSharedPointer<EntrySearcher::SearchTerm>> EntrySearcher::parseSearchTerms(const QString& searchString)
void EntrySearcher::parseSearchTerms(const QString& searchString)
{
auto terms = QList<QSharedPointer<SearchTerm>>();
m_searchTerms.clear();
auto results = m_termParser.globalMatch(searchString);
while (results.hasNext()) {
auto result = results.next();
@ -165,8 +215,6 @@ QList<QSharedPointer<EntrySearcher::SearchTerm>> EntrySearcher::parseSearchTerms
}
}
terms.append(term);
m_searchTerms.append(term);
}
return terms;
}

View file

@ -31,14 +31,15 @@ public:
explicit EntrySearcher(bool caseSensitive = false);
QList<Entry*> search(const QString& searchString, const Group* baseGroup, bool forceSearch = false);
QList<Entry*> repeat(const Group* baseGroup, bool forceSearch = false);
QList<Entry*> searchEntries(const QString& searchString, const QList<Entry*>& entries);
QList<Entry*> repeatEntries(const QList<Entry*>& entries);
void setCaseSensitive(bool state);
bool isCaseSensitive();
private:
bool searchEntryImpl(const QString& searchString, Entry* entry);
enum class Field
{
Undefined,
@ -59,10 +60,12 @@ private:
bool exclude;
};
QList<QSharedPointer<SearchTerm>> parseSearchTerms(const QString& searchString);
bool searchEntryImpl(Entry* entry);
void parseSearchTerms(const QString& searchString);
bool m_caseSensitive;
QRegularExpression m_termParser;
QList<QSharedPointer<SearchTerm>> m_searchTerms;
friend class TestEntrySearcher;
};

View file

@ -123,6 +123,7 @@ BulkFileWatcher::BulkFileWatcher(QObject* parent)
connect(&m_fileWatchUnblockTimer, SIGNAL(timeout()), this, SLOT(observeFileChanges()));
connect(&m_pendingSignalsTimer, SIGNAL(timeout()), this, SLOT(emitSignals()));
m_fileWatchUnblockTimer.setSingleShot(true);
m_pendingSignalsTimer.setSingleShot(true);
}
void BulkFileWatcher::clear()

View file

@ -1,8 +1,30 @@
/*
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2018 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 "OSEventFilter.h"
#include <QByteArray>
#include "autotype/AutoType.h"
#include "gui/MainWindow.h"
#ifdef Q_OS_WIN
#include <windows.h>
#endif
OSEventFilter::OSEventFilter()
{
@ -15,12 +37,18 @@ bool OSEventFilter::nativeEventFilter(const QByteArray& eventType, void* message
#if defined(Q_OS_UNIX)
if (eventType == QByteArrayLiteral("xcb_generic_event_t")) {
#elif defined(Q_OS_WIN)
if (eventType == QByteArrayLiteral("windows_generic_MSG")
|| eventType == QByteArrayLiteral("windows_dispatcher_MSG")) {
auto winmsg = static_cast<MSG*>(message);
if (winmsg->message == WM_QUERYENDSESSION) {
*result = 1;
return true;
} else if (winmsg->message == WM_ENDSESSION) {
getMainWindow()->appExit();
*result = 0;
return true;
} else if (eventType == QByteArrayLiteral("windows_generic_MSG")
|| eventType == QByteArrayLiteral("windows_dispatcher_MSG")) {
#endif
int retCode = autoType()->callEventFilter(message);
return retCode == 1;
return autoType()->callEventFilter(message) == 1;
}
return false;

View file

@ -1,3 +1,21 @@
/*
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2018 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 OSEVENTFILTER_H
#define OSEVENTFILTER_H
#include <QAbstractNativeEventFilter>

View file

@ -84,7 +84,8 @@ int Kdf::benchmark(int msec) const
}
Kdf::BenchmarkThread::BenchmarkThread(int msec, const Kdf* kdf)
: m_msec(msec)
: m_rounds(1)
, m_msec(msec)
, m_kdf(kdf)
{
}

View file

@ -19,12 +19,6 @@
#include "BinaryStream.h"
#include <QtEndian>
BinaryStream::BinaryStream(QObject* parent)
: QObject(parent)
, m_timeout(-1)
{
}
BinaryStream::BinaryStream(QIODevice* device)
: QObject(device)
, m_timeout(-1)
@ -36,7 +30,10 @@ BinaryStream::BinaryStream(QByteArray* ba, QObject* parent)
: QObject(parent)
, m_timeout(-1)
{
setData(ba);
m_buffer.reset(new QBuffer(ba));
m_buffer->open(QIODevice::ReadWrite);
m_device = m_buffer.data();
}
BinaryStream::~BinaryStream()
@ -53,19 +50,6 @@ QIODevice* BinaryStream::device() const
return m_device;
}
void BinaryStream::setDevice(QIODevice* device)
{
m_device = device;
}
void BinaryStream::setData(QByteArray* ba)
{
m_buffer.reset(new QBuffer(ba));
m_buffer->open(QIODevice::ReadWrite);
m_device = m_buffer.data();
}
void BinaryStream::setTimeout(int timeout)
{
m_timeout = timeout;

View file

@ -26,16 +26,14 @@
class BinaryStream : QObject
{
Q_OBJECT
Q_DISABLE_COPY(BinaryStream)
public:
BinaryStream(QObject* parent = nullptr);
BinaryStream(QIODevice* device);
BinaryStream(QByteArray* ba, QObject* parent = nullptr);
~BinaryStream();
explicit BinaryStream(QIODevice* device);
explicit BinaryStream(QByteArray* ba, QObject* parent = nullptr);
~BinaryStream() override;
const QString errorString() const;
QIODevice* device() const;
void setDevice(QIODevice* device);
void setData(QByteArray* ba);
void setTimeout(int timeout);
bool read(QByteArray& ba);

View file

@ -125,9 +125,8 @@ Application::Application(int& argc, char** argv)
break;
}
default:
qWarning() << QObject::tr("The lock file could not be created. Single-instance mode disabled.")
.toUtf8()
.constData();
qWarning()
<< QObject::tr("The lock file could not be created. Single-instance mode disabled.").toUtf8().constData();
}
}

View file

@ -176,7 +176,8 @@ void ApplicationSettingsWidget::loadSettings()
m_generalUi->minimizeOnCloseCheckBox->setChecked(config()->get("GUI/MinimizeOnClose").toBool());
m_generalUi->systrayMinimizeOnStartup->setChecked(config()->get("GUI/MinimizeOnStartup").toBool());
m_generalUi->checkForUpdatesOnStartupCheckBox->setChecked(config()->get("GUI/CheckForUpdates").toBool());
m_generalUi->checkForUpdatesIncludeBetasCheckBox->setChecked(config()->get("GUI/CheckForUpdatesIncludeBetas").toBool());
m_generalUi->checkForUpdatesIncludeBetasCheckBox->setChecked(
config()->get("GUI/CheckForUpdatesIncludeBetas").toBool());
m_generalUi->autoTypeAskCheckBox->setChecked(config()->get("security/autotypeask").toBool());
if (autoType()->isAvailable()) {

View file

@ -251,7 +251,11 @@ QSharedPointer<CompositeKey> DatabaseOpenWidget::databaseKey()
#ifdef WITH_XC_TOUCHID
// check if TouchID is available and enabled for unlocking the database
if (m_ui->checkTouchID->isChecked() && TouchID::getInstance().isAvailable() && masterKey->isEmpty()) {
if (m_ui->checkTouchID->isChecked() && TouchID::getInstance().isAvailable()
&& m_ui->editPassword->text().isEmpty()) {
// clear empty password from composite key
masterKey->clear();
// try to get, decrypt and use PasswordKey
QSharedPointer<QByteArray> passwordKey = TouchID::getInstance().getKey(m_filename);
if (passwordKey != NULL) {

View file

@ -61,8 +61,6 @@
#include "keeshare/KeeShare.h"
#include "touchid/TouchID.h"
#include "config-keepassx.h"
#ifdef Q_OS_LINUX
#include <sys/vfs.h>
#endif
@ -80,6 +78,9 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
, m_previewView(new EntryPreviewWidget(this))
, m_previewSplitter(new QSplitter(m_mainWidget))
, m_searchingLabel(new QLabel(this))
#ifdef WITH_XC_KEESHARE
, m_shareLabel(new QLabel(this))
#endif
, m_csvImportWizard(new CsvImportWizard(this))
, m_editEntryWidget(new EditEntryWidget(this))
, m_editGroupWidget(new EditGroupWidget(this))
@ -103,6 +104,9 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
auto* vbox = new QVBoxLayout();
vbox->setMargin(0);
vbox->addWidget(m_searchingLabel);
#ifdef WITH_XC_KEESHARE
vbox->addWidget(m_shareLabel);
#endif
vbox->addWidget(m_previewSplitter);
rightHandSideWidget->setLayout(vbox);
m_entryView = new EntryView(rightHandSideWidget);
@ -134,6 +138,16 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
"border-radius: 4px;");
m_searchingLabel->setVisible(false);
#ifdef WITH_XC_KEESHARE
m_shareLabel->setText(tr("Shared group..."));
m_shareLabel->setAlignment(Qt::AlignCenter);
m_shareLabel->setStyleSheet("color: rgb(0, 0, 0);"
"background-color: rgb(255, 253, 160);"
"border: 2px solid rgb(190, 190, 190);"
"border-radius: 4px;");
m_shareLabel->setVisible(false);
#endif
m_previewView->hide();
m_previewSplitter->addWidget(m_entryView);
m_previewSplitter->addWidget(m_previewView);
@ -194,6 +208,12 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
}
#endif
#ifdef WITH_XC_KEESHARE
// We need to reregister the database to allow exports
// from a newly created database
KeeShare::instance()->connectDatabase(m_db, {});
#endif
switchToMainView();
}
@ -377,6 +397,9 @@ void DatabaseWidget::replaceDatabase(QSharedPointer<Database> db)
processAutoOpen();
#if defined(WITH_XC_KEESHARE)
KeeShare::instance()->connectDatabase(m_db, oldDb);
#else
// Keep the instance active till the end of this function
Q_UNUSED(oldDb);
#endif
}
@ -765,9 +788,9 @@ void DatabaseWidget::switchToMainView(bool previousDialogAccepted)
setCurrentWidget(m_mainWidget);
if (sender() == m_entryView) {
if (sender() == m_entryView || sender() == m_editEntryWidget) {
onEntryChanged(m_entryView->currentEntry());
} else if (sender() == m_groupView) {
} else if (sender() == m_groupView || sender() == m_editGroupWidget) {
onGroupChanged(m_groupView->currentGroup());
}
}
@ -1089,6 +1112,7 @@ void DatabaseWidget::search(const QString& searchtext)
}
m_searchingLabel->setVisible(true);
m_shareLabel->setVisible(false);
emit searchModeActivated();
}
@ -1117,6 +1141,16 @@ void DatabaseWidget::onGroupChanged(Group* group)
}
m_previewView->setGroup(group);
#ifdef WITH_XC_KEESHARE
auto shareLabel = KeeShare::sharingLabel(group);
if (!shareLabel.isEmpty()) {
m_shareLabel->setText(shareLabel);
m_shareLabel->setVisible(true);
} else {
m_shareLabel->setVisible(false);
}
#endif
}
void DatabaseWidget::onDatabaseModified()
@ -1140,6 +1174,7 @@ void DatabaseWidget::endSearch()
// Show the normal entry view of the current group
m_entryView->displayGroup(currentGroup());
onGroupChanged(currentGroup());
emit listModeActivated();
}

View file

@ -29,6 +29,8 @@
#include "gui/csvImport/CsvImportWizard.h"
#include "gui/entry/EntryModel.h"
#include "config-keepassx.h"
class DatabaseOpenWidget;
class KeePass1OpenWidget;
class DatabaseSettingsDialog;
@ -233,6 +235,9 @@ private:
QPointer<EntryPreviewWidget> m_previewView;
QPointer<QSplitter> m_previewSplitter;
QPointer<QLabel> m_searchingLabel;
#ifdef WITH_XC_KEESHARE
QPointer<QLabel> m_shareLabel;
#endif
QPointer<CsvImportWizard> m_csvImportWizard;
QPointer<EditEntryWidget> m_editEntryWidget;
QPointer<EditGroupWidget> m_editGroupWidget;

View file

@ -59,6 +59,7 @@ void EditWidget::addPage(const QString& labelText, const QIcon& icon, QWidget* w
*/
auto* scrollArea = new QScrollArea(m_ui->stackedWidget);
scrollArea->setFrameShape(QFrame::NoFrame);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidget(widget);
scrollArea->setWidgetResizable(true);
m_ui->stackedWidget->addWidget(scrollArea);

View file

@ -30,6 +30,7 @@
#include "gui/MessageBox.h"
#ifdef WITH_XC_NETWORKING
#include <QHostInfo>
#include <QNetworkAccessManager>
#include <QtNetwork>
#endif
@ -65,7 +66,6 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
connect(m_ui->deleteButton, SIGNAL(clicked()), SLOT(removeCustomIcon()));
connect(m_ui->faviconButton, SIGNAL(clicked()), SLOT(downloadFavicon()));
connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated()));
connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated()));
connect(m_ui->defaultIconsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SIGNAL(widgetUpdated()));
@ -196,13 +196,27 @@ void EditWidgetIcons::downloadFavicon()
m_urlsToTry.clear();
QString fullyQualifiedDomain = m_url.host();
QString secondLevelDomain = getSecondLevelDomain(m_url);
// Attempt to simply load the favicon.ico file
if (fullyQualifiedDomain != secondLevelDomain) {
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + fullyQualifiedDomain + "/favicon.ico"));
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + fullyQualifiedDomain + "/favicon.ico"));
// Determine if host portion of URL is an IP address by resolving it and
// searching for a match with the returned address(es).
bool hostIsIp = false;
QList<QHostAddress> hostAddressess = QHostInfo::fromName(fullyQualifiedDomain).addresses();
for (auto addr : hostAddressess) {
if (addr.toString() == fullyQualifiedDomain) {
hostIsIp = true;
}
}
if (!hostIsIp) {
QString secondLevelDomain = getSecondLevelDomain(m_url);
// Attempt to simply load the favicon.ico file
if (fullyQualifiedDomain != secondLevelDomain) {
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + secondLevelDomain + "/favicon.ico"));
}
}
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + secondLevelDomain + "/favicon.ico"));
// Try to use alternative fallback URL, if enabled
if (config()->get("security/IconDownloadFallback", false).toBool()) {
@ -210,6 +224,15 @@ void EditWidgetIcons::downloadFavicon()
fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(fullyQualifiedDomain) + ".ico");
m_urlsToTry.append(fallbackUrl);
if (!hostIsIp) {
QString secondLevelDomain = getSecondLevelDomain(m_url);
if (fullyQualifiedDomain != secondLevelDomain) {
fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(secondLevelDomain) + ".ico");
m_urlsToTry.append(fallbackUrl);
}
}
}
startFetchFavicon(m_urlsToTry.takeFirst());
@ -277,7 +300,7 @@ void EditWidgetIcons::fetchFinished()
#endif
}
void EditWidgetIcons::fetchCanceled()
void EditWidgetIcons::abortRequests()
{
#ifdef WITH_XC_NETWORKING
if (m_reply) {

View file

@ -19,7 +19,6 @@
#ifndef KEEPASSX_EDITWIDGETICONS_H
#define KEEPASSX_EDITWIDGETICONS_H
#include <QSet>
#include <QUrl>
#include <QUuid>
#include <QWidget>
@ -66,6 +65,7 @@ public:
public slots:
void setUrl(const QString& url);
void abortRequests();
signals:
void messageEditEntry(QString, MessageWidget::MessageType);
@ -77,7 +77,6 @@ private slots:
void startFetchFavicon(const QUrl& url);
void fetchFinished();
void fetchReadyRead();
void fetchCanceled();
void addCustomIconFromFile();
bool addCustomIcon(const QImage& icon);
void removeCustomIcon();

View file

@ -42,9 +42,9 @@
#include "keys/PasswordKey.h"
#ifdef WITH_XC_NETWORKING
#include "updatecheck/UpdateChecker.h"
#include "gui/MessageBox.h"
#include "gui/UpdateCheckDialog.h"
#include "updatecheck/UpdateChecker.h"
#endif
#ifdef WITH_XC_SSHAGENT
@ -76,7 +76,7 @@
class BrowserPlugin : public ISettingsPage
{
public:
BrowserPlugin(DatabaseTabWidget* tabWidget)
explicit BrowserPlugin(DatabaseTabWidget* tabWidget)
{
m_nativeMessagingHost =
QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget, browserSettings()->isEnabled()));
@ -374,7 +374,9 @@ MainWindow::MainWindow()
#ifdef WITH_XC_NETWORKING
connect(m_ui->actionCheckForUpdates, SIGNAL(triggered()), SLOT(showUpdateCheckDialog()));
connect(UpdateChecker::instance(), SIGNAL(updateCheckFinished(bool, QString, bool)), SLOT(hasUpdateAvailable(bool, QString, bool)));
connect(UpdateChecker::instance(),
SIGNAL(updateCheckFinished(bool, QString, bool)),
SLOT(hasUpdateAvailable(bool, QString, bool)));
QTimer::singleShot(3000, this, SLOT(showUpdateCheckStartup()));
#else
m_ui->actionCheckForUpdates->setVisible(false);
@ -687,12 +689,13 @@ void MainWindow::showUpdateCheckStartup()
{
#ifdef WITH_XC_NETWORKING
if (!config()->get("UpdateCheckMessageShown", false).toBool()) {
auto result = MessageBox::question(this,
tr("Check for updates on startup?"),
tr("Would you like KeePassXC to check for updates on startup?") + "\n\n" +
tr("You can always check for updates manually from the application menu."),
MessageBox::Yes | MessageBox::No,
MessageBox::Yes);
auto result =
MessageBox::question(this,
tr("Check for updates on startup?"),
tr("Would you like KeePassXC to check for updates on startup?") + "\n\n"
+ tr("You can always check for updates manually from the application menu."),
MessageBox::Yes | MessageBox::No,
MessageBox::Yes);
config()->set("GUI/CheckForUpdates", (result == MessageBox::Yes));
config()->set("UpdateCheckMessageShown", true);
@ -713,6 +716,10 @@ void MainWindow::hasUpdateAvailable(bool hasUpdate, const QString& version, bool
updateCheckDialog->showUpdateCheckResponse(hasUpdate, version);
updateCheckDialog->show();
}
#else
Q_UNUSED(hasUpdate)
Q_UNUSED(version)
Q_UNUSED(isManuallyRequested)
#endif
}

View file

@ -120,6 +120,7 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event)
void SearchWidget::connectSignals(SignalMultiplexer& mx)
{
// Connects basically only to the current DatabaseWidget, but allows to switch between instances!
mx.connect(this, SIGNAL(search(QString)), SLOT(search(QString)));
mx.connect(this, SIGNAL(caseSensitiveChanged(bool)), SLOT(setSearchCaseSensitive(bool)));
mx.connect(this, SIGNAL(limitGroupChanged(bool)), SLOT(setSearchLimitGroup(bool)));

View file

@ -16,9 +16,9 @@
*/
#include "UpdateCheckDialog.h"
#include "core/FilePath.h"
#include "ui_UpdateCheckDialog.h"
#include "updatecheck/UpdateChecker.h"
#include "core/FilePath.h"
UpdateCheckDialog::UpdateCheckDialog(QWidget* parent)
: QDialog(parent)
@ -31,35 +31,34 @@ UpdateCheckDialog::UpdateCheckDialog(QWidget* parent)
m_ui->iconLabel->setPixmap(filePath()->applicationIcon().pixmap(48));
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(close()));
connect(UpdateChecker::instance(), SIGNAL(updateCheckFinished(bool, QString, bool)), SLOT(showUpdateCheckResponse(bool, QString)));
connect(UpdateChecker::instance(),
SIGNAL(updateCheckFinished(bool, QString, bool)),
SLOT(showUpdateCheckResponse(bool, QString)));
}
void UpdateCheckDialog::showUpdateCheckResponse(bool status, const QString& version) {
void UpdateCheckDialog::showUpdateCheckResponse(bool status, const QString& version)
{
m_ui->progressBar->setVisible(false);
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Close"));
if (version == QString("error")) {
setWindowTitle(tr("Update Error!"));
m_ui->statusLabel->setText(
"<strong>" + tr("Update Error!") + "</strong><br><br>" +
tr("An error occurred in retrieving update information.") + "<br>" +
tr("Please try again later."));
m_ui->statusLabel->setText("<strong>" + tr("Update Error!") + "</strong><br><br>"
+ tr("An error occurred in retrieving update information.") + "<br>"
+ tr("Please try again later."));
return;
}
if (status) {
setWindowTitle(tr("Software Update"));
m_ui->statusLabel->setText(
"<strong>" + tr("A new version of KeePassXC is available!") + "</strong><br><br>" +
tr("KeePassXC %1 is now available — you have %2.").arg(version, KEEPASSXC_VERSION) + "<br><br>" +
"<a href='https://keepassxc.org/download/'>" +
tr("Download it at keepassxc.org") +
"</a>");
m_ui->statusLabel->setText("<strong>" + tr("A new version of KeePassXC is available!") + "</strong><br><br>"
+ tr("KeePassXC %1 is now available — you have %2.").arg(version, KEEPASSXC_VERSION)
+ "<br><br>" + "<a href='https://keepassxc.org/download/'>"
+ tr("Download it at keepassxc.org") + "</a>");
} else {
setWindowTitle(tr("You're up-to-date!"));
m_ui->statusLabel->setText(tr(
"KeePassXC %1 is currently the newest version available").arg(KEEPASSXC_VERSION));
m_ui->statusLabel->setText(tr("KeePassXC %1 is currently the newest version available").arg(KEEPASSXC_VERSION));
}
}

View file

@ -18,13 +18,13 @@
#ifndef KEEPASSXC_UPDATECHECKDIALOG_H
#define KEEPASSXC_UPDATECHECKDIALOG_H
#include <QUrl>
#include <QDialog>
#include <QScopedPointer>
#include "gui/MessageBox.h"
#include "config-keepassx.h"
#include "core/Global.h"
#include "gui/MessageBox.h"
#include "updatecheck/UpdateChecker.h"
#include <QDialog>
#include <QScopedPointer>
#include <QUrl>
namespace Ui
{
@ -33,7 +33,7 @@ namespace Ui
class UpdateCheckDialog : public QDialog
{
Q_OBJECT
Q_OBJECT
public:
explicit UpdateCheckDialog(QWidget* parent = nullptr);
@ -46,5 +46,4 @@ private:
QScopedPointer<Ui::UpdateCheckDialog> m_ui;
};
#endif //KEEPASSXC_UPDATECHECKDIALOG_H
#endif // KEEPASSXC_UPDATECHECKDIALOG_H

View file

@ -81,7 +81,7 @@ void DatabaseSettingsWidgetEncryption::initialize()
isDirty = true;
}
if (!m_db->key()) {
m_db->setKey(QSharedPointer<CompositeKey>::create());
m_db->setKey(QSharedPointer<CompositeKey>::create(), true, false, false);
m_db->setCipher(KeePass2::CIPHER_AES256);
isDirty = true;
}
@ -127,8 +127,7 @@ void DatabaseSettingsWidgetEncryption::setupAlgorithmComboBox()
{
m_ui->algorithmComboBox->clear();
for (auto& cipher : asConst(KeePass2::CIPHERS)) {
m_ui->algorithmComboBox->addItem(cipher.second.toUtf8(),
cipher.first.toByteArray());
m_ui->algorithmComboBox->addItem(cipher.second.toUtf8(), cipher.first.toByteArray());
}
int cipherIndex = m_ui->algorithmComboBox->findData(m_db->cipher().toByteArray());
if (cipherIndex > -1) {
@ -142,8 +141,7 @@ void DatabaseSettingsWidgetEncryption::setupKdfComboBox()
bool block = m_ui->kdfComboBox->blockSignals(true);
m_ui->kdfComboBox->clear();
for (auto& kdf : asConst(KeePass2::KDFS)) {
m_ui->kdfComboBox->addItem(kdf.second.toUtf8(),
kdf.first.toByteArray());
m_ui->kdfComboBox->addItem(kdf.second.toUtf8(), kdf.first.toByteArray());
}
m_ui->kdfComboBox->blockSignals(block);
}

View file

@ -136,34 +136,34 @@ bool DatabaseSettingsWidgetMasterKey::save()
auto newKey = QSharedPointer<CompositeKey>::create();
QSharedPointer<Key> passwordKey;
QSharedPointer<Key> fileKey;
QSharedPointer<ChallengeResponseKey> ykCrKey;
QSharedPointer<Key> oldPasswordKey;
QSharedPointer<Key> oldFileKey;
QSharedPointer<ChallengeResponseKey> oldChallengeResponse;
for (const auto& key : m_db->key()->keys()) {
if (key->uuid() == PasswordKey::UUID) {
passwordKey = key;
oldPasswordKey = key;
} else if (key->uuid() == FileKey::UUID) {
fileKey = key;
oldFileKey = key;
}
}
for (const auto& key : m_db->key()->challengeResponseKeys()) {
if (key->uuid() == YkChallengeResponseKey::UUID) {
ykCrKey = key;
oldChallengeResponse = key;
}
}
if (!addToCompositeKey(m_passwordEditWidget, newKey, passwordKey)) {
if (!addToCompositeKey(m_passwordEditWidget, newKey, oldPasswordKey)) {
return false;
}
if (!addToCompositeKey(m_keyFileEditWidget, newKey, fileKey)) {
if (!addToCompositeKey(m_keyFileEditWidget, newKey, oldFileKey)) {
return false;
}
#ifdef WITH_XC_YUBIKEY
if (!addToCompositeKey(m_yubiKeyEditWidget, newKey, ykCrKey)) {
if (!addToCompositeKey(m_yubiKeyEditWidget, newKey, oldChallengeResponse)) {
return false;
}
#endif
@ -177,7 +177,7 @@ bool DatabaseSettingsWidgetMasterKey::save()
return false;
}
if (m_passwordEditWidget->visiblePage() == KeyComponentWidget::AddNew) {
if (m_passwordEditWidget->isEmpty()) {
auto answer = MessageBox::warning(this,
tr("No password set"),
tr("WARNING! You have not set a password. Using a database without "
@ -190,9 +190,13 @@ bool DatabaseSettingsWidgetMasterKey::save()
}
}
m_db->setKey(newKey);
m_db->setKey(newKey, true, false, false);
emit editFinished(true);
if (m_isDirty) {
m_db->markAsModified();
}
return true;
}

View file

@ -142,6 +142,7 @@ void EditEntryWidget::setupMain()
connect(m_mainUi->togglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool)));
#ifdef WITH_XC_NETWORKING
connect(m_mainUi->fetchFaviconButton, SIGNAL(clicked()), m_iconsWidget, SLOT(downloadFavicon()));
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), m_iconsWidget, SLOT(setUrl(QString)));
#endif
connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool)));
connect(m_mainUi->notesEnabled, SIGNAL(toggled(bool)), this, SLOT(toggleHideNotes(bool)));
@ -193,6 +194,8 @@ void EditEntryWidget::setupAdvanced()
void EditEntryWidget::setupIcon()
{
addPage(tr("Icon"), FilePath::instance()->icon("apps", "preferences-desktop-icons"), m_iconsWidget);
connect(this, SIGNAL(accepted()), m_iconsWidget, SLOT(abortRequests()));
connect(this, SIGNAL(rejected()), m_iconsWidget, SLOT(abortRequests()));
}
void EditEntryWidget::setupAutoType()
@ -764,7 +767,6 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
iconStruct.uuid = entry->iconUuid();
iconStruct.number = entry->iconNumber();
m_iconsWidget->load(entry->uuid(), m_db, iconStruct, entry->webUrl());
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), m_iconsWidget, SLOT(setUrl(QString)));
m_autoTypeUi->enableButton->setChecked(entry->autoTypeEnabled());
if (entry->defaultAutoTypeSequence().isEmpty()) {

View file

@ -36,9 +36,9 @@ public:
{
}
void set(Group* temporaryGroup) const
void set(Group* temporaryGroup, QSharedPointer<Database> database) const
{
editPage->set(widget, temporaryGroup);
editPage->set(widget, temporaryGroup, database);
}
void assign() const
@ -133,7 +133,7 @@ void EditGroupWidget::loadGroup(Group* group, bool create, const QSharedPointer<
m_editWidgetProperties->setCustomData(m_temporaryGroup->customData());
for (const ExtraPage& page : asConst(m_extraPages)) {
page.set(m_temporaryGroup.data());
page.set(m_temporaryGroup.data(), m_db);
}
setCurrentPage(0);

View file

@ -43,7 +43,7 @@ public:
virtual QString name() = 0;
virtual QIcon icon() = 0;
virtual QWidget* createWidget() = 0;
virtual void set(QWidget* widget, Group* tempoaryGroup) = 0;
virtual void set(QWidget* widget, Group* tempoaryGroup, QSharedPointer<Database> database) = 0;
virtual void assign(QWidget* widget) = 0;
};

View file

@ -124,7 +124,7 @@ QVariant GroupModel::data(const QModelIndex& index, int role) const
Group* group = groupFromIndex(index);
if (role == Qt::DisplayRole) {
QString nameTemplate = tr("%1", "Template for name without annotation");
QString nameTemplate = "%1";
#if defined(WITH_XC_KEESHARE)
nameTemplate = KeeShare::indicatorSuffix(group, nameTemplate);
#endif

View file

@ -40,7 +40,10 @@ PasswordEditWidget::~PasswordEditWidget()
bool PasswordEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
{
key->addKey(QSharedPointer<PasswordKey>::create(m_compUi->enterPasswordEdit->text()));
QString pw = m_compUi->enterPasswordEdit->text();
if (!pw.isEmpty()) {
key->addKey(QSharedPointer<PasswordKey>::create(pw));
}
return true;
}
@ -60,6 +63,11 @@ bool PasswordEditWidget::isPasswordVisible() const
return m_compUi->togglePasswordButton->isChecked();
}
bool PasswordEditWidget::isEmpty() const
{
return m_compUi->enterPasswordEdit->text().isEmpty();
}
QWidget* PasswordEditWidget::componentEditWidget()
{
m_compEditWidget = new QWidget();
@ -86,11 +94,6 @@ void PasswordEditWidget::initComponentEditWidget(QWidget* widget)
bool PasswordEditWidget::validate(QString& errorMessage) const
{
if (m_compUi->enterPasswordEdit->text().isEmpty()) {
errorMessage = tr("Password cannot be empty.");
return false;
}
if (m_compUi->enterPasswordEdit->text() != m_compUi->repeatPasswordEdit->text()) {
errorMessage = tr("Passwords do not match.");
return false;

View file

@ -38,6 +38,7 @@ public:
bool addToCompositeKey(QSharedPointer<CompositeKey> key) override;
void setPasswordVisible(bool visible);
bool isPasswordVisible() const;
bool isEmpty() const;
bool validate(QString& errorMessage) const override;
protected:

View file

@ -93,7 +93,7 @@ void KeeShare::setOwn(const KeeShareSettings::Own& own)
bool KeeShare::isShared(const Group* group)
{
return group->customData()->contains(KeeShare_Reference);
return group && group->customData()->contains(KeeShare_Reference);
}
KeeShareSettings::Reference KeeShare::referenceOf(const Group* group)
@ -142,6 +142,40 @@ bool KeeShare::isEnabled(const Group* group)
return (reference.isImporting() && active.in) || (reference.isExporting() && active.out);
}
const Group* KeeShare::resolveSharedGroup(const Group* group)
{
while (group && group != group->database()->rootGroup()) {
if (isShared(group)) {
return group;
}
group = group->parentGroup();
}
return nullptr;
}
QString KeeShare::sharingLabel(const Group* group)
{
auto* share = resolveSharedGroup(group);
if (!share) {
return {};
}
const auto reference = referenceOf(share);
switch (reference.type) {
case KeeShareSettings::Inactive:
return tr("Disabled share %1").arg(reference.path);
case KeeShareSettings::ImportFrom:
return tr("Import from share %1").arg(reference.path);
case KeeShareSettings::ExportTo:
return tr("Export to share %1").arg(reference.path);
case KeeShareSettings::SynchronizeWith:
return tr("Synchronize with share %1").arg(reference.path);
}
return {};
}
QPixmap KeeShare::indicatorBadge(const Group* group, QPixmap pixmap)
{
if (!isShared(group)) {

View file

@ -43,6 +43,9 @@ public:
static bool isShared(const Group* group);
static bool isEnabled(const Group* group);
static const Group* resolveSharedGroup(const Group* group);
static QString sharingLabel(const Group* group);
static KeeShareSettings::Own own();
static KeeShareSettings::Active active();
static KeeShareSettings::Foreign foreign();

View file

@ -183,7 +183,7 @@ void SettingsWidgetKeeShare::exportCertificate()
}
const auto filetype = tr("key.share", "Filetype for KeeShare key");
const auto filters = QString("%1 (*." + filetype + ");;%2 (*)").arg(tr("KeeShare key file"), tr("All files"));
QString filename = tr("%1.%2", "Template for KeeShare key file").arg(m_own.certificate.signer).arg(filetype);
QString filename = QString("%1.%2").arg(m_own.certificate.signer).arg(filetype);
filename = fileDialog()->getSaveFileName(
this, tr("Select path"), defaultDirPath, filters, nullptr, QFileDialog::Options(0), filetype, filename);
if (filename.isEmpty()) {

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>327</width>
<height>434</height>
<width>378</width>
<height>508</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
@ -82,7 +82,7 @@
<item row="2" column="0">
<widget class="QLabel" name="ownCertificateSignerLabel">
<property name="text">
<string>Signer</string>
<string>Signer:</string>
</property>
</widget>
</item>

View file

@ -25,6 +25,7 @@
#include "core/Entry.h"
#include "core/FilePath.h"
#include "core/FileWatcher.h"
#include "core/Global.h"
#include "core/Group.h"
#include "core/Merger.h"
#include "core/Metadata.h"
@ -191,7 +192,7 @@ void ShareObserver::reinitialize()
const auto active = KeeShare::active();
QList<Update> updated;
QList<Group*> groups = m_db->rootGroup()->groupsRecursive(true);
const QList<Group*> groups = m_db->rootGroup()->groupsRecursive(true);
for (Group* group : groups) {
Update couple{group, m_groupToReference.value(group), KeeShare::referenceOf(group)};
if (couple.oldReference == couple.newReference) {
@ -214,7 +215,9 @@ void ShareObserver::reinitialize()
QStringList success;
QStringList warning;
QStringList error;
for (const auto& update : updated) {
QMap<QString, QStringList> imported;
QMap<QString, QStringList> exported;
for (const auto& update : asConst(updated)) {
if (!update.oldReference.path.isEmpty()) {
m_fileWatcher->removePath(update.oldReference.path);
}
@ -222,8 +225,12 @@ void ShareObserver::reinitialize()
if (!update.newReference.path.isEmpty() && update.newReference.type != KeeShareSettings::Inactive) {
m_fileWatcher->addPath(update.newReference.path);
}
if (update.newReference.isExporting()) {
exported[update.newReference.path] << update.group->name();
}
if (update.newReference.isImporting()) {
imported[update.newReference.path] << update.group->name();
const auto result = this->importFromReferenceContainer(update.newReference.path);
if (!result.isValid()) {
// tolerable result - blocked import or missing source
@ -241,6 +248,16 @@ void ShareObserver::reinitialize()
}
}
}
for (auto it = imported.cbegin(); it != imported.cend(); ++it) {
if (it.value().count() > 1) {
warning << tr("Multiple import source path to %1 in %2").arg(it.key(), it.value().join(", "));
}
}
for (auto it = exported.cbegin(); it != exported.cend(); ++it) {
if (it.value().count() > 1) {
error << tr("Conflicting export target path %1 in %2").arg(it.key(), it.value().join(", "));
}
}
notifyAbout(success, warning, error);
}
@ -659,8 +676,10 @@ ShareObserver::Result ShareObserver::exportIntoReferenceSignedContainer(const Ke
QuaZipFile file(&zip);
const auto signatureOpened = file.open(QIODevice::WriteOnly, QuaZipNewInfo(KeeShare_Signature));
if (!signatureOpened) {
::qWarning("Embedding signature failed: %d", zip.getZipError());
return {reference.path, Result::Error, tr("Could not embed signature (%1)").arg(file.getZipError())};
::qWarning("Embedding signature failed: Could not open file to write (%d)", zip.getZipError());
return {reference.path,
Result::Error,
tr("Could not embed signature: Could not open file to write (%1)").arg(file.getZipError())};
}
QTextStream stream(&file);
KeeShareSettings::Sign sign;
@ -672,8 +691,10 @@ ShareObserver::Result ShareObserver::exportIntoReferenceSignedContainer(const Ke
stream << KeeShareSettings::Sign::serialize(sign);
stream.flush();
if (file.getZipError() != ZIP_OK) {
::qWarning("Embedding signature failed: %d", zip.getZipError());
return {reference.path, Result::Error, tr("Could not embed signature (%1)").arg(file.getZipError())};
::qWarning("Embedding signature failed: Could not write file (%d)", zip.getZipError());
return {reference.path,
Result::Error,
tr("Could not embed signature: Could not write file (%1)").arg(file.getZipError())};
}
file.close();
}
@ -681,14 +702,18 @@ ShareObserver::Result ShareObserver::exportIntoReferenceSignedContainer(const Ke
QuaZipFile file(&zip);
const auto dbOpened = file.open(QIODevice::WriteOnly, QuaZipNewInfo(KeeShare_Container));
if (!dbOpened) {
::qWarning("Embedding database failed: %d", zip.getZipError());
return {reference.path, Result::Error, tr("Could not embed database (%1)").arg(file.getZipError())};
}
if (file.getZipError() != ZIP_OK) {
::qWarning("Embedding database failed: %d", zip.getZipError());
return {reference.path, Result::Error, tr("Could not embed database (%1)").arg(file.getZipError())};
::qWarning("Embedding database failed: Could not open file to write (%d)", zip.getZipError());
return {reference.path,
Result::Error,
tr("Could not embed database: Could not open file to write (%1)").arg(file.getZipError())};
}
file.write(bytes);
if (file.getZipError() != ZIP_OK) {
::qWarning("Embedding database failed: Could not write file (%d)", zip.getZipError());
return {reference.path,
Result::Error,
tr("Could not embed database: Could not write file (%1)").arg(file.getZipError())};
}
file.close();
}
zip.close();
@ -725,28 +750,55 @@ ShareObserver::Result ShareObserver::exportIntoReferenceUnsignedContainer(const
QList<ShareObserver::Result> ShareObserver::exportIntoReferenceContainers()
{
QList<Result> results;
struct Reference
{
KeeShareSettings::Reference config;
const Group* group;
};
QMap<QString, QList<Reference>> references;
const auto groups = m_db->rootGroup()->groupsRecursive(true);
for (const auto* group : groups) {
const auto reference = KeeShare::referenceOf(group);
if (!reference.isExporting()) {
continue;
}
references[reference.path] << Reference{reference, group};
}
m_fileWatcher->ignoreFileChanges(reference.path);
QScopedPointer<Database> targetDb(exportIntoContainer(reference, group));
QFileInfo info(reference.path);
for (auto it = references.cbegin(); it != references.cend(); ++it) {
if (it.value().count() != 1) {
const auto path = it.value().first().config.path;
QStringList groups;
for (const auto& reference : it.value()) {
groups << reference.group->name();
}
results << Result{
path, Result::Error, tr("Conflicting export target path %1 in %2").arg(path, groups.join(", "))};
}
}
if (!results.isEmpty()) {
// We need to block export due to config
return results;
}
for (auto it = references.cbegin(); it != references.cend(); ++it) {
const auto& reference = it.value().first();
m_fileWatcher->ignoreFileChanges(reference.config.path);
QScopedPointer<Database> targetDb(exportIntoContainer(reference.config, reference.group));
QFileInfo info(reference.config.path);
if (isOfExportType(info, KeeShare::signedContainerFileType())) {
results << exportIntoReferenceSignedContainer(reference, targetDb.data());
results << exportIntoReferenceSignedContainer(reference.config, targetDb.data());
m_fileWatcher->observeFileChanges(true);
continue;
}
if (isOfExportType(info, KeeShare::unsignedContainerFileType())) {
results << exportIntoReferenceUnsignedContainer(reference, targetDb.data());
results << exportIntoReferenceUnsignedContainer(reference.config, targetDb.data());
m_fileWatcher->observeFileChanges(true);
continue;
}
Q_ASSERT(false);
results << Result{reference.path, Result::Error, tr("Unexpected export error occurred")};
results << Result{reference.config.path, Result::Error, tr("Unexpected export error occurred")};
}
return results;
}
@ -759,6 +811,7 @@ void ShareObserver::handleDatabaseSaved()
QStringList error;
QStringList warning;
QStringList success;
const auto results = exportIntoReferenceContainers();
for (const Result& result : results) {
if (!result.isValid()) {
@ -787,7 +840,7 @@ ShareObserver::Result::Result(const QString& path, ShareObserver::Result::Type t
bool ShareObserver::Result::isValid() const
{
return !path.isEmpty() || !message.isEmpty() || !message.isEmpty() || !message.isEmpty();
return !path.isEmpty() || !message.isEmpty();
}
bool ShareObserver::Result::isError() const

View file

@ -42,10 +42,10 @@ QWidget* EditGroupPageKeeShare::createWidget()
return new EditGroupWidgetKeeShare();
}
void EditGroupPageKeeShare::set(QWidget* widget, Group* temporaryGroup)
void EditGroupPageKeeShare::set(QWidget* widget, Group* temporaryGroup, QSharedPointer<Database> database)
{
EditGroupWidgetKeeShare* settingsWidget = reinterpret_cast<EditGroupWidgetKeeShare*>(widget);
settingsWidget->setGroup(temporaryGroup);
settingsWidget->setGroup(temporaryGroup, database);
}
void EditGroupPageKeeShare::assign(QWidget* widget)

View file

@ -30,7 +30,7 @@ public:
QString name() override;
QIcon icon() override;
QWidget* createWidget() override;
void set(QWidget* widget, Group* temporaryGroup) override;
void set(QWidget* widget, Group* temporaryGroup, QSharedPointer<Database> database) override;
void assign(QWidget* widget) override;
};

View file

@ -54,6 +54,7 @@ EditGroupWidgetKeeShare::EditGroupWidgetKeeShare(QWidget* parent)
connect(m_ui->pathEdit, SIGNAL(editingFinished()), SLOT(selectPath()));
connect(m_ui->pathSelectionButton, SIGNAL(pressed()), SLOT(launchPathSelectionDialog()));
connect(m_ui->typeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(selectType()));
connect(m_ui->clearButton, SIGNAL(clicked(bool)), SLOT(clearInputs()));
connect(KeeShare::instance(), SIGNAL(activeChanged()), SLOT(showSharingState()));
@ -84,12 +85,13 @@ EditGroupWidgetKeeShare::~EditGroupWidgetKeeShare()
{
}
void EditGroupWidgetKeeShare::setGroup(Group* temporaryGroup)
void EditGroupWidgetKeeShare::setGroup(Group* temporaryGroup, QSharedPointer<Database> database)
{
if (m_temporaryGroup) {
m_temporaryGroup->disconnect(this);
}
m_database = database;
m_temporaryGroup = temporaryGroup;
if (m_temporaryGroup) {
@ -127,9 +129,43 @@ void EditGroupWidgetKeeShare::showSharingState()
.arg(supportedExtensions.join(", ")),
MessageWidget::Warning);
return;
} else {
m_ui->messageWidget->hide();
}
const auto groups = m_database->rootGroup()->groupsRecursive(true);
bool conflictExport = false;
bool multipleImport = false;
bool cycleImportExport = false;
for (const auto* group : groups) {
if (group->uuid() == m_temporaryGroup->uuid()) {
continue;
}
const auto other = KeeShare::referenceOf(group);
if (other.path != reference.path) {
continue;
}
multipleImport |= other.isImporting() && reference.isImporting();
conflictExport |= other.isExporting() && reference.isExporting();
cycleImportExport |=
(other.isImporting() && reference.isExporting()) || (other.isExporting() && reference.isImporting());
}
if (conflictExport) {
m_ui->messageWidget->showMessage(tr("The export container %1 is already referenced.").arg(reference.path),
MessageWidget::Error);
return;
}
if (multipleImport) {
m_ui->messageWidget->showMessage(tr("The import container %1 is already imported.").arg(reference.path),
MessageWidget::Warning);
return;
}
if (cycleImportExport) {
m_ui->messageWidget->showMessage(
tr("The container %1 imported and export by different groups.").arg(reference.path),
MessageWidget::Warning);
return;
}
m_ui->messageWidget->hide();
}
const auto active = KeeShare::active();
if (!active.in && !active.out) {
@ -166,6 +202,17 @@ void EditGroupWidgetKeeShare::update()
m_ui->togglePasswordButton->setChecked(false);
}
void EditGroupWidgetKeeShare::clearInputs()
{
if (m_temporaryGroup) {
KeeShare::setReferenceTo(m_temporaryGroup, KeeShareSettings::Reference());
}
m_ui->passwordEdit->clear();
m_ui->pathEdit->clear();
m_ui->typeComboBox->setCurrentIndex(KeeShareSettings::Inactive);
m_ui->passwordGenerator->setVisible(false);
}
void EditGroupWidgetKeeShare::togglePasswordGeneratorButton(bool checked)
{
m_ui->passwordGenerator->regeneratePassword();

View file

@ -37,13 +37,14 @@ public:
explicit EditGroupWidgetKeeShare(QWidget* parent = nullptr);
~EditGroupWidgetKeeShare();
void setGroup(Group* temporaryGroup);
void setGroup(Group* temporaryGroup, QSharedPointer<Database> database);
private slots:
void showSharingState();
private slots:
void update();
void clearInputs();
void selectType();
void selectPassword();
void launchPathSelectionDialog();
@ -54,6 +55,7 @@ private slots:
private:
QScopedPointer<Ui::EditGroupWidgetKeeShare> m_ui;
QPointer<Group> m_temporaryGroup;
QSharedPointer<Database> m_database;
};
#endif // KEEPASSXC_EDITGROUPWIDGETKEESHARE_H

View file

@ -97,6 +97,13 @@
<item row="5" column="1">
<widget class="PasswordGeneratorWidget" name="passwordGenerator" native="true"/>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="clearButton">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
</layout>
</item>
<item>

View file

@ -67,19 +67,13 @@ int main(int argc, char** argv)
Bootstrap::bootstrapApplication();
QCommandLineParser parser;
parser.setApplicationDescription(
QObject::tr("KeePassXC - cross-platform password manager"));
parser.setApplicationDescription(QObject::tr("KeePassXC - cross-platform password manager"));
parser.addPositionalArgument(
"filename",
QObject::tr("filenames of the password databases to open (*.kdbx)"),
"[filename(s)]");
"filename", QObject::tr("filenames of the password databases to open (*.kdbx)"), "[filename(s)]");
QCommandLineOption configOption(
"config", QObject::tr("path to a custom config file"), "config");
QCommandLineOption keyfileOption(
"keyfile", QObject::tr("key file of the database"), "keyfile");
QCommandLineOption pwstdinOption("pw-stdin",
QObject::tr("read password of the database from stdin"));
QCommandLineOption configOption("config", QObject::tr("path to a custom config file"), "config");
QCommandLineOption keyfileOption("keyfile", QObject::tr("key file of the database"), "keyfile");
QCommandLineOption pwstdinOption("pw-stdin", QObject::tr("read password of the database from stdin"));
// This is needed under Windows where clients send --parent-window parameter with Native Messaging connect method
QCommandLineOption parentWindowOption(QStringList() << "pw"
<< "parent-window",
@ -106,9 +100,7 @@ int main(int argc, char** argv)
if (!fileNames.isEmpty()) {
app.sendFileNamesToRunningInstance(fileNames);
}
qWarning() << QObject::tr("Another instance of KeePassXC is already running.")
.toUtf8()
.constData();
qWarning() << QObject::tr("Another instance of KeePassXC is already running.").toUtf8().constData();
return 0;
}

View file

@ -19,7 +19,7 @@
#include <QCoreApplication>
#ifdef Q_OS_WIN
#include <Winsock2.h>
#include <winsock2.h>
#endif
NativeMessagingHost::NativeMessagingHost()
@ -36,14 +36,12 @@ NativeMessagingHost::NativeMessagingHost()
}
#ifdef Q_OS_WIN
m_running.store(true);
m_future =
QtConcurrent::run(this, static_cast<void (NativeMessagingHost::*)()>(&NativeMessagingHost::readNativeMessages));
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)),
this,
SLOT(socketStateChanged(QLocalSocket::LocalSocketState)));
}

View file

@ -25,7 +25,7 @@ class NativeMessagingHost : public NativeMessagingBase
Q_OBJECT
public:
NativeMessagingHost();
~NativeMessagingHost();
~NativeMessagingHost() override;
public slots:
void newLocalMessage();
@ -33,12 +33,14 @@ public slots:
void socketStateChanged(QLocalSocket::LocalSocketState socketState);
private:
void readNativeMessages();
void readLength();
bool readStdIn(const quint32 length);
void readNativeMessages() override;
void readLength() override;
bool readStdIn(const quint32 length) override;
private:
QLocalSocket* m_localSocket;
Q_DISABLE_COPY(NativeMessagingHost)
};
#endif // NATIVEMESSAGINGHOST_H

View file

@ -55,13 +55,29 @@ void catchUnixSignals(std::initializer_list<int> quitSignals)
sigaction(sig, &sa, nullptr);
}
}
#else
#include <windows.h>
BOOL WINAPI ConsoleHandler(DWORD dwType)
{
switch (dwType) {
case CTRL_C_EVENT:
case CTRL_SHUTDOWN_EVENT:
case CTRL_LOGOFF_EVENT:
QCoreApplication::quit();
break;
}
return TRUE;
}
#endif
int main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX)
#ifndef Q_OS_WIN
catchUnixSignals({SIGQUIT, SIGINT, SIGTERM, SIGHUP});
#else
SetConsoleCtrlHandler(static_cast<PHANDLER_ROUTINE>(ConsoleHandler), TRUE);
#endif
NativeMessagingHost host;
return a.exec();

View file

@ -28,6 +28,7 @@ SymmetricCipherStream::SymmetricCipherStream(QIODevice* baseDevice,
, m_error(false)
, m_isInitialized(false)
, m_dataWritten(false)
, m_streamCipher(false)
{
}

View file

@ -16,11 +16,11 @@
*/
#include "UpdateChecker.h"
#include "core/Config.h"
#include "config-keepassx.h"
#include "core/Config.h"
#include <QJsonObject>
#include <QtNetwork>
#include <QNetworkAccessManager>
#include <QtNetwork>
UpdateChecker* UpdateChecker::m_instance(nullptr);
@ -28,6 +28,7 @@ UpdateChecker::UpdateChecker(QObject* parent)
: QObject(parent)
, m_netMgr(new QNetworkAccessManager(this))
, m_reply(nullptr)
, m_isManuallyRequested(false)
{
}

View file

@ -17,8 +17,8 @@
#ifndef KEEPASSXC_UPDATECHECK_H
#define KEEPASSXC_UPDATECHECK_H
#include <QString>
#include <QObject>
#include <QString>
class QNetworkAccessManager;
class QNetworkReply;
@ -57,4 +57,4 @@ inline UpdateChecker* updateCheck()
return UpdateChecker::instance();
}
#endif //KEEPASSXC_UPDATECHECK_H
#endif // KEEPASSXC_UPDATECHECK_H