diff --git a/src/crypto/gui/signencryptwidget.cpp b/src/crypto/gui/signencryptwidget.cpp index a7aee76d9..9fbee76f1 100644 --- a/src/crypto/gui/signencryptwidget.cpp +++ b/src/crypto/gui/signencryptwidget.cpp @@ -1,827 +1,829 @@ /* crypto/gui/signencryptwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "signencryptwidget.h" #include "kleopatra_debug.h" #include "certificatelineedit.h" #include "fileoperationspreferences.h" #include "kleopatraapplication.h" #include "settings.h" #include "unknownrecipientwidget.h" #include "dialogs/certificateselectiondialog.h" #include "utils/gui-helper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Dialogs; using namespace GpgME; namespace { class SignCertificateFilter: public DefaultKeyFilter { public: SignCertificateFilter(GpgME::Protocol proto) : DefaultKeyFilter() { setRevoked(DefaultKeyFilter::NotSet); setExpired(DefaultKeyFilter::NotSet); setHasSecret(DefaultKeyFilter::Set); setCanSign(DefaultKeyFilter::Set); setValidIfSMIME(DefaultKeyFilter::Set); if (proto == GpgME::OpenPGP) { setIsOpenPGP(DefaultKeyFilter::Set); } else if (proto == GpgME::CMS) { setIsOpenPGP(DefaultKeyFilter::NotSet); } } }; class EncryptCertificateFilter: public DefaultKeyFilter { public: EncryptCertificateFilter(GpgME::Protocol proto): DefaultKeyFilter() { setRevoked(DefaultKeyFilter::NotSet); setExpired(DefaultKeyFilter::NotSet); setCanEncrypt(DefaultKeyFilter::Set); setValidIfSMIME(DefaultKeyFilter::Set); if (proto == GpgME::OpenPGP) { setIsOpenPGP(DefaultKeyFilter::Set); } else if (proto == GpgME::CMS) { setIsOpenPGP(DefaultKeyFilter::NotSet); } } }; class EncryptSelfCertificateFilter: public EncryptCertificateFilter { public: EncryptSelfCertificateFilter(GpgME::Protocol proto): EncryptCertificateFilter(proto) { setRevoked(DefaultKeyFilter::NotSet); setExpired(DefaultKeyFilter::NotSet); setCanEncrypt(DefaultKeyFilter::Set); setHasSecret(DefaultKeyFilter::Set); setValidIfSMIME(DefaultKeyFilter::Set); } }; } class SignEncryptWidget::Private { SignEncryptWidget *const q; public: struct RecipientWidgets { CertificateLineEdit *edit; KMessageWidget *expiryMessage; }; explicit Private(SignEncryptWidget *qq, bool sigEncExclusive) : q{qq} , mModel{AbstractKeyListModel::createFlatKeyListModel(qq)} , mIsExclusive{sigEncExclusive} , mExpiryChecker{new ExpiryChecker{ExpiryCheckerConfig{}.settings(), qq}} { } CertificateLineEdit* addRecipientWidget(); /* Inserts a new recipient widget after widget @p after or at the end * if @p after is null. */ CertificateLineEdit* insertRecipientWidget(CertificateLineEdit *after); void recpRemovalRequested(const RecipientWidgets &recipient); void onProtocolChanged(); void updateCheckBoxes(); void updateExpiryMessages(KMessageWidget *w, const GpgME::Key &key, ExpiryChecker::CheckFlags flags); public: KeySelectionCombo *mSigSelect = nullptr; KMessageWidget *mSignKeyExpiryMessage = nullptr; KeySelectionCombo *mSelfSelect = nullptr; KMessageWidget *mEncryptToSelfKeyExpiryMessage = nullptr; std::vector mRecpWidgets; QVector mUnknownWidgets; QVector mAddedKeys; QVector mAddedGroups; QVBoxLayout *mRecpLayout = nullptr; Operations mOp; AbstractKeyListModel *mModel = nullptr; QCheckBox *mSymmetric = nullptr; QCheckBox *mSigChk = nullptr; QCheckBox *mEncOtherChk = nullptr; QCheckBox *mEncSelfChk = nullptr; GpgME::Protocol mCurrentProto = GpgME::UnknownProtocol; const bool mIsExclusive; ExpiryChecker *mExpiryChecker; }; SignEncryptWidget::SignEncryptWidget(QWidget *parent, bool sigEncExclusive) : QWidget{parent} , d{new Private{this, sigEncExclusive}} { auto lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); d->mModel->useKeyCache(true, KeyList::IncludeGroups); const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); const bool havePublicKeys = !KeyCache::instance()->keys().empty(); const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); /* The signature selection */ { auto sigGrp = new QGroupBox{i18nc("@title:group", "Prove authenticity (sign)"), this}; d->mSigChk = new QCheckBox{i18n("Sign as:"), this}; d->mSigChk->setEnabled(haveSecretKeys); d->mSigChk->setChecked(haveSecretKeys); d->mSigSelect = new KeySelectionCombo{this}; d->mSigSelect->setEnabled(d->mSigChk->isChecked()); d->mSignKeyExpiryMessage = new KMessageWidget{this}; + d->mSignKeyExpiryMessage->setVisible(false); auto groupLayout = new QGridLayout{sigGrp}; groupLayout->setColumnStretch(1, 1); groupLayout->addWidget(d->mSigChk, 0, 0); groupLayout->addWidget(d->mSigSelect, 0, 1); groupLayout->addWidget(d->mSignKeyExpiryMessage, 1, 1); lay->addWidget(sigGrp); connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool checked) { d->mSigSelect->setEnabled(checked); updateOp(); d->updateExpiryMessages(d->mSignKeyExpiryMessage, signKey(), ExpiryChecker::OwnSigningKey); }); connect(d->mSigSelect, &KeySelectionCombo::currentKeyChanged, this, [this]() { updateOp(); d->updateExpiryMessages(d->mSignKeyExpiryMessage, signKey(), ExpiryChecker::OwnSigningKey); }); } // Recipient selection { auto encBox = new QGroupBox{i18nc("@title:group", "Encrypt"), this}; auto encBoxLay = new QVBoxLayout{encBox}; auto recipientGrid = new QGridLayout; int row = 0; // Own key d->mEncSelfChk = new QCheckBox{i18n("Encrypt for me:"), this}; d->mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly); d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly); d->mSelfSelect = new KeySelectionCombo{this}; d->mSelfSelect->setEnabled(d->mEncSelfChk->isChecked()); d->mEncryptToSelfKeyExpiryMessage = new KMessageWidget{this}; + d->mEncryptToSelfKeyExpiryMessage->setVisible(false); recipientGrid->addWidget(d->mEncSelfChk, row, 0); recipientGrid->addWidget(d->mSelfSelect, row, 1); row++; recipientGrid->addWidget(d->mEncryptToSelfKeyExpiryMessage, row, 1); // Checkbox for other keys row++; d->mEncOtherChk = new QCheckBox{i18n("Encrypt for others:"), this}; d->mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly); d->mEncOtherChk->setChecked(havePublicKeys && !symmetricOnly); recipientGrid->addWidget(d->mEncOtherChk, row, 0, Qt::AlignTop); connect(d->mEncOtherChk, &QCheckBox::toggled, this, [this](bool checked) { for (const auto &recipient : std::as_const(d->mRecpWidgets)) { recipient.edit->setEnabled(checked); d->updateExpiryMessages(recipient.expiryMessage, checked ? recipient.edit->key() : Key{}, ExpiryChecker::EncryptionKey); } updateOp(); }); d->mRecpLayout = new QVBoxLayout; recipientGrid->addLayout(d->mRecpLayout, row, 1); recipientGrid->setRowStretch(row + 1, 1); // Scroll area for other keys auto recipientWidget = new QWidget; auto recipientScroll = new QScrollArea; recipientWidget->setLayout(recipientGrid); recipientScroll->setWidget(recipientWidget); recipientScroll->setWidgetResizable(true); recipientScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow); recipientScroll->setFrameStyle(QFrame::NoFrame); recipientScroll->setFocusPolicy(Qt::NoFocus); recipientGrid->setContentsMargins(0, 0, 0, 0); encBoxLay->addWidget(recipientScroll, 1); auto bar = recipientScroll->verticalScrollBar(); connect (bar, &QScrollBar::rangeChanged, this, [bar] (int, int max) { bar->setValue(max); }); d->addRecipientWidget(); // Checkbox for password d->mSymmetric = new QCheckBox(i18n("Encrypt with password. Anyone you share the password with can read the data.")); d->mSymmetric->setToolTip(i18nc("Tooltip information for symmetric encryption", "Additionally to the keys of the recipients you can encrypt your data with a password. " "Anyone who has the password can read the data without any secret key. " "Using a password is less secure then public key cryptography. Even if you pick a very strong password.")); d->mSymmetric->setChecked(symmetricOnly || !havePublicKeys); encBoxLay->addWidget(d->mSymmetric); // Connect it connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) { d->mSelfSelect->setEnabled(checked); updateOp(); d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfKey(), ExpiryChecker::OwnEncryptionKey); }); connect(d->mSelfSelect, &KeySelectionCombo::currentKeyChanged, this, [this]() { updateOp(); d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfKey(), ExpiryChecker::OwnEncryptionKey); }); connect(d->mSymmetric, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp); if (d->mIsExclusive) { connect(d->mEncOtherChk, &QCheckBox::toggled, this, [this](bool value) { if (d->mCurrentProto != GpgME::CMS) { return; } if (value) { d->mSigChk->setChecked(false); } }); connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool value) { if (d->mCurrentProto != GpgME::CMS) { return; } if (value) { d->mSigChk->setChecked(false); } }); connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool value) { if (d->mCurrentProto != GpgME::CMS) { return; } if (value) { d->mEncSelfChk->setChecked(false); d->mEncOtherChk->setChecked(false); } }); } // Ensure that the d->mSigChk is aligned together with the encryption check boxes. d->mSigChk->setMinimumWidth(qMax(d->mEncOtherChk->width(), d->mEncSelfChk->width())); lay->addWidget(encBox); } connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() { d->updateCheckBoxes(); }); connect(KleopatraApplication::instance(), &KleopatraApplication::configurationChanged, this, [this]() { d->updateCheckBoxes(); }); loadKeys(); d->onProtocolChanged(); updateOp(); } SignEncryptWidget::~SignEncryptWidget() = default; void SignEncryptWidget::setSignAsText(const QString &text) { d->mSigChk->setText(text); } void SignEncryptWidget::setEncryptForMeText(const QString &text) { d->mEncSelfChk->setText(text); } void SignEncryptWidget::setEncryptForOthersText(const QString &text) { d->mEncOtherChk->setText(text); } void SignEncryptWidget::setEncryptWithPasswordText(const QString& text) { d->mSymmetric->setText(text); } CertificateLineEdit *SignEncryptWidget::Private::addRecipientWidget() { return insertRecipientWidget(nullptr); } CertificateLineEdit *SignEncryptWidget::Private::insertRecipientWidget(CertificateLineEdit *after) { Q_ASSERT(!after || mRecpLayout->indexOf(after) != -1); const auto index = after ? mRecpLayout->indexOf(after) + 2 : mRecpLayout->count(); const RecipientWidgets recipient{ new CertificateLineEdit{mModel, new EncryptCertificateFilter{mCurrentProto}, q}, new KMessageWidget{q} }; recipient.edit->setAccessibleNameOfLineEdit(i18nc("text for screen readers", "recipient key")); recipient.edit->setEnabled(mEncOtherChk->isChecked()); recipient.expiryMessage->setVisible(false); if (static_cast(index / 2) < mRecpWidgets.size()) { mRecpWidgets.insert(mRecpWidgets.begin() + index / 2, recipient); } else { mRecpWidgets.push_back(recipient); } if (mRecpLayout->count() > 0) { auto prevWidget = after ? after : mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget(); Kleo::forceSetTabOrder(prevWidget, recipient.edit); Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage); } mRecpLayout->insertWidget(index, recipient.edit); mRecpLayout->insertWidget(index + 1, recipient.expiryMessage); connect(recipient.edit, &CertificateLineEdit::keyChanged, q, &SignEncryptWidget::recipientsChanged); connect(recipient.edit, &CertificateLineEdit::editingStarted, q, &SignEncryptWidget::recipientsChanged); connect(recipient.edit, &CertificateLineEdit::certificateSelectionRequested, q, [this, recipient]() { q->certificateSelectionRequested(recipient.edit); }); return recipient.edit; } void SignEncryptWidget::addRecipient(const Key &key) { CertificateLineEdit *certSel = d->addRecipientWidget(); if (!key.isNull()) { certSel->setKey(key); d->mAddedKeys << key; } } void SignEncryptWidget::addRecipient(const KeyGroup &group) { CertificateLineEdit *certSel = d->addRecipientWidget(); if (!group.isNull()) { certSel->setGroup(group); d->mAddedGroups << group; } } void SignEncryptWidget::certificateSelectionRequested(CertificateLineEdit *certificateLineEdit) { CertificateSelectionDialog dlg{this}; dlg.setOptions(CertificateSelectionDialog::Options( CertificateSelectionDialog::MultiSelection | CertificateSelectionDialog::EncryptOnly | CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto) | CertificateSelectionDialog::IncludeGroups)); if (!certificateLineEdit->key().isNull()) { const auto key = certificateLineEdit->key(); const auto name = QString::fromUtf8(key.userID(0).name()); const auto email = QString::fromUtf8(key.userID(0).email()); dlg.setStringFilter(!name.isEmpty() ? name : email); } else if (!certificateLineEdit->group().isNull()) { dlg.setStringFilter(certificateLineEdit->group().name()); } else { dlg.setStringFilter(certificateLineEdit->text()); } if (dlg.exec()) { const std::vector keys = dlg.selectedCertificates(); const std::vector groups = dlg.selectedGroups(); if (keys.size() == 0 && groups.size() == 0) { return; } CertificateLineEdit *certWidget = nullptr; for (const Key &key : keys) { if (!certWidget) { certWidget = certificateLineEdit; } else { certWidget = d->insertRecipientWidget(certWidget); } certWidget->setKey(key); } for (const KeyGroup &group : groups) { if (!certWidget) { certWidget = certificateLineEdit; } else { certWidget = d->insertRecipientWidget(certWidget); } certWidget->setGroup(group); } } recipientsChanged(); } void SignEncryptWidget::clearAddedRecipients() { for (auto w: std::as_const(d->mUnknownWidgets)) { d->mRecpLayout->removeWidget(w); delete w; } for (auto &key: std::as_const(d->mAddedKeys)) { removeRecipient(key); } for (auto &group: std::as_const(d->mAddedGroups)) { removeRecipient(group); } } void SignEncryptWidget::addUnknownRecipient(const char *keyID) { auto unknownWidget = new UnknownRecipientWidget(keyID); d->mUnknownWidgets << unknownWidget; if (d->mRecpLayout->count() > 0) { auto lastWidget = d->mRecpLayout->itemAt(d->mRecpLayout->count() - 1)->widget(); setTabOrder(lastWidget, unknownWidget); } d->mRecpLayout->addWidget(unknownWidget); connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this] () { // Check if any unknown recipient can now be found. for (auto w: d->mUnknownWidgets) { auto key = KeyCache::instance()->findByKeyIDOrFingerprint(w->keyID().toLatin1().constData()); if (key.isNull()) { std::vector subids; subids.push_back(std::string(w->keyID().toLatin1().constData())); for (const auto &subkey: KeyCache::instance()->findSubkeysByKeyID(subids)) { key = subkey.parent(); } } if (key.isNull()) { continue; } // Key is now available replace by line edit. qCDebug(KLEOPATRA_LOG) << "Removing widget for keyid: " << w->keyID(); d->mRecpLayout->removeWidget(w); d->mUnknownWidgets.removeAll(w); delete w; addRecipient(key); } }); } void SignEncryptWidget::recipientsChanged() { const bool hasEmptyRecpWidget = std::any_of(std::cbegin(d->mRecpWidgets), std::cend(d->mRecpWidgets), [](auto w) { return w.edit->isEmpty(); }); if (!hasEmptyRecpWidget) { d->addRecipientWidget(); } updateOp(); for (const auto &recipient : std::as_const(d->mRecpWidgets)) { if (!recipient.edit->isEditingInProgress()) { d->updateExpiryMessages(recipient.expiryMessage, d->mEncOtherChk->isChecked() ? recipient.edit->key() : Key{}, ExpiryChecker::EncryptionKey); } } } Key SignEncryptWidget::signKey() const { if (d->mSigSelect->isEnabled()) { return d->mSigSelect->currentKey(); } return Key(); } Key SignEncryptWidget::selfKey() const { if (d->mSelfSelect->isEnabled()) { return d->mSelfSelect->currentKey(); } return Key(); } std::vector SignEncryptWidget::recipients() const { std::vector ret; for (const auto &recipient : std::as_const(d->mRecpWidgets)) { const auto *const w = recipient.edit; if (!w->isEnabled()) { // If one is disabled, all are disabled. break; } const Key k = w->key(); const KeyGroup g = w->group(); if (!k.isNull()) { ret.push_back(k); } else if (!g.isNull()) { const auto keys = g.keys(); std::copy(keys.begin(), keys.end(), std::back_inserter(ret)); } } const Key k = selfKey(); if (!k.isNull()) { ret.push_back(k); } return ret; } bool SignEncryptWidget::isDeVsAndValid() const { if (!signKey().isNull() && !DeVSCompliance::keyIsCompliant(signKey())) { return false; } if (!selfKey().isNull() && !DeVSCompliance::keyIsCompliant(selfKey())) { return false; } for (const auto &key: recipients()) { if (!DeVSCompliance::keyIsCompliant(key)) { return false; } } return true; } static QString expiryMessage(const ExpiryChecker::Result &result) { if (result.expiration.certificate.isNull()) { return {}; } switch (result.expiration.status) { case ExpiryChecker::Expired: return i18nc("@info", "This certificate is expired."); case ExpiryChecker::ExpiresSoon: { if (result.expiration.duration.count() == 0) { return i18nc("@info", "This certificate expires today."); } else { return i18ncp("@info", "This certificate expires tomorrow.", "This certificate expires in %1 days.", result.expiration.duration.count()); } } case ExpiryChecker::NoSuitableSubkey: if (result.checkFlags & ExpiryChecker::EncryptionKey) { return i18nc("@info", "This certificate cannot be used for encryption."); } else { return i18nc("@info", "This certificate cannot be used for signing."); } case ExpiryChecker::InvalidKey: case ExpiryChecker::InvalidCheckFlags: break; // wrong usage of ExpiryChecker; can be ignored case ExpiryChecker::NotNearExpiry: ; } return {}; } void SignEncryptWidget::updateOp() { const Key sigKey = signKey(); const std::vector recp = recipients(); Operations op = NoOperation; if (!sigKey.isNull()) { op |= Sign; } if (!recp.empty() || encryptSymmetric()) { op |= Encrypt; } d->mOp = op; Q_EMIT operationChanged(d->mOp); Q_EMIT keysChanged(); } SignEncryptWidget::Operations SignEncryptWidget::currentOp() const { return d->mOp; } namespace { bool recipientWidgetHasFocus(QWidget *w) { // check if w (or its focus proxy) or a child widget of w has focus return w->hasFocus() || w->isAncestorOf(qApp->focusWidget()); } } void SignEncryptWidget::Private::recpRemovalRequested(const RecipientWidgets &recipient) { if (!recipient.edit) { return; } const int emptyEdits = std::count_if(std::cbegin(mRecpWidgets), std::cend(mRecpWidgets), [](const auto &r) { return r.edit->isEmpty(); }); if (emptyEdits > 1) { if (recipientWidgetHasFocus(recipient.edit) || recipientWidgetHasFocus(recipient.expiryMessage)) { const int index = mRecpLayout->indexOf(recipient.edit); const auto focusWidget = (index < mRecpLayout->count() - 2) ? mRecpLayout->itemAt(index + 2)->widget() : mRecpLayout->itemAt(mRecpLayout->count() - 3)->widget(); focusWidget->setFocus(); } mRecpLayout->removeWidget(recipient.expiryMessage); mRecpLayout->removeWidget(recipient.edit); const auto it = std::find_if(std::begin(mRecpWidgets), std::end(mRecpWidgets), [recipient](const auto &r) { return r.edit == recipient.edit; }); mRecpWidgets.erase(it); recipient.expiryMessage->deleteLater(); recipient.edit->deleteLater(); } } void SignEncryptWidget::removeRecipient(const GpgME::Key &key) { for (const auto &recipient: std::as_const(d->mRecpWidgets)) { const auto editKey = recipient.edit->key(); if (key.isNull() && editKey.isNull()) { d->recpRemovalRequested(recipient); return; } if (editKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(editKey.primaryFingerprint(), key.primaryFingerprint())) { d->recpRemovalRequested(recipient); return; } } } void SignEncryptWidget::removeRecipient(const KeyGroup &group) { for (const auto &recipient: std::as_const(d->mRecpWidgets)) { const auto editGroup = recipient.edit->group(); if (group.isNull() && editGroup.isNull()) { d->recpRemovalRequested(recipient); return; } if (editGroup.name() == group.name()) { d->recpRemovalRequested(recipient); return; } } } bool SignEncryptWidget::encryptSymmetric() const { return d->mSymmetric->isChecked(); } void SignEncryptWidget::loadKeys() { KConfigGroup keys(KSharedConfig::openConfig(), "SignEncryptKeys"); auto cache = KeyCache::instance(); d->mSigSelect->setDefaultKey(keys.readEntry("SigningKey", QString())); d->mSelfSelect->setDefaultKey(keys.readEntry("EncryptKey", QString())); } void SignEncryptWidget::saveOwnKeys() const { KConfigGroup keys(KSharedConfig::openConfig(), "SignEncryptKeys"); auto sigKey = d->mSigSelect->currentKey(); auto encKey = d->mSelfSelect->currentKey(); if (!sigKey.isNull()) { keys.writeEntry("SigningKey", sigKey.primaryFingerprint()); } if (!encKey.isNull()) { keys.writeEntry("EncryptKey", encKey.primaryFingerprint()); } } void SignEncryptWidget::setSigningChecked(bool value) { d->mSigChk->setChecked(value && !KeyCache::instance()->secretKeys().empty()); } void SignEncryptWidget::setEncryptionChecked(bool checked) { if (checked) { const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); const bool havePublicKeys = !KeyCache::instance()->keys().empty(); const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly); d->mEncOtherChk->setChecked(havePublicKeys && !symmetricOnly); d->mSymmetric->setChecked(symmetricOnly || !havePublicKeys); } else { d->mEncSelfChk->setChecked(false); d->mEncOtherChk->setChecked(false); d->mSymmetric->setChecked(false); } } void SignEncryptWidget::setProtocol(GpgME::Protocol proto) { if (d->mCurrentProto == proto) { return; } d->mCurrentProto = proto; d->onProtocolChanged(); } void Kleo::SignEncryptWidget::Private::onProtocolChanged() { mSigSelect->setKeyFilter(std::shared_ptr(new SignCertificateFilter(mCurrentProto))); mSelfSelect->setKeyFilter(std::shared_ptr(new EncryptSelfCertificateFilter(mCurrentProto))); const auto encFilter = std::shared_ptr(new EncryptCertificateFilter(mCurrentProto)); for (const auto &recipient : std::as_const(mRecpWidgets)) { recipient.edit->setKeyFilter(encFilter); } if (mIsExclusive) { mSymmetric->setDisabled(mCurrentProto == GpgME::CMS); if (mSymmetric->isChecked() && mCurrentProto == GpgME::CMS) { mSymmetric->setChecked(false); } if (mSigChk->isChecked() && mCurrentProto == GpgME::CMS && (mEncSelfChk->isChecked() || mEncOtherChk->isChecked())) { mSigChk->setChecked(false); } } } bool SignEncryptWidget::isComplete() const { return currentOp() != NoOperation && std::all_of(std::cbegin(d->mRecpWidgets), std::cend(d->mRecpWidgets), [](const auto &r) { return !r.edit->isEnabled() || r.edit->hasAcceptableInput(); }); } bool SignEncryptWidget::validate() { CertificateLineEdit *firstUnresolvedRecipient = nullptr; QStringList unresolvedRecipients; for (const auto &recipient: std::as_const(d->mRecpWidgets)) { if (recipient.edit->isEnabled() && !recipient.edit->hasAcceptableInput()) { if (!firstUnresolvedRecipient) { firstUnresolvedRecipient = recipient.edit; } unresolvedRecipients.push_back(recipient.edit->text().toHtmlEscaped()); } } if (!unresolvedRecipients.isEmpty()) { KMessageBox::errorList(this, i18n("Could not find a key for the following recipients:"), unresolvedRecipients, i18n("Failed to find some keys")); } if (firstUnresolvedRecipient) { firstUnresolvedRecipient->setFocus(); } return unresolvedRecipients.isEmpty(); } void SignEncryptWidget::Private::updateCheckBoxes() { const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); const bool havePublicKeys = !KeyCache::instance()->keys().empty(); const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); mSigChk->setEnabled(haveSecretKeys); mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly); mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly); if (symmetricOnly) { mEncSelfChk->setChecked(false); mEncOtherChk->setChecked(false); mSymmetric->setChecked(true); } } void SignEncryptWidget::Private::updateExpiryMessages(KMessageWidget *messageWidget, const GpgME::Key &key, ExpiryChecker::CheckFlags flags) { messageWidget->setCloseButtonVisible(false); - if (key.isNull()) { + if (!Settings{}.showExpiryNotifications() || key.isNull()) { messageWidget->setVisible(false); } else { const auto result = mExpiryChecker->checkKey(key, flags); const auto message = expiryMessage(result); messageWidget->setText(message); messageWidget->setVisible(!message.isEmpty()); } } diff --git a/src/kcfg/settings.kcfg b/src/kcfg/settings.kcfg index de8806e8c..f7c4c582a 100644 --- a/src/kcfg/settings.kcfg +++ b/src/kcfg/settings.kcfg @@ -1,182 +1,190 @@ This text will be used as placeholder text for the common name (CN) field of S/MIME certificates. If true, then the common name (CN) field of S/MIME certificates will be prefilled with information gathered from the system, e.g., from the email settings of the desktop or, on Windows, from the Active Directory. true This text will be used as placeholder text for the email address field of OpenPGP and S/MIME certificates. If true, then the email address field of OpenPGP and S/MIME certificates will be prefilled with information gathered from the system, e.g., from the email settings of the desktop or, on Windows, from the Active Directory. true This text will be used as placeholder text for the name field of OpenPGP certificates. If true, then the name field of OpenPGP certificates will be prefilled with information gathered from the system, e.g., from the email settings of the desktop or, on Windows, from the Active Directory. true Specifies the default validity period of new OpenPGP keys in days. This setting specifies how many days a new OpenPGP key is valid by default, or, in other words, after how many days the key will expire. Set this to 0 for unlimited validity. If this setting is not set or if it is set to a negative value, then new OpenPGP keys will be valid for two years (possibly clamped to the allowed minimum or maximum validity period) by default. -1 Specifies the minimum validity period of new OpenPGP keys in days. This setting specifies how many days a new OpenPGP key is valid at least, or, in other words, after how many days the new key will expire at the earliest. 0 Specifies the maximum validity period of new OpenPGP keys in days. This setting specifies how many days a new OpenPGP key is valid at most, or, in other words, after how many days the new key will expire at the latest. If this setting is not set or if it is set to a negative value, then unlimited validity is allowed. -1 If true, hides the advanced settings button in the new certificate wizard. false sha256sum If true, then the results are shown after successfully signing the clipboard. true If true, then the results are shown after successfully encrypting the clipboard. true Enables support for S/MIME (CMS). If false, then Kleopatra's main UI will not offer any functionality related to S/MIME (CMS). true Allows the creation of S/MIME certificate signing requests. If false, then Kleopatra will not offer the creation of S/MIME certificate signing requests. true Allows signing of text or files with S/MIME certificates. If false, then Kleopatra will not offer functionality for creating signatures with S/MIME certificates. true true true true true true true Specifies the display order of the DN attributes of X.509 certificates. Enable usage of groups of keys. Enable usage of groups of keys to create lists of recipients. true If enabled, then Kleopatra will automatically try to retrieve the keys that were used to certify the user ids of newly imported OpenPGP keys. This is useful in combination with trusted introducers. false + + + + If enabled, then Kleopatra will show notifications in some place when using + certificates that are about to expire soon. + true + + This is a list of URL schemes that shall be blocked by the application. This can be used to prevent the application from opening external applications for certain URLs. Searches for the certificates belonging the smartcard keys on the configured keyserver. Searches on keyservers regardless of the protocol for the smartcards key, regardless of the keyserver protocol. Default behavior is to only do this for LDAP keyservers. false Automatically load S/MIME certificates from PKCS#15 (CardOS) smartcards If true, then Kleopatra will call gpgsm --learn if a PKCS#15 Smartcard is inserted with unknown certificates. This can take a while and blocks the smartcard while the command is running. true false