Merge branch 'master' into develop

This commit is contained in:
Janek Bevendorff
2021-01-12 18:24:59 +01:00
47 changed files with 8492 additions and 286 deletions

View File

@@ -322,7 +322,7 @@ QString BrowserService::storeKey(const QString& key)
do {
QInputDialog keyDialog;
connect(m_currentDatabaseWidget, SIGNAL(databaseLocked()), &keyDialog, SLOT(reject()));
connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &keyDialog, SLOT(reject()));
keyDialog.setWindowTitle(tr("KeePassXC: New key association request"));
keyDialog.setLabelText(tr("You have received an association request for the following database:\n%1\n\n"
"Give the connection a unique name or ID, for example:\nchrome-laptop.")
@@ -778,7 +778,7 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm,
updateWindowState();
BrowserAccessControlDialog accessControlDialog;
connect(m_currentDatabaseWidget, SIGNAL(databaseLocked()), &accessControlDialog, SLOT(reject()));
connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &accessControlDialog, SLOT(reject()));
connect(&accessControlDialog, &BrowserAccessControlDialog::disableAccess, [&](QTableWidgetItem* item) {
auto entry = pwEntriesToConfirm[item->row()];

View File

@@ -36,8 +36,6 @@ const int Entry::ResolveMaximumDepth = 10;
const QString Entry::AutoTypeSequenceUsername = "{USERNAME}{ENTER}";
const QString Entry::AutoTypeSequencePassword = "{PASSWORD}{ENTER}";
Entry::CloneFlags Entry::DefaultCloneFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo;
Entry::Entry()
: m_attributes(new EntryAttributes(this))
, m_attachments(new EntryAttachments(this))

View File

@@ -161,6 +161,8 @@ public:
CloneNewUuid = 1, // generate a random uuid for the clone
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
CloneIncludeHistory = 4, // clone the history items
CloneDefault = CloneNewUuid | CloneResetTimeInfo,
CloneCopy = CloneNewUuid | CloneResetTimeInfo | CloneIncludeHistory,
CloneRenameTitle = 8, // add "-Clone" after the original title
CloneUserAsRef = 16, // Add the user as a reference to the original entry
ClonePassAsRef = 32, // Add the password as a reference to the original entry
@@ -210,7 +212,6 @@ public:
static const int ResolveMaximumDepth;
static const QString AutoTypeSequenceUsername;
static const QString AutoTypeSequencePassword;
static CloneFlags DefaultCloneFlags;
/**
* Creates a duplicate of this entry except that the returned entry isn't
@@ -218,7 +219,7 @@ public:
* Note that you need to copy the custom icons manually when inserting the
* new entry into another database.
*/
Entry* clone(CloneFlags flags = DefaultCloneFlags) const;
Entry* clone(CloneFlags flags = CloneDefault) const;
void copyDataFrom(const Entry* other);
QString maskPasswordPlaceholders(const QString& str) const;
Entry* resolveReference(const QString& str) const;

View File

@@ -36,9 +36,6 @@ const int Group::DefaultIconNumber = 48;
const int Group::RecycleBinIconNumber = 43;
const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
Group::CloneFlags Group::DefaultCloneFlags =
Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries;
Group::Group()
: m_customData(new CustomData(this))
, m_updateTimeinfo(true)

View File

@@ -56,6 +56,7 @@ public:
CloneNewUuid = 1, // generate a random uuid for the clone
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
CloneIncludeEntries = 4, // clone the group entries
CloneDefault = CloneNewUuid | CloneResetTimeInfo | CloneIncludeEntries,
};
Q_DECLARE_FLAGS(CloneFlags, CloneFlag)
@@ -108,7 +109,6 @@ public:
static const int DefaultIconNumber;
static const int RecycleBinIconNumber;
static CloneFlags DefaultCloneFlags;
static const QString RootAutoTypeSequence;
Group* findChildByName(const QString& name);
@@ -157,8 +157,8 @@ public:
QSet<QUuid> customIconsRecursive() const;
QList<QString> usernamesRecursive(int topN = -1) const;
Group* clone(Entry::CloneFlags entryFlags = Entry::DefaultCloneFlags,
CloneFlags groupFlags = DefaultCloneFlags) const;
Group* clone(Entry::CloneFlags entryFlags = Entry::CloneDefault,
Group::CloneFlags groupFlags = Group::CloneDefault) const;
void copyDataFrom(const Group* other);
QString print(bool recursive = false, bool flatten = false, int depth = 0);

View File

@@ -73,7 +73,7 @@ QString Crypto::debugInfo()
Q_ASSERT(Crypto::initialized());
QString debugInfo = QObject::tr("Cryptographic libraries:").append("\n");
debugInfo.append(" libgcrypt ").append(m_backendVersion).append("\n");
debugInfo.append("- libgcrypt ").append(m_backendVersion).append("\n");
return debugInfo;
}

View File

@@ -59,6 +59,7 @@ static const QString aboutContributors = R"(
<li>Kernellinux</li>
<li>Micha Ober</li>
<li>PublicByte</li>
<li>Clayton Casciato</li>
</ul>
<h3>Notable Code Contributions:</h3>
<ul>
@@ -86,7 +87,6 @@ static const QString aboutContributors = R"(
</ul>
<h3>Patreon Supporters:</h3>
<ul>
<li>Igor Zinovik</li>
<li>Alexanderjb</li>
<li>Richard Ames</li>
<li>SLmanDR</li>
@@ -94,7 +94,7 @@ static const QString aboutContributors = R"(
<li>Tyler Gass</li>
<li>Nuutti Toivola</li>
<li>Gregory Werbin</li>
<li>Lionel Laské</li>
<li>Lionel Laské</li>
<li>Ivar</li>
<li>Darren</li>
<li>Brad</li>

View File

@@ -59,7 +59,12 @@ void Clipboard::setText(const QString& text, bool clear)
clipboard->setMimeData(mime, QClipboard::Clipboard);
#else
mime->setText(text);
#ifdef Q_OS_LINUX
mime->setData("x-kde-passwordManagerHint", QByteArrayLiteral("secret"));
#endif
#ifdef Q_OS_WIN
mime->setData("ExcludeClipboardContentFromMonitorProcessing", QByteArrayLiteral("1"));
#endif
clipboard->setMimeData(mime, QClipboard::Clipboard);
if (clipboard->supportsSelection()) {

View File

@@ -20,12 +20,19 @@
#include "DatabaseWidget.h"
#include "core/Database.h"
#ifdef Q_OS_WIN
#include <QtPlatformHeaders/QWindowsWindowFunctions>
#endif
DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent)
: QDialog(parent)
, m_view(new DatabaseOpenWidget(this))
{
setWindowTitle(tr("Unlock Database - KeePassXC"));
setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint);
#ifdef Q_OS_WIN
QWindowsWindowFunctions::setWindowActivationBehavior(QWindowsWindowFunctions::AlwaysActivateWindow);
#endif
connect(m_view, SIGNAL(dialogFinished(bool)), this, SLOT(complete(bool)));
auto* layout = new QVBoxLayout();
layout->setMargin(0);

View File

@@ -165,6 +165,7 @@ void DatabaseOpenWidget::clearForms()
m_ui->editPassword->setText("");
m_ui->editPassword->setShowPassword(false);
m_ui->keyFileLineEdit->clear();
m_ui->keyFileLineEdit->setShowPassword(false);
m_ui->checkTouchID->setChecked(false);
m_ui->challengeResponseCombo->clear();
m_db.reset();
@@ -380,6 +381,7 @@ void DatabaseOpenWidget::browseKeyFile()
void DatabaseOpenWidget::clearKeyFileText()
{
m_ui->keyFileLineEdit->clear();
m_ui->keyFileLineEdit->setShowPassword(false);
}
void DatabaseOpenWidget::pollHardwareKey()

View File

@@ -406,7 +406,7 @@
<number>0</number>
</property>
<item row="0" column="1">
<widget class="QLineEdit" name="keyFileLineEdit">
<widget class="PasswordEdit" name="keyFileLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -416,6 +416,9 @@
<property name="accessibleName">
<string>Key file to unlock the database</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>

View File

@@ -216,7 +216,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
#ifdef WITH_XC_SSHAGENT
if (sshAgent()->isEnabled()) {
connect(this, SIGNAL(databaseLockRequested()), sshAgent(), SLOT(databaseLocked()));
connect(this, SIGNAL(databaseLocked()), sshAgent(), SLOT(databaseLocked()));
connect(this, SIGNAL(databaseUnlocked()), sshAgent(), SLOT(databaseUnlocked()));
}
#endif
@@ -437,6 +437,7 @@ void DatabaseWidget::showTotp()
}
auto totpDialog = new TotpDialog(this, currentEntry);
connect(this, &DatabaseWidget::databaseLockRequested, totpDialog, &TotpDialog::close);
totpDialog->open();
}
@@ -460,6 +461,7 @@ void DatabaseWidget::setupTotp()
auto setupTotpDialog = new TotpSetupDialog(this, currentEntry);
connect(setupTotpDialog, SIGNAL(totpUpdated()), SIGNAL(entrySelectionChanged()));
connect(this, &DatabaseWidget::databaseLockRequested, setupTotpDialog, &TotpSetupDialog::close);
setupTotpDialog->open();
}
@@ -703,6 +705,7 @@ void DatabaseWidget::showTotpKeyQrCode()
auto currentEntry = currentSelectedEntry();
if (currentEntry) {
auto totpDisplayDialog = new TotpExportSettingsDialog(this, currentEntry);
connect(this, &DatabaseWidget::databaseLockRequested, totpDisplayDialog, &TotpExportSettingsDialog::close);
totpDisplayDialog->open();
}
}
@@ -1533,6 +1536,11 @@ bool DatabaseWidget::lock()
emit databaseLockRequested();
// ignore event if we are active and a modal dialog is still open (such as a message box or file dialog)
if (isVisible() && QApplication::activeModalWidget()) {
return false;
}
clipboard()->clearCopiedText();
if (isEditWidgetModified()) {

View File

@@ -74,6 +74,18 @@ void EditWidget::addPage(const QString& labelText, const QIcon& icon, QWidget* w
m_ui->categoryList->addCategory(labelText, icon);
}
bool EditWidget::hasPage(QWidget* widget)
{
for (int i = 0; i < m_ui->stackedWidget->count(); i++) {
auto* scrollArea = qobject_cast<QScrollArea*>(m_ui->stackedWidget->widget(i));
if (scrollArea && scrollArea->widget() == widget) {
return true;
}
}
return false;
}
void EditWidget::setPageHidden(QWidget* widget, bool hidden)
{
int index = -1;

View File

@@ -42,6 +42,7 @@ public:
~EditWidget();
void addPage(const QString& labelText, const QIcon& icon, QWidget* widget);
bool hasPage(QWidget* widget);
void setPageHidden(QWidget* widget, bool hidden);
void setCurrentPage(int index);
void setHeadline(const QString& text);

View File

@@ -286,14 +286,18 @@ void EntryPreviewWidget::updateEntryAdvancedTab()
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAdvancedTab, hasAttributes || hasAttachments);
if (hasAttributes) {
QString attributesText;
QString attributesText("<table>");
for (const QString& key : customAttributes) {
QString value = m_currentEntry->attributes()->value(key);
QString value;
if (m_currentEntry->attributes()->isProtected(key)) {
value = "<i>" + tr("[PROTECTED]") + "</i>";
} else {
value = m_currentEntry->attributes()->value(key).toHtmlEscaped();
value.replace('\n', QLatin1String("<br/>"));
}
attributesText.append(tr("<b>%1</b>: %2", "attributes line").arg(key, value).append("<br/>"));
attributesText.append(tr("<tr><td><b>%1</b>:</td><td>%2</td></tr>", "attributes line").arg(key, value));
}
attributesText.append("</table>");
m_ui->entryAttributesEdit->setText(attributesText);
}
@@ -303,6 +307,8 @@ void EntryPreviewWidget::updateEntryAdvancedTab()
void EntryPreviewWidget::updateEntryAutotypeTab()
{
Q_ASSERT(m_currentEntry);
m_ui->entrySequenceLabel->setText(m_currentEntry->effectiveAutoTypeSequence());
m_ui->entryAutotypeTree->clear();
QList<QTreeWidgetItem*> items;
const AutoTypeAssociations* autotypeAssociations = m_currentEntry->autoTypeAssociations();
@@ -314,7 +320,7 @@ void EntryPreviewWidget::updateEntryAutotypeTab()
}
m_ui->entryAutotypeTree->addTopLevelItems(items);
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAutotypeTab, !items.isEmpty());
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAutotypeTab, m_currentEntry->autoTypeEnabled());
}
void EntryPreviewWidget::updateGroupHeaderLine()

View File

@@ -705,6 +705,62 @@
<string>Autotype</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QWidget" name="entryAutotypeWidget" native="true">
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="entrySequenceTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Default Sequence</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="entrySequenceLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">sequence</string>
</property>
<property name="alignment">
<set>Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="entryAutotypeTree">
<property name="focusPolicy">

View File

@@ -536,6 +536,11 @@ MainWindow::MainWindow()
m_ui->actionGroupDownloadFavicons->setVisible(false);
m_ui->actionEntryDownloadIcon->setVisible(false);
#endif
#ifndef WITH_XC_DOCS
m_ui->actionGettingStarted->setVisible(false);
m_ui->actionUserGuide->setVisible(false);
m_ui->actionKeyboardShortcuts->setVisible(false);
#endif
// clang-format off
connect(m_ui->tabWidget, SIGNAL(messageGlobal(QString,MessageWidget::MessageType)),
@@ -793,7 +798,9 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
m_ui->actionGroupSortDesc->setEnabled(groupSelected && currentGroupHasChildren);
m_ui->actionGroupEmptyRecycleBin->setVisible(recycleBinSelected);
m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected);
#ifdef WITH_XC_NETWORKING
m_ui->actionGroupDownloadFavicons->setVisible(!recycleBinSelected);
#endif
m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && currentGroupHasEntries
&& !recycleBinSelected);
m_ui->actionDatabaseSecurity->setEnabled(true);
@@ -1579,11 +1586,6 @@ void MainWindow::toggleWindow()
void MainWindow::lockDatabasesAfterInactivity()
{
// ignore event if a modal dialog is open (such as a message box or file dialog)
if (QApplication::activeModalWidget()) {
return;
}
m_ui->tabWidget->lockDatabases();
}

View File

@@ -174,9 +174,9 @@ void PasswordGeneratorWidget::saveSettings()
config()->set(Config::PasswordGenerator_AdvancedMode, m_ui->buttonAdvancedMode->isChecked());
if (m_ui->buttonAdvancedMode->isChecked()) {
config()->set(Config::PasswordGenerator_SpecialChars, m_ui->checkBoxSpecialChars->isChecked());
} else {
config()->set(Config::PasswordGenerator_Logograms, m_ui->checkBoxSpecialChars->isChecked());
} else {
config()->set(Config::PasswordGenerator_SpecialChars, m_ui->checkBoxSpecialChars->isChecked());
}
config()->set(Config::PasswordGenerator_Braces, m_ui->checkBoxBraces->isChecked());
config()->set(Config::PasswordGenerator_Punctuation, m_ui->checkBoxPunctuation->isChecked());

View File

@@ -259,9 +259,6 @@ QProgressBar::chunk {
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>128</number>
</property>
<property name="value">
<number>20</number>
</property>

View File

@@ -31,10 +31,7 @@ TotpDialog::TotpDialog(QWidget* parent, Entry* entry)
, m_ui(new Ui::TotpDialog())
, m_entry(entry)
{
if (!m_entry->hasTotp()) {
close();
return;
}
setAttribute(Qt::WA_DeleteOnClose);
m_ui->setupUi(this);
@@ -42,14 +39,11 @@ TotpDialog::TotpDialog(QWidget* parent, Entry* entry)
resetCounter();
updateProgressBar();
connect(parent, SIGNAL(databaseLocked()), SLOT(close()));
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateProgressBar()));
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateSeconds()));
m_totpUpdateTimer.start(m_step * 10);
updateTotp();
setAttribute(Qt::WA_DeleteOnClose);
new QShortcut(QKeySequence(QKeySequence::Copy), this, SLOT(copyToClipboard()));
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Copy"));

View File

@@ -59,7 +59,6 @@ TotpExportSettingsDialog::TotpExportSettingsDialog(DatabaseWidget* parent, Entry
connect(m_buttonBox, SIGNAL(rejected()), SLOT(close()));
connect(m_buttonBox, SIGNAL(accepted()), SLOT(copyToClipboard()));
connect(m_timer, SIGNAL(timeout()), SLOT(autoClose()));
connect(parent, SIGNAL(lockedDatabase()), SLOT(close()));
new QShortcut(QKeySequence(QKeySequence::Copy), this, SLOT(copyToClipboard()));

View File

@@ -178,9 +178,6 @@ void EditEntryWidget::setupMain()
m_mainUi->expirePresets->setMenu(createPresetsMenu());
connect(m_mainUi->expirePresets->menu(), SIGNAL(triggered(QAction*)), this, SLOT(useExpiryPreset(QAction*)));
// HACK: Align username text with other line edits. Qt does not let you do this with an application stylesheet.
m_mainUi->usernameComboBox->lineEdit()->setStyleSheet("padding-left: 8px;");
}
void EditEntryWidget::setupAdvanced()
@@ -268,9 +265,8 @@ void EditEntryWidget::setupAutoType()
#ifdef WITH_XC_BROWSER
void EditEntryWidget::setupBrowser()
{
m_browserUi->setupUi(m_browserWidget);
if (config()->get(Config::Browser_Enabled).toBool()) {
m_browserUi->setupUi(m_browserWidget);
addPage(tr("Browser Integration"), icons()->icon("internet-web-browser"), m_browserWidget);
m_additionalURLsDataModel->setEntryAttributes(m_entryAttributes);
m_browserUi->additionalURLsView->setModel(m_additionalURLsDataModel);
@@ -944,28 +940,34 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
#endif
#ifdef WITH_XC_BROWSER
if (m_customData->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
// clang-format off
m_browserUi->skipAutoSubmitCheckbox->setChecked(m_customData->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == TRUE_STR);
// clang-format on
} else {
m_browserUi->skipAutoSubmitCheckbox->setChecked(false);
}
if (config()->get(Config::Browser_Enabled).toBool()) {
if (!hasPage(m_browserWidget)) {
setupBrowser();
}
if (m_customData->contains(BrowserService::OPTION_HIDE_ENTRY)) {
m_browserUi->hideEntryCheckbox->setChecked(m_customData->value(BrowserService::OPTION_HIDE_ENTRY) == TRUE_STR);
} else {
m_browserUi->hideEntryCheckbox->setChecked(false);
}
if (m_customData->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
// clang-format off
m_browserUi->skipAutoSubmitCheckbox->setChecked(m_customData->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == TRUE_STR);
// clang-format on
} else {
m_browserUi->skipAutoSubmitCheckbox->setChecked(false);
}
if (m_customData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)) {
m_browserUi->onlyHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_ONLY_HTTP_AUTH)
== TRUE_STR);
} else {
m_browserUi->onlyHttpAuthCheckbox->setChecked(false);
}
if (m_customData->contains(BrowserService::OPTION_HIDE_ENTRY)) {
m_browserUi->hideEntryCheckbox->setChecked(m_customData->value(BrowserService::OPTION_HIDE_ENTRY)
== TRUE_STR);
} else {
m_browserUi->hideEntryCheckbox->setChecked(false);
}
if (m_customData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)) {
if (m_customData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)) {
m_browserUi->onlyHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_ONLY_HTTP_AUTH)
== TRUE_STR);
} else {
m_browserUi->onlyHttpAuthCheckbox->setChecked(false);
}
if (m_customData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)) {
m_browserUi->notHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_NOT_HTTP_AUTH)
== TRUE_STR);
} else {
@@ -977,9 +979,12 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
m_browserUi->editURLButton->setEnabled(false);
m_browserUi->additionalURLsView->setEditTriggers(editTriggers);
if (m_additionalURLsDataModel->rowCount() != 0) {
m_browserUi->additionalURLsView->setCurrentIndex(m_additionalURLsDataModel->index(0, 0));
if (m_additionalURLsDataModel->rowCount() != 0) {
m_browserUi->additionalURLsView->setCurrentIndex(m_additionalURLsDataModel->index(0, 0));
}
}
setPageHidden(m_browserWidget, !config()->get(Config::Browser_Enabled).toBool());
#endif
m_editWidgetProperties->setFields(entry->timeInfo(), entry->uuid());
@@ -1013,6 +1018,15 @@ bool EditEntryWidget::commitEntry()
return true;
}
// HACK: Check that entry pointer is still valid, see https://github.com/keepassxreboot/keepassxc/issues/5722
if (!m_entry) {
QMessageBox::information(this,
tr("Invalid Entry"),
tr("An external merge operation has invalidated this entry.\n"
"Unfortunately, any changes made have been lost."));
return true;
}
// Check Auto-Type validity early
if (!AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) {
return false;

View File

@@ -262,13 +262,13 @@ bool GroupModel::dropMimeData(const QMimeData* data,
targetDb->metadata()->copyCustomIcons(customIcons, sourceDb->metadata());
// Always clone the group across db's to reset UUIDs
group = dragGroup->clone();
group = dragGroup->clone(Entry::CloneDefault | Entry::CloneIncludeHistory);
if (action == Qt::MoveAction) {
// Remove the original group from the sourceDb
delete dragGroup;
}
} else if (action == Qt::CopyAction) {
group = dragGroup->clone();
group = dragGroup->clone(Entry::CloneCopy);
}
group->setParent(parentGroup, row);
@@ -303,13 +303,13 @@ bool GroupModel::dropMimeData(const QMimeData* data,
targetDb->metadata()->addCustomIcon(customIcon, sourceDb->metadata()->customIcon(customIcon));
}
// Always clone the entry across db's to reset the UUID
entry = dragEntry->clone();
// Reset the UUID when moving across db boundary
entry = dragEntry->clone(Entry::CloneDefault | Entry::CloneIncludeHistory);
if (action == Qt::MoveAction) {
delete dragEntry;
}
} else if (action == Qt::CopyAction) {
entry = dragEntry->clone();
entry = dragEntry->clone(Entry::CloneCopy);
}
entry->setGroup(parentGroup);

View File

@@ -127,10 +127,12 @@ void NixUtils::setLaunchAtStartup(bool enable)
<< QStringLiteral("StartupNotify=true") << '\n'
<< QStringLiteral("Terminal=false") << '\n'
<< QStringLiteral("Type=Application") << '\n'
<< QStringLiteral("Version=1.0") << "true" << '\n'
<< QStringLiteral("Version=1.0") << '\n'
<< QStringLiteral("Categories=Utility;Security;Qt;") << '\n'
<< QStringLiteral("MimeType=application/x-keepass2;") << '\n'
<< QStringLiteral("X-GNOME-Autostart-enabled=true") << endl;
<< QStringLiteral("X-GNOME-Autostart-enabled=true") << '\n'
<< QStringLiteral("X-GNOME-Autostart-Delay=2") << '\n'
<< QStringLiteral("X-KDE-autostart-after=panel") << endl;
desktopFile.close();
} else if (isLaunchAtStartupEnabled()) {
QFile::remove(getAutostartDesktopFilename());

View File

@@ -58,7 +58,8 @@ namespace
// Get average password length
int averagePwdLength() const
{
return m_passwords.empty() ? 0 : pwdTotalLen / m_passwords.size();
const auto nPwds = nPwdsUnique + nPwdsReused;
return nPwds == 0 ? 0 : std::round(pwdTotalLen / double(nPwds));
}
// Get max number of password reuse (=how many entries

View File

@@ -4766,7 +4766,11 @@ QRect BaseStyle::subElementRect(SubElement sr, const QStyleOption* opt, const QW
}
case SE_LineEditContents: {
QRect r = QCommonStyle::subElementRect(sr, opt, w);
int pad = Phantom::dpiScaled(Phantom::LineEdit_ContentsHPad);
int pad = Phantom::LineEdit_ContentsHPad;
if (w && qobject_cast<const QComboBox*>(w->parentWidget())) {
pad += 3;
}
pad = Phantom::dpiScaled(pad);
return r.adjusted(pad, 0, -pad, 0);
}
default:

View File

@@ -13,3 +13,7 @@ DatabaseWidget #SearchBanner, DatabaseWidget #KeeShareBanner {
border: 1px solid rgb(190, 190, 190);
padding: 2px;
}
QLineEdit {
padding-left: 2px;
}

View File

@@ -186,23 +186,29 @@ void ShareObserver::handleDatabaseChanged()
void ShareObserver::handleFileUpdated(const QString& path)
{
const Result result = importShare(path);
if (!result.isValid()) {
return;
if (!m_inFileUpdate) {
QTimer::singleShot(100, this, [this, path] {
const Result result = importShare(path);
m_inFileUpdate = false;
if (!result.isValid()) {
return;
}
QStringList success;
QStringList warning;
QStringList error;
if (result.isError()) {
error << tr("Import from %1 failed (%2)").arg(result.path, result.message);
} else if (result.isWarning()) {
warning << tr("Import from %1 failed (%2)").arg(result.path, result.message);
} else if (result.isInfo()) {
success << tr("Import from %1 successful (%2)").arg(result.path, result.message);
} else {
success << tr("Imported from %1").arg(result.path);
}
notifyAbout(success, warning, error);
});
m_inFileUpdate = true;
}
QStringList success;
QStringList warning;
QStringList error;
if (result.isError()) {
error << tr("Import from %1 failed (%2)").arg(result.path, result.message);
} else if (result.isWarning()) {
warning << tr("Import from %1 failed (%2)").arg(result.path, result.message);
} else if (result.isInfo()) {
success << tr("Import from %1 successful (%2)").arg(result.path, result.message);
} else {
success << tr("Imported from %1").arg(result.path);
}
notifyAbout(success, warning, error);
}
ShareObserver::Result ShareObserver::importShare(const QString& path)

View File

@@ -83,6 +83,7 @@ private:
QMap<QPointer<Group>, KeeShareSettings::Reference> m_groupToReference;
QMap<QString, QPointer<Group>> m_shareToGroup;
QMap<QString, QSharedPointer<FileWatcher>> m_fileWatchers;
bool m_inFileUpdate = false;
};
#endif // KEEPASSXC_SHAREOBSERVER_H