diff --git a/src/commands/certifycertificatecommand.cpp b/src/commands/certifycertificatecommand.cpp index 851069fab..e31141ffa 100644 --- a/src/commands/certifycertificatecommand.cpp +++ b/src/commands/certifycertificatecommand.cpp @@ -1,313 +1,312 @@ /* -*- 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 "newopenpgpcertificatecommand.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; } 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(); + auto cmd = new NewOpenPGPCertificateCommand; cmd->setParentWidget(d->parentWidgetOrView()); - cmd->setProtocol(GpgME::OpenPGP); connect(cmd, &Command::finished, &loop, &QEventLoop::quit); - QMetaObject::invokeMethod(cmd, &Commands::NewCertificateCommand::start, Qt::QueuedConnection); + QMetaObject::invokeMethod(cmd, &NewOpenPGPCertificateCommand::start, Qt::QueuedConnection); loop.exec(); } else { Q_EMIT(canceled()); d->finished(); return; } // Check again for secret keys if (!findAnyGoodKey()) { qCDebug(KLEOPATRA_LOG) << "Sec Keys still empty after keygen."; Q_EMIT(canceled()); 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/commands/newopenpgpcertificatecommand.cpp b/src/commands/newopenpgpcertificatecommand.cpp index 9ec42ebff..9cada2478 100644 --- a/src/commands/newopenpgpcertificatecommand.cpp +++ b/src/commands/newopenpgpcertificatecommand.cpp @@ -1,301 +1,306 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/newopenpgpcertificatecommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "newopenpgpcertificatecommand.h" #include "command_p.h" #include "commands/detailscommand.h" #include "dialogs/newopenpgpcertificatedetailsdialog.h" #include "dialogs/newopenpgpcertificateresultdialog.h" #include "utils/emptypassphraseprovider.h" #include "utils/keyparameters.h" #include "utils/userinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace GpgME; class NewOpenPGPCertificateCommand::Private : public Command::Private { friend class ::Kleo::NewOpenPGPCertificateCommand; NewOpenPGPCertificateCommand *q_func() const { return static_cast(q); } public: explicit Private(NewOpenPGPCertificateCommand *qq, KeyListController *c) : Command::Private{qq, c} { } void getCertificateDetails(); void createCertificate(); void showResult(const KeyGenerationResult &result); void showResultDialog(const KeyGenerationResult &result, const Key &key); void showErrorDialog(const KeyGenerationResult &result); private: KeyParameters keyParameters; bool protectKeyWithPassword = false; EmptyPassphraseProvider emptyPassphraseProvider; QPointer detailsDialog; QPointer job; QPointer progressDialog; QPointer resultDialog; }; NewOpenPGPCertificateCommand::Private *NewOpenPGPCertificateCommand::d_func() { return static_cast(d.get()); } const NewOpenPGPCertificateCommand::Private *NewOpenPGPCertificateCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() void NewOpenPGPCertificateCommand::Private::getCertificateDetails() { detailsDialog = new NewOpenPGPCertificateDetailsDialog; detailsDialog->setAttribute(Qt::WA_DeleteOnClose); applyWindowID(detailsDialog); if (keyParameters.protocol() == KeyParameters::NoProtocol) { const auto settings = Kleo::Settings{}; const KConfigGroup config{KSharedConfig::openConfig(), "CertificateCreationWizard"}; // prefer the last used name and email address over the values retrieved from the system detailsDialog->setName(config.readEntry("NAME", QString{})); if (detailsDialog->name().isEmpty() && settings.prefillName()) { detailsDialog->setName(userFullName()); } detailsDialog->setEmail(config.readEntry("EMAIL", QString{})); if (detailsDialog->email().isEmpty() && settings.prefillEmail()) { detailsDialog->setEmail(userEmailAddress()); } } else { detailsDialog->setKeyParameters(keyParameters); detailsDialog->setProtectKeyWithPassword(protectKeyWithPassword); } connect(detailsDialog, &QDialog::accepted, q, [this]() { keyParameters = detailsDialog->keyParameters(); protectKeyWithPassword = detailsDialog->protectKeyWithPassword(); QMetaObject::invokeMethod( q, [this] { createCertificate(); }, Qt::QueuedConnection); }); connect(detailsDialog, &QDialog::rejected, q, [this]() { canceled(); }); detailsDialog->show(); } void NewOpenPGPCertificateCommand::Private::createCertificate() { Q_ASSERT(keyParameters.protocol() == KeyParameters::OpenPGP); auto keyGenJob = QGpgME::openpgp()->keyGenerationJob(); if (!keyGenJob) { finished(); return; } if (!protectKeyWithPassword) { auto ctx = QGpgME::Job::context(keyGenJob); ctx->setPassphraseProvider(&emptyPassphraseProvider); ctx->setPinentryMode(Context::PinentryLoopback); } connect(keyGenJob, &QGpgME::KeyGenerationJob::result, q, [this](const KeyGenerationResult &result) { QMetaObject::invokeMethod( q, [this, result] { showResult(result); }, Qt::QueuedConnection); }); if (const Error err = keyGenJob->start(keyParameters.toString())) { error(i18n("Could not start key pair creation: %1", QString::fromUtf8(err.asString()))); finished(); return; } else { job = keyGenJob; } progressDialog = new QProgressDialog; progressDialog->setAttribute(Qt::WA_DeleteOnClose); applyWindowID(progressDialog); progressDialog->setModal(true); progressDialog->setWindowTitle(i18nc("@title", "Creating Key Pair...")); progressDialog->setLabelText(i18n("The process of creating a key requires large amounts of random numbers. This may require several minutes...")); progressDialog->setRange(0, 0); connect(progressDialog, &QProgressDialog::canceled, job, &QGpgME::Job::slotCancel); connect(job, &QGpgME::Job::done, q, [this]() { if (progressDialog) { progressDialog->accept(); } }); progressDialog->show(); } void NewOpenPGPCertificateCommand::Private::showResult(const KeyGenerationResult &result) { if (result.error().isCanceled()) { finished(); return; } // Ensure that we have the key in the cache Key key; if (!result.error().code() && result.fingerprint()) { std::unique_ptr ctx{Context::createForProtocol(OpenPGP)}; if (ctx) { Error err; key = ctx->key(result.fingerprint(), err, /*secret=*/true); if (!key.isNull()) { KeyCache::mutableInstance()->insert(key); } } } const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); if (config.readEntry("SkipResultPage", false)) { if (!key.isNull()) { auto cmd = new Commands::DetailsCommand(key, view(), controller()); cmd->setParentWidget(parentWidgetOrView()); cmd->setParentWId(parentWId()); cmd->start(); } finished(); return; } if (!key.isNull()) { showResultDialog(result, key); } else { showErrorDialog(result); } } void NewOpenPGPCertificateCommand::Private::showResultDialog(const KeyGenerationResult &result, const Key &key) { resultDialog = new NewOpenPGPCertificateResultDialog{result, key}; resultDialog->setAttribute(Qt::WA_DeleteOnClose); applyWindowID(resultDialog); connect(resultDialog, &NewOpenPGPCertificateResultDialog::retry, q, [this]() { QMetaObject::invokeMethod( q, [this]() { getCertificateDetails(); }, Qt::QueuedConnection); }); connect(resultDialog, &QDialog::accepted, q, [this]() { finished(); }); connect(resultDialog, &QDialog::rejected, q, [this]() { finished(); }); resultDialog->show(); } void NewOpenPGPCertificateCommand::Private::showErrorDialog(const KeyGenerationResult &result) { QString text; if (result.error() || !result.fingerprint()) { text = xi18n( "The creation of a new OpenPGP certificate failed." "Error: %1", QString::fromLocal8Bit(result.error().asString())); } else { // no error and we have a fingerprint, but there was no corresponding key in the key ring text = xi18n( "A new OpenPGP certificate was created successfully, but it has not been found in the key ring." "Fingerprint of the new certificate:%1", Formatting::prettyID(result.fingerprint())); } auto dialog = new QDialog; applyWindowID(dialog); dialog->setWindowTitle(i18nc("@title:window", "Error")); auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Retry | QDialogButtonBox::Ok, dialog}; const auto buttonCode = KMessageBox::createKMessageBox(dialog, buttonBox, QMessageBox::Critical, text, {}, {}, nullptr, {}); if (buttonCode == QDialogButtonBox::Retry) { QMetaObject::invokeMethod( q, [this]() { getCertificateDetails(); }, Qt::QueuedConnection); } else { finished(); } } +NewOpenPGPCertificateCommand::NewOpenPGPCertificateCommand() + : NewOpenPGPCertificateCommand(nullptr, nullptr) +{ +} + NewOpenPGPCertificateCommand::NewOpenPGPCertificateCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { } NewOpenPGPCertificateCommand::~NewOpenPGPCertificateCommand() = default; void NewOpenPGPCertificateCommand::doStart() { d->getCertificateDetails(); } void NewOpenPGPCertificateCommand::doCancel() { if (d->detailsDialog) { d->detailsDialog->close(); } if (d->resultDialog) { d->resultDialog->close(); } if (d->job) { d->job->slotCancel(); } } #undef d #undef q #include "moc_newopenpgpcertificatecommand.cpp" diff --git a/src/commands/newopenpgpcertificatecommand.h b/src/commands/newopenpgpcertificatecommand.h index 529adb612..a983417cf 100644 --- a/src/commands/newopenpgpcertificatecommand.h +++ b/src/commands/newopenpgpcertificatecommand.h @@ -1,36 +1,37 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/newopenpgpcertificatecommand.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "command.h" namespace Kleo { class NewOpenPGPCertificateCommand : public Command { Q_OBJECT public: + NewOpenPGPCertificateCommand(); NewOpenPGPCertificateCommand(QAbstractItemView *view, KeyListController *parent); ~NewOpenPGPCertificateCommand() override; private: void doStart() override; void doCancel() override; private: class Private; inline Private *d_func(); inline const Private *d_func() const; }; } diff --git a/src/view/welcomewidget.cpp b/src/view/welcomewidget.cpp index 9e3295882..f93bec6ea 100644 --- a/src/view/welcomewidget.cpp +++ b/src/view/welcomewidget.cpp @@ -1,158 +1,157 @@ /* view/welcomewidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "welcomewidget.h" #include "htmllabel.h" #include #include #include #include #include #include #include #include "commands/importcertificatefromfilecommand.h" -#include "commands/newcertificatecommand.h" +#include "commands/newopenpgpcertificatecommand.h" #include #include #include static const QString templ = QStringLiteral( "

%1

" // Welcome "

%2

%3

" // Intro + Explanation "
  • %4
  • %5
" // "

%6

" // More info ""); using namespace Kleo; class WelcomeWidget::Private { public: Private(WelcomeWidget *qq): q(qq) { auto vLay = new QVBoxLayout(q); auto hLay = new QHBoxLayout; const QString welcome = i18nc("%1 is version", "Welcome to Kleopatra %1", #ifdef Q_OS_WIN Kleo::gpg4winVersion()); #else QStringLiteral(KLEOPATRA_VERSION_STRING)); #endif const QString introduction = i18n("Kleopatra is a front-end for the crypto software GnuPG."); const QString keyExplanation = i18n("For most actions you need either a public key (certificate) or your own private key."); const QString privateKeyExplanation = i18n("The private key is needed to decrypt or sign."); const QString publicKeyExplanation = i18n("The public key can be used by others to verify your identity or encrypt to you."); const QString wikiUrl = i18nc("More info about public key cryptography, please link to your local version of Wikipedia", "https://en.wikipedia.org/wiki/Public-key_cryptography"); const QString learnMore = i18nc("%1 is link a wiki article", "You can learn more about this on Wikipedia.", wikiUrl); const auto labelText = templ.arg(welcome).arg(introduction).arg(keyExplanation).arg(privateKeyExplanation).arg(publicKeyExplanation).arg(learnMore); mLabel = new HtmlLabel{labelText, q}; mLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); mLabel->setOpenExternalLinks(true); auto genKeyAction = new QAction(q); genKeyAction->setText(i18n("New Key Pair...")); genKeyAction->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-add"))); auto importAction = new QAction(q); importAction->setText(i18n("Import...")); importAction->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-import"))); connect(importAction, &QAction::triggered, q, [this] () { import(); }); connect(genKeyAction, &QAction::triggered, q, [this] () { generate(); }); mGenerateBtn = new QToolButton{q}; mGenerateBtn->setDefaultAction(genKeyAction); mGenerateBtn->setIconSize(QSize(64, 64)); mGenerateBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); mGenerateBtn->setToolTip(i18n("Create a new OpenPGP key pair") + QStringLiteral("
") + i18n("To create an S/MIME certificate request use \"New Key Pair\" from the 'File' Menu instead")); KConfigGroup restrictions(KSharedConfig::openConfig(), "KDE Action Restrictions"); mGenerateBtn->setEnabled(restrictions.readEntry("action/file_new_certificate", true)); mImportBtn = new QToolButton{q}; mImportBtn->setDefaultAction(importAction); mImportBtn->setIconSize(QSize(64, 64)); mImportBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); mImportBtn->setToolTip(i18n("Import from a file.") + QStringLiteral("
") + i18n("To import from a public keyserver use \"Lookup on Server\" instead.")); mImportBtn->setEnabled(restrictions.readEntry("action/file_import_certificate", true)); auto btnLayout = new QHBoxLayout; btnLayout->addStretch(-1); btnLayout->addWidget(mGenerateBtn); btnLayout->addWidget(mImportBtn); btnLayout->addStretch(-1); vLay->addStretch(-1); vLay->addLayout(hLay); vLay->addLayout(btnLayout); vLay->addStretch(-1); hLay->addStretch(-1); hLay->addWidget(mLabel); hLay->addStretch(-1); } void import() { mImportBtn->setEnabled(false); auto cmd = new Kleo::ImportCertificateFromFileCommand(); cmd->setParentWidget(q); QObject::connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, q, [this]() { mImportBtn->setEnabled(true); }); cmd->start(); } void generate() { mGenerateBtn->setEnabled(false); - auto cmd = new Commands::NewCertificateCommand(); - cmd->setProtocol(GpgME::OpenPGP); + auto cmd = new NewOpenPGPCertificateCommand; cmd->setParentWidget(q); - QObject::connect(cmd, &Commands::NewCertificateCommand::finished, + QObject::connect(cmd, &NewOpenPGPCertificateCommand::finished, q, [this]() { mGenerateBtn->setEnabled(true); }); cmd->start(); } WelcomeWidget *const q; HtmlLabel *mLabel = nullptr; QToolButton *mGenerateBtn = nullptr; QToolButton *mImportBtn = nullptr; }; WelcomeWidget::WelcomeWidget (QWidget *parent): QWidget(parent), d(new Private(this)) { } void WelcomeWidget::focusFirstChild(Qt::FocusReason reason) { d->mLabel->setFocus(reason); }