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