File indexing completed on 2025-02-02 05:08:37

0001 /*
0002     SPDX-FileCopyrightText: 2016 Daniel Vrátil <dvratil@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "key.h"
0008 #include "setupmanager.h"
0009 #include "transport.h"
0010 
0011 #include <QTimer>
0012 
0013 #include <QGpgME/CryptoConfig>
0014 #include <QGpgME/Protocol>
0015 #include <QGpgME/WKSPublishJob>
0016 
0017 #include <gpgme++/engineinfo.h>
0018 
0019 #include <Akonadi/MessageQueueJob>
0020 #include <MailTransport/Transport>
0021 #include <MailTransport/TransportManager>
0022 
0023 #include <KIdentityManagementCore/Identity>
0024 #include <KIdentityManagementCore/IdentityManager>
0025 
0026 #include <KMime/Headers>
0027 #include <KMime/Message>
0028 #include <KMime/Util>
0029 
0030 #include <KEmailAddress>
0031 
0032 #include <KLocalizedString>
0033 
0034 #include "accountwizard_debug.h"
0035 
0036 Key::Key(QObject *parent)
0037     : SetupObject(parent)
0038 {
0039 }
0040 
0041 void Key::setKey(const GpgME::Key &key)
0042 {
0043     m_key = key;
0044 }
0045 
0046 void Key::setMailBox(const QString &mailbox)
0047 {
0048     m_mailbox = KEmailAddress::extractEmailAddress(mailbox);
0049 }
0050 
0051 void Key::setTransportId(int transportId)
0052 {
0053     m_transportId = transportId;
0054 }
0055 
0056 void Key::setPublishingMethod(PublishingMethod method)
0057 {
0058     m_publishingMethod = method;
0059 }
0060 
0061 void Key::create()
0062 {
0063     switch (m_publishingMethod) {
0064     case NoPublishing:
0065         QTimer::singleShot(0, this, [this]() {
0066             Q_EMIT finished(i18n("Skipping key publishing"));
0067         });
0068         break;
0069     case WKS:
0070         publishWKS();
0071         break;
0072     case PKS:
0073         publishPKS();
0074         break;
0075     }
0076 }
0077 
0078 void Key::publishWKS()
0079 {
0080     Q_EMIT info(i18n("Publishing OpenPGP key..."));
0081 
0082     auto job = QGpgME::openpgp()->wksPublishJob();
0083     mJob = job;
0084     connect(job, &QGpgME::WKSPublishJob::result, this, &Key::onWKSPublishingCheckDone);
0085     job->startCheck(m_mailbox);
0086 }
0087 
0088 void Key::onWKSPublishingCheckDone(const GpgME::Error &gpgMeError, const QByteArray &, const QByteArray &returnedError)
0089 {
0090     mJob = nullptr;
0091 
0092     if (gpgMeError) {
0093         if (gpgMeError.isCanceled()) {
0094             Q_EMIT error(i18n("Key publishing was canceled."));
0095             return;
0096         }
0097 
0098         qCWarning(ACCOUNTWIZARD_LOG) << "Check error:" << returnedError;
0099         if (gpgMeError.code() == GPG_ERR_NOT_SUPPORTED) {
0100             Q_EMIT info(i18n("Key publishing failed: not online, or GnuPG too old."));
0101             Q_EMIT finished(QString());
0102         } else {
0103             Q_EMIT info(i18n("Your email provider does not support key publishing."));
0104             Q_EMIT finished(QString());
0105         }
0106         return;
0107     }
0108 
0109     auto job = QGpgME::openpgp()->wksPublishJob();
0110     mJob = job;
0111     connect(job, &QGpgME::WKSPublishJob::result, this, &Key::onWKSPublishingRequestCreated);
0112     job->startCreate(m_key.primaryFingerprint(), m_mailbox);
0113 }
0114 
0115 void Key::onWKSPublishingRequestCreated(const GpgME::Error &gpgMeError, const QByteArray &returnedData, const QByteArray &returnedError)
0116 {
0117     mJob = nullptr;
0118 
0119     if (gpgMeError) {
0120         if (gpgMeError.isCanceled()) {
0121             Q_EMIT error(i18n("Key publishing was canceled."));
0122             return;
0123         }
0124 
0125         qCWarning(ACCOUNTWIZARD_LOG) << "Publishing error:" << returnedData << returnedError;
0126         Q_EMIT error(i18n("An error occurred while creating key publishing request."));
0127         return;
0128     }
0129 
0130     if (m_transportId == 0 && qobject_cast<SetupManager *>(parent())) {
0131         const auto setupManager = qobject_cast<SetupManager *>(parent());
0132         const auto setupObjects = setupManager->setupObjects();
0133         auto it = std::find_if(setupObjects.cbegin(), setupObjects.cend(), [](SetupObject *obj) -> bool {
0134             return qobject_cast<Transport *>(obj);
0135         });
0136         if (it != setupObjects.cend()) {
0137             m_transportId = qobject_cast<Transport *>(*it)->transportId();
0138         }
0139     } else if (m_transportId) {
0140         auto ident = KIdentityManagementCore::IdentityManager::self()->identityForAddress(m_mailbox);
0141         if (!ident.transport().isEmpty()) {
0142             m_transportId = ident.transport().toInt();
0143         }
0144     }
0145 
0146     auto transport = MailTransport::TransportManager::self()->transportById(m_transportId, true);
0147     if (!transport) {
0148         qCWarning(ACCOUNTWIZARD_LOG) << "No MailTransport::Transport available?!?!?!";
0149         Q_EMIT error(i18n("Key publishing error: mail transport is not configured"));
0150         return;
0151     }
0152 
0153     qCDebug(ACCOUNTWIZARD_LOG) << returnedData;
0154 
0155     // Parse the data so that we can get "To" and "From" headers
0156     auto msg = KMime::Message::Ptr::create();
0157     msg->setContent(KMime::CRLFtoLF(returnedData));
0158     msg->parse();
0159 
0160     if (!msg->from(false) || !msg->to(false)) {
0161         qCWarning(ACCOUNTWIZARD_LOG) << "No FROM or TO in parsed message, source data were:" << returnedData;
0162         Q_EMIT error(i18n("Key publishing error: failed to create request email"));
0163         return;
0164     }
0165 
0166     auto header = new KMime::Headers::Generic("X-KMail-Transport");
0167     header->fromUnicodeString(QString::number(m_transportId), "utf-8");
0168     msg->setHeader(header);
0169 
0170     // Build the message
0171     msg->assemble();
0172 
0173     // Move to outbox
0174     auto job = new Akonadi::MessageQueueJob;
0175     mJob = job;
0176     job->addressAttribute().setTo({msg->to(false)->asUnicodeString()});
0177     job->transportAttribute().setTransportId(transport->id());
0178     job->addressAttribute().setFrom(msg->from(false)->asUnicodeString());
0179     // Don't leave any evidence :-)
0180     job->sentBehaviourAttribute().setSentBehaviour(Akonadi::SentBehaviourAttribute::Delete);
0181     job->sentBehaviourAttribute().setSendSilently(true);
0182     job->setMessage(msg);
0183     connect(job, &KJob::result, this, &Key::onWKSPublishingRequestSent);
0184     job->start();
0185 }
0186 
0187 void Key::onWKSPublishingRequestSent(KJob *job)
0188 {
0189     mJob = nullptr;
0190     if (job->error() == KJob::KilledJobError) {
0191         Q_EMIT error(i18n("Key publishing was canceled."));
0192     } else if (job->error()) {
0193         Q_EMIT error(i18n("Failed to send key publishing request: %1", job->errorString()));
0194     } else {
0195         Q_EMIT finished(i18n("Key publishing request sent."));
0196     }
0197 }
0198 
0199 void Key::publishPKS()
0200 {
0201     Q_EMIT info(i18n("Publishing OpenPGP key..."));
0202 
0203     // default
0204     QString keyServer = QStringLiteral("key.gnupg.net");
0205 
0206     const auto config = QGpgME::cryptoConfig();
0207     if (config) {
0208         const auto entry = config->entry(QStringLiteral("gpg"), QStringLiteral("Keyserver"), QStringLiteral("keyserver"));
0209         if (entry && !entry->stringValue().isEmpty()) {
0210             keyServer = entry->stringValue();
0211         }
0212     }
0213 
0214     const char *gpgName = GpgME::engineInfo(GpgME::OpenPGP).fileName();
0215     auto gpgProcess = new QProcess;
0216     gpgProcess->setProperty("keyServer", keyServer);
0217     connect(gpgProcess, &QProcess::finished, this, &Key::onPKSPublishingFinished);
0218     mJob = gpgProcess;
0219     gpgProcess->start(QString::fromLatin1(gpgName),
0220                       {QStringLiteral("--keyserver"), keyServer, QStringLiteral("--send-keys"), QString::fromLatin1(m_key.primaryFingerprint())});
0221 }
0222 
0223 void Key::onPKSPublishingFinished(int code, QProcess::ExitStatus status)
0224 {
0225     auto process = qobject_cast<QProcess *>(mJob);
0226     mJob = nullptr;
0227     process->deleteLater();
0228 
0229     if (status != QProcess::NormalExit || code != 0) {
0230         qCWarning(ACCOUNTWIZARD_LOG) << "PKS Publishing error:" << process->readAll();
0231         Q_EMIT info(i18n("Failed to publish the key."));
0232         Q_EMIT finished(QString());
0233         return;
0234     }
0235 
0236     const auto keyServer = process->property("keyServer").toString();
0237     Q_EMIT finished(i18n("Key has been published on %1", keyServer));
0238 }
0239 
0240 void Key::destroy()
0241 {
0242     // This is all we can do, there's no unpublish...
0243     if (auto job = qobject_cast<QGpgME::Job *>(mJob)) {
0244         job->slotCancel();
0245     } else if (auto job = qobject_cast<KJob *>(mJob)) {
0246         job->kill();
0247     } else if (auto job = qobject_cast<QProcess *>(mJob)) {
0248         job->kill();
0249     }
0250 }
0251 
0252 #include "moc_key.cpp"