diff options
Diffstat (limited to 'src/gui/entry/EditEntryWidget.cpp')
-rw-r--r-- | src/gui/entry/EditEntryWidget.cpp | 590 |
1 files changed, 415 insertions, 175 deletions
diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 5a058bda4..3b70e4453 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -19,6 +19,7 @@ #include "EditEntryWidget.h" #include "ui_EditEntryWidgetAdvanced.h" #include "ui_EditEntryWidgetAutoType.h" +#include "ui_EditEntryWidgetSSHAgent.h" #include "ui_EditEntryWidgetHistory.h" #include "ui_EditEntryWidgetMain.h" @@ -28,7 +29,11 @@ #include <QMenu> #include <QSortFilterProxyModel> #include <QTemporaryFile> +#include <QMimeData> +#include <QEvent> +#include <QColorDialog> +#include "autotype/AutoType.h" #include "core/Config.h" #include "core/Database.h" #include "core/Entry.h" @@ -36,10 +41,17 @@ #include "core/Metadata.h" #include "core/TimeDelta.h" #include "core/Tools.h" +#ifdef WITH_XC_SSHAGENT +#include "sshagent/KeeAgentSettings.h" +#include "sshagent/OpenSSHKey.h" +#include "sshagent/SSHAgent.h" +#endif #include "gui/EditWidgetIcons.h" #include "gui/EditWidgetProperties.h" #include "gui/FileDialog.h" #include "gui/MessageBox.h" +#include "gui/Clipboard.h" +#include "gui/Font.h" #include "gui/entry/AutoTypeAssociationsModel.h" #include "gui/entry/EntryAttachmentsModel.h" #include "gui/entry/EntryAttributesModel.h" @@ -51,15 +63,15 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) , m_mainUi(new Ui::EditEntryWidgetMain()) , m_advancedUi(new Ui::EditEntryWidgetAdvanced()) , m_autoTypeUi(new Ui::EditEntryWidgetAutoType()) + , m_sshAgentUi(new Ui::EditEntryWidgetSSHAgent()) , m_historyUi(new Ui::EditEntryWidgetHistory()) , m_mainWidget(new QWidget()) , m_advancedWidget(new QWidget()) , m_iconsWidget(new EditWidgetIcons()) , m_autoTypeWidget(new QWidget()) + , m_sshAgentWidget(new QWidget()) , m_editWidgetProperties(new EditWidgetProperties()) , m_historyWidget(new QWidget()) - , m_entryAttachments(new EntryAttachments(this)) - , m_attachmentsModel(new EntryAttachmentsModel(m_advancedWidget)) , m_entryAttributes(new EntryAttributes(this)) , m_attributesModel(new EntryAttributesModel(m_advancedWidget)) , m_historyModel(new EntryHistoryModel(this)) @@ -73,12 +85,20 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) setupAdvanced(); setupIcon(); setupAutoType(); +#ifdef WITH_XC_SSHAGENT + if (config()->get("SSHAgent", false).toBool()) { + setupSSHAgent(); + m_sshAgentEnabled = true; + } else { + m_sshAgentEnabled = false; + } +#endif setupProperties(); setupHistory(); connect(this, SIGNAL(accepted()), SLOT(acceptEntry())); connect(this, SIGNAL(rejected()), SLOT(cancel())); - connect(this, SIGNAL(apply()), SLOT(saveEntry())); + connect(this, SIGNAL(apply()), SLOT(commitEntry())); connect(m_iconsWidget, SIGNAL(messageEditEntry(QString, MessageWidget::MessageType)), SLOT(showMessage(QString, MessageWidget::MessageType))); connect(m_iconsWidget, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage())); @@ -99,6 +119,7 @@ void EditEntryWidget::setupMain() connect(m_mainUi->togglePasswordButton, SIGNAL(toggled(bool)), m_mainUi->passwordEdit, SLOT(setShowPassword(bool))); connect(m_mainUi->togglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool))); connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool))); + connect(m_mainUi->notesEnabled, SIGNAL(toggled(bool)), this, SLOT(toggleHideNotes(bool))); m_mainUi->passwordRepeatEdit->enableVerifyMode(m_mainUi->passwordEdit); connect(m_mainUi->passwordGenerator, SIGNAL(appliedPassword(QString)), SLOT(setGeneratedPassword(QString))); @@ -107,7 +128,7 @@ void EditEntryWidget::setupMain() QAction *action = new QAction(this); action->setShortcut(Qt::CTRL | Qt::Key_Return); - connect(action, SIGNAL(triggered()), this, SLOT(saveEntry())); + connect(action, SIGNAL(triggered()), this, SLOT(commitEntry())); this->addAction(action); m_mainUi->passwordGenerator->hide(); @@ -119,15 +140,12 @@ void EditEntryWidget::setupAdvanced() m_advancedUi->setupUi(m_advancedWidget); addPage(tr("Advanced"), FilePath::instance()->icon("categories", "preferences-other"), m_advancedWidget); - m_attachmentsModel->setEntryAttachments(m_entryAttachments); - m_advancedUi->attachmentsView->setModel(m_attachmentsModel); - connect(m_advancedUi->attachmentsView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), - SLOT(updateAttachmentButtonsEnabled(QModelIndex))); - connect(m_advancedUi->attachmentsView, SIGNAL(doubleClicked(QModelIndex)), SLOT(openAttachment(QModelIndex))); - connect(m_advancedUi->saveAttachmentButton, SIGNAL(clicked()), SLOT(saveCurrentAttachment())); - connect(m_advancedUi->openAttachmentButton, SIGNAL(clicked()), SLOT(openCurrentAttachment())); - connect(m_advancedUi->addAttachmentButton, SIGNAL(clicked()), SLOT(insertAttachment())); - connect(m_advancedUi->removeAttachmentButton, SIGNAL(clicked()), SLOT(removeCurrentAttachment())); + m_advancedUi->attachmentsWidget->setReadOnly(false); + m_advancedUi->attachmentsWidget->setButtonsVisible(true); + + connect(m_advancedUi->attachmentsWidget, &EntryAttachmentsWidget::errorOccurred, this, [this](const QString &error) { + showMessage(error, MessageWidget::Error); + }); m_attributesModel->setEntryAttributes(m_entryAttributes); m_advancedUi->attributesView->setModel(m_attributesModel); @@ -139,6 +157,8 @@ void EditEntryWidget::setupAdvanced() connect(m_advancedUi->attributesView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(updateCurrentAttribute())); + connect(m_advancedUi->fgColorButton, SIGNAL(clicked()), SLOT(pickColor())); + connect(m_advancedUi->bgColorButton, SIGNAL(clicked()), SLOT(pickColor())); } void EditEntryWidget::setupIcon() @@ -153,8 +173,6 @@ void EditEntryWidget::setupAutoType() m_autoTypeDefaultSequenceGroup->addButton(m_autoTypeUi->inheritSequenceButton); m_autoTypeDefaultSequenceGroup->addButton(m_autoTypeUi->customSequenceButton); - m_autoTypeWindowSequenceGroup->addButton(m_autoTypeUi->defaultWindowSequenceButton); - m_autoTypeWindowSequenceGroup->addButton(m_autoTypeUi->customWindowSequenceButton); m_autoTypeAssocModel->setAutoTypeAssociations(m_autoTypeAssoc); m_autoTypeUi->assocView->setModel(m_autoTypeAssocModel); m_autoTypeUi->assocView->setColumnHidden(1, true); @@ -173,7 +191,7 @@ void EditEntryWidget::setupAutoType() connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(clearCurrentAssoc())); connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)), SLOT(applyCurrentAssoc())); - connect(m_autoTypeUi->defaultWindowSequenceButton, SIGNAL(toggled(bool)), + connect(m_autoTypeUi->customWindowSequenceButton, SIGNAL(toggled(bool)), SLOT(applyCurrentAssoc())); connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(QString)), SLOT(applyCurrentAssoc())); @@ -243,6 +261,257 @@ void EditEntryWidget::updateHistoryButtons(const QModelIndex& current, const QMo } } +#ifdef WITH_XC_SSHAGENT +void EditEntryWidget::setupSSHAgent() +{ + m_sshAgentUi->setupUi(m_sshAgentWidget); + + QFont fixedFont = Font::fixedFont(); + m_sshAgentUi->fingerprintTextLabel->setFont(fixedFont); + m_sshAgentUi->commentTextLabel->setFont(fixedFont); + m_sshAgentUi->publicKeyEdit->setFont(fixedFont); + + connect(m_sshAgentUi->attachmentRadioButton, SIGNAL(clicked(bool)), SLOT(updateSSHAgentKeyInfo())); + connect(m_sshAgentUi->attachmentComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateSSHAgentAttachment())); + connect(m_sshAgentUi->externalFileRadioButton, SIGNAL(clicked(bool)), SLOT(updateSSHAgentKeyInfo())); + connect(m_sshAgentUi->externalFileEdit, SIGNAL(textChanged(QString)), SLOT(updateSSHAgentKeyInfo())); + connect(m_sshAgentUi->browseButton, SIGNAL(clicked()), SLOT(browsePrivateKey())); + connect(m_sshAgentUi->addToAgentButton, SIGNAL(clicked()), SLOT(addKeyToAgent())); + connect(m_sshAgentUi->removeFromAgentButton, SIGNAL(clicked()), SLOT(removeKeyFromAgent())); + connect(m_sshAgentUi->decryptButton, SIGNAL(clicked()), SLOT(decryptPrivateKey())); + connect(m_sshAgentUi->copyToClipboardButton, SIGNAL(clicked()), SLOT(copyPublicKey())); + + addPage(tr("SSH Agent"), FilePath::instance()->icon("apps", "utilities-terminal"), m_sshAgentWidget); +} + +void EditEntryWidget::updateSSHAgent() +{ + KeeAgentSettings settings; + settings.fromXml(m_advancedUi->attachmentsWidget->getAttachment("KeeAgent.settings")); + + m_sshAgentUi->addKeyToAgentCheckBox->setChecked(settings.addAtDatabaseOpen()); + m_sshAgentUi->removeKeyFromAgentCheckBox->setChecked(settings.removeAtDatabaseClose()); + m_sshAgentUi->requireUserConfirmationCheckBox->setChecked(settings.useConfirmConstraintWhenAdding()); + m_sshAgentUi->lifetimeCheckBox->setChecked(settings.useLifetimeConstraintWhenAdding()); + m_sshAgentUi->lifetimeSpinBox->setValue(settings.lifetimeConstraintDuration()); + m_sshAgentUi->attachmentComboBox->clear(); + m_sshAgentUi->addToAgentButton->setEnabled(false); + m_sshAgentUi->removeFromAgentButton->setEnabled(false); + m_sshAgentUi->copyToClipboardButton->setEnabled(false); + + m_sshAgentUi->attachmentComboBox->addItem(""); + + auto attachments = m_advancedUi->attachmentsWidget->entryAttachments(); + for (const QString& fileName : attachments->keys()) { + if (fileName == "KeeAgent.settings") { + continue; + } + + m_sshAgentUi->attachmentComboBox->addItem(fileName); + } + + m_sshAgentUi->attachmentComboBox->setCurrentText(settings.attachmentName()); + m_sshAgentUi->externalFileEdit->setText(settings.fileName()); + + if (settings.selectedType() == "attachment") { + m_sshAgentUi->attachmentRadioButton->setChecked(true); + } else { + m_sshAgentUi->externalFileRadioButton->setChecked(true); + } + + m_sshAgentSettings = settings; + + updateSSHAgentKeyInfo(); +} + +void EditEntryWidget::updateSSHAgentAttachment() +{ + m_sshAgentUi->attachmentRadioButton->setChecked(true); + updateSSHAgentKeyInfo(); +} + +void EditEntryWidget::updateSSHAgentKeyInfo() +{ + m_sshAgentUi->addToAgentButton->setEnabled(false); + m_sshAgentUi->removeFromAgentButton->setEnabled(false); + m_sshAgentUi->copyToClipboardButton->setEnabled(false); + m_sshAgentUi->fingerprintTextLabel->setText(tr("n/a")); + m_sshAgentUi->commentTextLabel->setText(tr("n/a")); + m_sshAgentUi->decryptButton->setEnabled(false); + m_sshAgentUi->publicKeyEdit->document()->setPlainText(""); + + OpenSSHKey key; + + if (!getOpenSSHKey(key)) { + return; + } + + m_sshAgentUi->fingerprintTextLabel->setText(key.fingerprint()); + + if (key.encrypted()) { + m_sshAgentUi->commentTextLabel->setText(tr("(encrypted)")); + m_sshAgentUi->decryptButton->setEnabled(true); + } else { + m_sshAgentUi->commentTextLabel->setText(key.comment()); + } + + m_sshAgentUi->publicKeyEdit->document()->setPlainText(key.publicKey()); + + // enable agent buttons only if we have an agent running + if (SSHAgent::instance()->isAgentRunning()) { + m_sshAgentUi->addToAgentButton->setEnabled(true); + m_sshAgentUi->removeFromAgentButton->setEnabled(true); + } + + m_sshAgentUi->copyToClipboardButton->setEnabled(true); +} + +void EditEntryWidget::saveSSHAgentConfig() +{ + KeeAgentSettings settings; + QString privateKeyPath = m_sshAgentUi->attachmentComboBox->currentText(); + + settings.setAddAtDatabaseOpen(m_sshAgentUi->addKeyToAgentCheckBox->isChecked()); + settings.setRemoveAtDatabaseClose(m_sshAgentUi->removeKeyFromAgentCheckBox->isChecked()); + settings.setUseConfirmConstraintWhenAdding(m_sshAgentUi->requireUserConfirmationCheckBox->isChecked()); + settings.setUseLifetimeConstraintWhenAdding(m_sshAgentUi->lifetimeCheckBox->isChecked()); + settings.setLifetimeConstraintDuration(m_sshAgentUi->lifetimeSpinBox->value()); + + if (m_sshAgentUi->attachmentRadioButton->isChecked()) { + settings.setSelectedType("attachment"); + } else { + settings.setSelectedType("file"); + } + settings.setAttachmentName(m_sshAgentUi->attachmentComboBox->currentText()); + settings.setFileName(m_sshAgentUi->externalFileEdit->text()); + + // we don't use this as we don't run an agent but for compatibility we set it if necessary + settings.setAllowUseOfSshKey(settings.addAtDatabaseOpen() || settings.removeAtDatabaseClose()); + + // we don't use this either but we don't want it to dirty flag the config + settings.setSaveAttachmentToTempFile(m_sshAgentSettings.saveAttachmentToTempFile()); + + if (settings.isDefault()) { + m_advancedUi->attachmentsWidget->removeAttachment("KeeAgent.settings"); + } else if (settings != m_sshAgentSettings) { + m_advancedUi->attachmentsWidget->setAttachment("KeeAgent.settings", settings.toXml()); + } + + m_sshAgentSettings = settings; +} + +void EditEntryWidget::browsePrivateKey() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Select private key"), ""); + if (!fileName.isEmpty()) { + m_sshAgentUi->externalFileEdit->setText(fileName); + m_sshAgentUi->externalFileRadioButton->setChecked(true); + updateSSHAgentKeyInfo(); + } +} + +bool EditEntryWidget::getOpenSSHKey(OpenSSHKey& key) +{ + QByteArray privateKeyData; + + if (m_sshAgentUi->attachmentRadioButton->isChecked()) { + privateKeyData = m_advancedUi->attachmentsWidget->getAttachment(m_sshAgentUi->attachmentComboBox->currentText()); + } else { + QFile localFile(m_sshAgentUi->externalFileEdit->text()); + + if (localFile.fileName().isEmpty()) { + return false; + } + + if (localFile.size() > 1024 * 1024) { + showMessage(tr("File too large to be a private key"), MessageWidget::Error); + return false; + } + + if (!localFile.open(QIODevice::ReadOnly)) { + showMessage(tr("Failed to open private key"), MessageWidget::Error); + return false; + } + + privateKeyData = localFile.readAll(); + } + + if (privateKeyData.length() == 0) { + return false; + } + + if (!key.parse(privateKeyData)) { + showMessage(key.errorString(), MessageWidget::Error); + return false; + } + + if (key.comment().isEmpty()) { + key.setComment(m_entry->username()); + } + + return true; +} + +void EditEntryWidget::addKeyToAgent() +{ + OpenSSHKey key; + + if (!getOpenSSHKey(key)) { + return; + } + + if (!key.openPrivateKey(m_entry->password())) { + showMessage(key.errorString(), MessageWidget::Error); + } else { + m_sshAgentUi->commentTextLabel->setText(key.comment()); + m_sshAgentUi->publicKeyEdit->document()->setPlainText(key.publicKey()); + } + + quint32 lifetime = 0; + bool confirm = m_sshAgentUi->requireUserConfirmationCheckBox->isChecked(); + + if (m_sshAgentUi->lifetimeCheckBox->isChecked()) { + lifetime = m_sshAgentUi->lifetimeSpinBox->value(); + } + + SSHAgent::instance()->addIdentity(key, lifetime, confirm); + + if (m_sshAgentUi->removeKeyFromAgentCheckBox->isChecked()) { + SSHAgent::instance()->removeIdentityAtLock(key, m_entry->uuid()); + } +} + +void EditEntryWidget::removeKeyFromAgent() +{ + OpenSSHKey key; + + if (getOpenSSHKey(key)) { + SSHAgent::instance()->removeIdentity(key); + } +} + +void EditEntryWidget::decryptPrivateKey() +{ + OpenSSHKey key; + + if (!getOpenSSHKey(key)) { + return; + } + + if (!key.openPrivateKey(m_entry->password())) { + showMessage(key.errorString(), MessageWidget::Error); + } else { + m_sshAgentUi->commentTextLabel->setText(key.comment()); + m_sshAgentUi->publicKeyEdit->document()->setPlainText(key.publicKey()); + } +} + +void EditEntryWidget::copyPublicKey() +{ + clipboard()->setText(m_sshAgentUi->publicKeyEdit->document()->toPlainText()); +} +#endif + void EditEntryWidget::useExpiryPreset(QAction* action) { m_mainUi->expireCheck->setChecked(true); @@ -252,13 +521,10 @@ void EditEntryWidget::useExpiryPreset(QAction* action) m_mainUi->expireDatePicker->setDateTime(expiryDateTime); } -void EditEntryWidget::updateAttachmentButtonsEnabled(const QModelIndex& current) +void EditEntryWidget::toggleHideNotes(bool visible) { - bool enable = current.isValid(); - - m_advancedUi->saveAttachmentButton->setEnabled(enable); - m_advancedUi->openAttachmentButton->setEnabled(enable); - m_advancedUi->removeAttachmentButton->setEnabled(enable && !m_history); + m_mainUi->notesEdit->setVisible(visible); + m_mainUi->notesHint->setVisible(!visible); } QString EditEntryWidget::entryTitle() const @@ -309,12 +575,15 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore) m_mainUi->passwordRepeatEdit->setReadOnly(m_history); m_mainUi->expireCheck->setEnabled(!m_history); m_mainUi->expireDatePicker->setReadOnly(m_history); + m_mainUi->notesEnabled->setChecked(!config()->get("security/hidenotes").toBool()); m_mainUi->notesEdit->setReadOnly(m_history); + m_mainUi->notesEdit->setVisible(!config()->get("security/hidenotes").toBool()); + m_mainUi->notesHint->setVisible(config()->get("security/hidenotes").toBool()); m_mainUi->togglePasswordGeneratorButton->setChecked(false); m_mainUi->togglePasswordGeneratorButton->setDisabled(m_history); m_mainUi->passwordGenerator->reset(); - m_advancedUi->addAttachmentButton->setEnabled(!m_history); - updateAttachmentButtonsEnabled(m_advancedUi->attachmentsView->currentIndex()); + + m_advancedUi->attachmentsWidget->setReadOnly(m_history); m_advancedUi->addAttributeButton->setEnabled(!m_history); m_advancedUi->editAttributeButton->setEnabled(false); m_advancedUi->removeAttributeButton->setEnabled(false); @@ -327,6 +596,8 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore) editTriggers = QAbstractItemView::DoubleClicked; } m_advancedUi->attributesView->setEditTriggers(editTriggers); + setupColorButton(true, entry->foregroundColor()); + setupColorButton(false, entry->backgroundColor()); m_iconsWidget->setEnabled(!m_history); m_autoTypeUi->sequenceEdit->setReadOnly(m_history); m_autoTypeUi->windowTitleCombo->lineEdit()->setReadOnly(m_history); @@ -345,7 +616,7 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore) m_mainUi->notesEdit->setPlainText(entry->notes()); - m_entryAttachments->copyDataFrom(entry->attachments()); + m_advancedUi->attachmentsWidget->setEntryAttachments(entry->attachments()); m_entryAttributes->copyCustomKeysFrom(entry->attributes()); if (m_attributesModel->rowCount() != 0) { @@ -376,9 +647,10 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore) } m_autoTypeUi->sequenceEdit->setText(entry->effectiveAutoTypeSequence()); m_autoTypeUi->windowTitleCombo->lineEdit()->clear(); - m_autoTypeUi->defaultWindowSequenceButton->setChecked(true); + m_autoTypeUi->customWindowSequenceButton->setChecked(false); m_autoTypeUi->windowSequenceEdit->setText(""); m_autoTypeAssoc->copyDataFrom(entry->autoTypeAssociations()); + m_autoTypeAssocModel->setEntry(entry); if (m_autoTypeAssoc->size() != 0) { m_autoTypeUi->assocView->setCurrentIndex(m_autoTypeAssocModel->index(0, 0)); } @@ -387,7 +659,14 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore) } updateAutoTypeEnabled(); +#ifdef WITH_XC_SSHAGENT + if (m_sshAgentEnabled) { + updateSSHAgent(); + } +#endif + m_editWidgetProperties->setFields(entry->timeInfo(), entry->uuid()); + m_editWidgetProperties->setCustomData(entry->customData()); if (!m_history && !restore) { m_historyModel->setEntries(entry->historyItems()); @@ -405,20 +684,39 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore) m_mainUi->titleEdit->setFocus(); } -void EditEntryWidget::saveEntry() +/** + * Commit the form values to in-memory database representation + * + * @return true is commit successful, otherwise false + */ +bool EditEntryWidget::commitEntry() { if (m_history) { clear(); hideMessage(); emit editFinished(false); - return; + return true; } if (!passwordsEqual()) { showMessage(tr("Different passwords supplied."), MessageWidget::Error); - return; + return false; + } + + // Ask the user to apply the generator password, if open + if (m_mainUi->togglePasswordGeneratorButton->isChecked() && + m_mainUi->passwordGenerator->getGeneratedPassword() != m_mainUi->passwordEdit->text()) { + auto answer = MessageBox::question(this, tr("Apply generated password?"), + tr("Do you want to apply the generated password to this entry?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (answer == QMessageBox::Yes) { + m_mainUi->passwordGenerator->applyPassword(); + } } + // Hide the password generator + m_mainUi->togglePasswordGeneratorButton->setChecked(false); + if (m_advancedUi->attributesView->currentIndex().isValid() && m_advancedUi->attributesEdit->isEnabled()) { QString key = m_attributesModel->keyByIndex(m_advancedUi->attributesView->currentIndex()); m_entryAttributes->set(key, m_advancedUi->attributesEdit->toPlainText(), @@ -434,6 +732,12 @@ void EditEntryWidget::saveEntry() m_autoTypeAssoc->removeEmpty(); +#ifdef WITH_XC_SSHAGENT + if (m_sshAgentEnabled) { + saveSSHAgentConfig(); + } +#endif + if (!m_create) { m_entry->beginUpdate(); } @@ -444,52 +748,72 @@ void EditEntryWidget::saveEntry() if (!m_create) { m_entry->endUpdate(); } + +#ifdef WITH_XC_SSHAGENT + if (m_sshAgentEnabled) { + updateSSHAgent(); + } +#endif + + m_historyModel->setEntries(m_entry->historyItems()); + + showMessage(tr("Entry updated successfully."), MessageWidget::Positive); + return true; } void EditEntryWidget::acceptEntry() { - // Check if passwords are mismatched first to prevent saving - if (!passwordsEqual()) { - showMessage(tr("Different passwords supplied."), MessageWidget::Error); - return; + if (commitEntry()) { + clear(); + hideMessage(); + emit editFinished(true); } - - saveEntry(); - clear(); - emit editFinished(true); } void EditEntryWidget::updateEntryData(Entry* entry) const { + QRegularExpression newLineRegex("(?:\r?\n|\r)"); + entry->attributes()->copyCustomKeysFrom(m_entryAttributes); - entry->attachments()->copyDataFrom(m_entryAttachments); - - entry->setTitle(m_mainUi->titleEdit->text()); - entry->setUsername(m_mainUi->usernameEdit->text()); - entry->setUrl(m_mainUi->urlEdit->text()); + entry->attachments()->copyDataFrom(m_advancedUi->attachmentsWidget->entryAttachments()); + entry->customData()->copyDataFrom(m_editWidgetProperties->customData()); + entry->setTitle(m_mainUi->titleEdit->text().replace(newLineRegex, " ")); + entry->setUsername(m_mainUi->usernameEdit->text().replace(newLineRegex, " ")); + entry->setUrl(m_mainUi->urlEdit->text().replace(newLineRegex, " ")); entry->setPassword(m_mainUi->passwordEdit->text()); entry->setExpires(m_mainUi->expireCheck->isChecked()); entry->setExpiryTime(m_mainUi->expireDatePicker->dateTime().toUTC()); entry->setNotes(m_mainUi->notesEdit->toPlainText()); + if (m_advancedUi->fgColorCheckBox->isChecked() && + m_advancedUi->fgColorButton->property("color").isValid()) { + entry->setForegroundColor(QColor(m_advancedUi->fgColorButton->property("color").toString())); + } else { + entry->setForegroundColor(QColor()); + } + + if (m_advancedUi->bgColorCheckBox->isChecked() && + m_advancedUi->bgColorButton->property("color").isValid()) { + entry->setBackgroundColor(QColor(m_advancedUi->bgColorButton->property("color").toString())); + } else { + entry->setBackgroundColor(QColor()); + } + IconStruct iconStruct = m_iconsWidget->state(); if (iconStruct.number < 0) { entry->setIcon(Entry::DefaultIconNumber); - } - else if (iconStruct.uuid.isNull()) { + } else if (iconStruct.uuid.isNull()) { entry->setIcon(iconStruct.number); - } - else { + } else { entry->setIcon(iconStruct.uuid); } entry->setAutoTypeEnabled(m_autoTypeUi->enableButton->isChecked()); if (m_autoTypeUi->inheritSequenceButton->isChecked()) { entry->setDefaultAutoTypeSequence(QString()); - } - else { + } else if (AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) { entry->setDefaultAutoTypeSequence(m_autoTypeUi->sequenceEdit->text()); } @@ -520,7 +844,7 @@ void EditEntryWidget::clear() m_entry = nullptr; m_database = nullptr; m_entryAttributes->clear(); - m_entryAttachments->clear(); + m_advancedUi->attachmentsWidget->clearAttachments(); m_autoTypeAssoc->clear(); m_historyModel->clear(); m_iconsWidget->reset(); @@ -633,7 +957,7 @@ void EditEntryWidget::displayAttribute(QModelIndex index, bool showProtected) if (index.isValid()) { QString key = m_attributesModel->keyByIndex(index); if (showProtected) { - m_advancedUi->attributesEdit->setPlainText(tr("[PROTECTED] Press reveal to view or edit")); + m_advancedUi->attributesEdit->setPlainText(tr("[PROTECTED]") + " " + tr("Press reveal to view or edit")); m_advancedUi->attributesEdit->setEnabled(false); m_advancedUi->revealAttributeButton->setEnabled(true); m_advancedUi->protectAttributeButton->setChecked(true); @@ -693,123 +1017,6 @@ void EditEntryWidget::revealCurrentAttribute() } } -void EditEntryWidget::insertAttachment() -{ - Q_ASSERT(!m_history); - - QString defaultDir = config()->get("LastAttachmentDir").toString(); - if (defaultDir.isEmpty() || !QDir(defaultDir).exists()) { - defaultDir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0); - } - QString filename = fileDialog()->getOpenFileName(this, tr("Select file"), defaultDir); - if (filename.isEmpty() || !QFile::exists(filename)) { - return; - } - - QFile file(filename); - if (!file.open(QIODevice::ReadOnly)) { - showMessage(tr("Unable to open file").append(":\n").append(file.errorString()), MessageWidget::Error); - return; - } - - QByteArray data; - if (!Tools::readAllFromDevice(&file, data)) { - showMessage(tr("Unable to open file").append(":\n").append(file.errorString()), MessageWidget::Error); - return; - } - - m_entryAttachments->set(QFileInfo(filename).fileName(), data); -} - -void EditEntryWidget::saveCurrentAttachment() -{ - QModelIndex index = m_advancedUi->attachmentsView->currentIndex(); - if (!index.isValid()) { - return; - } - - QString filename = m_attachmentsModel->keyByIndex(index); - QString defaultDirName = config()->get("LastAttachmentDir").toString(); - if (defaultDirName.isEmpty() || !QDir(defaultDirName).exists()) { - defaultDirName = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - } - QDir dir(defaultDirName); - QString savePath = fileDialog()->getSaveFileName(this, tr("Save attachment"), - dir.filePath(filename)); - if (!savePath.isEmpty()) { - QByteArray attachmentData = m_entryAttachments->value(filename); - - QFile file(savePath); - if (!file.open(QIODevice::WriteOnly)) { - showMessage(tr("Unable to save the attachment:\n").append(file.errorString()), MessageWidget::Error); - return; - } - if (file.write(attachmentData) != attachmentData.size()) { - showMessage(tr("Unable to save the attachment:\n").append(file.errorString()), MessageWidget::Error); - return; - } - } -} - -void EditEntryWidget::openAttachment(const QModelIndex& index) -{ - if (!index.isValid()) { - Q_ASSERT(false); - return; - } - - QString filename = m_attachmentsModel->keyByIndex(index); - QByteArray attachmentData = m_entryAttachments->value(filename); - - // tmp file will be removed once the database (or the application) has been closed - QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename)); - QTemporaryFile* file = new QTemporaryFile(tmpFileTemplate, this); - - if (!file->open()) { - showMessage(tr("Unable to save the attachment:\n").append(file->errorString()), MessageWidget::Error); - return; - } - - if (file->write(attachmentData) != attachmentData.size()) { - showMessage(tr("Unable to save the attachment:\n").append(file->errorString()), MessageWidget::Error); - return; - } - - if (!file->flush()) { - showMessage(tr("Unable to save the attachment:\n").append(file->errorString()), MessageWidget::Error); - return; - } - - file->close(); - - QDesktopServices::openUrl(QUrl::fromLocalFile(file->fileName())); -} - -void EditEntryWidget::openCurrentAttachment() -{ - QModelIndex index = m_advancedUi->attachmentsView->currentIndex(); - - openAttachment(index); -} - -void EditEntryWidget::removeCurrentAttachment() -{ - Q_ASSERT(!m_history); - - QModelIndex index = m_advancedUi->attachmentsView->currentIndex(); - if (!index.isValid()) { - return; - } - - QMessageBox::StandardButton ans = MessageBox::question(this, tr("Confirm Remove"), - tr("Are you sure you want to remove this attachment?"), - QMessageBox::Yes | QMessageBox::No); - if (ans == QMessageBox::Yes) { - QString key = m_attachmentsModel->keyByIndex(index); - m_entryAttachments->remove(key); - } -} - void EditEntryWidget::updateAutoTypeEnabled() { bool autoTypeEnabled = m_autoTypeUi->enableButton->isChecked(); @@ -826,7 +1033,6 @@ void EditEntryWidget::updateAutoTypeEnabled() m_autoTypeUi->windowTitleLabel->setEnabled(autoTypeEnabled && validIndex); m_autoTypeUi->windowTitleCombo->setEnabled(autoTypeEnabled && validIndex); - m_autoTypeUi->defaultWindowSequenceButton->setEnabled(!m_history && autoTypeEnabled && validIndex); m_autoTypeUi->customWindowSequenceButton->setEnabled(!m_history && autoTypeEnabled && validIndex); m_autoTypeUi->windowSequenceEdit->setEnabled(autoTypeEnabled && validIndex && m_autoTypeUi->customWindowSequenceButton->isChecked()); @@ -857,16 +1063,15 @@ void EditEntryWidget::loadCurrentAssoc(const QModelIndex& current) AutoTypeAssociations::Association assoc = m_autoTypeAssoc->get(current.row()); m_autoTypeUi->windowTitleCombo->setEditText(assoc.window); if (assoc.sequence.isEmpty()) { - m_autoTypeUi->defaultWindowSequenceButton->setChecked(true); - } - else { + m_autoTypeUi->customWindowSequenceButton->setChecked(false); + m_autoTypeUi->windowSequenceEdit->setText(m_entry->effectiveAutoTypeSequence()); + } else { m_autoTypeUi->customWindowSequenceButton->setChecked(true); + m_autoTypeUi->windowSequenceEdit->setText(assoc.sequence); } - m_autoTypeUi->windowSequenceEdit->setText(assoc.sequence); updateAutoTypeEnabled(); - } - else { + } else { clearCurrentAssoc(); } } @@ -875,7 +1080,7 @@ void EditEntryWidget::clearCurrentAssoc() { m_autoTypeUi->windowTitleCombo->setEditText(""); - m_autoTypeUi->defaultWindowSequenceButton->setChecked(true); + m_autoTypeUi->customWindowSequenceButton->setChecked(false); m_autoTypeUi->windowSequenceEdit->setText(""); updateAutoTypeEnabled(); @@ -954,3 +1159,38 @@ QMenu* EditEntryWidget::createPresetsMenu() expirePresetsMenu->addAction(tr("1 year"))->setData(QVariant::fromValue(TimeDelta::fromYears(1))); return expirePresetsMenu; } + +void EditEntryWidget::setupColorButton(bool foreground, const QColor& color) +{ + QWidget* button = m_advancedUi->fgColorButton; + QCheckBox* checkBox = m_advancedUi->fgColorCheckBox; + if (!foreground) { + button = m_advancedUi->bgColorButton; + checkBox = m_advancedUi->bgColorCheckBox; + } + + if (color.isValid()) { + button->setStyleSheet(QString("background-color:%1").arg(color.name())); + button->setProperty("color", color.name()); + checkBox->setChecked(true); + } else { + button->setStyleSheet(""); + button->setProperty("color", QVariant()); + checkBox->setChecked(false); + } +} + +void EditEntryWidget::pickColor() +{ + bool isForeground = (sender() == m_advancedUi->fgColorButton); + QColor oldColor = QColor(m_advancedUi->fgColorButton->property("color").toString()); + if (!isForeground) { + oldColor = QColor(m_advancedUi->bgColorButton->property("color").toString()); + } + + QColorDialog colorDialog(this); + QColor newColor = colorDialog.getColor(oldColor); + if (newColor.isValid()) { + setupColorButton(isForeground, newColor); + } +} |