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"