diff --git a/src/commands/certifycertificatecommand.cpp b/src/commands/certifycertificatecommand.cpp index d9d4150da..851069fab 100644 --- a/src/commands/certifycertificatecommand.cpp +++ b/src/commands/certifycertificatecommand.cpp @@ -1,317 +1,313 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/signcertificatecommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2019 g10code GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "certifycertificatecommand.h" #include "newcertificatecommand.h" #include "command_p.h" #include "exportopenpgpcertstoservercommand.h" #include "dialogs/certifycertificatedialog.h" #include "utils/tags.h" #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace GpgME; using namespace QGpgME; class CertifyCertificateCommand::Private : public Command::Private { friend class ::Kleo::Commands::CertifyCertificateCommand; CertifyCertificateCommand *q_func() const { return static_cast(q); } public: explicit Private(CertifyCertificateCommand *qq, KeyListController *c); ~Private() override; void init(); private: void slotDialogRejected(); void slotResult(const Error &err); void slotCertificationPrepared(); private: void ensureDialogCreated(); void createJob(); private: std::vector uids; QPointer dialog; QPointer job; }; CertifyCertificateCommand::Private *CertifyCertificateCommand::d_func() { return static_cast(d.get()); } const CertifyCertificateCommand::Private *CertifyCertificateCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() CertifyCertificateCommand::Private::Private(CertifyCertificateCommand *qq, KeyListController *c) : Command::Private(qq, c), uids(), dialog(), job() { } CertifyCertificateCommand::Private::~Private() { qCDebug(KLEOPATRA_LOG); if (dialog) { delete dialog; dialog = nullptr; } } CertifyCertificateCommand::CertifyCertificateCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } CertifyCertificateCommand::CertifyCertificateCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } CertifyCertificateCommand::CertifyCertificateCommand(const GpgME::Key &key) : Command(key, new Private(this, nullptr)) { d->init(); } CertifyCertificateCommand::CertifyCertificateCommand(const GpgME::UserID &uid) : Command(uid.parent(), new Private(this, nullptr)) { std::vector(1, uid).swap(d->uids); d->init(); } CertifyCertificateCommand::CertifyCertificateCommand(const std::vector &uids) : Command(uids.empty() ? Key() : uids.front().parent(), new Private(this, nullptr)) { d->uids = uids; d->init(); } void CertifyCertificateCommand::Private::init() { } CertifyCertificateCommand::~CertifyCertificateCommand() { qCDebug(KLEOPATRA_LOG); } void CertifyCertificateCommand::doStart() { const std::vector keys = d->keys(); if (keys.size() != 1 || keys.front().protocol() != GpgME::OpenPGP) { d->finished(); return; } - std::vector secKeys; - Q_FOREACH (const Key &secKey, KeyCache::instance()->secretKeys()) { - // Only include usable keys. - if (secKey.canCertify() && secKey.protocol() == OpenPGP && !secKey.isRevoked() && - !secKey.isExpired() && !secKey.isInvalid()) { - secKeys.push_back(secKey); - } - } - - if (secKeys.empty()) { + auto findAnyGoodKey = []() { + const std::vector secKeys = KeyCache::instance()->secretKeys(); + return std::any_of(secKeys.cbegin(), secKeys.cend(), [](const Key &secKey) { + return secKey.canCertify() && secKey.protocol() == OpenPGP && !secKey.isRevoked() + && !secKey.isExpired() && !secKey.isInvalid(); + }); + }; + + if (!findAnyGoodKey()) { auto sel = KMessageBox::questionYesNo(d->parentWidgetOrView(), xi18nc("@info", "To certify other certificates, you first need to create an OpenPGP certificate for yourself.") + QStringLiteral("

") + i18n("Do you wish to create one now?"), i18n("Certification Not Possible")); if (sel == KMessageBox::Yes) { QEventLoop loop; auto cmd = new Commands::NewCertificateCommand(); cmd->setParentWidget(d->parentWidgetOrView()); cmd->setProtocol(GpgME::OpenPGP); connect(cmd, &Command::finished, &loop, &QEventLoop::quit); QMetaObject::invokeMethod(cmd, &Commands::NewCertificateCommand::start, Qt::QueuedConnection); loop.exec(); } else { Q_EMIT(canceled()); d->finished(); return; } - Q_FOREACH (const Key &secKey, KeyCache::instance()->secretKeys()) { - // Check again for secret keys - if (secKey.canCertify() && secKey.protocol() == OpenPGP && !secKey.isRevoked() && - !secKey.isExpired() && !secKey.isInvalid()) { - secKeys.push_back(secKey); - } - } - if (secKeys.empty()) { + + // Check again for secret keys + if (!findAnyGoodKey()) { qCDebug(KLEOPATRA_LOG) << "Sec Keys still empty after keygen."; Q_EMIT(canceled()); d->finished(); return; } } - const Key &key = keys.front(); - for (const UserID &uid : std::as_const(d->uids)) - if (qstricmp(uid.parent().primaryFingerprint(), key.primaryFingerprint()) != 0) { - qCWarning(KLEOPATRA_LOG) << "User ID <-> Key mismatch!"; - d->finished(); - return; - } + const char *primary = keys.front().primaryFingerprint(); + const bool anyMismatch = std::any_of(d->uids.cbegin(), d->uids.cend(), [primary](const UserID &uid) { + return qstricmp(uid.parent().primaryFingerprint(), primary) != 0; + }); + if (anyMismatch) { + qCWarning(KLEOPATRA_LOG) << "User ID <-> Key mismatch!"; + d->finished(); + return; + } d->ensureDialogCreated(); Q_ASSERT(d->dialog); Key target = d->key(); if (!(target.keyListMode() & GpgME::SignatureNotations)) { target.update(); } d->dialog->setCertificateToCertify(target); if (d->uids.size()) { d->dialog->setSelectedUserIDs(d->uids); } d->dialog->show(); } void CertifyCertificateCommand::Private::slotDialogRejected() { Q_EMIT q->canceled(); finished(); } void CertifyCertificateCommand::Private::slotResult(const Error &err) { if (!err && !err.isCanceled() && dialog && dialog->exportableCertificationSelected() && dialog->sendToServer()) { auto const cmd = new ExportOpenPGPCertsToServerCommand(key()); cmd->start(); } else if (!err) { information(i18n("Certification successful."), i18n("Certification Succeeded")); } else { error(i18n("

An error occurred while trying to certify

" "%1:

\t%2

", Formatting::formatForComboBox(key()), QString::fromUtf8(err.asString())), i18n("Certification Error")); } if (!dialog->tags().isEmpty()) { Tags::enableTags(); } finished(); } void CertifyCertificateCommand::Private::slotCertificationPrepared() { Q_ASSERT(dialog); createJob(); Q_ASSERT(job); job->setExportable(dialog->exportableCertificationSelected()); job->setNonRevocable(dialog->nonRevocableCertificationSelected()); job->setUserIDsToSign(dialog->selectedUserIDs()); job->setSigningKey(dialog->selectedSecretKey()); job->setCheckLevel(dialog->selectedCheckLevel()); if (!dialog->tags().isEmpty()) { // do not set an empty remark to avoid an empty signature notation (GnuPG bug T5142) job->setRemark(dialog->tags()); } job->setDupeOk(true); if (dialog->trustSignatureSelected() && !dialog->trustSignatureDomain().isEmpty()) { // always create level 1 trust signatures with complete trust job->setTrustSignature(TrustSignatureTrust::Complete, 1, dialog->trustSignatureDomain()); } if (!dialog->expirationDate().isNull()) { job->setExpirationDate(dialog->expirationDate()); } if (const Error err = job->start(key())) { slotResult(err); } } void CertifyCertificateCommand::doCancel() { qCDebug(KLEOPATRA_LOG); if (d->job) { d->job->slotCancel(); } } void CertifyCertificateCommand::Private::ensureDialogCreated() { if (dialog) { return; } dialog = new CertifyCertificateDialog; applyWindowID(dialog); connect(dialog, &QDialog::rejected, q, [this]() { slotDialogRejected(); }); connect(dialog, &QDialog::accepted, q, [this]() { slotCertificationPrepared(); }); } void CertifyCertificateCommand::Private::createJob() { Q_ASSERT(!job); Q_ASSERT(key().protocol() == OpenPGP); const auto backend = QGpgME::openpgp(); if (!backend) { return; } SignKeyJob *const j = backend->signKeyJob(); if (!j) { return; } connect(j, &Job::progress, q, &Command::progress); connect(j, &SignKeyJob::result, q, [this](const GpgME::Error &result) { slotResult(result); }); job = j; } #undef d #undef q #include "moc_certifycertificatecommand.cpp" diff --git a/src/crypto/gui/newresultpage.cpp b/src/crypto/gui/newresultpage.cpp index b17890521..478eb8ce3 100644 --- a/src/crypto/gui/newresultpage.cpp +++ b/src/crypto/gui/newresultpage.cpp @@ -1,186 +1,186 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resultpage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "newresultpage.h" #include "resultlistwidget.h" #include "resultitemwidget.h" #include #include #include #include #include #include #include #include #include static const int ProgressBarHideDelay = 2000; // 2 secs using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; class NewResultPage::Private { NewResultPage *const q; public: explicit Private(NewResultPage *qq); void progress(const QString &msg, int progress, int total); void result(const std::shared_ptr &result); void started(const std::shared_ptr &result); void allDone(); QLabel *labelForTag(const QString &tag); std::vector< std::shared_ptr > m_collections; QTimer m_hideProgressTimer; QProgressBar *m_progressBar; QHash m_progressLabelByTag; QVBoxLayout *m_progressLabelLayout; int m_lastErrorItemIndex; ResultListWidget *m_resultList; }; NewResultPage::Private::Private(NewResultPage *qq) : q(qq), m_lastErrorItemIndex(0) { m_hideProgressTimer.setInterval(ProgressBarHideDelay); m_hideProgressTimer.setSingleShot(true); QBoxLayout *const layout = new QVBoxLayout(q); auto const labels = new QWidget; m_progressLabelLayout = new QVBoxLayout(labels); layout->addWidget(labels); m_progressBar = new QProgressBar; layout->addWidget(m_progressBar); m_resultList = new ResultListWidget; connect(m_resultList, &ResultListWidget::linkActivated, q, &NewResultPage::linkActivated); layout->addWidget(m_resultList, 1); connect(&m_hideProgressTimer, &QTimer::timeout, m_progressBar, &QProgressBar::hide); } void NewResultPage::Private::progress(const QString &msg, int progress, int total) { Q_UNUSED(msg) Q_ASSERT(progress >= 0); Q_ASSERT(total >= 0); m_progressBar->setRange(0, total); m_progressBar->setValue(progress); } void NewResultPage::Private::allDone() { Q_ASSERT(!m_collections.empty()); if (!m_resultList->isComplete()) { return; } m_progressBar->setRange(0, 100); m_progressBar->setValue(100); m_collections.clear(); const auto progressLabelByTagKeys{m_progressLabelByTag.keys()}; for (const QString &i : progressLabelByTagKeys) { if (!i.isEmpty()) { m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i)); } else { m_progressLabelByTag.value(i)->setText(i18n("All operations completed.")); } } if (QAbstractButton *cancel = q->wizard()->button(QWizard::CancelButton)) { cancel->setEnabled(false); } Q_EMIT q->completeChanged(); m_hideProgressTimer.start(); } void NewResultPage::Private::result(const std::shared_ptr &) { } void NewResultPage::Private::started(const std::shared_ptr &task) { Q_ASSERT(task); const QString tag = task->tag(); QLabel *const label = labelForTag(tag); Q_ASSERT(label); if (tag.isEmpty()) { label->setText(i18nc("number, operation description", "Operation %1: %2", m_resultList->numberOfCompletedTasks() + 1, task->label())); } else { label->setText(i18nc(R"(tag( "OpenPGP" or "CMS"), operation description)", "%1: %2", tag, task->label())); } } NewResultPage::NewResultPage(QWidget *parent) : QWizardPage(parent), d(new Private(this)) { setTitle(i18n("Results")); } NewResultPage::~NewResultPage() { } void NewResultPage::setTaskCollection(const std::shared_ptr &coll) { //clear(); ### PENDING(marc) implement addTaskCollection(coll); } void NewResultPage::addTaskCollection(const std::shared_ptr &coll) { Q_ASSERT(coll); if (std::find(d->m_collections.cbegin(), d->m_collections.cend(), coll) != d->m_collections.cend()) { return; } d->m_hideProgressTimer.stop(); d->m_progressBar->show(); d->m_collections.push_back(coll); d->m_resultList->addTaskCollection(coll); connect(coll.get(), SIGNAL(progress(QString,int,int)), this, SLOT(progress(QString,int,int))); connect(coll.get(), SIGNAL(done()), this, SLOT(allDone())); connect(coll.get(), SIGNAL(result(std::shared_ptr)), this, SLOT(result(std::shared_ptr))); connect(coll.get(), SIGNAL(started(std::shared_ptr)), this, SLOT(started(std::shared_ptr))); - Q_FOREACH (const std::shared_ptr &i, coll->tasks()) { // create labels for all tags in collection + for (const std::shared_ptr &i : coll->tasks()) { // create labels for all tags in collection Q_ASSERT(i); QLabel *l = d->labelForTag(i->tag()); Q_ASSERT(l); (void)l; } Q_EMIT completeChanged(); } QLabel *NewResultPage::Private::labelForTag(const QString &tag) { if (QLabel *const label = m_progressLabelByTag.value(tag)) { return label; } auto label = new QLabel; label->setTextFormat(Qt::RichText); label->setWordWrap(true); m_progressLabelLayout->addWidget(label); m_progressLabelByTag.insert(tag, label); return label; } bool NewResultPage::isComplete() const { return d->m_resultList->isComplete(); } #include "moc_newresultpage.cpp" diff --git a/src/crypto/gui/resolverecipientspage.cpp b/src/crypto/gui/resolverecipientspage.cpp index 73685f530..c22b6d4b1 100644 --- a/src/crypto/gui/resolverecipientspage.cpp +++ b/src/crypto/gui/resolverecipientspage.cpp @@ -1,697 +1,698 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resolverecipientspage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "resolverecipientspage.h" #include "resolverecipientspage_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Dialogs; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; using namespace KMime::Types; ResolveRecipientsPage::ListWidget::ListWidget(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), m_protocol(UnknownProtocol) { m_listWidget = new QListWidget; m_listWidget->setSelectionMode(QAbstractItemView::MultiSelection); auto const layout = new QVBoxLayout(this); layout->addWidget(m_listWidget); connect(m_listWidget, &QListWidget::itemSelectionChanged, this, &ListWidget::onSelectionChange); } ResolveRecipientsPage::ListWidget::~ListWidget() { } void ResolveRecipientsPage::ListWidget::onSelectionChange() { const auto widgetskeys = widgets.keys(); for (const QString &i : widgetskeys) { Q_ASSERT(items.contains(i)); widgets[i]->setSelected(items[i]->isSelected()); } Q_EMIT selectionChanged(); } void ResolveRecipientsPage::ListWidget::addEntry(const Mailbox &mbox) { addEntry(mbox.prettyAddress(), mbox.prettyAddress(), mbox); } void ResolveRecipientsPage::ListWidget::addEntry(const QString &id, const QString &name) { addEntry(id, name, Mailbox()); } void ResolveRecipientsPage::ListWidget::addEntry(const QString &id, const QString &name, const Mailbox &mbox) { Q_ASSERT(!widgets.contains(id) && !items.contains(id)); auto item = new QListWidgetItem; item->setData(IdRole, id); auto wid = new ItemWidget(id, name, mbox, this); connect(wid, &ItemWidget::changed, this, &ListWidget::completeChanged); wid->setProtocol(m_protocol); item->setSizeHint(wid->sizeHint()); m_listWidget->addItem(item); m_listWidget->setItemWidget(item, wid); widgets[id] = wid; items[id] = item; } Mailbox ResolveRecipientsPage::ListWidget::mailbox(const QString &id) const { return widgets.contains(id) ? widgets[id]->mailbox() : Mailbox(); } void ResolveRecipientsPage::ListWidget::setCertificates(const QString &id, const std::vector &pgp, const std::vector &cms) { Q_ASSERT(widgets.contains(id)); widgets[id]->setCertificates(pgp, cms); } Key ResolveRecipientsPage::ListWidget::selectedCertificate(const QString &id) const { return widgets.contains(id) ? widgets[id]->selectedCertificate() : Key(); } GpgME::Key ResolveRecipientsPage::ListWidget::selectedCertificate(const QString &id, GpgME::Protocol prot) const { return widgets.contains(id) ? widgets[id]->selectedCertificate(prot) : Key(); } QStringList ResolveRecipientsPage::ListWidget::identifiers() const { return widgets.keys(); } void ResolveRecipientsPage::ListWidget::setProtocol(GpgME::Protocol prot) { if (m_protocol == prot) { return; } m_protocol = prot; for (ItemWidget *i : std::as_const(widgets)) { i->setProtocol(prot); } } void ResolveRecipientsPage::ListWidget::removeEntry(const QString &id) { if (!widgets.contains(id)) { return; } delete items[id]; items.remove(id); delete widgets[id]; widgets.remove(id); } void ResolveRecipientsPage::ListWidget::showSelectionDialog(const QString &id) { if (!widgets.contains(id)) { return; } widgets[id]->showSelectionDialog(); } QStringList ResolveRecipientsPage::ListWidget::selectedEntries() const { QStringList entries; const QList items = m_listWidget->selectedItems(); entries.reserve(items.count()); for (const QListWidgetItem *i : items) { entries.append(i->data(IdRole).toString()); } return entries; } ResolveRecipientsPage::ItemWidget::ItemWidget(const QString &id, const QString &name, const Mailbox &mbox, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), m_id(id), m_mailbox(mbox), m_protocol(UnknownProtocol), m_selected(false) { Q_ASSERT(!m_id.isEmpty()); setAutoFillBackground(true); auto layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addSpacing(15); m_nameLabel = new QLabel; m_nameLabel->setText(name); layout->addWidget(m_nameLabel); layout->addStretch(); m_certLabel = new QLabel; m_certLabel->setText(i18n("No certificate selected")); layout->addWidget(m_certLabel); m_certCombo = new QComboBox; connect(m_certCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed())); layout->addWidget(m_certCombo); m_selectButton = new QToolButton; m_selectButton->setText(i18n("...")); connect(m_selectButton, &QAbstractButton::clicked, this, &ItemWidget::showSelectionDialog); layout->addWidget(m_selectButton); layout->addSpacing(15); setCertificates(std::vector(), std::vector()); } void ResolveRecipientsPage::ItemWidget::updateVisibility() { m_certLabel->setVisible(m_certCombo->count() == 0); m_certCombo->setVisible(m_certCombo->count() > 0); } ResolveRecipientsPage::ItemWidget::~ItemWidget() { } QString ResolveRecipientsPage::ItemWidget::id() const { return m_id; } void ResolveRecipientsPage::ItemWidget::setSelected(bool selected) { if (m_selected == selected) { return; } m_selected = selected; setBackgroundRole(selected ? QPalette::Highlight : QPalette::Base); const QPalette::ColorRole foreground = selected ? QPalette::HighlightedText : QPalette::Text; setForegroundRole(foreground); m_nameLabel->setForegroundRole(foreground); m_certLabel->setForegroundRole(foreground); } bool ResolveRecipientsPage::ItemWidget::isSelected() const { return m_selected; } static CertificateSelectionDialog *createCertificateSelectionDialog(QWidget *parent, GpgME::Protocol prot) { auto const dlg = new CertificateSelectionDialog(parent); const CertificateSelectionDialog::Options options = CertificateSelectionDialog::SingleSelection | CertificateSelectionDialog::EncryptOnly | CertificateSelectionDialog::MultiSelection | CertificateSelectionDialog::optionsFromProtocol(prot); dlg->setOptions(options); return dlg; } void ResolveRecipientsPage::ItemWidget::showSelectionDialog() { QPointer dlg = createCertificateSelectionDialog(this, m_protocol); if (dlg->exec() == QDialog::Accepted && dlg /* still with us? */) { const GpgME::Key cert = dlg->selectedCertificate(); if (!cert.isNull()) { addCertificateToComboBox(cert); selectCertificateInComboBox(cert); } } delete dlg; } Mailbox ResolveRecipientsPage::ItemWidget::mailbox() const { return m_mailbox; } void ResolveRecipientsPage::ItemWidget::selectCertificateInComboBox(const Key &key) { m_certCombo->setCurrentIndex(m_certCombo->findData(QLatin1String(key.keyID()))); } void ResolveRecipientsPage::ItemWidget::addCertificateToComboBox(const GpgME::Key &key) { m_certCombo->addItem(Formatting::formatForComboBox(key), QByteArray(key.keyID())); if (m_certCombo->count() == 1) { m_certCombo->setCurrentIndex(0); } updateVisibility(); } void ResolveRecipientsPage::ItemWidget::resetCertificates() { std::vector certs; Key selected; switch (m_protocol) { case OpenPGP: certs = m_pgp; break; case CMS: certs = m_cms; break; case UnknownProtocol: certs = m_cms; certs.insert(certs.end(), m_pgp.begin(), m_pgp.end()); } m_certCombo->clear(); for (const Key &i : std::as_const(certs)) { addCertificateToComboBox(i); } if (!m_selectedCertificates[m_protocol].isNull()) { selectCertificateInComboBox(m_selectedCertificates[m_protocol]); } else if (m_certCombo->count() > 0) { m_certCombo->setCurrentIndex(0); } updateVisibility(); Q_EMIT changed(); } void ResolveRecipientsPage::ItemWidget::setProtocol(Protocol prot) { if (m_protocol == prot) { return; } m_selectedCertificates[m_protocol] = selectedCertificate(); if (m_protocol != UnknownProtocol) { (m_protocol == OpenPGP ? m_pgp : m_cms) = certificates(); } m_protocol = prot; resetCertificates(); } void ResolveRecipientsPage::ItemWidget::setCertificates(const std::vector &pgp, const std::vector &cms) { m_pgp = pgp; m_cms = cms; resetCertificates(); } Key ResolveRecipientsPage::ItemWidget::selectedCertificate() const { return KeyCache::instance()->findByKeyIDOrFingerprint(m_certCombo->itemData(m_certCombo->currentIndex(), ListWidget::IdRole).toString().toStdString()); } GpgME::Key ResolveRecipientsPage::ItemWidget::selectedCertificate(GpgME::Protocol prot) const { return prot == m_protocol ? selectedCertificate() : m_selectedCertificates.value(prot); } std::vector ResolveRecipientsPage::ItemWidget::certificates() const { std::vector certs; for (int i = 0; i < m_certCombo->count(); ++i) { certs.push_back(KeyCache::instance()->findByKeyIDOrFingerprint(m_certCombo->itemData(i, ListWidget::IdRole).toString().toStdString())); } return certs; } class ResolveRecipientsPage::Private { friend class ::Kleo::Crypto::Gui::ResolveRecipientsPage; ResolveRecipientsPage *const q; public: explicit Private(ResolveRecipientsPage *qq); ~Private(); void setSelectedProtocol(Protocol protocol); void selectionChanged(); void removeSelectedEntries(); void addRecipient(); void addRecipient(const Mailbox &mbox); void addRecipient(const QString &id, const QString &name); void updateProtocolRBVisibility(); void protocolSelected(int prot); void writeSelectedCertificatesToPreferences(); void completeChangedInternal(); private: ListWidget *m_listWidget; QPushButton *m_addButton; QPushButton *m_removeButton; QRadioButton *m_pgpRB; QRadioButton *m_cmsRB; QLabel *m_additionalRecipientsLabel; Protocol m_presetProtocol; Protocol m_selectedProtocol; bool m_multipleProtocolsAllowed; std::shared_ptr m_recipientPreferences; }; ResolveRecipientsPage::Private::Private(ResolveRecipientsPage *qq) : q(qq), m_presetProtocol(UnknownProtocol), m_selectedProtocol(m_presetProtocol), m_multipleProtocolsAllowed(false), m_recipientPreferences() { connect(q, SIGNAL(completeChanged()), q, SLOT(completeChangedInternal())); q->setTitle(i18n("Recipients")); auto const layout = new QVBoxLayout(q); m_listWidget = new ListWidget; connect(m_listWidget, SIGNAL(selectionChanged()), q, SLOT(selectionChanged())); connect(m_listWidget, &ListWidget::completeChanged, q, &WizardPage::completeChanged); layout->addWidget(m_listWidget); m_additionalRecipientsLabel = new QLabel; m_additionalRecipientsLabel->setWordWrap(true); layout->addWidget(m_additionalRecipientsLabel); m_additionalRecipientsLabel->setVisible(false); auto buttonWidget = new QWidget; auto buttonLayout = new QHBoxLayout(buttonWidget); buttonLayout->setContentsMargins(0, 0, 0, 0); m_addButton = new QPushButton; connect(m_addButton, SIGNAL(clicked()), q, SLOT(addRecipient())); m_addButton->setText(i18n("Add Recipient...")); buttonLayout->addWidget(m_addButton); m_removeButton = new QPushButton; m_removeButton->setEnabled(false); m_removeButton->setText(i18n("Remove Selected")); connect(m_removeButton, SIGNAL(clicked()), q, SLOT(removeSelectedEntries())); buttonLayout->addWidget(m_removeButton); buttonLayout->addStretch(); layout->addWidget(buttonWidget); auto protocolWidget = new QWidget; auto protocolLayout = new QHBoxLayout(protocolWidget); auto protocolGroup = new QButtonGroup(q); connect(protocolGroup, SIGNAL(buttonClicked(int)), q, SLOT(protocolSelected(int))); m_pgpRB = new QRadioButton; m_pgpRB->setText(i18n("OpenPGP")); protocolGroup->addButton(m_pgpRB, OpenPGP); protocolLayout->addWidget(m_pgpRB); m_cmsRB = new QRadioButton; m_cmsRB->setText(i18n("S/MIME")); protocolGroup->addButton(m_cmsRB, CMS); protocolLayout->addWidget(m_cmsRB); protocolLayout->addStretch(); layout->addWidget(protocolWidget); } ResolveRecipientsPage::Private::~Private() {} void ResolveRecipientsPage::Private::completeChangedInternal() { const bool isComplete = q->isComplete(); const std::vector keys = q->resolvedCertificates(); const bool haveSecret = std::find_if(keys.begin(), keys.end(), [](const Key &key) { return key.hasSecret(); }) != keys.end(); if (isComplete && !haveSecret) { q->setExplanation(i18n("Warning: None of the selected certificates seem to be your own. You will not be able to decrypt the encrypted data again.")); } else { q->setExplanation(QString()); } } void ResolveRecipientsPage::Private::updateProtocolRBVisibility() { const bool visible = !m_multipleProtocolsAllowed && m_presetProtocol == UnknownProtocol; m_cmsRB->setVisible(visible); m_pgpRB->setVisible(visible); if (visible) { if (m_selectedProtocol == CMS) { m_cmsRB->click(); } else { m_pgpRB->click(); } } } bool ResolveRecipientsPage::isComplete() const { const QStringList ids = d->m_listWidget->identifiers(); if (ids.isEmpty()) { return false; } for (const QString &i : ids) { if (d->m_listWidget->selectedCertificate(i).isNull()) { return false; } } return true; } ResolveRecipientsPage::ResolveRecipientsPage(QWidget *parent) : WizardPage(parent), d(new Private(this)) { } ResolveRecipientsPage::~ResolveRecipientsPage() {} Protocol ResolveRecipientsPage::selectedProtocol() const { return d->m_selectedProtocol; } void ResolveRecipientsPage::Private::setSelectedProtocol(Protocol protocol) { if (m_selectedProtocol == protocol) { return; } m_selectedProtocol = protocol; m_listWidget->setProtocol(m_selectedProtocol); Q_EMIT q->selectedProtocolChanged(); } void ResolveRecipientsPage::Private::protocolSelected(int p) { const auto protocol = static_cast(p); Q_ASSERT(protocol != UnknownProtocol); setSelectedProtocol(protocol); } void ResolveRecipientsPage::setPresetProtocol(Protocol prot) { if (d->m_presetProtocol == prot) { return; } d->m_presetProtocol = prot; d->setSelectedProtocol(prot); if (prot != UnknownProtocol) { d->m_multipleProtocolsAllowed = false; } d->updateProtocolRBVisibility(); } Protocol ResolveRecipientsPage::presetProtocol() const { return d->m_presetProtocol; } bool ResolveRecipientsPage::multipleProtocolsAllowed() const { return d->m_multipleProtocolsAllowed; } void ResolveRecipientsPage::setMultipleProtocolsAllowed(bool allowed) { if (d->m_multipleProtocolsAllowed == allowed) { return; } d->m_multipleProtocolsAllowed = allowed; if (d->m_multipleProtocolsAllowed) { setPresetProtocol(UnknownProtocol); d->setSelectedProtocol(UnknownProtocol); } d->updateProtocolRBVisibility(); } void ResolveRecipientsPage::Private::addRecipient(const QString &id, const QString &name) { m_listWidget->addEntry(id, name); } void ResolveRecipientsPage::Private::addRecipient(const Mailbox &mbox) { m_listWidget->addEntry(mbox); } void ResolveRecipientsPage::Private::addRecipient() { QPointer dlg = createCertificateSelectionDialog(q, q->selectedProtocol()); if (dlg->exec() != QDialog::Accepted || !dlg /*q already deleted*/) { return; } const std::vector keys = dlg->selectedCertificates(); int i = 0; for (const Key &key : keys) { const QStringList existing = m_listWidget->identifiers(); QString rec = i18n("Recipient"); while (existing.contains(rec)) { rec = i18nc("%1 == number", "Recipient (%1)", ++i); } addRecipient(rec, rec); const std::vector pgp = key.protocol() == OpenPGP ? std::vector(1, key) : std::vector(); const std::vector cms = key.protocol() == CMS ? std::vector(1, key) : std::vector(); m_listWidget->setCertificates(rec, pgp, cms); } Q_EMIT q->completeChanged(); } namespace { std::vector makeSuggestions(const std::shared_ptr &prefs, const Mailbox &mb, GpgME::Protocol prot) { std::vector suggestions; const Key remembered = prefs ? prefs->preferredCertificate(mb, prot) : Key(); if (!remembered.isNull()) { suggestions.push_back(remembered); } else { suggestions = CertificateResolver::resolveRecipient(mb, prot); } return suggestions; } } static QString listKeysForInfo(const std::vector &keys) { QStringList list; std::transform(keys.begin(), keys.end(), list.begin(), &Formatting::formatKeyLink); return list.join(QLatin1String("
")); } void ResolveRecipientsPage::setAdditionalRecipientsInfo(const std::vector &recipients) { d->m_additionalRecipientsLabel->setVisible(!recipients.empty()); if (recipients.empty()) { return; } d->m_additionalRecipientsLabel->setText( i18n("

Recipients predefined via GnuPG settings:

%1
", listKeysForInfo(recipients))); } void ResolveRecipientsPage::setRecipients(const std::vector &recipients, const std::vector &encryptToSelfRecipients) { uint cmsCount = 0; uint pgpCount = 0; uint senders = 0; for (const Mailbox &mb : encryptToSelfRecipients) { const QString id = QLatin1String("sender-") + QString::number(++senders); d->m_listWidget->addEntry(id, i18n("Sender"), mb); const std::vector pgp = makeSuggestions(d->m_recipientPreferences, mb, OpenPGP); const std::vector cms = makeSuggestions(d->m_recipientPreferences, mb, CMS); pgpCount += !pgp.empty(); cmsCount += !cms.empty(); d->m_listWidget->setCertificates(id, pgp, cms); } for (const Mailbox &i : recipients) { //TODO: const QString address = i.prettyAddress(); d->addRecipient(i); const std::vector pgp = makeSuggestions(d->m_recipientPreferences, i, OpenPGP); const std::vector cms = makeSuggestions(d->m_recipientPreferences, i, CMS); pgpCount += pgp.empty() ? 0 : 1; cmsCount += cms.empty() ? 0 : 1; d->m_listWidget->setCertificates(address, pgp, cms); } if (d->m_presetProtocol == UnknownProtocol && !d->m_multipleProtocolsAllowed) { (cmsCount > pgpCount ? d->m_cmsRB : d->m_pgpRB)->click(); } } std::vector ResolveRecipientsPage::resolvedCertificates() const { std::vector certs; - Q_FOREACH (const QString &i, d->m_listWidget->identifiers()) { + const QStringList identifiers = d->m_listWidget->identifiers(); + for (const QString &i : identifiers) { const GpgME::Key cert = d->m_listWidget->selectedCertificate(i); if (!cert.isNull()) { certs.push_back(cert); } } return certs; } void ResolveRecipientsPage::Private::selectionChanged() { m_removeButton->setEnabled(!m_listWidget->selectedEntries().isEmpty()); } void ResolveRecipientsPage::Private::removeSelectedEntries() { const auto selectedEntries{m_listWidget->selectedEntries()}; for (const QString &i : selectedEntries) { m_listWidget->removeEntry(i); } Q_EMIT q->completeChanged(); } void ResolveRecipientsPage::setRecipientsUserMutable(bool isMutable) { d->m_addButton->setVisible(isMutable); d->m_removeButton->setVisible(isMutable); } bool ResolveRecipientsPage::recipientsUserMutable() const { return d->m_addButton->isVisible(); } std::shared_ptr ResolveRecipientsPage::recipientPreferences() const { return d->m_recipientPreferences; } void ResolveRecipientsPage::setRecipientPreferences(const std::shared_ptr &prefs) { d->m_recipientPreferences = prefs; } void ResolveRecipientsPage::Private::writeSelectedCertificatesToPreferences() { if (!m_recipientPreferences) { return; } const auto identifiers{m_listWidget->identifiers()}; for (const QString &i : identifiers) { const Mailbox mbox = m_listWidget->mailbox(i); if (!mbox.hasAddress()) { continue; } const Key pgp = m_listWidget->selectedCertificate(i, OpenPGP); if (!pgp.isNull()) { m_recipientPreferences->setPreferredCertificate(mbox, OpenPGP, pgp); } const Key cms = m_listWidget->selectedCertificate(i, CMS); if (!cms.isNull()) { m_recipientPreferences->setPreferredCertificate(mbox, CMS, cms); } } } void ResolveRecipientsPage::onNext() { d->writeSelectedCertificatesToPreferences(); } #include "moc_resolverecipientspage_p.cpp" #include "moc_resolverecipientspage.cpp" diff --git a/src/crypto/gui/resultpage.cpp b/src/crypto/gui/resultpage.cpp index fb18627d6..4cd058972 100644 --- a/src/crypto/gui/resultpage.cpp +++ b/src/crypto/gui/resultpage.cpp @@ -1,182 +1,182 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resultpage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "resultpage.h" #include "resultlistwidget.h" #include "resultitemwidget.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; class ResultPage::Private { ResultPage *const q; public: explicit Private(ResultPage *qq); void progress(const QString &msg, int progress, int total); void result(const std::shared_ptr &result); void started(const std::shared_ptr &result); void allDone(); void keepOpenWhenDone(bool keep); QLabel *labelForTag(const QString &tag); std::shared_ptr m_tasks; QProgressBar *m_progressBar; QHash m_progressLabelByTag; QVBoxLayout *m_progressLabelLayout; int m_lastErrorItemIndex = 0; ResultListWidget *m_resultList; QCheckBox *m_keepOpenCB; }; ResultPage::Private::Private(ResultPage *qq) : q(qq) { QBoxLayout *const layout = new QVBoxLayout(q); auto const labels = new QWidget; m_progressLabelLayout = new QVBoxLayout(labels); layout->addWidget(labels); m_progressBar = new QProgressBar; layout->addWidget(m_progressBar); m_resultList = new ResultListWidget; layout->addWidget(m_resultList); m_keepOpenCB = new QCheckBox; m_keepOpenCB->setText(i18n("Keep open after operation completed")); m_keepOpenCB->setChecked(true); connect(m_keepOpenCB, &QAbstractButton::toggled, q, &ResultPage::keepOpenWhenDone); layout->addWidget(m_keepOpenCB); } void ResultPage::Private::progress(const QString &msg, int progress, int total) { Q_UNUSED(msg) Q_ASSERT(progress >= 0); Q_ASSERT(total >= 0); m_progressBar->setRange(0, total); m_progressBar->setValue(progress); } void ResultPage::Private::keepOpenWhenDone(bool) { } void ResultPage::Private::allDone() { Q_ASSERT(m_tasks); q->setAutoAdvance(!m_keepOpenCB->isChecked() && !m_tasks->errorOccurred()); m_progressBar->setRange(0, 100); m_progressBar->setValue(100); m_tasks.reset(); const auto progressLabelByTagKeys{m_progressLabelByTag.keys()}; for (const QString &i : progressLabelByTagKeys) { if (!i.isEmpty()) { m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i)); } else { m_progressLabelByTag.value(i)->setText(i18n("All operations completed.")); } } Q_EMIT q->completeChanged(); } void ResultPage::Private::result(const std::shared_ptr &) { } void ResultPage::Private::started(const std::shared_ptr &task) { Q_ASSERT(task); const QString tag = task->tag(); QLabel *const label = labelForTag(tag); Q_ASSERT(label); if (tag.isEmpty()) { label->setText(i18nc("number, operation description", "Operation %1: %2", m_tasks->numberOfCompletedTasks() + 1, task->label())); } else { label->setText(i18nc(R"(tag( "OpenPGP" or "CMS"), operation description)", "%1: %2", tag, task->label())); } } ResultPage::ResultPage(QWidget *parent, Qt::WindowFlags flags) : WizardPage(parent, flags), d(new Private(this)) { setTitle(i18n("Results")); } ResultPage::~ResultPage() { } bool ResultPage::keepOpenWhenDone() const { return d->m_keepOpenCB->isChecked(); } void ResultPage::setKeepOpenWhenDone(bool keep) { d->m_keepOpenCB->setChecked(keep); } void ResultPage::setTaskCollection(const std::shared_ptr &coll) { Q_ASSERT(!d->m_tasks); if (d->m_tasks == coll) { return; } d->m_tasks = coll; Q_ASSERT(d->m_tasks); d->m_resultList->setTaskCollection(coll); connect(d->m_tasks.get(), SIGNAL(progress(QString,int,int)), this, SLOT(progress(QString,int,int))); connect(d->m_tasks.get(), SIGNAL(done()), this, SLOT(allDone())); connect(d->m_tasks.get(), SIGNAL(result(std::shared_ptr)), this, SLOT(result(std::shared_ptr))); connect(d->m_tasks.get(), SIGNAL(started(std::shared_ptr)), this, SLOT(started(std::shared_ptr))); - Q_FOREACH (const std::shared_ptr &i, d->m_tasks->tasks()) { // create labels for all tags in collection + for (const std::shared_ptr &i : d->m_tasks->tasks()) { // create labels for all tags in collection Q_ASSERT(i && d->labelForTag(i->tag())); Q_UNUSED(i) } Q_EMIT completeChanged(); } QLabel *ResultPage::Private::labelForTag(const QString &tag) { if (QLabel *const label = m_progressLabelByTag.value(tag)) { return label; } auto label = new QLabel; label->setTextFormat(Qt::RichText); label->setWordWrap(true); m_progressLabelLayout->addWidget(label); m_progressLabelByTag.insert(tag, label); return label; } bool ResultPage::isComplete() const { return d->m_tasks ? d->m_tasks->allTasksCompleted() : true; } #include "moc_resultpage.cpp" diff --git a/src/crypto/gui/signerresolvepage.cpp b/src/crypto/gui/signerresolvepage.cpp index 96f339e07..9d59a0d89 100644 --- a/src/crypto/gui/signerresolvepage.cpp +++ b/src/crypto/gui/signerresolvepage.cpp @@ -1,666 +1,668 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/signerresolvepage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signerresolvepage.h" #include "signerresolvepage_p.h" #include "signingcertificateselectiondialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; namespace { static SignerResolvePage::Operation operationFromFlags(bool sign, bool encrypt) { if (!encrypt && sign) { return SignerResolvePage::SignOnly; } if (!sign && encrypt) { return SignerResolvePage::EncryptOnly; } return SignerResolvePage::SignAndEncrypt; } static QString formatLabel(Protocol p, const Key &key) { return i18nc("%1=protocol (S/Mime, OpenPGP), %2=certificate", "Sign using %1: %2", Formatting::displayName(p), !key.isNull() ? Formatting::formatForComboBox(key) : i18n("No certificate selected")); } static std::vector supportedProtocols() { std::vector protocols; protocols.push_back(OpenPGP); protocols.push_back(CMS); return protocols; } } AbstractSigningProtocolSelectionWidget::AbstractSigningProtocolSelectionWidget(QWidget *p, Qt::WindowFlags f) : QWidget(p, f) { } ReadOnlyProtocolSelectionWidget::ReadOnlyProtocolSelectionWidget(QWidget *p, Qt::WindowFlags f) : AbstractSigningProtocolSelectionWidget(p, f) { auto const layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); const auto supportedProtocolsLst = supportedProtocols(); for (const Protocol i: supportedProtocolsLst) { auto const l = new QLabel; l->setText(formatLabel(i, Key())); layout->addWidget(l); m_labels[i] = l; } } void ReadOnlyProtocolSelectionWidget::setProtocolChecked(Protocol protocol, bool checked) { QLabel *const l = label(protocol); Q_ASSERT(l); l->setVisible(checked); } bool ReadOnlyProtocolSelectionWidget::isProtocolChecked(Protocol protocol) const { QLabel *const l = label(protocol); Q_ASSERT(l); return l->isVisible(); } std::set ReadOnlyProtocolSelectionWidget::checkedProtocols() const { std::set res; - Q_FOREACH (const Protocol i, supportedProtocols()) //krazy:exclude=foreach + for (const Protocol i : supportedProtocols()) { if (isProtocolChecked(i)) { res.insert(i); } + } return res; - } SigningProtocolSelectionWidget::SigningProtocolSelectionWidget(QWidget *parent, Qt::WindowFlags f) : AbstractSigningProtocolSelectionWidget(parent, f) { m_buttonGroup = new QButtonGroup(this); connect(m_buttonGroup, &QButtonGroup::idClicked, this, &SigningProtocolSelectionWidget::userSelectionChanged); auto const layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); - Q_FOREACH (const Protocol i, supportedProtocols()) { //krazy:exclude=foreach + for (const Protocol i : supportedProtocols()) { auto const b = new QCheckBox; b->setText(formatLabel(i, Key())); m_buttons[i] = b; layout->addWidget(b); m_buttonGroup->addButton(b); } setExclusive(true); } void SigningProtocolSelectionWidget::setProtocolChecked(Protocol p, bool checked) { Q_ASSERT(p != UnknownProtocol); QCheckBox *const b = button(p); Q_ASSERT(b); b->setChecked(checked); } bool SigningProtocolSelectionWidget::isProtocolChecked(Protocol p) const { Q_ASSERT(p != UnknownProtocol); const QAbstractButton *const b = button(p); Q_ASSERT(b); return b->isChecked(); } std::set SigningProtocolSelectionWidget::checkedProtocols() const { std::set res; for (auto it = m_buttons.begin(), end = m_buttons.end(); it != end; ++it) if (it->second->isChecked()) { res.insert(it->first); } return res; } void SigningProtocolSelectionWidget::setExclusive(bool exclusive) { if (exclusive == isExclusive()) { return; } m_buttonGroup->setExclusive(exclusive); Q_EMIT userSelectionChanged(); } QCheckBox *SigningProtocolSelectionWidget::button(Protocol p) const { const auto it = m_buttons.find(p); return it == m_buttons.end() ? nullptr : it->second; } QLabel *ReadOnlyProtocolSelectionWidget::label(Protocol p) const { const auto it = m_labels.find(p); return it == m_labels.end() ? nullptr : it->second; } bool SigningProtocolSelectionWidget::isExclusive() const { return m_buttonGroup->exclusive(); } void SigningProtocolSelectionWidget::setCertificate(Protocol prot, const Key &key) { QAbstractButton *const b = button(prot); Q_ASSERT(b); b->setText(formatLabel(prot, key)); } void ReadOnlyProtocolSelectionWidget::setCertificate(Protocol prot, const Key &key) { QLabel *const l = label(prot); l->setText(formatLabel(prot, key)); } namespace { class ValidatorImpl : public SignerResolvePage::Validator { public: QString explanation() const override { return QString(); } bool isComplete() const override { return true; } QString customWindowTitle() const override { return QString(); } }; } class SignerResolvePage::Private { friend class ::Kleo::Crypto::Gui::SignerResolvePage; SignerResolvePage *const q; public: explicit Private(SignerResolvePage *qq); ~Private(); void setOperation(Operation operation); void operationButtonClicked(int operation); void selectCertificates(); void setCertificates(const QMap &certs); void updateModeSelectionWidgets(); void updateUi(); bool protocolSelected(Protocol p) const; bool protocolSelectionActuallyUserMutable() const; private: QButtonGroup *signEncryptGroup; QRadioButton *signAndEncryptRB; QRadioButton *encryptOnlyRB; QRadioButton *signOnlyRB; QGroupBox *signingCertificateBox; QLabel *signerLabelLabel; QLabel *signerLabel; QGroupBox *encryptBox; QCheckBox *textArmorCO; QPushButton *selectCertificatesButton; SigningProtocolSelectionWidget *signingProtocolSelectionWidget; ReadOnlyProtocolSelectionWidget *readOnlyProtocolSelectionWidget; std::vector presetProtocols; bool signingMutable; bool encryptionMutable; bool signingSelected; bool encryptionSelected; bool multipleProtocolsAllowed; bool protocolSelectionUserMutable; QMap certificates; std::shared_ptr validator; std::shared_ptr signingPreferences; }; bool SignerResolvePage::Private::protocolSelectionActuallyUserMutable() const { return (q->protocolSelectionUserMutable() || presetProtocols.empty()) && q->operation() == SignOnly; } SignerResolvePage::Private::Private(SignerResolvePage *qq) : q(qq) , presetProtocols() , signingMutable(true) , encryptionMutable(true) , signingSelected(false) , encryptionSelected(false) , multipleProtocolsAllowed(false) , protocolSelectionUserMutable(true) , validator(new ValidatorImpl) { auto layout = new QVBoxLayout(q); signEncryptGroup = new QButtonGroup(q); q->connect(signEncryptGroup, SIGNAL(buttonClicked(int)), q, SLOT(operationButtonClicked(int))); signAndEncryptRB = new QRadioButton; signAndEncryptRB->setText(i18n("Sign and encrypt (OpenPGP only)")); signAndEncryptRB->setChecked(true); signEncryptGroup->addButton(signAndEncryptRB, SignAndEncrypt); layout->addWidget(signAndEncryptRB); encryptOnlyRB = new QRadioButton; encryptOnlyRB->setText(i18n("Encrypt only")); signEncryptGroup->addButton(encryptOnlyRB, EncryptOnly); layout->addWidget(encryptOnlyRB); signOnlyRB = new QRadioButton; signOnlyRB->setText(i18n("Sign only")); signEncryptGroup->addButton(signOnlyRB, SignOnly); layout->addWidget(signOnlyRB); encryptBox = new QGroupBox; encryptBox->setTitle(i18n("Encryption Options")); QBoxLayout *const encryptLayout = new QVBoxLayout(encryptBox); textArmorCO = new QCheckBox; textArmorCO->setText(i18n("Text output (ASCII armor)")); encryptLayout->addWidget(textArmorCO); layout->addWidget(encryptBox); signingCertificateBox = new QGroupBox; signingCertificateBox->setTitle(i18n("Signing Options")); auto signerLayout = new QGridLayout(signingCertificateBox); signerLayout->setColumnStretch(1, 1); signerLabelLabel = new QLabel; signerLabelLabel->setText(i18n("Signer:")); signerLayout->addWidget(signerLabelLabel, 1, 0); signerLabel = new QLabel; signerLayout->addWidget(signerLabel, 1, 1); signerLabelLabel->setVisible(false); signerLabel->setVisible(false); signingProtocolSelectionWidget = new SigningProtocolSelectionWidget; connect(signingProtocolSelectionWidget, SIGNAL(userSelectionChanged()), q, SLOT(updateUi())); signerLayout->addWidget(signingProtocolSelectionWidget, 2, 0, 1, -1); readOnlyProtocolSelectionWidget = new ReadOnlyProtocolSelectionWidget; signerLayout->addWidget(readOnlyProtocolSelectionWidget, 3, 0, 1, -1); selectCertificatesButton = new QPushButton; selectCertificatesButton->setText(i18n("Change Signing Certificates...")); selectCertificatesButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); signerLayout->addWidget(selectCertificatesButton, 4, 0, 1, -1, Qt::AlignLeft); q->connect(selectCertificatesButton, SIGNAL(clicked()), q, SLOT(selectCertificates())); layout->addWidget(signingCertificateBox); layout->addStretch(); } void SignerResolvePage::setValidator(const std::shared_ptr &validator) { Q_ASSERT(validator); d->validator = validator; d->updateUi(); } std::shared_ptr SignerResolvePage::validator() const { return d->validator; } SignerResolvePage::Private::~Private() {} bool SignerResolvePage::Private::protocolSelected(Protocol p) const { Q_ASSERT(p != UnknownProtocol); return signingProtocolSelectionWidget->isProtocolChecked(p); } void SignerResolvePage::Private::setCertificates(const QMap &certs) { certificates = certs; - Q_FOREACH (const Protocol i, certs.keys()) { //krazy:exclude=foreach - const Key key = certs.value(i); - readOnlyProtocolSelectionWidget->setCertificate(i, key); - signingProtocolSelectionWidget->setCertificate(i, key); + for (auto it = certs.cbegin(), endIt = certs.cend(); it != endIt; ++it) { + const Protocol proto = it.key(); + const Key &key = it.value(); + readOnlyProtocolSelectionWidget->setCertificate(proto, key); + signingProtocolSelectionWidget->setCertificate(proto, key); } updateUi(); } void SignerResolvePage::Private::updateUi() { const bool ismutable = protocolSelectionActuallyUserMutable(); readOnlyProtocolSelectionWidget->setVisible(!ismutable); signingProtocolSelectionWidget->setVisible(ismutable); q->setExplanation(validator->explanation()); Q_EMIT q->completeChanged(); const QString customTitle = validator->customWindowTitle(); if (!customTitle.isEmpty()) { Q_EMIT q->windowTitleChanged(customTitle); } selectCertificatesButton->setEnabled(signingProtocolSelectionWidget->checkedProtocols().size() > 0); } void SignerResolvePage::setProtocolSelectionUserMutable(bool ismutable) { if (d->protocolSelectionUserMutable == ismutable) { return; } d->protocolSelectionUserMutable = ismutable; d->updateModeSelectionWidgets(); } bool SignerResolvePage::protocolSelectionUserMutable() const { return d->protocolSelectionUserMutable; } void SignerResolvePage::setMultipleProtocolsAllowed(bool allowed) { if (d->multipleProtocolsAllowed == allowed) { return; } d->multipleProtocolsAllowed = allowed; d->updateModeSelectionWidgets(); } bool SignerResolvePage::multipleProtocolsAllowed() const { return d->multipleProtocolsAllowed; } void SignerResolvePage::Private::updateModeSelectionWidgets() { const bool bothMutable = signingMutable && encryptionMutable; const bool noSigningPossible = !signingSelected && !signingMutable; const bool noEncryptionPossible = !encryptionSelected && !encryptionMutable; signAndEncryptRB->setChecked(signingSelected && encryptionSelected); signOnlyRB->setChecked(signingSelected && !encryptionSelected); encryptOnlyRB->setChecked(encryptionSelected && !signingSelected); const bool canSignAndEncrypt = !noSigningPossible && !noEncryptionPossible && bothMutable && presetProtocols != std::vector(1, CMS); const bool canSignOnly = !encryptionSelected || encryptionMutable; const bool canEncryptOnly = !signingSelected || signingMutable; signAndEncryptRB->setEnabled(canSignAndEncrypt); signOnlyRB->setEnabled(canSignOnly); encryptOnlyRB->setEnabled(canEncryptOnly); const bool buttonsVisible = signingMutable || encryptionMutable; signOnlyRB->setVisible(buttonsVisible); encryptOnlyRB->setVisible(buttonsVisible); signAndEncryptRB->setVisible(buttonsVisible); signingProtocolSelectionWidget->setExclusive(!multipleProtocolsAllowed); signingCertificateBox->setVisible(!noSigningPossible); encryptBox->setVisible(!noEncryptionPossible); updateUi(); } void SignerResolvePage::Private::selectCertificates() { QPointer dlg = new SigningCertificateSelectionDialog(q); dlg->setAllowedProtocols(signingProtocolSelectionWidget->checkedProtocols()); if (dlg->exec() == QDialog::Accepted && dlg) { const QMap certs = dlg->selectedCertificates(); setCertificates(certs); if (signingPreferences && dlg->rememberAsDefault()) { signingPreferences->setPreferredCertificate(OpenPGP, certs.value(OpenPGP)); signingPreferences->setPreferredCertificate(CMS, certs.value(CMS)); } } delete dlg; updateUi(); } void SignerResolvePage::Private::operationButtonClicked(int mode_) { const auto op = static_cast(mode_); signingCertificateBox->setEnabled(op != EncryptOnly); encryptBox->setEnabled(op != SignOnly); if (op == SignAndEncrypt) { signingProtocolSelectionWidget->setProtocolChecked(CMS, false); readOnlyProtocolSelectionWidget->setProtocolChecked(CMS, false); signingProtocolSelectionWidget->setProtocolChecked(OpenPGP, true); readOnlyProtocolSelectionWidget->setProtocolChecked(OpenPGP, true); } updateUi(); } void SignerResolvePage::Private::setOperation(Operation op) { switch (op) { case SignOnly: signOnlyRB->click(); break; case EncryptOnly: encryptOnlyRB->click(); break; case SignAndEncrypt: signAndEncryptRB->click(); break; } } SignerResolvePage::Operation SignerResolvePage::operation() const { return operationFromFlags(signingSelected(), encryptionSelected()); } SignerResolvePage::SignerResolvePage(QWidget *parent, Qt::WindowFlags f) : WizardPage(parent, f), d(new Private(this)) { setTitle(i18n("Choose Operation to be Performed")); // setSubTitle( i18n( "TODO" ) ); setPresetProtocol(UnknownProtocol); d->setCertificates(QMap()); d->updateModeSelectionWidgets(); d->operationButtonClicked(EncryptOnly); } SignerResolvePage::~SignerResolvePage() {} void SignerResolvePage::setSignersAndCandidates(const std::vector &signers, const std::vector< std::vector > &keys) { kleo_assert(signers.empty() || signers.size() == keys.size()); switch (signers.size()) { case 0: d->signerLabelLabel->setVisible(false); d->signerLabel->setVisible(false); // TODO: use default identity? break; case 1: d->signerLabelLabel->setVisible(true); d->signerLabel->setVisible(true); // TODO: use default identity? d->signerLabel->setText(signers.front().prettyAddress()); break; default: // > 1 kleo_assert(!"Resolving multiple signers not implemented"); } d->updateUi(); } void SignerResolvePage::setPresetProtocol(Protocol protocol) { std::vector protocols; if (protocol != CMS) { protocols.push_back(OpenPGP); } if (protocol != OpenPGP) { protocols.push_back(CMS); } setPresetProtocols(protocols); d->updateUi(); } void SignerResolvePage::setPresetProtocols(const std::vector &protocols) { d->presetProtocols = protocols; - Q_FOREACH (const Protocol i, supportedProtocols()) { //krazy:exclude=foreach + for (const Protocol i : supportedProtocols()) { const bool checked = std::find(protocols.begin(), protocols.end(), i) != protocols.end(); d->signingProtocolSelectionWidget->setProtocolChecked(i, checked); d->readOnlyProtocolSelectionWidget->setProtocolChecked(i, checked); } d->updateModeSelectionWidgets(); } std::set SignerResolvePage::selectedProtocols() const { return d->signingProtocolSelectionWidget->checkedProtocols(); } std::vector SignerResolvePage::signingCertificates(Protocol protocol) const { std::vector result; if (protocol != CMS && d->signingProtocolSelectionWidget->isProtocolChecked(OpenPGP) && !d->certificates[OpenPGP].isNull()) { result.push_back(d->certificates[OpenPGP]); } if (protocol != OpenPGP && d->signingProtocolSelectionWidget->isProtocolChecked(CMS) && !d->certificates[CMS].isNull()) { result.push_back(d->certificates[CMS]); } return result; } std::vector SignerResolvePage::resolvedSigners() const { std::vector result = signingCertificates(CMS); const std::vector pgp = signingCertificates(OpenPGP); result.insert(result.end(), pgp.begin(), pgp.end()); return result; } bool SignerResolvePage::isComplete() const { Q_ASSERT(d->validator); return d->validator->isComplete(); } bool SignerResolvePage::encryptionSelected() const { return !d->signOnlyRB->isChecked(); } void SignerResolvePage::setEncryptionSelected(bool selected) { d->encryptionSelected = selected; d->updateModeSelectionWidgets(); d->setOperation(operationFromFlags(d->signingSelected, d->encryptionSelected)); } bool SignerResolvePage::signingSelected() const { return !d->encryptOnlyRB->isChecked(); } void SignerResolvePage::setSigningSelected(bool selected) { d->signingSelected = selected; d->updateModeSelectionWidgets(); d->setOperation(operationFromFlags(d->signingSelected, d->encryptionSelected)); } bool SignerResolvePage::isEncryptionUserMutable() const { return d->encryptionMutable; } bool SignerResolvePage::isSigningUserMutable() const { return d->signingMutable; } void SignerResolvePage::setEncryptionUserMutable(bool ismutable) { d->encryptionMutable = ismutable; d->updateModeSelectionWidgets(); } void SignerResolvePage::setSigningUserMutable(bool ismutable) { d->signingMutable = ismutable; d->updateModeSelectionWidgets(); } std::set SignerResolvePage::selectedProtocolsWithoutSigningCertificate() const { std::set res; - Q_FOREACH (const Protocol i, selectedProtocols()) //krazy:exclude=foreach + for (const Protocol i : selectedProtocols()) { if (signingCertificates(i).empty()) { res.insert(i); } + } return res; } bool SignerResolvePage::isAsciiArmorEnabled() const { return d->textArmorCO->isChecked(); } void SignerResolvePage::setAsciiArmorEnabled(bool enabled) { d->textArmorCO->setChecked(enabled); } void SignerResolvePage::setSigningPreferences(const std::shared_ptr &prefs) { d->signingPreferences = prefs; QMap map; map[OpenPGP] = prefs ? prefs->preferredCertificate(OpenPGP) : Key(); map[CMS] = prefs ? prefs->preferredCertificate(CMS) : Key(); d->setCertificates(map); } std::shared_ptr SignerResolvePage::signingPreferences() const { return d->signingPreferences; } void SignerResolvePage::onNext() { } #include "moc_signerresolvepage.cpp" #include "moc_signerresolvepage_p.cpp" diff --git a/src/crypto/signemailcontroller.cpp b/src/crypto/signemailcontroller.cpp index c4da96b91..89f1dcbef 100644 --- a/src/crypto/signemailcontroller.cpp +++ b/src/crypto/signemailcontroller.cpp @@ -1,343 +1,343 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/signemailcontroller.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signemailcontroller.h" #include "kleopatra_debug.h" #include "signemailtask.h" #include "certificateresolver.h" #include "taskcollection.h" #include #include #include #include #include "emailoperationspreferences.h" #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; using namespace GpgME; using namespace KMime::Types; class SignEMailController::Private { friend class ::Kleo::Crypto::SignEMailController; SignEMailController *const q; public: explicit Private(Mode m, SignEMailController *qq); ~Private(); private: void slotWizardSignersResolved(); void slotWizardCanceled(); // ### extract to base private: void ensureWizardCreated(); // ### extract to base void ensureWizardVisible(); // ### extract to base void cancelAllJobs(); // ### extract to base void schedule(); // ### extract to base std::shared_ptr takeRunnable(GpgME::Protocol proto); // ### extract to base private: const Mode mode; std::vector< std::shared_ptr > runnable, completed; // ### extract to base std::shared_ptr cms, openpgp; // ### extract to base QPointer wizard; // ### extract to base Protocol protocol; // ### extract to base bool detached : 1; }; SignEMailController::Private::Private(Mode m, SignEMailController *qq) : q(qq), mode(m), runnable(), cms(), openpgp(), wizard(), protocol(UnknownProtocol), detached(false) { } SignEMailController::Private::~Private() {} SignEMailController::SignEMailController(Mode mode, QObject *p) : Controller(p), d(new Private(mode, this)) { } SignEMailController::SignEMailController(const std::shared_ptr &xc, Mode mode, QObject *p) : Controller(xc, p), d(new Private(mode, this)) { } SignEMailController::~SignEMailController() { /// ### extract to base if (d->wizard && !d->wizard->isVisible()) { delete d->wizard; } //d->wizard->close(); ### ? } SignEMailController::Mode SignEMailController::mode() const { return d->mode; } // ### extract to base void SignEMailController::setProtocol(Protocol proto) { kleo_assert(d->protocol == UnknownProtocol || d->protocol == proto); d->protocol = proto; d->ensureWizardCreated(); d->wizard->setPresetProtocol(proto); } Protocol SignEMailController::protocol() const { return d->protocol; } void SignEMailController::startResolveSigners() { startResolveSigners(std::vector()); } void SignEMailController::startResolveSigners(const std::vector &signers) { const std::vector< std::vector > keys = CertificateResolver::resolveSigners(signers, d->protocol); if (!signers.empty()) { kleo_assert(keys.size() == static_cast(signers.size())); } d->ensureWizardCreated(); d->wizard->setSignersAndCandidates(signers, keys); d->ensureWizardVisible(); } void SignEMailController::setDetachedSignature(bool detached) { kleo_assert(!d->openpgp); kleo_assert(!d->cms); kleo_assert(d->completed.empty()); kleo_assert(d->runnable.empty()); d->detached = detached; } void SignEMailController::Private::slotWizardSignersResolved() { Q_EMIT q->signersResolved(); } // ### extract to base void SignEMailController::Private::slotWizardCanceled() { q->setLastError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel")); q->emitDoneOrError(); } void SignEMailController::setInputAndOutput(const std::shared_ptr &input, const std::shared_ptr &output) { setInputsAndOutputs(std::vector< std::shared_ptr >(1, input), std::vector< std::shared_ptr >(1, output)); } // ### extract to base void SignEMailController::setInputsAndOutputs(const std::vector< std::shared_ptr > &inputs, const std::vector< std::shared_ptr > &outputs) { kleo_assert(!inputs.empty()); kleo_assert(!outputs.empty()); std::vector< std::shared_ptr > tasks; tasks.reserve(inputs.size()); d->ensureWizardCreated(); const std::vector keys = d->wizard->resolvedSigners(); kleo_assert(!keys.empty()); for (unsigned int i = 0, end = inputs.size(); i < end; ++i) { const std::shared_ptr task(new SignEMailTask); task->setInput(inputs[i]); task->setOutput(outputs[i]); task->setSigners(keys); task->setDetachedSignature(d->detached); if (d->mode == ClipboardMode) { if (d->protocol == OpenPGP) { task->setClearsign(true); } else { task->setAsciiArmor(true); } } tasks.push_back(task); } d->runnable.swap(tasks); } // ### extract to base void SignEMailController::start() { std::shared_ptr coll(new TaskCollection); std::vector > tmp; std::copy(d->runnable.begin(), d->runnable.end(), std::back_inserter(tmp)); coll->setTasks(tmp); d->ensureWizardCreated(); d->wizard->setTaskCollection(coll); for (const std::shared_ptr &t : std::as_const(tmp)) { connectTask(t); } d->schedule(); } // ### extract to base void SignEMailController::Private::schedule() { if (!cms) if (const std::shared_ptr t = takeRunnable(CMS)) { t->start(); cms = t; } if (!openpgp) if (const std::shared_ptr t = takeRunnable(OpenPGP)) { t->start(); openpgp = t; } if (!cms && !openpgp) { kleo_assert(runnable.empty()); QPointer Q = q; - Q_FOREACH (const std::shared_ptr t, completed) { + for (const std::shared_ptr &t : completed) { Q_EMIT q->reportMicAlg(t->micAlg()); if (!Q) { return; } } q->emitDoneOrError(); } } // ### extract to base std::shared_ptr SignEMailController::Private::takeRunnable(GpgME::Protocol proto) { const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr &task) { return task->protocol() == proto; }); if (it == runnable.end()) { return std::shared_ptr(); } const std::shared_ptr result = *it; runnable.erase(it); return result; } // ### extract to base void SignEMailController::doTaskDone(const Task *task, const std::shared_ptr &result) { Q_UNUSED(result) Q_ASSERT(task); // We could just delete the tasks here, but we can't use // Qt::QueuedConnection here (we need sender()) and other slots // might not yet have executed. Therefore, we push completed tasks // into a burial container if (task == d->cms.get()) { d->completed.push_back(d->cms); d->cms.reset(); } else if (task == d->openpgp.get()) { d->completed.push_back(d->openpgp); d->openpgp.reset(); } QTimer::singleShot(0, this, SLOT(schedule())); } // ### extract to base void SignEMailController::cancel() { try { if (d->wizard) { d->wizard->close(); } d->cancelAllJobs(); } catch (const std::exception &e) { qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what(); } } // ### extract to base void SignEMailController::Private::cancelAllJobs() { // we just kill all runnable tasks - this will not result in // signal emissions. runnable.clear(); // a cancel() will result in a call to if (cms) { cms->cancel(); } if (openpgp) { openpgp->cancel(); } } // ### extract to base void SignEMailController::Private::ensureWizardCreated() { if (wizard) { return; } std::unique_ptr w(new SignEMailWizard); w->setAttribute(Qt::WA_DeleteOnClose); connect(w.get(), SIGNAL(signersResolved()), q, SLOT(slotWizardSignersResolved()), Qt::QueuedConnection); connect(w.get(), SIGNAL(canceled()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection); w->setPresetProtocol(protocol); EMailOperationsPreferences prefs; w->setQuickMode(prefs.quickSignEMail()); wizard = w.release(); } // ### extract to base void SignEMailController::Private::ensureWizardVisible() { ensureWizardCreated(); q->bringToForeground(wizard); } #include "moc_signemailcontroller.cpp" diff --git a/src/crypto/signencryptfilescontroller.cpp b/src/crypto/signencryptfilescontroller.cpp index 1a789a350..486eed47f 100644 --- a/src/crypto/signencryptfilescontroller.cpp +++ b/src/crypto/signencryptfilescontroller.cpp @@ -1,703 +1,703 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/signencryptfilescontroller.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signencryptfilescontroller.h" #include "signencrypttask.h" #include "certificateresolver.h" #include "crypto/gui/signencryptfileswizard.h" #include "crypto/taskcollection.h" #include "fileoperationspreferences.h" #include "utils/input.h" #include "utils/output.h" #include "utils/kleo_assert.h" #include "utils/archivedefinition.h" #include "utils/path-helper.h" #include #include #include #include "kleopatra_debug.h" #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace GpgME; using namespace KMime::Types; class SignEncryptFilesController::Private { friend class ::Kleo::Crypto::SignEncryptFilesController; SignEncryptFilesController *const q; public: explicit Private(SignEncryptFilesController *qq); ~Private(); private: void slotWizardOperationPrepared(); void slotWizardCanceled(); private: void ensureWizardCreated(); void ensureWizardVisible(); void updateWizardMode(); void cancelAllTasks(); void reportError(int err, const QString &details) { q->setLastError(err, details); q->emitDoneOrError(); } void schedule(); std::shared_ptr takeRunnable(GpgME::Protocol proto); static void assertValidOperation(unsigned int); static QString titleForOperation(unsigned int op); private: std::vector< std::shared_ptr > runnable, completed; std::shared_ptr cms, openpgp; QPointer wizard; QStringList files; unsigned int operation; Protocol protocol; }; SignEncryptFilesController::Private::Private(SignEncryptFilesController *qq) : q(qq), runnable(), cms(), openpgp(), wizard(), files(), operation(SignAllowed | EncryptAllowed | ArchiveAllowed), protocol(UnknownProtocol) { } SignEncryptFilesController::Private::~Private() { qCDebug(KLEOPATRA_LOG); } QString SignEncryptFilesController::Private::titleForOperation(unsigned int op) { const bool signDisallowed = (op & SignMask) == SignDisallowed; const bool encryptDisallowed = (op & EncryptMask) == EncryptDisallowed; const bool archiveSelected = (op & ArchiveMask) == ArchiveForced; kleo_assert(!signDisallowed || !encryptDisallowed); if (!signDisallowed && encryptDisallowed) { if (archiveSelected) { return i18n("Archive and Sign Files"); } else { return i18n("Sign Files"); } } if (signDisallowed && !encryptDisallowed) { if (archiveSelected) { return i18n("Archive and Encrypt Files"); } else { return i18n("Encrypt Files"); } } if (archiveSelected) { return i18n("Archive and Sign/Encrypt Files"); } else { return i18n("Sign/Encrypt Files"); } } SignEncryptFilesController::SignEncryptFilesController(QObject *p) : Controller(p), d(new Private(this)) { } SignEncryptFilesController::SignEncryptFilesController(const std::shared_ptr &ctx, QObject *p) : Controller(ctx, p), d(new Private(this)) { } SignEncryptFilesController::~SignEncryptFilesController() { qCDebug(KLEOPATRA_LOG); if (d->wizard && !d->wizard->isVisible()) { delete d->wizard; } //d->wizard->close(); ### ? } void SignEncryptFilesController::setProtocol(Protocol proto) { kleo_assert(d->protocol == UnknownProtocol || d->protocol == proto); d->protocol = proto; d->ensureWizardCreated(); } Protocol SignEncryptFilesController::protocol() const { return d->protocol; } // static void SignEncryptFilesController::Private::assertValidOperation(unsigned int op) { kleo_assert((op & SignMask) == SignDisallowed || (op & SignMask) == SignAllowed || (op & SignMask) == SignSelected); kleo_assert((op & EncryptMask) == EncryptDisallowed || (op & EncryptMask) == EncryptAllowed || (op & EncryptMask) == EncryptSelected); kleo_assert((op & ArchiveMask) == ArchiveDisallowed || (op & ArchiveMask) == ArchiveAllowed || (op & ArchiveMask) == ArchiveForced); kleo_assert((op & ~(SignMask | EncryptMask | ArchiveMask)) == 0); } void SignEncryptFilesController::setOperationMode(unsigned int mode) { Private::assertValidOperation(mode); d->operation = mode; d->updateWizardMode(); } void SignEncryptFilesController::Private::updateWizardMode() { if (!wizard) { return; } wizard->setWindowTitle(titleForOperation(operation)); const unsigned int signOp = (operation & SignMask); const unsigned int encrOp = (operation & EncryptMask); const unsigned int archOp = (operation & ArchiveMask); if (signOp == SignDisallowed) { wizard->setSigningUserMutable(false); wizard->setSigningPreset(false); } else { wizard->setSigningUserMutable(true); wizard->setSigningPreset(signOp == SignSelected); } if (encrOp == EncryptDisallowed) { wizard->setEncryptionPreset(false); wizard->setEncryptionUserMutable(false); } else { wizard->setEncryptionUserMutable(true); wizard->setEncryptionPreset(encrOp == EncryptSelected); } wizard->setArchiveForced(archOp == ArchiveForced); wizard->setArchiveMutable(archOp == ArchiveAllowed); } unsigned int SignEncryptFilesController::operationMode() const { return d->operation; } static const char *extension(bool pgp, bool sign, bool encrypt, bool ascii, bool detached) { unsigned int cls = pgp ? Class::OpenPGP : Class::CMS; if (encrypt) { cls |= Class::CipherText; } else if (sign) { cls |= detached ? Class::DetachedSignature : Class::OpaqueSignature; } cls |= ascii ? Class::Ascii : Class::Binary; const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt(); if (const char *const ext = outputFileExtension(cls, usePGPFileExt)) { return ext; } else { return "out"; } } static std::shared_ptr getDefaultAd() { std::vector > ads = ArchiveDefinition::getArchiveDefinitions(); Q_ASSERT(!ads.empty()); std::shared_ptr ad = ads.front(); const FileOperationsPreferences prefs; Q_FOREACH (const std::shared_ptr toCheck, ads) { if (toCheck->id() == prefs.archiveCommand()) { ad = toCheck; break; } } return ad; } static QMap buildOutputNames(const QStringList &files, const bool archive) { QMap nameMap; // Build the default names for the wizard. QString baseNameCms; QString baseNamePgp; const QFileInfo firstFile(files.first()); if (archive) { QString baseName; baseName = QDir(heuristicBaseDirectory(files)).absoluteFilePath(files.size() > 1 ? i18nc("base name of an archive file, e.g. archive.zip or archive.tar.gz", "archive") : firstFile.baseName()); const auto ad = getDefaultAd(); baseNamePgp = baseName + QLatin1Char('.') + ad->extensions(GpgME::OpenPGP).first() + QLatin1Char('.'); baseNameCms = baseName + QLatin1Char('.') + ad->extensions(GpgME::CMS).first() + QLatin1Char('.'); } else { baseNameCms = baseNamePgp = files.first() + QLatin1Char('.'); } const FileOperationsPreferences prefs; const bool ascii = prefs.addASCIIArmor(); nameMap.insert(SignEncryptFilesWizard::SignatureCMS, baseNameCms + QString::fromLatin1(extension(false, true, false, ascii, true))); nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + QString::fromLatin1(extension(false, false, true, ascii, false))); nameMap.insert(SignEncryptFilesWizard::CombinedPGP, baseNamePgp + QString::fromLatin1(extension(true, true, true, ascii, false))); nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + QString::fromLatin1(extension(true, false, true, ascii, false))); nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + QString::fromLatin1(extension(true, true, false, ascii, true))); nameMap.insert(SignEncryptFilesWizard::Directory, heuristicBaseDirectory(files)); return nameMap; } static QMap buildOutputNamesForDir(const QString &file, const QMap &orig) { QMap ret; const QString dir = orig.value(SignEncryptFilesWizard::Directory); if (dir.isEmpty()) { return orig; } // Build the default names for the wizard. const QFileInfo fi(file); const QString baseName = dir + QLatin1Char('/') + fi.fileName() + QLatin1Char('.'); const FileOperationsPreferences prefs; const bool ascii = prefs.addASCIIArmor(); ret.insert(SignEncryptFilesWizard::SignatureCMS, baseName + QString::fromLatin1(extension(false, true, false, ascii, true))); ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + QString::fromLatin1(extension(false, false, true, ascii, false))); ret.insert(SignEncryptFilesWizard::CombinedPGP, baseName + QString::fromLatin1(extension(true, true, true, ascii, false))); ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + QString::fromLatin1(extension(true, false, true, ascii, false))); ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + QString::fromLatin1(extension(true, true, false, ascii, true))); return ret; } void SignEncryptFilesController::setFiles(const QStringList &files) { kleo_assert(!files.empty()); d->files = files; bool archive = false; if (files.size() > 1) { setOperationMode((operationMode() & ~ArchiveMask) | ArchiveAllowed); archive = true; } for (const auto &file: files) { if (QFileInfo(file).isDir()) { setOperationMode((operationMode() & ~ArchiveMask) | ArchiveForced); archive = true; break; } } d->ensureWizardCreated(); d->wizard->setSingleFile(!archive); d->wizard->setOutputNames(buildOutputNames(files, archive)); } void SignEncryptFilesController::Private::slotWizardCanceled() { qCDebug(KLEOPATRA_LOG); reportError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel")); } void SignEncryptFilesController::start() { d->ensureWizardVisible(); } static std::shared_ptr createSignEncryptTaskForFileInfo(const QFileInfo &fi, bool ascii, const std::vector &recipients, const std::vector &signers, const QString &outputName, bool symmetric) { const std::shared_ptr task(new SignEncryptTask); Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric); task->setAsciiArmor(ascii); if (!signers.empty()) { task->setSign(true); task->setSigners(signers); task->setDetachedSignature(true); } else { task->setSign(false); } if (!recipients.empty()) { task->setEncrypt(true); task->setRecipients(recipients); task->setDetachedSignature(false); } else { task->setEncrypt(false); } task->setEncryptSymmetric(symmetric); const QString input = fi.absoluteFilePath(); task->setInputFileName(input); task->setInput(Input::createFromFile(input)); task->setOutputFileName(outputName); return task; } static std::shared_ptr createArchiveSignEncryptTaskForFiles(const QStringList &files, const std::shared_ptr &ad, bool pgp, bool ascii, const std::vector &recipients, const std::vector &signers, const QString& outputName, bool symmetric) { const std::shared_ptr task(new SignEncryptTask); task->setEncryptSymmetric(symmetric); Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric); task->setAsciiArmor(ascii); if (!signers.empty()) { task->setSign(true); task->setSigners(signers); task->setDetachedSignature(false); } else { task->setSign(false); } if (!recipients.empty()) { task->setEncrypt(true); task->setRecipients(recipients); } else { task->setEncrypt(false); } kleo_assert(ad); const Protocol proto = pgp ? OpenPGP : CMS; task->setInputFileNames(files); task->setInput(ad->createInputFromPackCommand(proto, files)); task->setOutputFileName(outputName); return task; } static std::vector< std::shared_ptr > createSignEncryptTasksForFileInfo(const QFileInfo &fi, bool ascii, const std::vector &pgpRecipients, const std::vector &pgpSigners, const std::vector &cmsRecipients, const std::vector &cmsSigners, const QMap &outputNames, bool symmetric) { std::vector< std::shared_ptr > result; const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty(); const bool cms = !cmsSigners.empty() || !cmsRecipients.empty(); result.reserve(pgp + cms); if (pgp || symmetric) { // Symmetric encryption is only supported for PGP int outKind = 0; if ((!pgpRecipients.empty() || symmetric)&& !pgpSigners.empty()) { outKind = SignEncryptFilesWizard::CombinedPGP; } else if (!pgpRecipients.empty() || symmetric) { outKind = SignEncryptFilesWizard::EncryptedPGP; } else { outKind = SignEncryptFilesWizard::SignaturePGP; } result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric)); } if (cms) { // There is no combined sign / encrypt in gpgsm so we create one sign task // and one encrypt task. Which leaves us with the age old dilemma, encrypt // then sign, or sign then encrypt. Ugly. if (!cmsSigners.empty()) { result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, std::vector(), cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS], false)); } if (!cmsRecipients.empty()) { result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, cmsRecipients, std::vector(), outputNames[SignEncryptFilesWizard::EncryptedCMS], false)); } } return result; } static std::vector< std::shared_ptr > createArchiveSignEncryptTasksForFiles(const QStringList &files, const std::shared_ptr &ad, bool ascii, const std::vector &pgpRecipients, const std::vector &pgpSigners, const std::vector &cmsRecipients, const std::vector &cmsSigners, const QMap &outputNames, bool symmetric) { std::vector< std::shared_ptr > result; const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty(); const bool cms = !cmsSigners.empty() || !cmsRecipients.empty(); result.reserve(pgp + cms); if (pgp || symmetric) { int outKind = 0; if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) { outKind = SignEncryptFilesWizard::CombinedPGP; } else if (!pgpRecipients.empty() || symmetric) { outKind = SignEncryptFilesWizard::EncryptedPGP; } else { outKind = SignEncryptFilesWizard::SignaturePGP; } result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, true, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric)); } if (cms) { if (!cmsSigners.empty()) { result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, false, ascii, std::vector(), cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS], false)); } if (!cmsRecipients.empty()) { result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, false, ascii, cmsRecipients, std::vector(), outputNames[SignEncryptFilesWizard::EncryptedCMS], false)); } } return result; } void SignEncryptFilesController::Private::slotWizardOperationPrepared() { try { kleo_assert(wizard); kleo_assert(!files.empty()); const bool archive = (wizard->outputNames().value(SignEncryptFilesWizard::Directory).isNull() && files.size() > 1) || ((operation & ArchiveMask) == ArchiveForced); const std::vector recipients = wizard->resolvedRecipients(); const std::vector signers = wizard->resolvedSigners(); const FileOperationsPreferences prefs; const bool ascii = prefs.addASCIIArmor(); std::vector pgpRecipients, cmsRecipients, pgpSigners, cmsSigners; - Q_FOREACH (const Key &k, recipients) { + for (const Key &k : recipients) { if (k.protocol() == GpgME::OpenPGP) { pgpRecipients.push_back(k); } else { cmsRecipients.push_back(k); } } - Q_FOREACH (const Key &k, signers) { + for (const Key &k : signers) { if (k.protocol() == GpgME::OpenPGP) { pgpSigners.push_back(k); } else { cmsSigners.push_back(k); } } std::vector< std::shared_ptr > tasks; if (!archive) { tasks.reserve(files.size()); } if (archive) { tasks = createArchiveSignEncryptTasksForFiles(files, getDefaultAd(), ascii, pgpRecipients, pgpSigners, cmsRecipients, cmsSigners, wizard->outputNames(), wizard->encryptSymmetric()); } else { - Q_FOREACH (const QString &file, files) { + for (const QString &file : std::as_const(files)) { const std::vector< std::shared_ptr > created = createSignEncryptTasksForFileInfo(QFileInfo(file), ascii, pgpRecipients, pgpSigners, cmsRecipients, cmsSigners, buildOutputNamesForDir(file, wizard->outputNames()), wizard->encryptSymmetric()); tasks.insert(tasks.end(), created.begin(), created.end()); } } const std::shared_ptr overwritePolicy(new OverwritePolicy(wizard)); - Q_FOREACH (const std::shared_ptr &i, tasks) { + for (const std::shared_ptr &i : tasks) { i->setOverwritePolicy(overwritePolicy); } kleo_assert(runnable.empty()); runnable.swap(tasks); for (const auto &task : std::as_const(runnable)) { q->connectTask(task); } std::shared_ptr coll(new TaskCollection); std::vector > tmp; std::copy(runnable.begin(), runnable.end(), std::back_inserter(tmp)); coll->setTasks(tmp); wizard->setTaskCollection(coll); QTimer::singleShot(0, q, SLOT(schedule())); } catch (const Kleo::Exception &e) { reportError(e.error().encodedError(), e.message()); } catch (const std::exception &e) { reportError(gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unexpected exception in SignEncryptFilesController::Private::slotWizardOperationPrepared: %1", QString::fromLocal8Bit(e.what()))); } catch (...) { reportError(gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignEncryptFilesController::Private::slotWizardOperationPrepared")); } } void SignEncryptFilesController::Private::schedule() { if (!cms) if (const std::shared_ptr t = takeRunnable(CMS)) { t->start(); cms = t; } if (!openpgp) if (const std::shared_ptr t = takeRunnable(OpenPGP)) { t->start(); openpgp = t; } if (!cms && !openpgp) { kleo_assert(runnable.empty()); q->emitDoneOrError(); } } std::shared_ptr SignEncryptFilesController::Private::takeRunnable(GpgME::Protocol proto) { const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr &task) { return task->protocol() == proto; }); if (it == runnable.end()) { return std::shared_ptr(); } const std::shared_ptr result = *it; runnable.erase(it); return result; } void SignEncryptFilesController::doTaskDone(const Task *task, const std::shared_ptr &result) { Q_UNUSED(result) Q_ASSERT(task); // We could just delete the tasks here, but we can't use // Qt::QueuedConnection here (we need sender()) and other slots // might not yet have executed. Therefore, we push completed tasks // into a burial container if (task == d->cms.get()) { d->completed.push_back(d->cms); d->cms.reset(); } else if (task == d->openpgp.get()) { d->completed.push_back(d->openpgp); d->openpgp.reset(); } QTimer::singleShot(0, this, SLOT(schedule())); } void SignEncryptFilesController::cancel() { qCDebug(KLEOPATRA_LOG); try { if (d->wizard) { d->wizard->close(); } d->cancelAllTasks(); } catch (const std::exception &e) { qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what(); } } void SignEncryptFilesController::Private::cancelAllTasks() { // we just kill all runnable tasks - this will not result in // signal emissions. runnable.clear(); // a cancel() will result in a call to if (cms) { cms->cancel(); } if (openpgp) { openpgp->cancel(); } } void SignEncryptFilesController::Private::ensureWizardCreated() { if (wizard) { return; } std::unique_ptr w(new SignEncryptFilesWizard); w->setAttribute(Qt::WA_DeleteOnClose); connect(w.get(), SIGNAL(operationPrepared()), q, SLOT(slotWizardOperationPrepared()), Qt::QueuedConnection); connect(w.get(), SIGNAL(rejected()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection); wizard = w.release(); updateWizardMode(); } void SignEncryptFilesController::Private::ensureWizardVisible() { ensureWizardCreated(); q->bringToForeground(wizard); } #include "moc_signencryptfilescontroller.cpp" diff --git a/src/dialogs/lookupcertificatesdialog.cpp b/src/dialogs/lookupcertificatesdialog.cpp index 1a3104330..f3b183c18 100644 --- a/src/dialogs/lookupcertificatesdialog.cpp +++ b/src/dialogs/lookupcertificatesdialog.cpp @@ -1,348 +1,350 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/lookupcertificatesdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "lookupcertificatesdialog.h" #include "view/keytreeview.h" #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; static const int minimalSearchTextLength = 2; class LookupCertificatesDialog::Private { friend class ::Kleo::Dialogs::LookupCertificatesDialog; LookupCertificatesDialog *const q; public: explicit Private(LookupCertificatesDialog *qq); ~Private(); private: void slotSelectionChanged() { enableDisableWidgets(); } void slotSearchTextChanged() { enableDisableWidgets(); } void slotSearchClicked() { Q_EMIT q->searchTextChanged(ui.findED->text()); } void slotDetailsClicked() { Q_ASSERT(q->selectedCertificates().size() == 1); Q_EMIT q->detailsRequested(q->selectedCertificates().front()); } void slotSaveAsClicked() { Q_EMIT q->saveAsRequested(q->selectedCertificates()); } void readConfig(); void writeConfig(); void enableDisableWidgets(); QString searchText() const { return ui.findED->text().trimmed(); } std::vector selectedCertificates() const { const QAbstractItemView *const view = ui.resultTV->view(); if (!view) { return std::vector(); } const auto *const model = dynamic_cast(view->model()); Q_ASSERT(model); const QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); return model->keys(sm->selectedRows()); } int numSelectedCertificates() const { return ui.resultTV->selectedKeys().size(); } private: bool passive; struct Ui { QLabel *findLB; QLineEdit *findED; QPushButton *findPB; Kleo::KeyTreeView *resultTV; QPushButton *selectAllPB; QPushButton *deselectAllPB; QPushButton *detailsPB; QPushButton *saveAsPB; KMessageWidget *messageWidget; QDialogButtonBox *buttonBox; void setupUi(QDialog *dialog) { auto verticalLayout = new QVBoxLayout{dialog}; auto gridLayout = new QGridLayout{}; int row = 0; findLB = new QLabel{i18n("Find:"), dialog}; gridLayout->addWidget(findLB, row, 0, 1, 1); findED = new QLineEdit{dialog}; findLB->setBuddy(findED); gridLayout->addWidget(findED, row, 1, 1, 1); findPB = new QPushButton{i18n("Search"), dialog}; findPB->setAutoDefault(false); gridLayout->addWidget(findPB, row, 2, 1, 1); row++; gridLayout->addWidget(new KSeparator{Qt::Horizontal, dialog}, row, 0, 1, 3); row++; resultTV = new Kleo::KeyTreeView(dialog); resultTV->setEnabled(true); resultTV->setMinimumSize(QSize(400, 0)); gridLayout->addWidget(resultTV, row, 0, 1, 2); auto buttonLayout = new QVBoxLayout{}; selectAllPB = new QPushButton{i18n("Select All"), dialog}; selectAllPB->setEnabled(false); selectAllPB->setAutoDefault(false); buttonLayout->addWidget(selectAllPB); deselectAllPB = new QPushButton{i18n("Deselect All"), dialog}; deselectAllPB->setEnabled(false); deselectAllPB->setAutoDefault(false); buttonLayout->addWidget(deselectAllPB); buttonLayout->addStretch(); detailsPB = new QPushButton{i18n("Details..."), dialog}; detailsPB->setEnabled(false); detailsPB->setAutoDefault(false); buttonLayout->addWidget(detailsPB); saveAsPB = new QPushButton{i18n("Save As..."), dialog}; saveAsPB->setEnabled(false); saveAsPB->setAutoDefault(false); buttonLayout->addWidget(saveAsPB); gridLayout->addLayout(buttonLayout, row, 2, 1, 1); row++; messageWidget = new KMessageWidget{dialog}; messageWidget->setMessageType(KMessageWidget::Information); messageWidget->setVisible(false); gridLayout->addWidget(messageWidget, row, 0, 1, 3); verticalLayout->addLayout(gridLayout); buttonBox = new QDialogButtonBox{dialog}; buttonBox->setStandardButtons(QDialogButtonBox::Close|QDialogButtonBox::Save); verticalLayout->addWidget(buttonBox); QObject::connect(findED, SIGNAL(returnPressed()), findPB, SLOT(animateClick())); QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); QObject::connect(findPB, SIGNAL(clicked()), dialog, SLOT(slotSearchClicked())); QObject::connect(detailsPB, SIGNAL(clicked()), dialog, SLOT(slotDetailsClicked())); QObject::connect(saveAsPB, SIGNAL(clicked()), dialog, SLOT(slotSaveAsClicked())); QObject::connect(findED, SIGNAL(textChanged(QString)), dialog, SLOT(slotSearchTextChanged())); QMetaObject::connectSlotsByName(dialog); } explicit Ui(LookupCertificatesDialog *q) { q->setWindowTitle(i18n("Lookup on Server")); setupUi(q); saveAsPB->hide(); // ### not yet implemented in LookupCertificatesCommand findED->setClearButtonEnabled(true); resultTV->setFlatModel(AbstractKeyListModel::createFlatKeyListModel(q)); resultTV->setHierarchicalView(false); importPB()->setText(i18n("Import")); importPB()->setEnabled(false); connect(resultTV->view(), SIGNAL(doubleClicked(QModelIndex)), importPB(), SLOT(animateClick())); findED->setFocus(); connect(selectAllPB, &QPushButton::clicked, resultTV->view(), &QTreeView::selectAll); connect(deselectAllPB, &QPushButton::clicked, resultTV->view(), &QTreeView::clearSelection); } QPushButton *importPB() const { return buttonBox->button(QDialogButtonBox::Save); } QPushButton *closePB() const { return buttonBox->button(QDialogButtonBox::Close); } } ui; }; LookupCertificatesDialog::Private::Private(LookupCertificatesDialog *qq) : q(qq), passive(false), ui(q) { connect(ui.resultTV->view()->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(slotSelectionChanged())); } LookupCertificatesDialog::Private::~Private() {} void LookupCertificatesDialog::Private::readConfig() { KConfigGroup configGroup(KSharedConfig::openStateConfig(), "LookupCertificatesDialog"); KConfigGroup resultKeysConfig = configGroup.group("ResultKeysView"); ui.resultTV->restoreLayout(resultKeysConfig); const QSize size = configGroup.readEntry("Size", QSize(600, 400)); if (size.isValid()) { q->resize(size); } } void LookupCertificatesDialog::Private::writeConfig() { KConfigGroup configGroup(KSharedConfig::openStateConfig(), "LookupCertificatesDialog"); configGroup.writeEntry("Size", q->size()); KConfigGroup resultKeysConfig = configGroup.group("ResultKeysView"); ui.resultTV->saveLayout(resultKeysConfig); configGroup.sync(); } LookupCertificatesDialog::LookupCertificatesDialog(QWidget *p, Qt::WindowFlags f) : QDialog(p, f), d(new Private(this)) { d->ui.findPB->setEnabled(false); d->readConfig(); } LookupCertificatesDialog::~LookupCertificatesDialog() { d->writeConfig(); } void LookupCertificatesDialog::setCertificates(const std::vector &certs) { d->ui.resultTV->view()->setFocus(); d->ui.resultTV->setKeys(certs); } std::vector LookupCertificatesDialog::selectedCertificates() const { return d->selectedCertificates(); } void LookupCertificatesDialog::setPassive(bool on) { if (d->passive == on) { return; } d->passive = on; d->enableDisableWidgets(); } bool LookupCertificatesDialog::isPassive() const { return d->passive; } void LookupCertificatesDialog::setSearchText(const QString &text) { d->ui.findED->setText(text); } QString LookupCertificatesDialog::searchText() const { return d->ui.findED->text(); } void LookupCertificatesDialog::showInformation(const QString &message) { d->ui.messageWidget->setText(message); if (message.isEmpty()) { d->ui.messageWidget->animatedHide(); } else { d->ui.messageWidget->animatedShow(); } } void LookupCertificatesDialog::accept() { Q_ASSERT(!selectedCertificates().empty()); Q_EMIT importRequested(selectedCertificates()); QDialog::accept(); } void LookupCertificatesDialog::Private::enableDisableWidgets() { // enable/disable everything except 'close', based on passive: - Q_FOREACH (QObject *const o, q->children()) + const QList list = q->children(); + for (QObject *const o : list) { if (QWidget *const w = qobject_cast(o)) { w->setDisabled(passive && w != ui.closePB() && w != ui.buttonBox); } + } if (passive) { return; } ui.findPB->setEnabled(searchText().length() > minimalSearchTextLength); const int n = q->selectedCertificates().size(); ui.detailsPB->setEnabled(n == 1); ui.saveAsPB->setEnabled(n == 1); ui.importPB()->setEnabled(n != 0); ui.importPB()->setDefault(false); // otherwise Import becomes default button if enabled and return triggers both a search and accept() } #include "moc_lookupcertificatesdialog.cpp" diff --git a/src/kleopatraapplication.cpp b/src/kleopatraapplication.cpp index 2d1b597a1..850878d67 100644 --- a/src/kleopatraapplication.cpp +++ b/src/kleopatraapplication.cpp @@ -1,712 +1,713 @@ /* kleopatraapplication.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 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 "kleopatraapplication.h" #include "mainwindow.h" #include "kleopatra_options.h" #include "systrayicon.h" #include "settings.h" #include "smimevalidationpreferences.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_USABLE_ASSUAN # include #endif #include "commands/signencryptfilescommand.h" #include "commands/decryptverifyfilescommand.h" #include "commands/lookupcertificatescommand.h" #include "commands/checksumcreatefilescommand.h" #include "commands/checksumverifyfilescommand.h" #include "commands/detailscommand.h" #include "commands/newcertificatecommand.h" #include "dialogs/updatenotification.h" #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #endif using namespace Kleo; using namespace Kleo::Commands; static void add_resources() { KIconLoader::global()->addAppDir(QStringLiteral("libkleopatra")); KIconLoader::global()->addAppDir(QStringLiteral("kwatchgnupg")); } static QList default_logging_options() { QList result; result.push_back("io"); return result; } class KleopatraApplication::Private { friend class ::KleopatraApplication; KleopatraApplication *const q; public: explicit Private(KleopatraApplication *qq) : q(qq) , ignoreNewInstance(true) , firstNewInstance(true) , sysTray(nullptr) , groupConfig{std::make_shared(QStringLiteral("kleopatragroupsrc"))} { } ~Private() { #ifndef QT_NO_SYSTEMTRAYICON delete sysTray; #endif } void setUpSysTrayIcon() { KDAB_SET_OBJECT_NAME(readerStatus); #ifndef QT_NO_SYSTEMTRAYICON sysTray = new SysTrayIcon(); sysTray->setFirstCardWithNullPin(readerStatus.firstCardWithNullPin()); sysTray->setAnyCardCanLearnKeys(readerStatus.anyCardCanLearnKeys()); connect(&readerStatus, &SmartCard::ReaderStatus::firstCardWithNullPinChanged, sysTray, &SysTrayIcon::setFirstCardWithNullPin); connect(&readerStatus, &SmartCard::ReaderStatus::anyCardCanLearnKeysChanged, sysTray, &SysTrayIcon::setAnyCardCanLearnKeys); #endif } private: void connectConfigureDialog() { if (configureDialog) { if (q->mainWindow()) { connect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted())); } connect(configureDialog, &ConfigureDialog::configCommitted, q, &KleopatraApplication::configurationChanged); } } void disconnectConfigureDialog() { if (configureDialog) { if (q->mainWindow()) { disconnect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted())); } disconnect(configureDialog, &ConfigureDialog::configCommitted, q, &KleopatraApplication::configurationChanged); } } public: bool ignoreNewInstance; bool firstNewInstance; QPointer configureDialog; QPointer mainWindow; SmartCard::ReaderStatus readerStatus; #ifndef QT_NO_SYSTEMTRAYICON SysTrayIcon *sysTray; #endif std::shared_ptr groupConfig; std::shared_ptr keyCache; std::shared_ptr log; std::shared_ptr watcher; public: void setupKeyCache() { keyCache = KeyCache::mutableInstance(); keyCache->setRefreshInterval(SMimeValidationPreferences{}.refreshInterval()); watcher.reset(new FileSystemWatcher); watcher->whitelistFiles(gnupgFileWhitelist()); watcher->addPaths(gnupgFolderWhitelist()); watcher->setDelay(1000); keyCache->addFileSystemWatcher(watcher); keyCache->setGroupConfig(groupConfig); keyCache->setGroupsEnabled(Settings().groupsEnabled()); } void setUpFilterManager() { if (!Settings{}.cmsEnabled()) { KeyFilterManager::instance()->alwaysFilterByProtocol(GpgME::OpenPGP); } } void setupLogging() { log = Log::mutableInstance(); const QByteArray envOptions = qgetenv("KLEOPATRA_LOGOPTIONS"); const bool logAll = envOptions.trimmed() == "all"; const QList options = envOptions.isEmpty() ? default_logging_options() : envOptions.split(','); const QByteArray dirNative = qgetenv("KLEOPATRA_LOGDIR"); if (dirNative.isEmpty()) { return; } const QString dir = QFile::decodeName(dirNative); const QString logFileName = QDir(dir).absoluteFilePath(QStringLiteral("kleopatra.log.%1").arg(QCoreApplication::applicationPid())); std::unique_ptr logFile(new QFile(logFileName)); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append)) { qCDebug(KLEOPATRA_LOG) << "Could not open file for logging: " << logFileName << "\nLogging disabled"; return; } log->setOutputDirectory(dir); if (logAll || options.contains("io")) { log->setIOLoggingEnabled(true); } qInstallMessageHandler(Log::messageHandler); #ifdef HAVE_USABLE_ASSUAN if (logAll || options.contains("pipeio")) { KDPipeIODevice::setDebugLevel(KDPipeIODevice::Debug); } UiServer::setLogStream(log->logFile()); #endif } }; KleopatraApplication::KleopatraApplication(int &argc, char *argv[]) : QApplication(argc, argv), d(new Private(this)) { } void KleopatraApplication::init() { #ifdef Q_OS_WIN QWindowsWindowFunctions::setWindowActivationBehavior( QWindowsWindowFunctions::AlwaysActivateWindow); #endif const auto blockedUrlSchemes = Settings{}.blockedUrlSchemes(); for (const auto &scheme : blockedUrlSchemes) { QDesktopServices::setUrlHandler(scheme, this, "blockUrl"); } add_resources(); DN::setAttributeOrder(Settings{}.attributeOrder()); /* Start the gpg-agent early, this is done explicitly * because on an empty keyring our keylistings wont start * the agent. In that case any assuan-connect calls to * the agent will fail. The requested start via the * connection is additionally done in case the gpg-agent * is killed while Kleopatra is running. */ startGpgAgent(); connect(&d->readerStatus, &SmartCard::ReaderStatus::startOfGpgAgentRequested, this, &KleopatraApplication::startGpgAgent); d->setupKeyCache(); d->setUpSysTrayIcon(); d->setUpFilterManager(); d->setupLogging(); #ifndef QT_NO_SYSTEMTRAYICON d->sysTray->show(); #endif setQuitOnLastWindowClosed(false); KWindowSystem::allowExternalProcessWindowActivation(); } KleopatraApplication::~KleopatraApplication() { // main window doesn't receive "close" signal and cannot // save settings before app exit delete d->mainWindow; // work around kdelibs bug https://bugs.kde.org/show_bug.cgi?id=162514 KSharedConfig::openConfig()->sync(); } namespace { using Func = void (KleopatraApplication::*)(const QStringList &, GpgME::Protocol); } void KleopatraApplication::slotActivateRequested(const QStringList &arguments, const QString &workingDirectory) { QCommandLineParser parser; kleopatra_options(&parser); QString err; if (!arguments.isEmpty() && !parser.parse(arguments)) { err = parser.errorText(); } else if (arguments.isEmpty()) { // KDBusServices omits the application name if no other // arguments are provided. In that case the parser prints // a warning. parser.parse(QStringList() << QCoreApplication::applicationFilePath()); } if (err.isEmpty()) { err = newInstance(parser, workingDirectory); } if (!err.isEmpty()) { KMessageBox::sorry(nullptr, err.toHtmlEscaped(), i18n("Failed to execute command")); Q_EMIT setExitValue(1); return; } Q_EMIT setExitValue(0); } QString KleopatraApplication::newInstance(const QCommandLineParser &parser, const QString &workingDirectory) { if (d->ignoreNewInstance) { qCDebug(KLEOPATRA_LOG) << "New instance ignored because of ignoreNewInstance"; return QString(); } QStringList files; const QDir cwd = QDir(workingDirectory); bool queryMode = parser.isSet(QStringLiteral("query")) || parser.isSet(QStringLiteral("search")); // Query and Search treat positional arguments differently, see below. if (!queryMode) { const auto positionalArguments = parser.positionalArguments(); for (const QString &file : positionalArguments) { // We do not check that file exists here. Better handle // these errors in the UI. if (QFileInfo(file).isAbsolute()) { files << file; } else { files << cwd.absoluteFilePath(file); } } } GpgME::Protocol protocol = GpgME::UnknownProtocol; if (parser.isSet(QStringLiteral("openpgp"))) { qCDebug(KLEOPATRA_LOG) << "found OpenPGP"; protocol = GpgME::OpenPGP; } if (parser.isSet(QStringLiteral("cms"))) { qCDebug(KLEOPATRA_LOG) << "found CMS"; if (protocol == GpgME::OpenPGP) { return i18n("Ambiguous protocol: --openpgp and --cms"); } protocol = GpgME::CMS; } // Check for Parent Window id WId parentId = 0; if (parser.isSet(QStringLiteral("parent-windowid"))) { #ifdef Q_OS_WIN // WId is not a portable type as it is a pointer type on Windows. // casting it from an integer is ok though as the values are guaranteed to // be compatible in the documentation. parentId = reinterpret_cast(parser.value(QStringLiteral("parent-windowid")).toUInt()); #else parentId = parser.value(QStringLiteral("parent-windowid")).toUInt(); #endif } // Handle openpgp4fpr URI scheme QString needle; if (queryMode) { needle = parser.positionalArguments().join(QLatin1Char(' ')); } if (needle.startsWith(QLatin1String("openpgp4fpr:"))) { needle.remove(0, 12); } // Check for --search command. if (parser.isSet(QStringLiteral("search"))) { // This is an extra command instead of a combination with the // similar query to avoid changing the older query commands behavior // and query's "show details if a certificate exist or search on a // keyserver" logic is hard to explain and use consistently. if (needle.isEmpty()) { return i18n("No search string specified for --search"); } auto const cmd = new LookupCertificatesCommand(needle, nullptr); cmd->setParentWId(parentId); cmd->start(); return QString(); } // Check for --query command if (parser.isSet(QStringLiteral("query"))) { if (needle.isEmpty()) { return i18n("No fingerprint argument specified for --query"); } auto cmd = Command::commandForQuery(needle); cmd->setParentWId(parentId); cmd->start(); return QString(); } // Check for --gen-key command if (parser.isSet(QStringLiteral("gen-key"))) { auto cmd = new NewCertificateCommand(nullptr); cmd->setParentWId(parentId); cmd->setProtocol(protocol); cmd->start(); return QString(); } // Check for --config command if (parser.isSet(QStringLiteral("config"))) { openConfigDialogWithForeignParent(parentId); return QString(); } static const QMap funcMap { { QStringLiteral("import-certificate"), &KleopatraApplication::importCertificatesFromFile }, { QStringLiteral("encrypt"), &KleopatraApplication::encryptFiles }, { QStringLiteral("sign"), &KleopatraApplication::signFiles }, { QStringLiteral("encrypt-sign"), &KleopatraApplication::signEncryptFiles }, { QStringLiteral("sign-encrypt"), &KleopatraApplication::signEncryptFiles }, { QStringLiteral("decrypt"), &KleopatraApplication::decryptFiles }, { QStringLiteral("verify"), &KleopatraApplication::verifyFiles }, { QStringLiteral("decrypt-verify"), &KleopatraApplication::decryptVerifyFiles }, { QStringLiteral("checksum"), &KleopatraApplication::checksumFiles }, }; QString found; Q_FOREACH (const QString &opt, funcMap.keys()) { if (parser.isSet(opt) && found.isEmpty()) { found = opt; } else if (parser.isSet(opt)) { return i18n(R"(Ambiguous commands "%1" and "%2")", found, opt); } } QStringList errors; if (!found.isEmpty()) { if (files.empty()) { return i18n("No files specified for \"%1\" command", found); } qCDebug(KLEOPATRA_LOG) << "found" << found; (this->*funcMap.value(found))(files, protocol); } else { if (files.empty()) { if (!(d->firstNewInstance && isSessionRestored())) { qCDebug(KLEOPATRA_LOG) << "openOrRaiseMainWindow"; openOrRaiseMainWindow(); } } else { for (const QString& fileName : std::as_const(files)) { QFileInfo fi(fileName); if (!fi.isReadable()) { errors << i18n("Cannot read \"%1\"", fileName); } } - Q_FOREACH (Command *cmd, Command::commandsForFiles(files)) { + const QVector allCmds = Command::commandsForFiles(files); + for (Command *cmd : allCmds) { if (parentId) { cmd->setParentWId(parentId); } else { MainWindow *mw = mainWindow(); if (!mw) { mw = new MainWindow; mw->setAttribute(Qt::WA_DeleteOnClose); setMainWindow(mw); d->connectConfigureDialog(); } cmd->setParentWidget(mw); } cmd->start(); } } } d->firstNewInstance = false; #ifdef Q_OS_WIN // On Windows we might be started from the // explorer in any working directory. E.g. // a double click on a file. To avoid preventing // the folder from deletion we set the // working directory to the users homedir. QDir::setCurrent(QDir::homePath()); #endif return errors.join(QLatin1Char('\n')); } #ifndef QT_NO_SYSTEMTRAYICON const SysTrayIcon *KleopatraApplication::sysTrayIcon() const { return d->sysTray; } SysTrayIcon *KleopatraApplication::sysTrayIcon() { return d->sysTray; } #endif const MainWindow *KleopatraApplication::mainWindow() const { return d->mainWindow; } MainWindow *KleopatraApplication::mainWindow() { return d->mainWindow; } void KleopatraApplication::setMainWindow(MainWindow *mainWindow) { if (mainWindow == d->mainWindow) { return; } d->disconnectConfigureDialog(); d->mainWindow = mainWindow; #ifndef QT_NO_SYSTEMTRAYICON d->sysTray->setMainWindow(mainWindow); #endif d->connectConfigureDialog(); } static void open_or_raise(QWidget *w) { if (w->isMinimized()) { KWindowSystem::unminimizeWindow(w->winId()); w->raise(); } else if (w->isVisible()) { w->raise(); } else { w->show(); } } void KleopatraApplication::toggleMainWindowVisibility() { if (mainWindow()) { mainWindow()->setVisible(!mainWindow()->isVisible()); } else { openOrRaiseMainWindow(); } } void KleopatraApplication::restoreMainWindow() { qCDebug(KLEOPATRA_LOG) << "restoring main window"; // Sanity checks if (!isSessionRestored()) { qCDebug(KLEOPATRA_LOG) << "Not in session restore"; return; } if (mainWindow()) { qCDebug(KLEOPATRA_LOG) << "Already have main window"; return; } auto mw = new MainWindow; if (KMainWindow::canBeRestored(1)) { // restore to hidden state, Mainwindow::readProperties() will // restore saved visibility. mw->restore(1, false); } mw->setAttribute(Qt::WA_DeleteOnClose); setMainWindow(mw); d->connectConfigureDialog(); } void KleopatraApplication::openOrRaiseMainWindow() { MainWindow *mw = mainWindow(); if (!mw) { mw = new MainWindow; mw->setAttribute(Qt::WA_DeleteOnClose); setMainWindow(mw); d->connectConfigureDialog(); } open_or_raise(mw); UpdateNotification::checkUpdate(mw); } void KleopatraApplication::openConfigDialogWithForeignParent(WId parentWId) { if (!d->configureDialog) { d->configureDialog = new ConfigureDialog; d->configureDialog->setAttribute(Qt::WA_DeleteOnClose); d->connectConfigureDialog(); } // This is similar to what the commands do. if (parentWId) { if (QWidget *pw = QWidget::find(parentWId)) { d->configureDialog->setParent(pw, d->configureDialog->windowFlags()); } else { d->configureDialog->setAttribute(Qt::WA_NativeWindow, true); KWindowSystem::setMainWindow(d->configureDialog->windowHandle(), parentWId); } } open_or_raise(d->configureDialog); // If we have a parent we want to raise over it. if (parentWId) { d->configureDialog->raise(); } } void KleopatraApplication::openOrRaiseConfigDialog() { openConfigDialogWithForeignParent(0); } #ifndef QT_NO_SYSTEMTRAYICON void KleopatraApplication::startMonitoringSmartCard() { d->readerStatus.startMonitoring(); } #endif // QT_NO_SYSTEMTRAYICON void KleopatraApplication::importCertificatesFromFile(const QStringList &files, GpgME::Protocol /*proto*/) { openOrRaiseMainWindow(); if (!files.empty()) { mainWindow()->importCertificatesFromFile(files); } } void KleopatraApplication::encryptFiles(const QStringList &files, GpgME::Protocol proto) { auto const cmd = new SignEncryptFilesCommand(files, nullptr); cmd->setEncryptionPolicy(Force); cmd->setSigningPolicy(Allow); if (proto != GpgME::UnknownProtocol) { cmd->setProtocol(proto); } cmd->start(); } void KleopatraApplication::signFiles(const QStringList &files, GpgME::Protocol proto) { auto const cmd = new SignEncryptFilesCommand(files, nullptr); cmd->setSigningPolicy(Force); cmd->setEncryptionPolicy(Deny); if (proto != GpgME::UnknownProtocol) { cmd->setProtocol(proto); } cmd->start(); } void KleopatraApplication::signEncryptFiles(const QStringList &files, GpgME::Protocol proto) { auto const cmd = new SignEncryptFilesCommand(files, nullptr); if (proto != GpgME::UnknownProtocol) { cmd->setProtocol(proto); } cmd->start(); } void KleopatraApplication::decryptFiles(const QStringList &files, GpgME::Protocol /*proto*/) { auto const cmd = new DecryptVerifyFilesCommand(files, nullptr); cmd->setOperation(Decrypt); cmd->start(); } void KleopatraApplication::verifyFiles(const QStringList &files, GpgME::Protocol /*proto*/) { auto const cmd = new DecryptVerifyFilesCommand(files, nullptr); cmd->setOperation(Verify); cmd->start(); } void KleopatraApplication::decryptVerifyFiles(const QStringList &files, GpgME::Protocol /*proto*/) { auto const cmd = new DecryptVerifyFilesCommand(files, nullptr); cmd->start(); } void KleopatraApplication::checksumFiles(const QStringList &files, GpgME::Protocol /*proto*/) { QStringList verifyFiles, createFiles; for (const QString &file : files) { if (isChecksumFile(file)) { verifyFiles << file; } else { createFiles << file; } } if (!verifyFiles.isEmpty()) { auto const cmd = new ChecksumVerifyFilesCommand(verifyFiles, nullptr); cmd->start(); } if (!createFiles.isEmpty()) { auto const cmd = new ChecksumCreateFilesCommand(createFiles, nullptr); cmd->start(); } } void KleopatraApplication::setIgnoreNewInstance(bool ignore) { d->ignoreNewInstance = ignore; } bool KleopatraApplication::ignoreNewInstance() const { return d->ignoreNewInstance; } void KleopatraApplication::blockUrl(const QUrl &url) { qCDebug(KLEOPATRA_LOG) << "Blocking URL" << url; KMessageBox::sorry(mainWindow(),i18n ("Opening an external link is administratively prohibited."), i18n ("Prohibited")); } void KleopatraApplication::startGpgAgent() { Kleo::launchGpgAgent(); } diff --git a/src/libkleopatraclient/core/command.cpp b/src/libkleopatraclient/core/command.cpp index ea9195e4c..cc8193a56 100644 --- a/src/libkleopatraclient/core/command.cpp +++ b/src/libkleopatraclient/core/command.cpp @@ -1,707 +1,710 @@ /* -*- mode: c++; c-basic-offset:4 -*- command.cpp This file is part of KleopatraClient, the Kleopatra interface library SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: LGPL-2.0-or-later */ #include #include "command.h" #include "command_p.h" #include // Q_OS_WIN #ifdef Q_OS_WIN // HACK: AllowSetForegroundWindow needs _WIN32_WINDOWS >= 0x0490 set # ifndef _WIN32_WINDOWS # define _WIN32_WINDOWS 0x0500 # define _WIN32_WINNT 0x0500 // good enough for Vista too # endif # include # include #endif #include #include #include "libkleopatraclientcore_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KleopatraClientCopy; // copied from kleopatra/utils/hex.cpp static std::string hexencode(const std::string &in) { std::string result; result.reserve(3 * in.size()); static const char hex[] = "0123456789ABCDEF"; for (std::string::const_iterator it = in.begin(), end = in.end(); it != end; ++it) switch (const unsigned char ch = *it) { default: if ((ch >= '!' && ch <= '~') || ch > 0xA0) { result += ch; break; } Q_FALLTHROUGH(); // else fall through case ' ': result += '+'; break; case '"': case '#': case '$': case '%': case '\'': case '+': case '=': result += '%'; result += hex[(ch & 0xF0) >> 4 ]; result += hex[(ch & 0x0F) ]; break; } return result; } #ifdef UNUSED static std::string hexencode(const char *in) { if (!in) { return std::string(); } return hexencode(std::string(in)); } #endif // changed from returning QByteArray to returning std::string static std::string hexencode(const QByteArray &in) { if (in.isNull()) { return std::string(); } return hexencode(std::string(in.data(), in.size())); } // end copied from kleopatra/utils/hex.cpp Command::Command(QObject *p) : QObject(p), d(new Private(this)) { d->init(); } Command::Command(Private *pp, QObject *p) : QObject(p), d(pp) { d->init(); } Command::~Command() { delete d; d = nullptr; } void Command::Private::init() { connect(this, &QThread::started, q, &Command::started); connect(this, &QThread::finished, q, &Command::finished); } void Command::setParentWId(WId wid) { const QMutexLocker locker(&d->mutex); d->inputs.parentWId = wid; } WId Command::parentWId() const { const QMutexLocker locker(&d->mutex); return d->inputs.parentWId; } void Command::setServerLocation(const QString &location) { const QMutexLocker locker(&d->mutex); d->outputs.serverLocation = location; } QString Command::serverLocation() const { const QMutexLocker locker(&d->mutex); return d->outputs.serverLocation; } bool Command::waitForFinished() { return d->wait(); } bool Command::waitForFinished(unsigned long ms) { return d->wait(ms); } bool Command::error() const { const QMutexLocker locker(&d->mutex); return !d->outputs.errorString.isEmpty(); } bool Command::wasCanceled() const { const QMutexLocker locker(&d->mutex); return d->outputs.canceled; } QString Command::errorString() const { const QMutexLocker locker(&d->mutex); return d->outputs.errorString; } qint64 Command::serverPid() const { const QMutexLocker locker(&d->mutex); return d->outputs.serverPid; } void Command::start() { d->start(); } void Command::cancel() { qCDebug(LIBKLEOPATRACLIENTCORE_LOG) << "Sorry, not implemented: KleopatraClient::Command::Cancel"; } void Command::setOptionValue(const char *name, const QVariant &value, bool critical) { if (!name || !*name) { return; } const Private::Option opt = { value, true, critical }; const QMutexLocker locker(&d->mutex); d->inputs.options[name] = opt; } QVariant Command::optionValue(const char *name) const { if (!name || !*name) { return QVariant(); } const QMutexLocker locker(&d->mutex); const auto it = d->inputs.options.find(name); if (it == d->inputs.options.end()) { return QVariant(); } else { return it->second.value; } } void Command::setOption(const char *name, bool critical) { if (!name || !*name) { return; } const QMutexLocker locker(&d->mutex); if (isOptionSet(name)) { unsetOption(name); } const Private::Option opt = { QVariant(), false, critical }; d->inputs.options[name] = opt; } void Command::unsetOption(const char *name) { if (!name || !*name) { return; } const QMutexLocker locker(&d->mutex); d->inputs.options.erase(name); } bool Command::isOptionSet(const char *name) const { if (!name || !*name) { return false; } const QMutexLocker locker(&d->mutex); return d->inputs.options.count(name); } bool Command::isOptionCritical(const char *name) const { if (!name || !*name) { return false; } const QMutexLocker locker(&d->mutex); const auto it = d->inputs.options.find(name); return it != d->inputs.options.end() && it->second.isCritical; } void Command::setFilePaths(const QStringList &filePaths) { const QMutexLocker locker(&d->mutex); d->inputs.filePaths = filePaths; } QStringList Command::filePaths() const { const QMutexLocker locker(&d->mutex); return d->inputs.filePaths; } void Command::setRecipients(const QStringList &recipients, bool informative) { const QMutexLocker locker(&d->mutex); d->inputs.recipients = recipients; d->inputs.areRecipientsInformative = informative; } QStringList Command::recipients() const { const QMutexLocker locker(&d->mutex); return d->inputs.recipients; } bool Command::areRecipientsInformative() const { const QMutexLocker locker(&d->mutex); return d->inputs.areRecipientsInformative; } void Command::setSenders(const QStringList &senders, bool informative) { const QMutexLocker locker(&d->mutex); d->inputs.senders = senders; d->inputs.areSendersInformative = informative; } QStringList Command::senders() const { const QMutexLocker locker(&d->mutex); return d->inputs.senders; } bool Command::areSendersInformative() const { const QMutexLocker locker(&d->mutex); return d->inputs.areSendersInformative; } void Command::setInquireData(const char *what, const QByteArray &data) { const QMutexLocker locker(&d->mutex); d->inputs.inquireData[what] = data; } void Command::unsetInquireData(const char *what) { const QMutexLocker locker(&d->mutex); d->inputs.inquireData.erase(what); } QByteArray Command::inquireData(const char *what) const { const QMutexLocker locker(&d->mutex); const auto it = d->inputs.inquireData.find(what); if (it == d->inputs.inquireData.end()) { return QByteArray(); } else { return it->second; } } bool Command::isInquireDataSet(const char *what) const { const QMutexLocker locker(&d->mutex); const auto it = d->inputs.inquireData.find(what); return it != d->inputs.inquireData.end(); } QByteArray Command::receivedData() const { const QMutexLocker locker(&d->mutex); return d->outputs.data; } void Command::setCommand(const char *command) { const QMutexLocker locker(&d->mutex); d->inputs.command = command; } QByteArray Command::command() const { const QMutexLocker locker(&d->mutex); return d->inputs.command; } // // here comes the ugly part // #ifdef HAVE_ASSUAN2 static void my_assuan_release(assuan_context_t ctx) { if (ctx) { assuan_release(ctx); } } #endif using AssuanContextBase = std::shared_ptr::type>; namespace { struct AssuanClientContext : AssuanContextBase { AssuanClientContext() : AssuanContextBase() {} #ifndef HAVE_ASSUAN2 explicit AssuanClientContext(assuan_context_t ctx) : AssuanContextBase(ctx, &assuan_disconnect) {} void reset(assuan_context_t ctx = nullptr) { AssuanContextBase::reset(ctx, &assuan_disconnect); } #else explicit AssuanClientContext(assuan_context_t ctx) : AssuanContextBase(ctx, &my_assuan_release) {} void reset(assuan_context_t ctx = nullptr) { AssuanContextBase::reset(ctx, &my_assuan_release); } #endif }; } #ifdef HAVE_ASSUAN2 // compatibility typedef - remove when we require assuan v2... using assuan_error_t = gpg_error_t; #endif static assuan_error_t my_assuan_transact(const AssuanClientContext &ctx, const char *command, assuan_error_t (*data_cb)(void *, const void *, size_t) = nullptr, void *data_cb_arg = nullptr, assuan_error_t (*inquire_cb)(void *, const char *) = nullptr, void *inquire_cb_arg = nullptr, assuan_error_t (*status_cb)(void *, const char *) = nullptr, void *status_cb_arg = nullptr) { return assuan_transact(ctx.get(), command, data_cb, data_cb_arg, inquire_cb, inquire_cb_arg, status_cb, status_cb_arg); } static QString to_error_string(int err) { char buffer[1024]; gpg_strerror_r(static_cast(err), buffer, sizeof buffer); buffer[sizeof buffer - 1] = '\0'; return QString::fromLocal8Bit(buffer); } static QString gnupg_home_directory() { static const char *hDir = GpgME::dirInfo("homedir"); return QFile::decodeName(hDir); } static QString get_default_socket_name() { const QString socketPath{QString::fromUtf8(GpgME::dirInfo("uiserver-socket"))}; if (!socketPath.isEmpty()) { // Note: The socket directory exists after GpgME::dirInfo() has been called. return socketPath; } const QString homeDir = gnupg_home_directory(); if (homeDir.isEmpty()) { return QString(); } return QDir(homeDir).absoluteFilePath(QStringLiteral("S.uiserver")); } static QString default_socket_name() { static QString name = get_default_socket_name(); return name; } static QString uiserver_executable() { return QStringLiteral("kleopatra"); } static QString start_uiserver() { const QString executable = uiserver_executable(); const QString exec = QStandardPaths::findExecutable(executable); if (exec.isEmpty()) { qCWarning(LIBKLEOPATRACLIENTCORE_LOG) << "Could not find " << executable << " in PATH."; return i18n("Failed to start uiserver %1", executable); } else { QProcess::startDetached(executable, QStringList() << QStringLiteral("--daemon")); } return QString(); } static assuan_error_t getinfo_pid_cb(void *opaque, const void *buffer, size_t length) { qint64 &pid = *static_cast(opaque); pid = QByteArray(static_cast(buffer), length).toLongLong(); return 0; } static assuan_error_t command_data_cb(void *opaque, const void *buffer, size_t length) { QByteArray &ba = *static_cast(opaque); ba.append(QByteArray(static_cast(buffer), length)); return 0; } namespace { struct inquire_data { const std::map *map; const AssuanClientContext *ctx; }; } static assuan_error_t command_inquire_cb(void *opaque, const char *what) { if (!opaque) { return 0; } const inquire_data &id = *static_cast(opaque); const auto it = id.map->find(what); if (it != id.map->end()) { const QByteArray &v = it->second; assuan_send_data(id.ctx->get(), v.data(), v.size()); } return 0; } static inline std::ostream &operator<<(std::ostream &s, const QByteArray &ba) { return s << std::string(ba.data(), ba.size()); } static assuan_error_t send_option(const AssuanClientContext &ctx, const char *name, const QVariant &value) { std::stringstream ss; ss << "OPTION " << name; if (value.isValid()) { ss << '=' << value.toString().toUtf8(); } return my_assuan_transact(ctx, ss.str().c_str()); } static assuan_error_t send_file(const AssuanClientContext &ctx, const QString &file) { std::stringstream ss; ss << "FILE " << hexencode(QFile::encodeName(file)); return my_assuan_transact(ctx, ss.str().c_str()); } static assuan_error_t send_recipient(const AssuanClientContext &ctx, const QString &recipient, bool info) { std::stringstream ss; ss << "RECIPIENT "; if (info) { ss << "--info "; } ss << "--" << hexencode(recipient.toUtf8()); return my_assuan_transact(ctx, ss.str().c_str()); } static assuan_error_t send_sender(const AssuanClientContext &ctx, const QString &sender, bool info) { std::stringstream ss; ss << "SENDER "; if (info) { ss << "--info "; } ss << "--" << hexencode(sender.toUtf8()); return my_assuan_transact(ctx, ss.str().c_str()); } void Command::Private::run() { // Take a snapshot of the input data, and clear the output data: Inputs in; Outputs out; { const QMutexLocker locker(&mutex); in = inputs; outputs = out; } out.canceled = false; if (out.serverLocation.isEmpty()) { out.serverLocation = default_socket_name(); } #ifndef HAVE_ASSUAN2 assuan_context_t naked_ctx = 0; #endif AssuanClientContext ctx; assuan_error_t err = 0; inquire_data id = { &in.inquireData, &ctx }; const QString socketName = out.serverLocation; if (socketName.isEmpty()) { out.errorString = i18n("Invalid socket name!"); goto leave; } #ifndef HAVE_ASSUAN2 err = assuan_socket_connect(&naked_ctx, socketName.toUtf8().constData(), -1); #else { assuan_context_t naked_ctx = nullptr; err = assuan_new(&naked_ctx); if (err) { out.errorString = i18n("Could not allocate resources to connect to Kleopatra UI server at %1: %2" , socketName, to_error_string(err)); goto leave; } ctx.reset(naked_ctx); } err = assuan_socket_connect(ctx.get(), socketName.toUtf8().constData(), -1, 0); #endif if (err) { qDebug("UI server not running, starting it"); const QString errorString = start_uiserver(); if (!errorString.isEmpty()) { out.errorString = errorString; goto leave; } // give it a bit of time to start up and try a couple of times for (int i = 0; err && i < 20; ++i) { msleep(500); err = assuan_socket_connect(ctx.get(), socketName.toUtf8().constData(), -1, 0); } } if (err) { out.errorString = i18n("Could not connect to Kleopatra UI server at %1: %2", socketName, to_error_string(err)); goto leave; } #ifndef HAVE_ASSUAN2 ctx.reset(naked_ctx); naked_ctx = 0; #endif out.serverPid = -1; err = my_assuan_transact(ctx, "GETINFO pid", &getinfo_pid_cb, &out.serverPid); if (err || out.serverPid <= 0) { out.errorString = i18n("Could not get the process-id of the Kleopatra UI server at %1: %2", socketName, to_error_string(err)); goto leave; } qCDebug(LIBKLEOPATRACLIENTCORE_LOG) << "Server PID =" << out.serverPid; #if defined(Q_OS_WIN) if (!AllowSetForegroundWindow((pid_t)out.serverPid)) { qCDebug(LIBKLEOPATRACLIENTCORE_LOG) << "AllowSetForegroundWindow(" << out.serverPid << ") failed: " << GetLastError(); } #endif if (in.command.isEmpty()) { goto leave; } if (in.parentWId) { #if defined(Q_OS_WIN32) err = send_option(ctx, "window-id", QString::asprintf("%lx", reinterpret_cast(in.parentWId))); #else err = send_option(ctx, "window-id", QString::asprintf("%lx", static_cast(in.parentWId))); #endif if (err) { qDebug("sending option window-id failed - ignoring"); } } for (auto it = in.options.begin(), end = in.options.end(); it != end; ++it) if ((err = send_option(ctx, it->first.c_str(), it->second.hasValue ? it->second.value.toString() : QVariant()))) { if (it->second.isCritical) { out.errorString = i18n("Failed to send critical option %1: %2", QString::fromLatin1(it->first.c_str()), to_error_string(err)); goto leave; } else { qCDebug(LIBKLEOPATRACLIENTCORE_LOG) << "Failed to send non-critical option" << it->first.c_str() << ":" << to_error_string(err); } } - Q_FOREACH (const QString &filePath, in.filePaths) + for (const QString &filePath : std::as_const(in.filePaths)) { if ((err = send_file(ctx, filePath))) { out.errorString = i18n("Failed to send file path %1: %2", filePath, to_error_string(err)); goto leave; } + } - Q_FOREACH (const QString &sender, in.senders) + for (const QString &sender : std::as_const(in.senders)) { if ((err = send_sender(ctx, sender, in.areSendersInformative))) { out.errorString = i18n("Failed to send sender %1: %2", sender, to_error_string(err)); goto leave; } + } - Q_FOREACH (const QString &recipient, in.recipients) + for (const QString &recipient : std::as_const(in.recipients)) { if ((err = send_recipient(ctx, recipient, in.areRecipientsInformative))) { out.errorString = i18n("Failed to send recipient %1: %2", recipient, to_error_string(err)); goto leave; } + } #if 0 setup I / O; #endif err = my_assuan_transact(ctx, in.command.constData(), &command_data_cb, &out.data, &command_inquire_cb, &id); if (err) { if (gpg_err_code(err) == GPG_ERR_CANCELED) { out.canceled = true; } else { out.errorString = i18n("Command (%1) failed: %2", QString::fromLatin1(in.command.constData()), to_error_string(err)); } goto leave; } leave: const QMutexLocker locker(&mutex); // copy outputs to where Command can see them: outputs = out; } diff --git a/src/uiserver/decryptverifycommandemailbase.cpp b/src/uiserver/decryptverifycommandemailbase.cpp index f1a85d57f..5e4fc683b 100644 --- a/src/uiserver/decryptverifycommandemailbase.cpp +++ b/src/uiserver/decryptverifycommandemailbase.cpp @@ -1,212 +1,214 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/decryptverifycommandemailbase.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "decryptverifycommandemailbase.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Formatting; using namespace GpgME; class DecryptVerifyCommandEMailBase::Private : public QObject { Q_OBJECT friend class ::Kleo::DecryptVerifyCommandEMailBase; DecryptVerifyCommandEMailBase *const q; public: explicit Private(DecryptVerifyCommandEMailBase *qq) : QObject(), q(qq), controller() { } ~Private() override { } void checkForErrors() const; public Q_SLOTS: void slotProgress(const QString &what, int current, int total); void verificationResult(const GpgME::VerificationResult &); void slotDone() { q->done(); } void slotError(int err, const QString &details) { q->done(err, details); } public: private: std::shared_ptr controller; }; DecryptVerifyCommandEMailBase::DecryptVerifyCommandEMailBase() : AssuanCommandMixin(), d(new Private(this)) { } DecryptVerifyCommandEMailBase::~DecryptVerifyCommandEMailBase() {} int DecryptVerifyCommandEMailBase::doStart() { d->checkForErrors(); d->controller.reset(new DecryptVerifyEMailController(shared_from_this())); const QString st = sessionTitle(); - if (!st.isEmpty()) - Q_FOREACH (const std::shared_ptr &i, inputs()) { + if (!st.isEmpty()) { + const std::vector> allInputs = inputs(); + for (const std::shared_ptr &i : allInputs) { i->setLabel(st); } + } d->controller->setSessionId(sessionId()); d->controller->setOperation(operation()); d->controller->setVerificationMode(messages().empty() ? Opaque : Detached); d->controller->setInputs(inputs()); d->controller->setSignedData(messages()); d->controller->setOutputs(outputs()); d->controller->setWizardShown(!hasOption("silent")); d->controller->setProtocol(checkProtocol(mode())); if (informativeSenders()) { d->controller->setInformativeSenders(senders()); } QObject::connect(d->controller.get(), SIGNAL(done()), d.get(), SLOT(slotDone()), Qt::QueuedConnection); QObject::connect(d->controller.get(), SIGNAL(error(int,QString)), d.get(), SLOT(slotError(int,QString)), Qt::QueuedConnection); QObject::connect(d->controller.get(), &DecryptVerifyEMailController::verificationResult, d.get(), &Private::verificationResult, Qt::QueuedConnection); d->controller->start(); return 0; } void DecryptVerifyCommandEMailBase::Private::checkForErrors() const { if (!q->senders().empty() && !q->informativeSenders()) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("Cannot use non-info SENDER")); if (!q->recipients().empty() && !q->informativeRecipients()) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("Cannot use non-info RECIPIENT")); // ### use informative recipients and senders const unsigned int numInputs = q->inputs().size(); const unsigned int numMessages = q->messages().size(); const unsigned int numOutputs = q->outputs().size(); const unsigned int numInformativeSenders = q->informativeSenders() ? q->senders().size() : 0; const DecryptVerifyOperation op = q->operation(); const GpgME::Protocol proto = q->checkProtocol(q->mode()); const unsigned int numFiles = q->numFiles(); if (numFiles) { throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("FILES present")); } if (!numInputs) throw Kleo::Exception(q->makeError(GPG_ERR_ASS_NO_INPUT), i18n("At least one INPUT needs to be provided")); if (numInformativeSenders != 0) if (numInformativeSenders != numInputs) throw Kleo::Exception(q->makeError(GPG_ERR_ASS_NO_INPUT), //TODO use better error code if possible i18n("INPUT/SENDER --info count mismatch")); if (numMessages) { if (numMessages != numInputs) throw Kleo::Exception(q->makeError(GPG_ERR_ASS_NO_INPUT), //TODO use better error code if possible i18n("INPUT/MESSAGE count mismatch")); else if (op != Verify) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("MESSAGE can only be given for detached signature verification")); } if (numOutputs) { if (numOutputs != numInputs) throw Kleo::Exception(q->makeError(GPG_ERR_ASS_NO_OUTPUT), //TODO use better error code if possible i18n("INPUT/OUTPUT count mismatch")); else if (numMessages) throw Kleo::Exception(q->makeError(GPG_ERR_CONFLICT), i18n("Cannot use OUTPUT and MESSAGE simultaneously")); } kleo_assert(proto != UnknownProtocol); const auto backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); if (!backend) throw Kleo::Exception(q->makeError(GPG_ERR_UNSUPPORTED_PROTOCOL), proto == OpenPGP ? i18n("No backend support for OpenPGP") : proto == CMS ? i18n("No backend support for S/MIME") : QString()); } void DecryptVerifyCommandEMailBase::doCanceled() { if (d->controller) { d->controller->cancel(); } } void DecryptVerifyCommandEMailBase::Private::slotProgress(const QString &what, int current, int total) { Q_UNUSED(what) Q_UNUSED(current) Q_UNUSED(total) // ### FIXME report progress, via sendStatus() } void DecryptVerifyCommandEMailBase::Private::verificationResult(const VerificationResult &vResult) { try { const std::vector sigs = vResult.signatures(); for (const Signature &sig : sigs) { const QString s = signatureToString(sig, sig.key(true, true)); const char *color = summaryToString(sig.summary()); q->sendStatusEncoded("SIGSTATUS", color + (' ' + hexencode(s.toUtf8().constData()))); } } catch (...) {} } #include "decryptverifycommandemailbase.moc" diff --git a/src/uiserver/encryptcommand.cpp b/src/uiserver/encryptcommand.cpp index 22540e733..8e65d221e 100644 --- a/src/uiserver/encryptcommand.cpp +++ b/src/uiserver/encryptcommand.cpp @@ -1,208 +1,210 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/encryptcommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "encryptcommand.h" #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; class EncryptCommand::Private : public QObject { Q_OBJECT private: friend class ::Kleo::EncryptCommand; EncryptCommand *const q; public: explicit Private(EncryptCommand *qq) : q(qq), controller() { } private: void checkForErrors() const; private Q_SLOTS: void slotDone(); void slotError(int, const QString &); void slotRecipientsResolved(); private: std::shared_ptr controller; }; EncryptCommand::EncryptCommand() : AssuanCommandMixin(), d(new Private(this)) { } EncryptCommand::~EncryptCommand() {} void EncryptCommand::Private::checkForErrors() const { if (q->numFiles()) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("ENCRYPT is an email mode command, connection seems to be in filemanager mode")); if (!q->senders().empty() && !q->informativeSenders()) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("SENDER may not be given prior to ENCRYPT, except with --info")); if (q->inputs().empty()) throw Exception(makeError(GPG_ERR_ASS_NO_INPUT), i18n("At least one INPUT must be present")); if (q->outputs().empty()) throw Exception(makeError(GPG_ERR_ASS_NO_OUTPUT), i18n("At least one OUTPUT must be present")); if (q->outputs().size() != q->inputs().size()) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("INPUT/OUTPUT count mismatch")); if (!q->messages().empty()) throw Exception(makeError(GPG_ERR_INV_VALUE), i18n("MESSAGE command is not allowed before ENCRYPT")); const auto m = q->mementoContent< std::shared_ptr >(NewSignEncryptEMailController::mementoName()); kleo_assert(m); if (m && m->isEncrypting()) { if (m->protocol() != q->checkProtocol(EMail)) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("Protocol given conflicts with protocol determined by PREP_ENCRYPT")); if (!q->recipients().empty()) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("New recipients added after PREP_ENCRYPT command")); if (!q->senders().empty()) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("New senders added after PREP_ENCRYPT command")); } else { if (q->recipients().empty() || q->informativeRecipients()) throw Exception(makeError(GPG_ERR_MISSING_VALUE), i18n("No recipients given, or only with --info")); } } static void connectController(const QObject *controller, const QObject *d) { QObject::connect(controller, SIGNAL(certificatesResolved()), d, SLOT(slotRecipientsResolved())); QObject::connect(controller, SIGNAL(done()), d, SLOT(slotDone())); QObject::connect(controller, SIGNAL(error(int,QString)), d, SLOT(slotError(int,QString))); } int EncryptCommand::doStart() { d->checkForErrors(); const auto seec = mementoContent< std::shared_ptr >(NewSignEncryptEMailController::mementoName()); if (seec && seec->isEncrypting()) { // reuse the controller from a previous PREP_ENCRYPT, if available: d->controller = seec; connectController(seec.get(), d.get()); removeMemento(NewSignEncryptEMailController::mementoName()); d->controller->setExecutionContext(shared_from_this()); if (seec->areCertificatesResolved()) { QTimer::singleShot(0, d.get(), &Private::slotRecipientsResolved); } else { kleo_assert(seec->isResolvingInProgress()); } } else { // use a new controller d->controller.reset(new NewSignEncryptEMailController(shared_from_this())); const QString session = sessionTitle(); if (!session.isEmpty()) { d->controller->setSubject(session); } d->controller->setEncrypting(true); d->controller->setSigning(false); d->controller->setProtocol(checkProtocol(EMail)); connectController(d->controller.get(), d.get()); d->controller->startResolveCertificates(recipients(), senders()); } return 0; } void EncryptCommand::Private::slotRecipientsResolved() { //hold local std::shared_ptr to member as q->done() deletes *this const std::shared_ptr cont(controller); try { const QString sessionTitle = q->sessionTitle(); - if (!sessionTitle.isEmpty()) - Q_FOREACH (const std::shared_ptr &i, q->inputs()) { + if (!sessionTitle.isEmpty()) { + const std::vector> allInputs = q->inputs(); + for (const std::shared_ptr &i : allInputs) { i->setLabel(sessionTitle); } + } cont->startEncryption(q->inputs(), q->outputs()); return; } catch (const Exception &e) { q->done(e.error(), e.message()); } catch (const std::exception &e) { q->done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unexpected exception in EncryptCommand::Private::slotRecipientsResolved: %1", QString::fromLocal8Bit(e.what()))); } catch (...) { q->done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in EncryptCommand::Private::slotRecipientsResolved")); } cont->cancel(); } void EncryptCommand::Private::slotDone() { q->done(); } void EncryptCommand::Private::slotError(int err, const QString &details) { q->done(err, details); } void EncryptCommand::doCanceled() { if (d->controller) { d->controller->cancel(); } } #include "encryptcommand.moc" diff --git a/src/uiserver/signcommand.cpp b/src/uiserver/signcommand.cpp index a2ceeaa34..71d908b84 100644 --- a/src/uiserver/signcommand.cpp +++ b/src/uiserver/signcommand.cpp @@ -1,228 +1,230 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/signcommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signcommand.h" #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; class SignCommand::Private : public QObject { Q_OBJECT private: friend class ::Kleo::SignCommand; SignCommand *const q; public: explicit Private(SignCommand *qq) : q(qq), controller() { } private: void checkForErrors() const; private Q_SLOTS: void slotSignersResolved(); void slotMicAlgDetermined(const QString &); void slotDone(); void slotError(int, const QString &); private: std::shared_ptr controller; }; SignCommand::SignCommand() : AssuanCommandMixin(), d(new Private(this)) { } SignCommand::~SignCommand() {} void SignCommand::Private::checkForErrors() const { if (q->numFiles()) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("SIGN is an email mode command, connection seems to be in filemanager mode")); if (!q->recipients().empty() && !q->informativeRecipients()) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("RECIPIENT may not be given prior to SIGN, except with --info")); if (q->inputs().empty()) throw Exception(makeError(GPG_ERR_ASS_NO_INPUT), i18n("At least one INPUT must be present")); if (q->outputs().size() != q->inputs().size()) throw Exception(makeError(GPG_ERR_ASS_NO_INPUT), i18n("INPUT/OUTPUT count mismatch")); if (!q->messages().empty()) throw Exception(makeError(GPG_ERR_INV_VALUE), i18n("MESSAGE command is not allowed before SIGN")); const auto m = q->mementoContent< std::shared_ptr >(NewSignEncryptEMailController::mementoName()); if (m && m->isSigning()) { if (m->protocol() != q->checkProtocol(EMail)) throw Exception(makeError(GPG_ERR_CONFLICT), i18n("Protocol given conflicts with protocol determined by PREP_ENCRYPT in this session")); // ### check that any SENDER here is the same as the one for PREP_ENCRYPT // ### ditto RECIPIENT } else { // ### support the stupid "default signer" semantics of GpgOL // ### where SENDER is missing if (false) if (q->senders().empty() || q->informativeSenders()) throw Exception(makeError(GPG_ERR_MISSING_VALUE), i18n("No senders given, or only with --info")); } } static void connectController(const QObject *controller, const QObject *d) { QObject::connect(controller, SIGNAL(certificatesResolved()), d, SLOT(slotSignersResolved())); QObject::connect(controller, SIGNAL(reportMicAlg(QString)), d, SLOT(slotMicAlgDetermined(QString))); QObject::connect(controller, SIGNAL(done()), d, SLOT(slotDone())); QObject::connect(controller, SIGNAL(error(int,QString)), d, SLOT(slotError(int,QString))); } int SignCommand::doStart() { d->checkForErrors(); const auto seec = mementoContent< std::shared_ptr >(NewSignEncryptEMailController::mementoName()); if (seec && seec->isSigning()) { // reuse the controller from a previous PREP_ENCRYPT --expect-sign, if available: d->controller = seec; connectController(seec.get(), d.get()); if (!seec->isEncrypting()) { removeMemento(NewSignEncryptEMailController::mementoName()); } seec->setExecutionContext(shared_from_this()); if (seec->areCertificatesResolved()) { QTimer::singleShot(0, d.get(), &Private::slotSignersResolved); } else { kleo_assert(seec->isResolvingInProgress()); } } else { // use a new controller d->controller.reset(new NewSignEncryptEMailController(shared_from_this())); const QString session = sessionTitle(); if (!session.isEmpty()) { d->controller->setSubject(session); } d->controller->setSigning(true); d->controller->setEncrypting(false); d->controller->setProtocol(checkProtocol(EMail, AssuanCommand::AllowProtocolMissing)); connectController(d->controller.get(), d.get()); d->controller->startResolveCertificates(recipients(), senders()); } return 0; } void SignCommand::Private::slotSignersResolved() { //hold local std::shared_ptr to member as q->done() deletes *this const std::shared_ptr cont(controller); try { const QString sessionTitle = q->sessionTitle(); - if (!sessionTitle.isEmpty()) - Q_FOREACH (const std::shared_ptr &i, q->inputs()) { + if (!sessionTitle.isEmpty()) { + const std::vector> allInputs = q->inputs(); + for (const std::shared_ptr &i : allInputs) { i->setLabel(sessionTitle); } + } cont->setDetachedSignature(q->hasOption("detached")); cont->startSigning(q->inputs(), q->outputs()); return; } catch (const Exception &e) { q->done(e.error(), e.message()); } catch (const std::exception &e) { q->done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unexpected exception in SignCommand::Private::slotRecipientsResolved: %1", QString::fromLocal8Bit(e.what()))); } catch (...) { q->done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignCommand::Private::slotRecipientsResolved")); } cont->cancel(); } void SignCommand::Private::slotMicAlgDetermined(const QString &micalg) { //hold local std::shared_ptr to member as q->done() deletes *this const std::shared_ptr cont(controller); try { q->sendStatus("MICALG", micalg); return; } catch (const Exception &e) { q->done(e.error(), e.message()); } catch (const std::exception &e) { q->done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unexpected exception in SignCommand::Private::slotMicAlgDetermined: %1", QString::fromLocal8Bit(e.what()))); } catch (...) { q->done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignCommand::Private::slotMicAlgDetermined")); } cont->cancel(); } void SignCommand::Private::slotDone() { q->done(); } void SignCommand::Private::slotError(int err, const QString &details) { q->done(err, details); } void SignCommand::doCanceled() { if (d->controller) { d->controller->cancel(); } } #include "signcommand.moc"