File indexing completed on 2025-03-09 04:54:28

0001 /*
0002    SPDX-FileCopyrightText: 2019-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "dkimcheckfulljob.h"
0008 #include "dkimauthenticationstatusinfoconverter.h"
0009 #include "dkimcheckauthenticationstatusjob.h"
0010 #include "dkimcheckpolicyjob.h"
0011 #include "dkimgeneraterulejob.h"
0012 #include "dkimmanagerkey.h"
0013 #include "dkimstoreresultjob.h"
0014 #include "messageviewer_dkimcheckerdebug.h"
0015 #include <KLocalizedString>
0016 #include <KMessageBox>
0017 using namespace MessageViewer;
0018 
0019 DKIMCheckFullJob::DKIMCheckFullJob(QObject *parent)
0020     : QObject(parent)
0021 {
0022 }
0023 
0024 DKIMCheckFullJob::~DKIMCheckFullJob() = default;
0025 
0026 DKIMCheckPolicy DKIMCheckFullJob::policy() const
0027 {
0028     return mCheckPolicy;
0029 }
0030 
0031 void DKIMCheckFullJob::setPolicy(const DKIMCheckPolicy &policy)
0032 {
0033     mCheckPolicy = policy;
0034 }
0035 
0036 void DKIMCheckFullJob::startCheckFullInfo(const Akonadi::Item &item)
0037 {
0038     if (!item.isValid()) {
0039         deleteLater();
0040         qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Invalid item";
0041         return;
0042     }
0043     mAkonadiItem = item;
0044     if (mAkonadiItem.hasPayload<KMime::Message::Ptr>()) {
0045         mMessage = mAkonadiItem.payload<KMime::Message::Ptr>();
0046     }
0047     if (!mMessage) {
0048         deleteLater();
0049         qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Invalid message";
0050         return;
0051     }
0052     checkAuthenticationResults();
0053 }
0054 
0055 void DKIMCheckFullJob::checkAuthenticationResults()
0056 {
0057     if (mCheckPolicy.useAuthenticationResults()) {
0058         auto job = new DKIMCheckAuthenticationStatusJob(this);
0059         mHeaderParser.setHead(mMessage->head());
0060         mHeaderParser.parse();
0061         job->setHeaderParser(mHeaderParser);
0062         job->setUseRelaxedParsing(mCheckPolicy.useRelaxedParsing());
0063         connect(job, &DKIMCheckAuthenticationStatusJob::result, this, &DKIMCheckFullJob::slotCheckAuthenticationStatusResult);
0064         job->start();
0065     } else {
0066         checkSignature();
0067     }
0068 }
0069 
0070 void DKIMCheckFullJob::checkSignature(const QList<DKIMCheckSignatureJob::DKIMCheckSignatureAuthenticationResult> &lst)
0071 {
0072     auto job = new DKIMCheckSignatureJob(this);
0073     connect(job, &DKIMCheckSignatureJob::storeKey, this, &DKIMCheckFullJob::storeKey);
0074     connect(job, &DKIMCheckSignatureJob::result, this, &DKIMCheckFullJob::slotCheckSignatureResult);
0075     job->setMessage(mMessage);
0076     job->setHeaderParser(mHeaderParser);
0077     job->setPolicy(mCheckPolicy);
0078     job->setCheckSignatureAuthenticationResult(lst);
0079     job->start();
0080 }
0081 
0082 void DKIMCheckFullJob::startCheckFullInfo(const KMime::Message::Ptr &message)
0083 {
0084     mMessage = message;
0085     if (!mMessage) {
0086         deleteLater();
0087         qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Invalid message";
0088         return;
0089     }
0090     checkAuthenticationResults();
0091 }
0092 
0093 void DKIMCheckFullJob::storeKey(const QString &key, const QString &domain, const QString &selector)
0094 {
0095     switch (mCheckPolicy.saveKey()) {
0096     case MessageViewer::MessageViewerSettings::EnumSaveKey::NotSaving:
0097         // Nothing
0098         break;
0099     case MessageViewer::MessageViewerSettings::EnumSaveKey::Save:
0100         storeInKeyManager(key, selector, domain, false);
0101         break;
0102     case MessageViewer::MessageViewerSettings::EnumSaveKey::SaveAndCompare:
0103         storeInKeyManager(key, selector, domain, true);
0104         break;
0105     }
0106 }
0107 
0108 void DKIMCheckFullJob::storeInKeyManager(const QString &key, const QString &domain, const QString &selector, bool verify)
0109 {
0110     const MessageViewer::KeyInfo info{key, selector, domain, QDateTime::currentDateTime()};
0111     if (verify) {
0112         const QString keyStored = MessageViewer::DKIMManagerKey::self()->keyValue(selector, domain);
0113         if (!keyStored.isEmpty()) {
0114             if (keyStored != key) {
0115                 // qDebug() << "storeInKeyManager : keyStored  " << keyStored << " key " << key;
0116                 // qDebug() << "domain " << domain << " selector " << selector;
0117                 const int answer = KMessageBox::warningTwoActions(nullptr,
0118                                                                   i18n("Stored DKIM key is different from the current one. Do you want to store this one too?"),
0119                                                                   i18nc("@title:window", "Key Changed"),
0120                                                                   KGuiItem(i18nc("@action:button", "Store")),
0121                                                                   KStandardGuiItem::discard());
0122                 if (answer == KMessageBox::ButtonCode::SecondaryAction) {
0123                     return;
0124                 }
0125             }
0126         }
0127     }
0128     MessageViewer::DKIMManagerKey::self()->addKey(info);
0129 }
0130 
0131 void DKIMCheckFullJob::slotCheckAuthenticationStatusResult(const MessageViewer::DKIMAuthenticationStatusInfo &info)
0132 {
0133     // qDebug() << "info " << info;
0134     DKIMAuthenticationStatusInfoConverter converter;
0135     converter.setStatusInfo(info);
0136     // TODO Convert to CheckSignatureAuthenticationResult + add this list to CheckSignatureResult directly
0137     const QList<DKIMCheckSignatureJob::DKIMCheckSignatureAuthenticationResult> lst = converter.convert();
0138     // qDebug() << "  lst " << lst;
0139     // TODO use it.
0140 
0141     // TODO check info ! if auth is ok not necessary to checkSignature
0142     if (mCheckPolicy.useOnlyAuthenticationResults()) {
0143         // Don't check signature if not necessary.
0144     }
0145     checkSignature(lst);
0146 }
0147 
0148 void DKIMCheckFullJob::storeResult(const DKIMCheckSignatureJob::CheckSignatureResult &checkResult)
0149 {
0150     if (mCheckPolicy.saveDkimResult()) {
0151         if (checkResult.status == DKIMCheckSignatureJob::DKIMStatus::Valid || checkResult.status == DKIMCheckSignatureJob::DKIMStatus::Invalid
0152             || checkResult.status == DKIMCheckSignatureJob::DKIMStatus::NeedToBeSigned) {
0153             auto job = new DKIMStoreResultJob(this);
0154             job->setItem(mAkonadiItem);
0155             job->setResult(checkResult);
0156             job->start();
0157         }
0158     }
0159     if (mCheckPolicy.autogenerateRule()) {
0160         if (mCheckPolicy.autogenerateRuleOnlyIfSenderInSDID()) {
0161             // TODO
0162             // FIXME Check value SDID !
0163             generateRule(checkResult);
0164         } else {
0165             generateRule(checkResult);
0166         }
0167     }
0168 
0169     qCDebug(MESSAGEVIEWER_DKIMCHECKER_LOG) << "result : status " << checkResult.status << " error : " << checkResult.error << " warning "
0170                                            << checkResult.warning;
0171     Q_EMIT result(checkResult, mAkonadiItem.id());
0172     deleteLater();
0173 }
0174 
0175 void DKIMCheckFullJob::generateRule(const DKIMCheckSignatureJob::CheckSignatureResult &checkResult)
0176 {
0177     if (checkResult.status == DKIMCheckSignatureJob::DKIMStatus::Valid) {
0178         auto job = new DKIMGenerateRuleJob(this);
0179         job->setResult(checkResult);
0180         if (!job->start()) {
0181             qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to start autogenerate rule";
0182         }
0183     }
0184 }
0185 
0186 void DKIMCheckFullJob::slotCheckSignatureResult(const DKIMCheckSignatureJob::CheckSignatureResult &checkResult)
0187 {
0188     if (mCheckPolicy.checkIfEmailShouldBeSigned() && (checkResult.status == DKIMCheckSignatureJob::DKIMStatus::EmailNotSigned)) {
0189         auto job = new DKIMCheckPolicyJob(this);
0190         connect(job, &DKIMCheckPolicyJob::result, this, &DKIMCheckFullJob::storeResult);
0191         job->setCheckResult(checkResult);
0192         job->setEmailAddress(checkResult.fromEmail);
0193         if (!job->start()) {
0194             storeResult(checkResult);
0195         }
0196     } else {
0197         storeResult(checkResult);
0198     }
0199 }
0200 
0201 #include "moc_dkimcheckfulljob.cpp"