File indexing completed on 2024-06-23 05:14:10

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     newcertificatewizard/resultpage.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006     SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik
0007     SPDX-FileContributor: Intevation GmbH
0008     SPDX-FileCopyrightText: 2022 g10 Code GmbH
0009     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0010 
0011     SPDX-License-Identifier: GPL-2.0-or-later
0012 */
0013 
0014 #include <config-kleopatra.h>
0015 
0016 #include "resultpage_p.h"
0017 
0018 #include "commands/exportcertificatecommand.h"
0019 #include "commands/exportopenpgpcertstoservercommand.h"
0020 #include "commands/exportsecretkeycommand.h"
0021 #include "utils/dragqueen.h"
0022 #include "utils/email.h"
0023 #include "utils/filedialog.h"
0024 #include "utils/scrollarea.h"
0025 
0026 #include <Libkleo/KeyCache>
0027 
0028 #include <KConfigGroup>
0029 #include <KLocalizedString>
0030 #include <KMessageBox>
0031 #include <KSharedConfig>
0032 
0033 #include <QGroupBox>
0034 #include <QHBoxLayout>
0035 #include <QLineEdit>
0036 #include <QPushButton>
0037 #include <QTextBrowser>
0038 #include <QVBoxLayout>
0039 
0040 #include <gpgme++/key.h>
0041 
0042 #include "kleopatra_debug.h"
0043 
0044 using namespace Kleo;
0045 using namespace Kleo::Commands;
0046 using namespace Kleo::NewCertificateUi;
0047 using namespace GpgME;
0048 
0049 struct ResultPage::UI {
0050     QTextBrowser *resultTB = nullptr;
0051     QTextBrowser *errorTB = nullptr;
0052     DragQueen *dragQueen = nullptr;
0053     QPushButton *restartWizardPB = nullptr;
0054     QGroupBox *nextStepsGB = nullptr;
0055     QPushButton *saveRequestToFilePB = nullptr;
0056     QPushButton *sendRequestByEMailPB = nullptr;
0057     QPushButton *makeBackupPB = nullptr;
0058     QPushButton *sendCertificateByEMailPB = nullptr;
0059     QPushButton *uploadToKeyserverPB = nullptr;
0060     QPushButton *createRevocationRequestPB = nullptr;
0061     QPushButton *createSigningCertificatePB = nullptr;
0062     QPushButton *createEncryptionCertificatePB = nullptr;
0063 
0064     UI(QWizardPage *parent)
0065     {
0066         auto mainLayout = new QVBoxLayout{parent};
0067         const auto margins = mainLayout->contentsMargins();
0068         mainLayout->setContentsMargins(margins.left(), 0, margins.right(), 0);
0069 
0070         auto scrollArea = new ScrollArea{parent};
0071         scrollArea->setFocusPolicy(Qt::NoFocus);
0072         scrollArea->setFrameStyle(QFrame::NoFrame);
0073         scrollArea->setBackgroundRole(parent->backgroundRole());
0074         scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0075         scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
0076         auto scrollAreaLayout = qobject_cast<QBoxLayout *>(scrollArea->widget()->layout());
0077         scrollAreaLayout->setContentsMargins(0, margins.top(), 0, margins.bottom());
0078 
0079         auto resultGB = new QGroupBox{i18nc("@title:group", "Result"), parent};
0080         auto resultGBLayout = new QHBoxLayout{resultGB};
0081 
0082         resultTB = new QTextBrowser{resultGB};
0083         resultGBLayout->addWidget(resultTB);
0084 
0085         errorTB = new QTextBrowser{resultGB};
0086         resultGBLayout->addWidget(errorTB);
0087 
0088         dragQueen = new Kleo::DragQueen{resultGB};
0089         dragQueen->setToolTip(i18n("Drag this icon to your mail application's composer to attach the request to a mail."));
0090         dragQueen->setAlignment(Qt::AlignCenter);
0091         resultGBLayout->addWidget(dragQueen);
0092 
0093         scrollAreaLayout->addWidget(resultGB);
0094 
0095         restartWizardPB = new QPushButton{i18n("Restart This Wizard (Keeps Your Parameters)"), parent};
0096 
0097         scrollAreaLayout->addWidget(restartWizardPB);
0098 
0099         nextStepsGB = new QGroupBox{i18nc("@title:group", "Next Steps"), parent};
0100         auto nextStepsGBLayout = new QVBoxLayout{nextStepsGB};
0101 
0102         saveRequestToFilePB = new QPushButton{i18n("Save Certificate Request To File..."), nextStepsGB};
0103         nextStepsGBLayout->addWidget(saveRequestToFilePB);
0104 
0105         sendRequestByEMailPB = new QPushButton{i18n("Send Certificate Request By EMail..."), nextStepsGB};
0106         nextStepsGBLayout->addWidget(sendRequestByEMailPB);
0107 
0108         makeBackupPB = new QPushButton{i18n("Make a Backup Of Your Key Pair..."), nextStepsGB};
0109         nextStepsGBLayout->addWidget(makeBackupPB);
0110 
0111         sendCertificateByEMailPB = new QPushButton{i18n("Send Public Key By EMail..."), nextStepsGB};
0112         nextStepsGBLayout->addWidget(sendCertificateByEMailPB);
0113 
0114         uploadToKeyserverPB = new QPushButton{i18n("Upload Public Key To Directory Service..."), nextStepsGB};
0115         nextStepsGBLayout->addWidget(uploadToKeyserverPB);
0116 
0117         createRevocationRequestPB = new QPushButton{i18n("Create Revocation Request..."), nextStepsGB};
0118         nextStepsGBLayout->addWidget(createRevocationRequestPB);
0119 
0120         createSigningCertificatePB = new QPushButton{i18n("Create Signing Certificate With Same Parameters"), nextStepsGB};
0121         nextStepsGBLayout->addWidget(createSigningCertificatePB);
0122 
0123         createEncryptionCertificatePB = new QPushButton{i18n("Create Encryption Certificate With Same Parameters"), nextStepsGB};
0124         nextStepsGBLayout->addWidget(createEncryptionCertificatePB);
0125 
0126         scrollAreaLayout->addWidget(nextStepsGB);
0127 
0128         mainLayout->addWidget(scrollArea);
0129     }
0130 };
0131 
0132 ResultPage::ResultPage(QWidget *p)
0133     : WizardPage{p}
0134     , ui{new UI{this}}
0135     , initialized{false}
0136     , successfullyCreatedSigningCertificate{false}
0137     , successfullyCreatedEncryptionCertificate{false}
0138 {
0139     setObjectName(QString::fromUtf8("Kleo__NewCertificateUi__ResultPage"));
0140 
0141     connect(ui->saveRequestToFilePB, &QPushButton::clicked, this, &ResultPage::slotSaveRequestToFile);
0142     connect(ui->sendRequestByEMailPB, &QPushButton::clicked, this, &ResultPage::slotSendRequestByEMail);
0143     connect(ui->sendCertificateByEMailPB, &QPushButton::clicked, this, &ResultPage::slotSendCertificateByEMail);
0144     connect(ui->uploadToKeyserverPB, &QPushButton::clicked, this, &ResultPage::slotUploadCertificateToDirectoryServer);
0145     connect(ui->makeBackupPB, &QPushButton::clicked, this, &ResultPage::slotBackupCertificate);
0146     connect(ui->createRevocationRequestPB, &QPushButton::clicked, this, &ResultPage::slotCreateRevocationRequest);
0147     connect(ui->createSigningCertificatePB, &QPushButton::clicked, this, &ResultPage::slotCreateSigningCertificate);
0148     connect(ui->createEncryptionCertificatePB, &QPushButton::clicked, this, &ResultPage::slotCreateEncryptionCertificate);
0149 
0150     ui->dragQueen->setPixmap(QIcon::fromTheme(QStringLiteral("kleopatra")).pixmap(64, 64));
0151     registerField(QStringLiteral("error"), ui->errorTB, "plainText");
0152     registerField(QStringLiteral("result"), ui->resultTB, "plainText");
0153     registerField(QStringLiteral("url"), ui->dragQueen, "url");
0154     // hidden field, since QWizard can't deal with non-widget-backed fields...
0155     auto le = new QLineEdit(this);
0156     le->hide();
0157     registerField(QStringLiteral("fingerprint"), le);
0158 }
0159 
0160 ResultPage::~ResultPage() = default;
0161 
0162 void ResultPage::initializePage()
0163 {
0164     const bool error = isError();
0165 
0166     if (error) {
0167         setTitle(i18nc("@title", "Key Creation Failed"));
0168         setSubTitle(i18n("Key pair creation failed. Please find details about the failure below."));
0169     } else {
0170         setTitle(i18nc("@title", "Key Pair Successfully Created"));
0171         setSubTitle(i18n("Your new key pair was created successfully. Please find details on the result and some suggested next steps below."));
0172     }
0173 
0174     ui->resultTB->setVisible(!error);
0175     ui->errorTB->setVisible(error);
0176     ui->dragQueen->setVisible(!error && !pgp());
0177     ui->restartWizardPB->setVisible(error);
0178     ui->nextStepsGB->setVisible(!error);
0179     ui->saveRequestToFilePB->setVisible(!pgp());
0180     ui->makeBackupPB->setVisible(pgp());
0181     ui->createRevocationRequestPB->setVisible(pgp() && false); // not implemented
0182 
0183     ui->sendCertificateByEMailPB->setVisible(pgp());
0184     ui->sendRequestByEMailPB->setVisible(!pgp());
0185     ui->uploadToKeyserverPB->setVisible(pgp());
0186 
0187     if (!error && !pgp()) {
0188         if (signingAllowed() && !encryptionAllowed()) {
0189             successfullyCreatedSigningCertificate = true;
0190         } else if (!signingAllowed() && encryptionAllowed()) {
0191             successfullyCreatedEncryptionCertificate = true;
0192         } else {
0193             successfullyCreatedEncryptionCertificate = successfullyCreatedSigningCertificate = true;
0194         }
0195     }
0196 
0197     ui->createSigningCertificatePB->setVisible(successfullyCreatedEncryptionCertificate && !successfullyCreatedSigningCertificate);
0198     ui->createEncryptionCertificatePB->setVisible(successfullyCreatedSigningCertificate && !successfullyCreatedEncryptionCertificate);
0199 
0200     if (error) {
0201         wizard()->setOptions(wizard()->options() & ~QWizard::NoCancelButtonOnLastPage);
0202     } else {
0203         wizard()->setOptions(wizard()->options() | QWizard::NoCancelButtonOnLastPage);
0204     }
0205 
0206     if (!initialized) {
0207         connect(ui->restartWizardPB, &QAbstractButton::clicked, this, [this]() {
0208             restartAtEnterDetailsPage();
0209         });
0210     }
0211     initialized = true;
0212 }
0213 
0214 bool ResultPage::isError() const
0215 {
0216     return !ui->errorTB->document()->isEmpty();
0217 }
0218 
0219 bool ResultPage::isComplete() const
0220 {
0221     return !isError();
0222 }
0223 
0224 Key ResultPage::key() const
0225 {
0226     return KeyCache::instance()->findByFingerprint(fingerprint().toLatin1().constData());
0227 }
0228 
0229 void ResultPage::slotSaveRequestToFile()
0230 {
0231     QString fileName = FileDialog::getSaveFileName(this, i18nc("@title", "Save Request"), QStringLiteral("imp"), i18n("PKCS#10 Requests (*.p10)"));
0232     if (fileName.isEmpty()) {
0233         return;
0234     }
0235     if (!fileName.endsWith(QLatin1StringView(".p10"), Qt::CaseInsensitive)) {
0236         fileName += QLatin1StringView(".p10");
0237     }
0238     QFile src(QUrl(url()).toLocalFile());
0239     if (!src.copy(fileName))
0240         KMessageBox::error(this,
0241                            xi18nc("@info",
0242                                   "Could not copy temporary file <filename>%1</filename> "
0243                                   "to file <filename>%2</filename>: <message>%3</message>",
0244                                   src.fileName(),
0245                                   fileName,
0246                                   src.errorString()),
0247                            i18nc("@title", "Error Saving Request"));
0248     else
0249         KMessageBox::information(this,
0250                                  xi18nc("@info",
0251                                         "<para>Successfully wrote request to <filename>%1</filename>.</para>"
0252                                         "<para>You should now send the request to the Certification Authority (CA).</para>",
0253                                         fileName),
0254                                  i18nc("@title", "Request Saved"));
0255 }
0256 
0257 void ResultPage::slotSendRequestByEMail()
0258 {
0259     if (pgp()) {
0260         return;
0261     }
0262     const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
0263     invokeMailer(config.readEntry("CAEmailAddress"), // to
0264                  i18n("Please process this certificate."), // subject
0265                  i18n("Please process this certificate and inform the sender about the location to fetch the resulting certificate.\n\nThanks,\n"), // body
0266                  QFileInfo{QUrl(url()).toLocalFile()}); // attachment
0267     KMessageBox::information(this,
0268                              xi18nc("@info",
0269                                     "<para><application>Kleopatra</application> tried to send a mail via your default mail client.</para>"
0270                                     "<para>Some mail clients are known not to support attachments when invoked this way.</para>"
0271                                     "<para>If your mail client does not have an attachment, then drag the <application>Kleopatra</application> icon and drop "
0272                                     "it on the message compose window of your mail client.</para>"
0273                                     "<para>If that does not work, either, save the request to a file, and then attach that.</para>"),
0274                              i18nc("@title", "Sending Mail"),
0275                              QStringLiteral("newcertificatewizard-mailto-troubles"));
0276 }
0277 
0278 void ResultPage::slotSendCertificateByEMail()
0279 {
0280     if (!pgp() || exportCertificateCommand) {
0281         return;
0282     }
0283     auto cmd = new ExportCertificateCommand(key());
0284     connect(cmd, &ExportCertificateCommand::finished, this, &ResultPage::slotSendCertificateByEMailContinuation);
0285     cmd->setOpenPGPFileName(tmpDir().absoluteFilePath(fingerprint() + QLatin1StringView(".asc")));
0286     cmd->start();
0287     exportCertificateCommand = cmd;
0288 }
0289 
0290 void ResultPage::slotSendCertificateByEMailContinuation()
0291 {
0292     if (!exportCertificateCommand) {
0293         return;
0294     }
0295     // ### better error handling?
0296     const QString fileName = exportCertificateCommand->openPGPFileName();
0297     qCDebug(KLEOPATRA_LOG) << "fileName" << fileName;
0298     exportCertificateCommand = nullptr;
0299     if (fileName.isEmpty()) {
0300         return;
0301     }
0302     invokeMailer(i18n("My new public OpenPGP key"), // subject
0303                  i18n("Please find attached my new public OpenPGP key."), // body
0304                  QFileInfo{fileName});
0305     KMessageBox::information(this,
0306                              xi18nc("@info",
0307                                     "<para><application>Kleopatra</application> tried to send a mail via your default mail client.</para>"
0308                                     "<para>Some mail clients are known not to support attachments when invoked this way.</para>"
0309                                     "<para>If your mail client does not have an attachment, then attach the file <filename>%1</filename> manually.</para>",
0310                                     fileName),
0311                              i18nc("@title", "Sending Mail"),
0312                              QStringLiteral("newcertificatewizard-openpgp-mailto-troubles"));
0313 }
0314 
0315 void ResultPage::slotUploadCertificateToDirectoryServer()
0316 {
0317     if (pgp()) {
0318         (new ExportOpenPGPCertsToServerCommand(key()))->start();
0319     }
0320 }
0321 
0322 void ResultPage::slotBackupCertificate()
0323 {
0324     if (pgp()) {
0325         (new ExportSecretKeyCommand(key()))->start();
0326     }
0327 }
0328 
0329 void ResultPage::slotCreateRevocationRequest()
0330 {
0331 }
0332 
0333 void ResultPage::slotCreateSigningCertificate()
0334 {
0335     if (successfullyCreatedSigningCertificate) {
0336         return;
0337     }
0338     toggleSignEncryptAndRestart();
0339 }
0340 
0341 void ResultPage::slotCreateEncryptionCertificate()
0342 {
0343     if (successfullyCreatedEncryptionCertificate) {
0344         return;
0345     }
0346     toggleSignEncryptAndRestart();
0347 }
0348 
0349 void ResultPage::toggleSignEncryptAndRestart()
0350 {
0351     if (!wizard()) {
0352         return;
0353     }
0354     if (KMessageBox::warningContinueCancel(this,
0355                                            i18nc("@info",
0356                                                  "This operation will delete the certification request. "
0357                                                  "Please make sure that you have sent or saved it before proceeding."),
0358                                            i18nc("@title", "Certification Request About To Be Deleted"))
0359         != KMessageBox::Continue) {
0360         return;
0361     }
0362     const bool sign = signingAllowed();
0363     const bool encr = encryptionAllowed();
0364     setField(QStringLiteral("signingAllowed"), !sign);
0365     setField(QStringLiteral("encryptionAllowed"), !encr);
0366     restartAtEnterDetailsPage();
0367 }
0368 
0369 #include "moc_resultpage_p.cpp"