File indexing completed on 2024-06-23 05:13:52
0001 /* crypto/gui/signencryptwidget.cpp 0002 0003 This file is part of Kleopatra, the KDE keymanager 0004 SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik 0005 SPDX-FileContributor: Intevation GmbH 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "signencryptwidget.h" 0011 0012 #include "kleopatra_debug.h" 0013 0014 #include "certificatelineedit.h" 0015 #include "fileoperationspreferences.h" 0016 #include "kleopatraapplication.h" 0017 #include "settings.h" 0018 #include "unknownrecipientwidget.h" 0019 0020 #include "dialogs/certificateselectiondialog.h" 0021 #include "utils/gui-helper.h" 0022 0023 #include <QCheckBox> 0024 #include <QGroupBox> 0025 #include <QHBoxLayout> 0026 #include <QScrollArea> 0027 #include <QScrollBar> 0028 #include <QVBoxLayout> 0029 0030 #include <Libkleo/Algorithm> 0031 #include <Libkleo/Compliance> 0032 #include <Libkleo/DefaultKeyFilter> 0033 #include <Libkleo/ExpiryChecker> 0034 #include <Libkleo/ExpiryCheckerConfig> 0035 #include <Libkleo/ExpiryCheckerSettings> 0036 #include <Libkleo/KeyCache> 0037 #include <Libkleo/KeyHelpers> 0038 #include <Libkleo/KeyListModel> 0039 #include <Libkleo/KeyListSortFilterProxyModel> 0040 #include <Libkleo/KeySelectionCombo> 0041 0042 #include <Libkleo/GnuPG> 0043 0044 #include <KConfigGroup> 0045 #include <KLocalizedString> 0046 #include <KMessageBox> 0047 #include <KMessageWidget> 0048 #include <KSharedConfig> 0049 0050 using namespace Kleo; 0051 using namespace Kleo::Dialogs; 0052 using namespace GpgME; 0053 0054 namespace 0055 { 0056 class SignCertificateFilter : public DefaultKeyFilter 0057 { 0058 public: 0059 SignCertificateFilter(GpgME::Protocol proto) 0060 : DefaultKeyFilter() 0061 { 0062 setRevoked(DefaultKeyFilter::NotSet); 0063 setExpired(DefaultKeyFilter::NotSet); 0064 setHasSecret(DefaultKeyFilter::Set); 0065 setCanSign(DefaultKeyFilter::Set); 0066 setValidIfSMIME(DefaultKeyFilter::Set); 0067 0068 if (proto == GpgME::OpenPGP) { 0069 setIsOpenPGP(DefaultKeyFilter::Set); 0070 } else if (proto == GpgME::CMS) { 0071 setIsOpenPGP(DefaultKeyFilter::NotSet); 0072 } 0073 } 0074 }; 0075 class EncryptCertificateFilter : public DefaultKeyFilter 0076 { 0077 public: 0078 EncryptCertificateFilter(GpgME::Protocol proto) 0079 : DefaultKeyFilter() 0080 { 0081 setRevoked(DefaultKeyFilter::NotSet); 0082 setExpired(DefaultKeyFilter::NotSet); 0083 setCanEncrypt(DefaultKeyFilter::Set); 0084 setValidIfSMIME(DefaultKeyFilter::Set); 0085 0086 if (proto == GpgME::OpenPGP) { 0087 setIsOpenPGP(DefaultKeyFilter::Set); 0088 } else if (proto == GpgME::CMS) { 0089 setIsOpenPGP(DefaultKeyFilter::NotSet); 0090 } 0091 } 0092 }; 0093 class EncryptSelfCertificateFilter : public EncryptCertificateFilter 0094 { 0095 public: 0096 EncryptSelfCertificateFilter(GpgME::Protocol proto) 0097 : EncryptCertificateFilter(proto) 0098 { 0099 setRevoked(DefaultKeyFilter::NotSet); 0100 setExpired(DefaultKeyFilter::NotSet); 0101 setCanEncrypt(DefaultKeyFilter::Set); 0102 setHasSecret(DefaultKeyFilter::Set); 0103 setValidIfSMIME(DefaultKeyFilter::Set); 0104 } 0105 }; 0106 } 0107 0108 class SignEncryptWidget::Private 0109 { 0110 SignEncryptWidget *const q; 0111 0112 public: 0113 struct RecipientWidgets { 0114 CertificateLineEdit *edit; 0115 KMessageWidget *expiryMessage; 0116 }; 0117 0118 explicit Private(SignEncryptWidget *qq, bool sigEncExclusive) 0119 : q{qq} 0120 , mModel{AbstractKeyListModel::createFlatKeyListModel(qq)} 0121 , mIsExclusive{sigEncExclusive} 0122 { 0123 } 0124 0125 CertificateLineEdit *addRecipientWidget(); 0126 /* Inserts a new recipient widget after widget @p after or at the end 0127 * if @p after is null. */ 0128 CertificateLineEdit *insertRecipientWidget(CertificateLineEdit *after); 0129 void recpRemovalRequested(const RecipientWidgets &recipient); 0130 void onProtocolChanged(); 0131 void updateCheckBoxes(); 0132 ExpiryChecker *expiryChecker(); 0133 void updateExpiryMessages(KMessageWidget *w, const GpgME::Key &key, ExpiryChecker::CheckFlags flags); 0134 void updateAllExpiryMessages(); 0135 0136 public: 0137 KeySelectionCombo *mSigSelect = nullptr; 0138 KMessageWidget *mSignKeyExpiryMessage = nullptr; 0139 KeySelectionCombo *mSelfSelect = nullptr; 0140 KMessageWidget *mEncryptToSelfKeyExpiryMessage = nullptr; 0141 std::vector<RecipientWidgets> mRecpWidgets; 0142 QList<UnknownRecipientWidget *> mUnknownWidgets; 0143 QList<GpgME::Key> mAddedKeys; 0144 QList<KeyGroup> mAddedGroups; 0145 QVBoxLayout *mRecpLayout = nullptr; 0146 Operations mOp; 0147 AbstractKeyListModel *mModel = nullptr; 0148 QCheckBox *mSymmetric = nullptr; 0149 QCheckBox *mSigChk = nullptr; 0150 QCheckBox *mEncOtherChk = nullptr; 0151 QCheckBox *mEncSelfChk = nullptr; 0152 GpgME::Protocol mCurrentProto = GpgME::UnknownProtocol; 0153 const bool mIsExclusive; 0154 std::unique_ptr<ExpiryChecker> mExpiryChecker; 0155 }; 0156 0157 SignEncryptWidget::SignEncryptWidget(QWidget *parent, bool sigEncExclusive) 0158 : QWidget{parent} 0159 , d{new Private{this, sigEncExclusive}} 0160 { 0161 auto lay = new QVBoxLayout(this); 0162 lay->setContentsMargins(0, 0, 0, 0); 0163 0164 d->mModel->useKeyCache(true, KeyList::IncludeGroups); 0165 0166 const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); 0167 const bool havePublicKeys = !KeyCache::instance()->keys().empty(); 0168 const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); 0169 0170 /* The signature selection */ 0171 { 0172 auto sigGrp = new QGroupBox{i18nc("@title:group", "Prove authenticity (sign)"), this}; 0173 d->mSigChk = new QCheckBox{i18n("Sign as:"), this}; 0174 d->mSigChk->setEnabled(haveSecretKeys); 0175 d->mSigChk->setChecked(haveSecretKeys); 0176 0177 d->mSigSelect = new KeySelectionCombo{KeyUsage::Sign, this}; 0178 d->mSigSelect->setEnabled(d->mSigChk->isChecked()); 0179 0180 d->mSignKeyExpiryMessage = new KMessageWidget{this}; 0181 d->mSignKeyExpiryMessage->setVisible(false); 0182 0183 auto groupLayout = new QGridLayout{sigGrp}; 0184 groupLayout->setColumnStretch(1, 1); 0185 groupLayout->addWidget(d->mSigChk, 0, 0); 0186 groupLayout->addWidget(d->mSigSelect, 0, 1); 0187 groupLayout->addWidget(d->mSignKeyExpiryMessage, 1, 1); 0188 lay->addWidget(sigGrp); 0189 0190 connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool checked) { 0191 d->mSigSelect->setEnabled(checked); 0192 updateOp(); 0193 d->updateExpiryMessages(d->mSignKeyExpiryMessage, signKey(), ExpiryChecker::OwnSigningKey); 0194 }); 0195 connect(d->mSigSelect, &KeySelectionCombo::currentKeyChanged, this, [this]() { 0196 updateOp(); 0197 d->updateExpiryMessages(d->mSignKeyExpiryMessage, signKey(), ExpiryChecker::OwnSigningKey); 0198 }); 0199 } 0200 0201 // Recipient selection 0202 { 0203 auto encBox = new QGroupBox{i18nc("@title:group", "Encrypt"), this}; 0204 auto encBoxLay = new QVBoxLayout{encBox}; 0205 auto recipientGrid = new QGridLayout; 0206 int row = 0; 0207 0208 // Own key 0209 d->mEncSelfChk = new QCheckBox{i18n("Encrypt for me:"), this}; 0210 d->mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly); 0211 d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly); 0212 d->mSelfSelect = new KeySelectionCombo{KeyUsage::Encrypt, this}; 0213 d->mSelfSelect->setEnabled(d->mEncSelfChk->isChecked()); 0214 d->mEncryptToSelfKeyExpiryMessage = new KMessageWidget{this}; 0215 d->mEncryptToSelfKeyExpiryMessage->setVisible(false); 0216 recipientGrid->addWidget(d->mEncSelfChk, row, 0); 0217 recipientGrid->addWidget(d->mSelfSelect, row, 1); 0218 row++; 0219 recipientGrid->addWidget(d->mEncryptToSelfKeyExpiryMessage, row, 1); 0220 0221 // Checkbox for other keys 0222 row++; 0223 d->mEncOtherChk = new QCheckBox{i18n("Encrypt for others:"), this}; 0224 d->mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly); 0225 d->mEncOtherChk->setChecked(havePublicKeys && !symmetricOnly); 0226 recipientGrid->addWidget(d->mEncOtherChk, row, 0, Qt::AlignTop); 0227 connect(d->mEncOtherChk, &QCheckBox::toggled, this, [this](bool checked) { 0228 for (const auto &recipient : std::as_const(d->mRecpWidgets)) { 0229 recipient.edit->setEnabled(checked); 0230 d->updateExpiryMessages(recipient.expiryMessage, checked ? recipient.edit->key() : Key{}, ExpiryChecker::EncryptionKey); 0231 } 0232 updateOp(); 0233 }); 0234 d->mRecpLayout = new QVBoxLayout; 0235 recipientGrid->addLayout(d->mRecpLayout, row, 1); 0236 recipientGrid->setRowStretch(row + 1, 1); 0237 0238 // Scroll area for other keys 0239 auto recipientWidget = new QWidget; 0240 auto recipientScroll = new QScrollArea; 0241 recipientWidget->setLayout(recipientGrid); 0242 recipientScroll->setWidget(recipientWidget); 0243 recipientScroll->setWidgetResizable(true); 0244 recipientScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow); 0245 recipientScroll->setFrameStyle(QFrame::NoFrame); 0246 recipientScroll->setFocusPolicy(Qt::NoFocus); 0247 recipientGrid->setContentsMargins(0, 0, 0, 0); 0248 encBoxLay->addWidget(recipientScroll, 1); 0249 0250 auto bar = recipientScroll->verticalScrollBar(); 0251 connect(bar, &QScrollBar::rangeChanged, this, [bar](int, int max) { 0252 bar->setValue(max); 0253 }); 0254 0255 d->addRecipientWidget(); 0256 0257 // Checkbox for password 0258 d->mSymmetric = new QCheckBox(i18n("Encrypt with password. Anyone you share the password with can read the data.")); 0259 d->mSymmetric->setToolTip(i18nc("Tooltip information for symmetric encryption", 0260 "Additionally to the keys of the recipients you can encrypt your data with a password. " 0261 "Anyone who has the password can read the data without any secret key. " 0262 "Using a password is <b>less secure</b> then public key cryptography. Even if you pick a very strong password.")); 0263 d->mSymmetric->setChecked(symmetricOnly || !havePublicKeys); 0264 encBoxLay->addWidget(d->mSymmetric); 0265 0266 // Connect it 0267 connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) { 0268 d->mSelfSelect->setEnabled(checked); 0269 updateOp(); 0270 d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfKey(), ExpiryChecker::OwnEncryptionKey); 0271 }); 0272 connect(d->mSelfSelect, &KeySelectionCombo::currentKeyChanged, this, [this]() { 0273 updateOp(); 0274 d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfKey(), ExpiryChecker::OwnEncryptionKey); 0275 }); 0276 connect(d->mSymmetric, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp); 0277 0278 if (d->mIsExclusive) { 0279 connect(d->mEncOtherChk, &QCheckBox::toggled, this, [this](bool value) { 0280 if (d->mCurrentProto != GpgME::CMS) { 0281 return; 0282 } 0283 if (value) { 0284 d->mSigChk->setChecked(false); 0285 } 0286 }); 0287 connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool value) { 0288 if (d->mCurrentProto != GpgME::CMS) { 0289 return; 0290 } 0291 if (value) { 0292 d->mSigChk->setChecked(false); 0293 } 0294 }); 0295 connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool value) { 0296 if (d->mCurrentProto != GpgME::CMS) { 0297 return; 0298 } 0299 if (value) { 0300 d->mEncSelfChk->setChecked(false); 0301 d->mEncOtherChk->setChecked(false); 0302 } 0303 }); 0304 } 0305 0306 // Ensure that the d->mSigChk is aligned together with the encryption check boxes. 0307 d->mSigChk->setMinimumWidth(qMax(d->mEncOtherChk->width(), d->mEncSelfChk->width())); 0308 0309 lay->addWidget(encBox); 0310 } 0311 0312 connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() { 0313 d->updateCheckBoxes(); 0314 d->updateAllExpiryMessages(); 0315 }); 0316 connect(KleopatraApplication::instance(), &KleopatraApplication::configurationChanged, this, [this]() { 0317 d->updateCheckBoxes(); 0318 d->mExpiryChecker.reset(); 0319 d->updateAllExpiryMessages(); 0320 }); 0321 0322 loadKeys(); 0323 d->onProtocolChanged(); 0324 updateOp(); 0325 } 0326 0327 SignEncryptWidget::~SignEncryptWidget() = default; 0328 0329 void SignEncryptWidget::setSignAsText(const QString &text) 0330 { 0331 d->mSigChk->setText(text); 0332 } 0333 0334 void SignEncryptWidget::setEncryptForMeText(const QString &text) 0335 { 0336 d->mEncSelfChk->setText(text); 0337 } 0338 0339 void SignEncryptWidget::setEncryptForOthersText(const QString &text) 0340 { 0341 d->mEncOtherChk->setText(text); 0342 } 0343 0344 void SignEncryptWidget::setEncryptWithPasswordText(const QString &text) 0345 { 0346 d->mSymmetric->setText(text); 0347 } 0348 0349 CertificateLineEdit *SignEncryptWidget::Private::addRecipientWidget() 0350 { 0351 return insertRecipientWidget(nullptr); 0352 } 0353 0354 CertificateLineEdit *SignEncryptWidget::Private::insertRecipientWidget(CertificateLineEdit *after) 0355 { 0356 Q_ASSERT(!after || mRecpLayout->indexOf(after) != -1); 0357 0358 const auto index = after ? mRecpLayout->indexOf(after) + 2 : mRecpLayout->count(); 0359 0360 const RecipientWidgets recipient{new CertificateLineEdit{mModel, KeyUsage::Encrypt, new EncryptCertificateFilter{mCurrentProto}, q}, new KMessageWidget{q}}; 0361 recipient.edit->setAccessibleNameOfLineEdit(i18nc("text for screen readers", "recipient key")); 0362 recipient.edit->setEnabled(mEncOtherChk->isChecked()); 0363 recipient.expiryMessage->setVisible(false); 0364 if (static_cast<unsigned>(index / 2) < mRecpWidgets.size()) { 0365 mRecpWidgets.insert(mRecpWidgets.begin() + index / 2, recipient); 0366 } else { 0367 mRecpWidgets.push_back(recipient); 0368 } 0369 0370 if (mRecpLayout->count() > 0) { 0371 auto prevWidget = after ? after : mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget(); 0372 Kleo::forceSetTabOrder(prevWidget, recipient.edit); 0373 Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage); 0374 } 0375 mRecpLayout->insertWidget(index, recipient.edit); 0376 mRecpLayout->insertWidget(index + 1, recipient.expiryMessage); 0377 0378 connect(recipient.edit, &CertificateLineEdit::keyChanged, q, &SignEncryptWidget::recipientsChanged); 0379 connect(recipient.edit, &CertificateLineEdit::editingStarted, q, &SignEncryptWidget::recipientsChanged); 0380 connect(recipient.edit, &CertificateLineEdit::cleared, q, &SignEncryptWidget::recipientsChanged); 0381 connect(recipient.edit, &CertificateLineEdit::certificateSelectionRequested, q, [this, recipient]() { 0382 q->certificateSelectionRequested(recipient.edit); 0383 }); 0384 0385 return recipient.edit; 0386 } 0387 0388 void SignEncryptWidget::addRecipient(const Key &key) 0389 { 0390 CertificateLineEdit *certSel = d->addRecipientWidget(); 0391 if (!key.isNull()) { 0392 certSel->setKey(key); 0393 d->mAddedKeys << key; 0394 } 0395 } 0396 0397 void SignEncryptWidget::addRecipient(const KeyGroup &group) 0398 { 0399 CertificateLineEdit *certSel = d->addRecipientWidget(); 0400 if (!group.isNull()) { 0401 certSel->setGroup(group); 0402 d->mAddedGroups << group; 0403 } 0404 } 0405 0406 void SignEncryptWidget::certificateSelectionRequested(CertificateLineEdit *certificateLineEdit) 0407 { 0408 CertificateSelectionDialog dlg{this}; 0409 0410 dlg.setOptions(CertificateSelectionDialog::Options( // 0411 CertificateSelectionDialog::MultiSelection | // 0412 CertificateSelectionDialog::EncryptOnly | // 0413 CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto) | // 0414 CertificateSelectionDialog::IncludeGroups)); 0415 0416 if (!certificateLineEdit->key().isNull()) { 0417 const auto key = certificateLineEdit->key(); 0418 const auto name = QString::fromUtf8(key.userID(0).name()); 0419 const auto email = QString::fromUtf8(key.userID(0).email()); 0420 dlg.setStringFilter(!name.isEmpty() ? name : email); 0421 } else if (!certificateLineEdit->group().isNull()) { 0422 dlg.setStringFilter(certificateLineEdit->group().name()); 0423 } else { 0424 dlg.setStringFilter(certificateLineEdit->text()); 0425 } 0426 0427 if (dlg.exec()) { 0428 const std::vector<Key> keys = dlg.selectedCertificates(); 0429 const std::vector<KeyGroup> groups = dlg.selectedGroups(); 0430 if (keys.size() == 0 && groups.size() == 0) { 0431 return; 0432 } 0433 CertificateLineEdit *certWidget = nullptr; 0434 for (const Key &key : keys) { 0435 if (!certWidget) { 0436 certWidget = certificateLineEdit; 0437 } else { 0438 certWidget = d->insertRecipientWidget(certWidget); 0439 } 0440 certWidget->setKey(key); 0441 } 0442 for (const KeyGroup &group : groups) { 0443 if (!certWidget) { 0444 certWidget = certificateLineEdit; 0445 } else { 0446 certWidget = d->insertRecipientWidget(certWidget); 0447 } 0448 certWidget->setGroup(group); 0449 } 0450 } 0451 0452 recipientsChanged(); 0453 } 0454 0455 void SignEncryptWidget::clearAddedRecipients() 0456 { 0457 for (auto w : std::as_const(d->mUnknownWidgets)) { 0458 d->mRecpLayout->removeWidget(w); 0459 delete w; 0460 } 0461 0462 for (auto &key : std::as_const(d->mAddedKeys)) { 0463 removeRecipient(key); 0464 } 0465 0466 for (auto &group : std::as_const(d->mAddedGroups)) { 0467 removeRecipient(group); 0468 } 0469 } 0470 0471 void SignEncryptWidget::addUnknownRecipient(const char *keyID) 0472 { 0473 auto unknownWidget = new UnknownRecipientWidget(keyID); 0474 d->mUnknownWidgets << unknownWidget; 0475 0476 if (d->mRecpLayout->count() > 0) { 0477 auto lastWidget = d->mRecpLayout->itemAt(d->mRecpLayout->count() - 1)->widget(); 0478 setTabOrder(lastWidget, unknownWidget); 0479 } 0480 d->mRecpLayout->addWidget(unknownWidget); 0481 0482 connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() { 0483 // Check if any unknown recipient can now be found. 0484 for (auto w : d->mUnknownWidgets) { 0485 auto key = KeyCache::instance()->findByKeyIDOrFingerprint(w->keyID().toLatin1().constData()); 0486 if (key.isNull()) { 0487 std::vector<std::string> subids; 0488 subids.push_back(std::string(w->keyID().toLatin1().constData())); 0489 for (const auto &subkey : KeyCache::instance()->findSubkeysByKeyID(subids)) { 0490 key = subkey.parent(); 0491 } 0492 } 0493 if (key.isNull()) { 0494 continue; 0495 } 0496 // Key is now available replace by line edit. 0497 qCDebug(KLEOPATRA_LOG) << "Removing widget for keyid: " << w->keyID(); 0498 d->mRecpLayout->removeWidget(w); 0499 d->mUnknownWidgets.removeAll(w); 0500 delete w; 0501 addRecipient(key); 0502 } 0503 }); 0504 } 0505 0506 void SignEncryptWidget::recipientsChanged() 0507 { 0508 const bool hasEmptyRecpWidget = std::any_of(std::cbegin(d->mRecpWidgets), std::cend(d->mRecpWidgets), [](auto w) { 0509 return w.edit->isEmpty(); 0510 }); 0511 if (!hasEmptyRecpWidget) { 0512 d->addRecipientWidget(); 0513 } 0514 updateOp(); 0515 for (const auto &recipient : std::as_const(d->mRecpWidgets)) { 0516 if (!recipient.edit->isEditingInProgress() || recipient.edit->isEmpty()) { 0517 d->updateExpiryMessages(recipient.expiryMessage, d->mEncOtherChk->isChecked() ? recipient.edit->key() : Key{}, ExpiryChecker::EncryptionKey); 0518 } 0519 } 0520 } 0521 0522 Key SignEncryptWidget::signKey() const 0523 { 0524 if (d->mSigSelect->isEnabled()) { 0525 return d->mSigSelect->currentKey(); 0526 } 0527 return Key(); 0528 } 0529 0530 Key SignEncryptWidget::selfKey() const 0531 { 0532 if (d->mSelfSelect->isEnabled()) { 0533 return d->mSelfSelect->currentKey(); 0534 } 0535 return Key(); 0536 } 0537 0538 std::vector<Key> SignEncryptWidget::recipients() const 0539 { 0540 std::vector<Key> ret; 0541 for (const auto &recipient : std::as_const(d->mRecpWidgets)) { 0542 const auto *const w = recipient.edit; 0543 if (!w->isEnabled()) { 0544 // If one is disabled, all are disabled. 0545 break; 0546 } 0547 const Key k = w->key(); 0548 const KeyGroup g = w->group(); 0549 if (!k.isNull()) { 0550 ret.push_back(k); 0551 } else if (!g.isNull()) { 0552 const auto keys = g.keys(); 0553 std::copy(keys.begin(), keys.end(), std::back_inserter(ret)); 0554 } 0555 } 0556 const Key k = selfKey(); 0557 if (!k.isNull()) { 0558 ret.push_back(k); 0559 } 0560 return ret; 0561 } 0562 0563 bool SignEncryptWidget::isDeVsAndValid() const 0564 { 0565 if (!signKey().isNull() && !DeVSCompliance::keyIsCompliant(signKey())) { 0566 return false; 0567 } 0568 0569 if (!selfKey().isNull() && !DeVSCompliance::keyIsCompliant(selfKey())) { 0570 return false; 0571 } 0572 0573 for (const auto &key : recipients()) { 0574 if (!DeVSCompliance::keyIsCompliant(key)) { 0575 return false; 0576 } 0577 } 0578 0579 return true; 0580 } 0581 0582 static QString expiryMessage(const ExpiryChecker::Result &result) 0583 { 0584 if (result.expiration.certificate.isNull()) { 0585 return {}; 0586 } 0587 switch (result.expiration.status) { 0588 case ExpiryChecker::Expired: 0589 return i18nc("@info", "This certificate is expired."); 0590 case ExpiryChecker::ExpiresSoon: { 0591 if (result.expiration.duration.count() == 0) { 0592 return i18nc("@info", "This certificate expires today."); 0593 } else { 0594 return i18ncp("@info", "This certificate expires tomorrow.", "This certificate expires in %1 days.", result.expiration.duration.count()); 0595 } 0596 } 0597 case ExpiryChecker::NoSuitableSubkey: 0598 if (result.checkFlags & ExpiryChecker::EncryptionKey) { 0599 return i18nc("@info", "This certificate cannot be used for encryption."); 0600 } else { 0601 return i18nc("@info", "This certificate cannot be used for signing."); 0602 } 0603 case ExpiryChecker::InvalidKey: 0604 case ExpiryChecker::InvalidCheckFlags: 0605 break; // wrong usage of ExpiryChecker; can be ignored 0606 case ExpiryChecker::NotNearExpiry:; 0607 } 0608 return {}; 0609 } 0610 0611 void SignEncryptWidget::updateOp() 0612 { 0613 const Key sigKey = signKey(); 0614 const std::vector<Key> recp = recipients(); 0615 0616 Operations op = NoOperation; 0617 if (!sigKey.isNull()) { 0618 op |= Sign; 0619 } 0620 if (!recp.empty() || encryptSymmetric()) { 0621 op |= Encrypt; 0622 } 0623 d->mOp = op; 0624 Q_EMIT operationChanged(d->mOp); 0625 Q_EMIT keysChanged(); 0626 } 0627 0628 SignEncryptWidget::Operations SignEncryptWidget::currentOp() const 0629 { 0630 return d->mOp; 0631 } 0632 0633 namespace 0634 { 0635 bool recipientWidgetHasFocus(QWidget *w) 0636 { 0637 // check if w (or its focus proxy) or a child widget of w has focus 0638 return w->hasFocus() || w->isAncestorOf(qApp->focusWidget()); 0639 } 0640 } 0641 0642 void SignEncryptWidget::Private::recpRemovalRequested(const RecipientWidgets &recipient) 0643 { 0644 if (!recipient.edit) { 0645 return; 0646 } 0647 const int emptyEdits = std::count_if(std::cbegin(mRecpWidgets), std::cend(mRecpWidgets), [](const auto &r) { 0648 return r.edit->isEmpty(); 0649 }); 0650 if (emptyEdits > 1) { 0651 if (recipientWidgetHasFocus(recipient.edit) || recipientWidgetHasFocus(recipient.expiryMessage)) { 0652 const int index = mRecpLayout->indexOf(recipient.edit); 0653 const auto focusWidget = (index < mRecpLayout->count() - 2) ? // 0654 mRecpLayout->itemAt(index + 2)->widget() 0655 : mRecpLayout->itemAt(mRecpLayout->count() - 3)->widget(); 0656 focusWidget->setFocus(); 0657 } 0658 mRecpLayout->removeWidget(recipient.expiryMessage); 0659 mRecpLayout->removeWidget(recipient.edit); 0660 const auto it = std::find_if(std::begin(mRecpWidgets), std::end(mRecpWidgets), [recipient](const auto &r) { 0661 return r.edit == recipient.edit; 0662 }); 0663 mRecpWidgets.erase(it); 0664 recipient.expiryMessage->deleteLater(); 0665 recipient.edit->deleteLater(); 0666 } 0667 } 0668 0669 void SignEncryptWidget::removeRecipient(const GpgME::Key &key) 0670 { 0671 for (const auto &recipient : std::as_const(d->mRecpWidgets)) { 0672 const auto editKey = recipient.edit->key(); 0673 if (key.isNull() && editKey.isNull()) { 0674 d->recpRemovalRequested(recipient); 0675 return; 0676 } 0677 if (editKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(editKey.primaryFingerprint(), key.primaryFingerprint())) { 0678 d->recpRemovalRequested(recipient); 0679 return; 0680 } 0681 } 0682 } 0683 0684 void SignEncryptWidget::removeRecipient(const KeyGroup &group) 0685 { 0686 for (const auto &recipient : std::as_const(d->mRecpWidgets)) { 0687 const auto editGroup = recipient.edit->group(); 0688 if (group.isNull() && editGroup.isNull()) { 0689 d->recpRemovalRequested(recipient); 0690 return; 0691 } 0692 if (editGroup.name() == group.name()) { 0693 d->recpRemovalRequested(recipient); 0694 return; 0695 } 0696 } 0697 } 0698 0699 bool SignEncryptWidget::encryptSymmetric() const 0700 { 0701 return d->mSymmetric->isChecked(); 0702 } 0703 0704 void SignEncryptWidget::loadKeys() 0705 { 0706 KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys")); 0707 auto cache = KeyCache::instance(); 0708 d->mSigSelect->setDefaultKey(keys.readEntry("SigningKey", QString())); 0709 d->mSelfSelect->setDefaultKey(keys.readEntry("EncryptKey", QString())); 0710 } 0711 0712 void SignEncryptWidget::saveOwnKeys() const 0713 { 0714 KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys")); 0715 auto sigKey = d->mSigSelect->currentKey(); 0716 auto encKey = d->mSelfSelect->currentKey(); 0717 if (!sigKey.isNull()) { 0718 keys.writeEntry("SigningKey", sigKey.primaryFingerprint()); 0719 } 0720 if (!encKey.isNull()) { 0721 keys.writeEntry("EncryptKey", encKey.primaryFingerprint()); 0722 } 0723 } 0724 0725 void SignEncryptWidget::setSigningChecked(bool value) 0726 { 0727 d->mSigChk->setChecked(value && !KeyCache::instance()->secretKeys().empty()); 0728 } 0729 0730 void SignEncryptWidget::setEncryptionChecked(bool checked) 0731 { 0732 if (checked) { 0733 const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); 0734 const bool havePublicKeys = !KeyCache::instance()->keys().empty(); 0735 const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); 0736 d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly); 0737 d->mEncOtherChk->setChecked(havePublicKeys && !symmetricOnly); 0738 d->mSymmetric->setChecked(symmetricOnly || !havePublicKeys); 0739 } else { 0740 d->mEncSelfChk->setChecked(false); 0741 d->mEncOtherChk->setChecked(false); 0742 d->mSymmetric->setChecked(false); 0743 } 0744 } 0745 0746 void SignEncryptWidget::setProtocol(GpgME::Protocol proto) 0747 { 0748 if (d->mCurrentProto == proto) { 0749 return; 0750 } 0751 d->mCurrentProto = proto; 0752 d->onProtocolChanged(); 0753 } 0754 0755 void Kleo::SignEncryptWidget::Private::onProtocolChanged() 0756 { 0757 mSigSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new SignCertificateFilter(mCurrentProto))); 0758 mSelfSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new EncryptSelfCertificateFilter(mCurrentProto))); 0759 const auto encFilter = std::shared_ptr<KeyFilter>(new EncryptCertificateFilter(mCurrentProto)); 0760 for (const auto &recipient : std::as_const(mRecpWidgets)) { 0761 recipient.edit->setKeyFilter(encFilter); 0762 } 0763 0764 if (mIsExclusive) { 0765 mSymmetric->setDisabled(mCurrentProto == GpgME::CMS); 0766 if (mSymmetric->isChecked() && mCurrentProto == GpgME::CMS) { 0767 mSymmetric->setChecked(false); 0768 } 0769 if (mSigChk->isChecked() && mCurrentProto == GpgME::CMS && (mEncSelfChk->isChecked() || mEncOtherChk->isChecked())) { 0770 mSigChk->setChecked(false); 0771 } 0772 } 0773 } 0774 0775 static bool recipientIsOkay(const CertificateLineEdit *edit) 0776 { 0777 if (!edit->isEnabled() || edit->isEmpty()) { 0778 return true; 0779 } 0780 if (!edit->hasAcceptableInput()) { 0781 return false; 0782 } 0783 if (const auto key = edit->key(); !key.isNull()) { 0784 return Kleo::canBeUsedForEncryption(key); 0785 } 0786 if (const auto group = edit->group(); !group.isNull()) { 0787 return Kleo::all_of(group.keys(), Kleo::canBeUsedForEncryption); 0788 } 0789 // we should never reach this point 0790 return false; 0791 } 0792 0793 bool SignEncryptWidget::isComplete() const 0794 { 0795 if (currentOp() == NoOperation) { 0796 return false; 0797 } 0798 if ((currentOp() & SignEncryptWidget::Sign) && !Kleo::canBeUsedForSigning(signKey())) { 0799 return false; 0800 } 0801 if (currentOp() & SignEncryptWidget::Encrypt) { 0802 if (!selfKey().isNull() && !Kleo::canBeUsedForEncryption(selfKey())) { 0803 return false; 0804 } 0805 const bool allOtherRecipientsAreOkay = Kleo::all_of(d->mRecpWidgets, [](const auto &r) { 0806 return recipientIsOkay(r.edit); 0807 }); 0808 if (!allOtherRecipientsAreOkay) { 0809 return false; 0810 } 0811 } 0812 return true; 0813 } 0814 0815 bool SignEncryptWidget::validate() 0816 { 0817 CertificateLineEdit *firstUnresolvedRecipient = nullptr; 0818 QStringList unresolvedRecipients; 0819 for (const auto &recipient : std::as_const(d->mRecpWidgets)) { 0820 if (recipient.edit->isEnabled() && !recipient.edit->hasAcceptableInput()) { 0821 if (!firstUnresolvedRecipient) { 0822 firstUnresolvedRecipient = recipient.edit; 0823 } 0824 unresolvedRecipients.push_back(recipient.edit->text().toHtmlEscaped()); 0825 } 0826 } 0827 if (!unresolvedRecipients.isEmpty()) { 0828 KMessageBox::errorList(this, 0829 i18n("Could not find a key for the following recipients:"), 0830 unresolvedRecipients, 0831 i18nc("@title:window", "Failed to find some keys")); 0832 } 0833 if (firstUnresolvedRecipient) { 0834 firstUnresolvedRecipient->setFocus(); 0835 } 0836 return unresolvedRecipients.isEmpty(); 0837 } 0838 0839 void SignEncryptWidget::Private::updateCheckBoxes() 0840 { 0841 const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty(); 0842 const bool havePublicKeys = !KeyCache::instance()->keys().empty(); 0843 const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly(); 0844 mSigChk->setEnabled(haveSecretKeys); 0845 mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly); 0846 mEncOtherChk->setEnabled(havePublicKeys && !symmetricOnly); 0847 if (symmetricOnly) { 0848 mEncSelfChk->setChecked(false); 0849 mEncOtherChk->setChecked(false); 0850 mSymmetric->setChecked(true); 0851 } 0852 } 0853 0854 ExpiryChecker *Kleo::SignEncryptWidget::Private::expiryChecker() 0855 { 0856 if (!mExpiryChecker) { 0857 mExpiryChecker.reset(new ExpiryChecker{ExpiryCheckerConfig{}.settings()}); 0858 } 0859 return mExpiryChecker.get(); 0860 } 0861 0862 void SignEncryptWidget::Private::updateExpiryMessages(KMessageWidget *messageWidget, const GpgME::Key &key, ExpiryChecker::CheckFlags flags) 0863 { 0864 messageWidget->setCloseButtonVisible(false); 0865 if (!Settings{}.showExpiryNotifications() || key.isNull()) { 0866 messageWidget->setVisible(false); 0867 } else { 0868 const auto result = expiryChecker()->checkKey(key, flags); 0869 const auto message = expiryMessage(result); 0870 messageWidget->setText(message); 0871 messageWidget->setVisible(!message.isEmpty()); 0872 } 0873 } 0874 0875 void SignEncryptWidget::Private::updateAllExpiryMessages() 0876 { 0877 updateExpiryMessages(mSignKeyExpiryMessage, q->signKey(), ExpiryChecker::OwnSigningKey); 0878 updateExpiryMessages(mEncryptToSelfKeyExpiryMessage, q->selfKey(), ExpiryChecker::OwnEncryptionKey); 0879 for (const auto &recipient : std::as_const(mRecpWidgets)) { 0880 if (recipient.edit->isEnabled()) { 0881 updateExpiryMessages(recipient.expiryMessage, recipient.edit->key(), ExpiryChecker::EncryptionKey); 0882 } 0883 } 0884 } 0885 0886 #include "moc_signencryptwidget.cpp"