diff --git a/src/crypto/gui/signencryptfileswizard.cpp b/src/crypto/gui/signencryptfileswizard.cpp index 4d291dbdd..1c11e264b 100644 --- a/src/crypto/gui/signencryptfileswizard.cpp +++ b/src/crypto/gui/signencryptfileswizard.cpp @@ -1,542 +1,542 @@ /* crypto/gui/signencryptfileswizard.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signencryptfileswizard.h" #include "signencryptwidget.h" #include "newresultpage.h" #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Crypto::Gui; enum Page { SigEncPageId, ResultPageId, NumPages }; class SigEncPage: public QWizardPage { Q_OBJECT public: explicit SigEncPage(QWidget *parent = nullptr) : QWizardPage(parent), mParent((SignEncryptFilesWizard *) parent), mWidget(new SignEncryptWidget), mOutLayout(new QVBoxLayout), mOutputLabel{nullptr}, mArchive(false), mUseOutputDir(false) { setTitle(i18nc("@title", "Sign / Encrypt Files")); auto vLay = new QVBoxLayout(this); vLay->setContentsMargins(0, 0, 0, 0); mWidget->setSignAsText(i18nc("@option:check on SignEncryptPage", "&Sign as:")); mWidget->setEncryptForMeText(i18nc("@option:check on SignEncryptPage", "Encrypt for &me:")); mWidget->setEncryptForOthersText(i18nc("@option:check on SignEncryptPage", "Encrypt for &others:")); mWidget->setEncryptWithPasswordText(i18nc("@option:check on SignEncryptPage", "Encrypt with &password. Anyone you share the password with can read the data.")); vLay->addWidget(mWidget); connect(mWidget, &SignEncryptWidget::operationChanged, this, &SigEncPage::updateCommitButton); connect(mWidget, &SignEncryptWidget::keysChanged, this, &SigEncPage::updateFileWidgets); updateCommitButton(mWidget->currentOp()); - auto outputGrp = new QGroupBox(i18n("Output")); + auto outputGrp = new QGroupBox(i18nc("@title:group", "Output")); outputGrp->setLayout(mOutLayout); mPlaceholderWidget = new QLabel(i18n("Please select an action.")); mOutLayout->addWidget(mPlaceholderWidget); mUseOutputDirChk = new QCheckBox(i18nc("@option:check on SignEncryptPage", "Encrypt / Sign &each file separately.")); - mUseOutputDirChk->setToolTip(i18nc("@info", - "Keep each file separate instead of creating an archive for all.")); + mUseOutputDirChk->setToolTip(i18nc("@info:tooltip", + "Keep each file separate instead of creating an archive for all.")); mOutLayout->addWidget(mUseOutputDirChk); connect (mUseOutputDirChk, &QCheckBox::toggled, this, [this] (bool state) { mUseOutputDir = state; mArchive = !mUseOutputDir; updateFileWidgets(); }); mOutputLabel = new QLabel(i18nc("@label on SignEncryptPage", "Output &files/folder:")); mOutLayout->addWidget(mOutputLabel); vLay->addWidget(outputGrp); setMinimumHeight(300); } void setEncryptionPreset(bool value) { mWidget->setEncryptionChecked(value); } void setSigningPreset(bool value) { mWidget->setSigningChecked(value); } bool isComplete() const override { return !mWidget->currentOp().isNull(); } int nextId() const override { return ResultPageId; } void initializePage() override { setCommitPage(true); } void setArchiveForced(bool archive) { mArchive = archive; setArchiveMutable(!archive); } void setArchiveMutable(bool archive) { mUseOutputDirChk->setVisible(archive); if (archive) { const KConfigGroup archCfg(KSharedConfig::openConfig(), "SignEncryptFilesWizard"); mUseOutputDirChk->setChecked(archCfg.readEntry("LastUseOutputDir", false)); } else { mUseOutputDirChk->setChecked(false); } } bool validatePage() override { bool sign = !mWidget->signKey().isNull(); bool encrypt = !mWidget->selfKey().isNull() || !mWidget->recipients().empty(); if (!mWidget->validate()) { return false; } mWidget->saveOwnKeys(); if (mUseOutputDirChk->isVisible()) { KConfigGroup archCfg(KSharedConfig::openConfig(), "SignEncryptFilesWizard"); archCfg.writeEntry("LastUseOutputDir", mUseOutputDir); } if (sign && !encrypt && mArchive) { return KMessageBox::warningContinueCancel(this, xi18nc("@info", "Archiving in combination with sign-only currently requires what are known as opaque signatures - " "unlike detached ones, these embed the content in the signature." "This format is rather unusual. You might want to archive the files separately, " "and then sign the archive as one file with Kleopatra." "Future versions of Kleopatra are expected to also support detached signatures in this case."), i18nc("@title:window", "Unusual Signature Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("signencryptfileswizard-archive+sign-only-warning")) == KMessageBox::Continue; } else if (sign && !encrypt) { return true; } if (!mWidget->selfKey().isNull()) { return true; } bool hasSecret = false; Q_FOREACH (const Key k, mWidget->recipients()) { if (k.hasSecret()) { hasSecret = true; break; } } if (!hasSecret && !mWidget->encryptSymmetric()) { if (KMessageBox::warningContinueCancel(this, xi18nc("@info", "None of the recipients you are encrypting to seems to be your own." "This means that you will not be able to decrypt the data anymore, once encrypted." "Do you want to continue, or cancel to change the recipient selection?"), i18nc("@title:window", "Encrypt-To-Self Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("warn-encrypt-to-non-self"), KMessageBox::Notify | KMessageBox::Dangerous) == KMessageBox::Cancel) { return false; } } return true; } std::vector recipients() const { return mWidget->recipients(); } /* In the future we might find a usecase for multiple * signers */ std::vector signers() const { const Key k = mWidget->signKey(); if (!k.isNull()) { return {k}; } return {}; } private: QWidget *createRequester(int forKind, QBoxLayout *lay) { static const QMap icons = { { SignEncryptFilesWizard::SignatureCMS, QStringLiteral("document-sign") }, { SignEncryptFilesWizard::SignaturePGP, QStringLiteral("document-sign") }, { SignEncryptFilesWizard::CombinedPGP, QStringLiteral("document-edit-sign-encrypt") }, { SignEncryptFilesWizard::EncryptedPGP, QStringLiteral("document-encrypt") }, { SignEncryptFilesWizard::EncryptedCMS, QStringLiteral("document-encrypt") }, { SignEncryptFilesWizard::Directory, QStringLiteral("folder") } }; static const QMap toolTips = { { SignEncryptFilesWizard::SignatureCMS, i18n("The S/MIME signature.") }, { SignEncryptFilesWizard::SignaturePGP, i18n("The signature.") }, { SignEncryptFilesWizard::CombinedPGP, i18n("The signed and encrypted file.") }, { SignEncryptFilesWizard::EncryptedPGP, i18n("The encrypted file.") }, { SignEncryptFilesWizard::EncryptedCMS, i18n("The S/MIME encrypted file.") }, { SignEncryptFilesWizard::Directory, i18n("Output directory.") } }; auto req = new FileNameRequester(forKind == SignEncryptFilesWizard::Directory ? QDir::Dirs : QDir::Files, this); req->setFileName(mOutNames[forKind]); req->setExistingOnly(false); auto hLay = new QHBoxLayout; auto iconLabel = new QLabel; auto ret = new QWidget; ret->setFocusPolicy(req->focusPolicy()); ret->setFocusProxy(req); iconLabel->setPixmap(QIcon::fromTheme(icons[forKind]).pixmap(32,32)); hLay->addWidget(iconLabel); iconLabel->setToolTip(toolTips[forKind]); req->setToolTip(toolTips[forKind]); hLay->addWidget(req); ret->setLayout(hLay); lay->addWidget(ret); connect (req, &FileNameRequester::fileNameChanged, this, [this, forKind](const QString &newName) { mOutNames[forKind] = newName; }); return ret; } public: void setOutputNames(const QMap &names) { Q_ASSERT(mOutNames.isEmpty()); mOutNames = names; const auto keys = mOutNames.keys(); for (int i : keys) { mRequester[i] = createRequester(i, mOutLayout); } updateFileWidgets(); } QMap outputNames() const { if (!mUseOutputDir) { auto ret = mOutNames; ret.remove(SignEncryptFilesWizard::Directory); return ret; } return mOutNames; } bool encryptSymmetric() const { return mWidget->encryptSymmetric(); } private Q_SLOTS: void updateCommitButton(const QString &label) { auto btn = mParent->button(QWizard::CommitButton); if (!label.isEmpty()) { mParent->setButtonText(QWizard::CommitButton, label); if (Kleo::gpgComplianceP("de-vs")) { bool de_vs = mWidget->isDeVsAndValid(); btn->setIcon(QIcon::fromTheme(de_vs ? QStringLiteral("security-high") : QStringLiteral("security-medium"))); btn->setStyleSheet(QStringLiteral("background-color: ") + (de_vs ? KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color().name() : KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color().name())); mParent->setLabelText(de_vs ? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant", "%1 communication possible.", Formatting::deVsString()) : i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant", "%1 communication not possible.", Formatting::deVsString())); } } else { mParent->setButtonText(QWizard::CommitButton, i18n("Next")); btn->setIcon(QIcon()); btn->setStyleSheet(QString()); } Q_EMIT completeChanged(); } void updateFileWidgets() { if (mRequester.isEmpty()) { return; } const std::vector recipients = mWidget->recipients(); const Key sigKey = mWidget->signKey(); bool pgp = mWidget->encryptSymmetric(); bool cms = false; for (const Key &k : recipients) { if (pgp && cms) { break; } if (k.protocol() == Protocol::OpenPGP) { pgp = true; } else { cms = true; } } mOutLayout->setEnabled(false); if (cms || pgp || !sigKey.isNull()) { mPlaceholderWidget->setVisible(false); mOutputLabel->setVisible(true); mRequester[SignEncryptFilesWizard::SignatureCMS]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::CMS); mRequester[SignEncryptFilesWizard::EncryptedCMS]->setVisible(!mUseOutputDir && cms); mRequester[SignEncryptFilesWizard::CombinedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && pgp); mRequester[SignEncryptFilesWizard::EncryptedPGP]->setVisible(!mUseOutputDir && pgp && sigKey.protocol() != Protocol::OpenPGP); mRequester[SignEncryptFilesWizard::SignaturePGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && !pgp); mRequester[SignEncryptFilesWizard::Directory]->setVisible(mUseOutputDir); auto firstVisible = std::find_if(std::cbegin(mRequester), std::cend(mRequester), [](auto w) { return w->isVisible(); }); mOutputLabel->setBuddy(*firstVisible); } else { mPlaceholderWidget->setVisible(true); mOutputLabel->setVisible(false); std::for_each(std::cbegin(mRequester), std::cend(mRequester), [](auto w) { w->setVisible(false); }); mOutputLabel->setBuddy(nullptr); } mOutLayout->setEnabled(true); } private: SignEncryptFilesWizard *mParent; SignEncryptWidget *mWidget; QMap mOutNames; QMap mRequester; QVBoxLayout *mOutLayout; QWidget *mPlaceholderWidget; QCheckBox *mUseOutputDirChk; QLabel *mOutputLabel; bool mArchive; bool mUseOutputDir; }; class ResultPage : public NewResultPage { Q_OBJECT public: explicit ResultPage(QWidget *parent = nullptr) : NewResultPage(parent), mParent((SignEncryptFilesWizard *) parent) { setTitle(i18nc("@title", "Results")); setSubTitle(i18nc("@title", "Status and progress of the crypto operations is shown here.")); } void initializePage() override { mParent->setLabelText(QString()); } private: SignEncryptFilesWizard *mParent; }; SignEncryptFilesWizard::SignEncryptFilesWizard(QWidget *parent, Qt::WindowFlags f) : QWizard(parent, f) { readConfig(); bool de_vs = Kleo::gpgComplianceP("de-vs"); #ifdef Q_OS_WIN // Enforce modern style to avoid vista style ugliness. setWizardStyle(QWizard::ModernStyle); #endif mSigEncPage = new SigEncPage(this); mResultPage = new ResultPage(this); connect(this, &QWizard::currentIdChanged, this, &SignEncryptFilesWizard::slotCurrentIdChanged); setPage(SigEncPageId, mSigEncPage); setPage(ResultPageId, mResultPage); setOptions(QWizard::IndependentPages | (de_vs ? QWizard::HaveCustomButton1 : QWizard::WizardOption(0)) | QWizard::NoBackButtonOnLastPage | QWizard::NoBackButtonOnStartPage); if (de_vs) { /* We use a custom button to display a label next to the buttons. */ auto btn = button(QWizard::CustomButton1); /* We style the button so that it looks and acts like a label. */ btn->setStyleSheet(QStringLiteral("border: none")); btn->setFocusPolicy(Qt::NoFocus); } } void SignEncryptFilesWizard::setLabelText(const QString &label) { setButtonText(QWizard::CustomButton1, label); } void SignEncryptFilesWizard::slotCurrentIdChanged(int id) { if (id == ResultPageId) { Q_EMIT operationPrepared(); } } SignEncryptFilesWizard::~SignEncryptFilesWizard() { qCDebug(KLEOPATRA_LOG); writeConfig(); } void SignEncryptFilesWizard::setSigningPreset(bool preset) { mSigEncPage->setSigningPreset(preset); } void SignEncryptFilesWizard::setSigningUserMutable(bool mut) { if (mut == mSigningUserMutable) { return; } mSigningUserMutable = mut; } void SignEncryptFilesWizard::setEncryptionPreset(bool preset) { mSigEncPage->setEncryptionPreset(preset); } void SignEncryptFilesWizard::setEncryptionUserMutable(bool mut) { if (mut == mEncryptionUserMutable) { return; } mEncryptionUserMutable = mut; } void SignEncryptFilesWizard::setArchiveForced(bool archive) { mSigEncPage->setArchiveForced(archive); } void SignEncryptFilesWizard::setArchiveMutable(bool archive) { mSigEncPage->setArchiveMutable(archive); } std::vector SignEncryptFilesWizard::resolvedRecipients() const { return mSigEncPage->recipients(); } std::vector SignEncryptFilesWizard::resolvedSigners() const { return mSigEncPage->signers(); } void SignEncryptFilesWizard::setTaskCollection(const std::shared_ptr &coll) { mResultPage->setTaskCollection(coll); } void SignEncryptFilesWizard::setOutputNames(const QMap &map) const { mSigEncPage->setOutputNames(map); } QMap SignEncryptFilesWizard::outputNames() const { return mSigEncPage->outputNames(); } bool SignEncryptFilesWizard::encryptSymmetric() const { return mSigEncPage->encryptSymmetric(); } void SignEncryptFilesWizard::readConfig() { winId(); // ensure there's a window created // set default window size windowHandle()->resize(640, 480); // restore size from config file KConfigGroup cfgGroup(KSharedConfig::openConfig(), "SignEncryptFilesWizard"); KWindowConfig::restoreWindowSize(windowHandle(), cfgGroup); // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform // window was created -> QTBUG-40584. We therefore copy the size here. // TODO: remove once this was resolved in QWidget QPA resize(windowHandle()->size()); } void SignEncryptFilesWizard::writeConfig() { KConfigGroup cfgGroup(KSharedConfig::openConfig(), "SignEncryptFilesWizard"); KWindowConfig::saveWindowSize(windowHandle(), cfgGroup); cfgGroup.sync(); } #include "signencryptfileswizard.moc" diff --git a/src/crypto/gui/signencryptwidget.cpp b/src/crypto/gui/signencryptwidget.cpp index 92a0b18ff..5865cc8e6 100644 --- a/src/crypto/gui/signencryptwidget.cpp +++ b/src/crypto/gui/signencryptwidget.cpp @@ -1,628 +1,628 @@ /* 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 "settings.h" #include "unknownrecipientwidget.h" #include "commands/detailscommand.h" #include "dialogs/certificateselectiondialog.h" #include "dialogs/groupdetailsdialog.h" #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); 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); 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); } }; } SignEncryptWidget::SignEncryptWidget(QWidget *parent, bool sigEncExclusive) : QWidget(parent), mModel(AbstractKeyListModel::createFlatKeyListModel(this)), mIsExclusive(sigEncExclusive) { auto lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); mModel->useKeyCache(true, KeyList::IncludeGroups); /* The signature selection */ auto sigLay = new QHBoxLayout; - auto sigGrp = new QGroupBox(i18n("Prove authenticity (sign)")); + auto sigGrp = new QGroupBox(i18nc("@title:group", "Prove authenticity (sign)")); mSigChk = new QCheckBox(i18n("Sign as:")); mSigChk->setChecked(true); mSigSelect = new KeySelectionCombo(); sigLay->addWidget(mSigChk); sigLay->addWidget(mSigSelect, 1); sigGrp->setLayout(sigLay); lay->addWidget(sigGrp); connect(mSigChk, &QCheckBox::toggled, mSigSelect, &QWidget::setEnabled); connect(mSigChk, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp); connect(mSigSelect, &KeySelectionCombo::currentKeyChanged, this, &SignEncryptWidget::updateOp); // Recipient selection auto recipientGrid = new QGridLayout; auto encBoxLay = new QVBoxLayout; - auto encBox = new QGroupBox(i18nc("@action", "Encrypt")); + auto encBox = new QGroupBox(i18nc("@title:group", "Encrypt")); encBox->setLayout(encBoxLay); // Own key mEncSelfChk = new QCheckBox(i18n("Encrypt for me:")); mEncSelfChk->setChecked(true); mSelfSelect = new KeySelectionCombo(); recipientGrid->addWidget(mEncSelfChk, 0, 0); recipientGrid->addWidget(mSelfSelect, 0, 1); // Checkbox for other keys mEncOtherChk = new QCheckBox(i18n("Encrypt for others:")); recipientGrid->addWidget(mEncOtherChk, 1, 0, Qt::AlignTop); mEncOtherChk->setChecked(true); connect(mEncOtherChk, &QCheckBox::toggled, this, [this](bool toggled) { for (CertificateLineEdit *edit : std::as_const(mRecpWidgets)) { edit->setEnabled(toggled); } updateOp(); }); mRecpLayout = new QVBoxLayout; recipientGrid->addLayout(mRecpLayout, 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); }); addRecipientWidget(); // Checkbox for password mSymmetric = new QCheckBox(i18n("Encrypt with password. Anyone you share the password with can read the data.")); - mSymmetric->setToolTip(i18nc("Tooltip information for symetric encryption", + 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.")); encBoxLay->addWidget(mSymmetric); // Connect it connect(mEncSelfChk, &QCheckBox::toggled, mSelfSelect, &QWidget::setEnabled); connect(mEncSelfChk, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp); connect(mSymmetric, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp); connect(mSelfSelect, &KeySelectionCombo::currentKeyChanged, this, &SignEncryptWidget::updateOp); if (mIsExclusive) { connect(mEncOtherChk, &QCheckBox::toggled, this, [this](bool value) { if (mCurrentProto != GpgME::CMS) { return; } if (value) { mSigChk->setChecked(false); } }); connect(mEncSelfChk, &QCheckBox::toggled, this, [this](bool value) { if (mCurrentProto != GpgME::CMS) { return; } if (value) { mSigChk->setChecked(false); } }); connect(mSigChk, &QCheckBox::toggled, this, [this](bool value) { if (mCurrentProto != GpgME::CMS) { return; } if (value) { mEncSelfChk->setChecked(false); mEncOtherChk->setChecked(false); } }); } // Ensure that the mSigChk is aligned togehter with the encryption check boxes. mSigChk->setMinimumWidth(qMax(mEncOtherChk->width(), mEncSelfChk->width())); lay->addWidget(encBox); loadKeys(); onProtocolChanged(); updateOp(); } void SignEncryptWidget::setSignAsText(const QString &text) { mSigChk->setText(text); } void SignEncryptWidget::setEncryptForMeText(const QString &text) { mEncSelfChk->setText(text); } void SignEncryptWidget::setEncryptForOthersText(const QString &text) { mEncOtherChk->setText(text); } void SignEncryptWidget::setEncryptWithPasswordText(const QString& text) { mSymmetric->setText(text); } CertificateLineEdit *SignEncryptWidget::addRecipientWidget() { auto certSel = new CertificateLineEdit(mModel, this, new EncryptCertificateFilter(mCurrentProto)); mRecpWidgets << certSel; if (mRecpLayout->count() > 0) { auto lastWidget = mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget(); setTabOrder(lastWidget, certSel); } mRecpLayout->addWidget(certSel); connect(certSel, &CertificateLineEdit::keyChanged, this, &SignEncryptWidget::recipientsChanged); connect(certSel, &CertificateLineEdit::wantsRemoval, this, &SignEncryptWidget::recpRemovalRequested); connect(certSel, &CertificateLineEdit::editingStarted, this, [this] () { addRecipientWidget(); }); connect(certSel, &CertificateLineEdit::dialogRequested, this, [this, certSel] () { dialogRequested(certSel); }); return certSel; } void SignEncryptWidget::addRecipient(const Key &key) { CertificateLineEdit *certSel = addRecipientWidget(); if (!key.isNull()) { certSel->setKey(key); mAddedKeys << key; } } void SignEncryptWidget::addRecipient(const KeyGroup &group) { CertificateLineEdit *certSel = addRecipientWidget(); if (!group.isNull()) { certSel->setGroup(group); mAddedGroups << group; } } void SignEncryptWidget::dialogRequested(CertificateLineEdit *certificateLineEdit) { if (!certificateLineEdit->key().isNull()) { auto cmd = new Commands::DetailsCommand(certificateLineEdit->key(), nullptr); cmd->start(); return; } if (!certificateLineEdit->group().isNull()) { auto dlg = new GroupDetailsDialog; dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setGroup(certificateLineEdit->group()); dlg->show(); return; } auto const dlg = new CertificateSelectionDialog(this); dlg->setOptions(CertificateSelectionDialog::Options( CertificateSelectionDialog::MultiSelection | CertificateSelectionDialog::EncryptOnly | CertificateSelectionDialog::optionsFromProtocol(mCurrentProto) | CertificateSelectionDialog::IncludeGroups)); if (dlg->exec()) { const std::vector keys = dlg->selectedCertificates(); const std::vector groups = dlg->selectedGroups(); if (keys.size() == 0 && groups.size() == 0) { return; } bool isFirstItem = true; for (const Key &key : keys) { if (isFirstItem) { certificateLineEdit->setKey(key); isFirstItem = false; } else { addRecipient(key); } } for (const KeyGroup &group : groups) { if (isFirstItem) { certificateLineEdit->setGroup(group); isFirstItem = false; } else { addRecipient(group); } } } delete dlg; recipientsChanged(); } void SignEncryptWidget::clearAddedRecipients() { for (auto w: std::as_const(mUnknownWidgets)) { mRecpLayout->removeWidget(w); delete w; } for (auto &key: std::as_const(mAddedKeys)) { removeRecipient(key); } for (auto &group: std::as_const(mAddedGroups)) { removeRecipient(group); } } void SignEncryptWidget::addUnknownRecipient(const char *keyID) { auto unknownWidget = new UnknownRecipientWidget(keyID); mUnknownWidgets << unknownWidget; if (mRecpLayout->count() > 0) { auto lastWidget = mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget(); setTabOrder(lastWidget, unknownWidget); } mRecpLayout->addWidget(unknownWidget); connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this] () { // Check if any unknown recipient can now be found. for (auto w: 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(); mRecpLayout->removeWidget(w); mUnknownWidgets.removeAll(w); delete w; addRecipient(key); } }); } void SignEncryptWidget::recipientsChanged() { bool oneEmpty = false; for (const CertificateLineEdit *w : std::as_const(mRecpWidgets)) { if (w->key().isNull() && w->group().isNull()) { oneEmpty = true; break; } } if (!oneEmpty) { addRecipientWidget(); } updateOp(); } Key SignEncryptWidget::signKey() const { if (mSigSelect->isEnabled()) { return mSigSelect->currentKey(); } return Key(); } Key SignEncryptWidget::selfKey() const { if (mSelfSelect->isEnabled()) { return mSelfSelect->currentKey(); } return Key(); } std::vector SignEncryptWidget::recipients() const { std::vector ret; for (const CertificateLineEdit *w : std::as_const(mRecpWidgets)) { 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() && (!IS_DE_VS(signKey()) || keyValidity(signKey()) < GpgME::UserID::Validity::Full)) { return false; } if (!selfKey().isNull() && (!IS_DE_VS(selfKey()) || keyValidity(selfKey()) < GpgME::UserID::Validity::Full)) { return false; } for (const auto &key: recipients()) { if (!IS_DE_VS(key) || keyValidity(key) < GpgME::UserID::Validity::Full) { return false; } } return true; } void SignEncryptWidget::updateOp() { const Key sigKey = signKey(); const std::vector recp = recipients(); QString newOp; if (!sigKey.isNull() && (!recp.empty() || encryptSymmetric())) { newOp = i18nc("@action", "Sign / Encrypt"); } else if (!recp.empty() || encryptSymmetric()) { newOp = i18nc("@action", "Encrypt"); } else if (!sigKey.isNull()) { newOp = i18nc("@action", "Sign"); } else { newOp = QString(); } mOp = newOp; Q_EMIT operationChanged(mOp); Q_EMIT keysChanged(); } QString SignEncryptWidget::currentOp() const { return mOp; } void SignEncryptWidget::recpRemovalRequested(CertificateLineEdit *w) { if (!w) { return; } int emptyEdits = 0; for (const CertificateLineEdit *edit : std::as_const(mRecpWidgets)) { if (edit->isEmpty()) { emptyEdits++; } if (emptyEdits > 1) { mRecpLayout->removeWidget(w); mRecpWidgets.removeAll(w); w->deleteLater(); return; } } } void SignEncryptWidget::removeRecipient(const GpgME::Key &key) { for (CertificateLineEdit *edit: std::as_const(mRecpWidgets)) { const auto editKey = edit->key(); if (key.isNull() && editKey.isNull()) { recpRemovalRequested(edit); return; } if (editKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(editKey.primaryFingerprint(), key.primaryFingerprint())) { recpRemovalRequested(edit); return; } } } void SignEncryptWidget::removeRecipient(const KeyGroup &group) { for (CertificateLineEdit *edit: std::as_const(mRecpWidgets)) { const auto editGroup = edit->group(); if (group.isNull() && editGroup.isNull()) { recpRemovalRequested(edit); return; } if (editGroup.name() == group.name()) { recpRemovalRequested(edit); return; } } } bool SignEncryptWidget::encryptSymmetric() const { return mSymmetric->isChecked(); } void SignEncryptWidget::loadKeys() { KConfigGroup keys(KSharedConfig::openConfig(), "SignEncryptKeys"); auto cache = KeyCache::instance(); mSigSelect->setDefaultKey(keys.readEntry("SigningKey", QString())); mSelfSelect->setDefaultKey(keys.readEntry("EncryptKey", QString())); } void SignEncryptWidget::saveOwnKeys() const { KConfigGroup keys(KSharedConfig::openConfig(), "SignEncryptKeys"); auto sigKey = mSigSelect->currentKey(); auto encKey = mSelfSelect->currentKey(); if (!sigKey.isNull()) { keys.writeEntry("SigningKey", sigKey.primaryFingerprint()); } if (!encKey.isNull()) { keys.writeEntry("EncryptKey", encKey.primaryFingerprint()); } } void SignEncryptWidget::setSigningChecked(bool value) { mSigChk->setChecked(value); } void SignEncryptWidget::setEncryptionChecked(bool checked) { if (checked) { const bool haveOwnKeys = !KeyCache::instance()->secretKeys().empty(); const bool haveOtherKeys = !KeyCache::instance()->keys().empty(); const bool haveKeys = haveOwnKeys && haveOtherKeys; mEncSelfChk->setChecked(haveKeys); mEncOtherChk->setChecked(haveKeys); mSymmetric->setChecked(!haveKeys); } else { mEncSelfChk->setChecked(false); mEncOtherChk->setChecked(false); mSymmetric->setChecked(false); } } void SignEncryptWidget::setProtocol(GpgME::Protocol proto) { if (mCurrentProto == proto) { return; } mCurrentProto = proto; onProtocolChanged(); } void Kleo::SignEncryptWidget::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 (CertificateLineEdit *edit : std::as_const(mRecpWidgets)) { 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::validate() { for (const auto edit: std::as_const(mRecpWidgets)) { if (!edit->isEmpty() && edit->key().isNull() && edit->group().isNull()) { KMessageBox::error(this, i18nc("%1 is user input that could not be found", "Could not find a key for '%1'", edit->text().toHtmlEscaped()), i18n("Failed to find recipient"), KMessageBox::Notify); return false; } } return true; }