File indexing completed on 2025-03-09 04:54:31
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 "dmarcpolicyjob.h" 0008 #include "dkimutil.h" 0009 #include "dmarcrecordjob.h" 0010 #include "messageviewer_dkimcheckerdebug.h" 0011 using namespace MessageViewer; 0012 0013 DMARCPolicyJob::DMARCPolicyJob(QObject *parent) 0014 : QObject(parent) 0015 { 0016 } 0017 0018 DMARCPolicyJob::~DMARCPolicyJob() = default; 0019 0020 bool DMARCPolicyJob::canStart() const 0021 { 0022 return !mEmailAddress.isEmpty(); 0023 } 0024 0025 bool DMARCPolicyJob::start() 0026 { 0027 if (!canStart()) { 0028 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << " Impossible to start DMARCPolicyJob" << mEmailAddress; 0029 Q_EMIT result({}, mEmailAddress); 0030 deleteLater(); 0031 return false; 0032 } 0033 0034 auto job = new DMARCRecordJob(this); 0035 job->setDomainName(emailDomain()); 0036 connect(job, &MessageViewer::DMARCRecordJob::success, this, &DMARCPolicyJob::slotCheckDomain); 0037 connect(job, &MessageViewer::DMARCRecordJob::error, this, [this](const QString &err, const QString &domainName) { 0038 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "error: " << err << " domain " << domainName; 0039 // Verify subdomain 0040 checkSubDomain(domainName); 0041 }); 0042 if (!job->start()) { 0043 Q_EMIT result({}, mEmailAddress); 0044 deleteLater(); 0045 return false; 0046 } 0047 return true; 0048 } 0049 0050 QByteArray DMARCPolicyJob::generateDMARCFromList(const QList<QByteArray> &lst) const 0051 { 0052 QByteArray ba; 0053 if (lst.count() != 1) { 0054 for (const QByteArray &b : lst) { 0055 ba += b; 0056 } 0057 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "DMARCPolicyJob Key result has more that 1 element" << lst; 0058 } else { 0059 ba = lst.at(0); 0060 } 0061 return ba; 0062 } 0063 0064 void DMARCPolicyJob::slotCheckSubDomain(const QList<QByteArray> &lst, const QString &domainName) 0065 { 0066 const QByteArray ba = generateDMARCFromList(lst); 0067 DMARCInfo info; 0068 if (info.parseDMARC(QString::fromLocal8Bit(ba))) { 0069 if ((info.version() != QLatin1StringView("DMARC1")) || info.policy().isEmpty() || (info.percentage() > 100 || info.percentage() < 0)) { 0070 Q_EMIT result({}, mEmailAddress); 0071 deleteLater(); 0072 return; 0073 } else { 0074 DMARCPolicyJob::DMARCResult val; 0075 val.mAdkim = info.adkim(); 0076 val.mPercentage = info.percentage(); 0077 val.mPolicy = info.subDomainPolicy().isEmpty() ? info.policy() : info.subDomainPolicy(); 0078 // TODO verify it ! 0079 val.mDomain = domainName; 0080 val.mSource = domainName; 0081 Q_EMIT result(val, mEmailAddress); 0082 deleteLater(); 0083 return; 0084 } 0085 } 0086 Q_EMIT result({}, mEmailAddress); 0087 } 0088 0089 void DMARCPolicyJob::checkSubDomain(const QString &domainName) 0090 { 0091 const QString subDomain = emailSubDomain(domainName); 0092 if (subDomain != domainName) { 0093 auto job = new DMARCRecordJob(this); 0094 job->setDomainName(subDomain); 0095 connect(job, &MessageViewer::DMARCRecordJob::success, this, &DMARCPolicyJob::slotCheckSubDomain); 0096 connect(job, &MessageViewer::DMARCRecordJob::error, this, [this](const QString &err, const QString &domainName) { 0097 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "error: " << err << " domain " << domainName; 0098 Q_EMIT result({}, mEmailAddress); 0099 deleteLater(); 0100 }); 0101 if (!job->start()) { 0102 Q_EMIT result({}, mEmailAddress); 0103 deleteLater(); 0104 return; 0105 } 0106 } else { 0107 // Invalid 0108 Q_EMIT result({}, mEmailAddress); 0109 deleteLater(); 0110 return; 0111 } 0112 } 0113 0114 void DMARCPolicyJob::slotCheckDomain(const QList<QByteArray> &lst, const QString &domainName) 0115 { 0116 const QByteArray ba = generateDMARCFromList(lst); 0117 DMARCInfo info; 0118 if (info.parseDMARC(QString::fromLocal8Bit(ba))) { 0119 if ((info.version() != QLatin1StringView("DMARC1")) || info.policy().isEmpty() 0120 || (info.percentage() != -1 && (info.percentage() > 100 || info.percentage() < 0))) { 0121 // Invalid 0122 // Check subdomain 0123 checkSubDomain(domainName); 0124 } else { 0125 DMARCPolicyJob::DMARCResult val; 0126 val.mAdkim = info.adkim(); 0127 val.mPercentage = info.percentage(); 0128 val.mPolicy = info.policy(); 0129 val.mDomain = domainName; 0130 val.mSource = domainName; 0131 Q_EMIT result(val, mEmailAddress); 0132 deleteLater(); 0133 return; 0134 } 0135 } else { 0136 // Check subdomain 0137 checkSubDomain(domainName); 0138 } 0139 } 0140 0141 QString DMARCPolicyJob::emailDomain() const 0142 { 0143 return MessageViewer::DKIMUtil::emailDomain(mEmailAddress); 0144 } 0145 0146 QString DMARCPolicyJob::emailSubDomain(const QString &domainName) const 0147 { 0148 return MessageViewer::DKIMUtil::emailSubDomain(domainName); 0149 } 0150 0151 QString DMARCPolicyJob::emailAddress() const 0152 { 0153 return mEmailAddress; 0154 } 0155 0156 void DMARCPolicyJob::setEmailAddress(const QString &emailAddress) 0157 { 0158 mEmailAddress = emailAddress; 0159 } 0160 0161 #include "moc_dmarcpolicyjob.cpp"