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 "dmarcinfo.h"
0008 #include "messageviewer_dkimcheckerdebug.h"
0009 
0010 using namespace MessageViewer;
0011 DMARCInfo::DMARCInfo() = default;
0012 
0013 bool DMARCInfo::parseDMARC(const QString &key)
0014 {
0015     if (key.isEmpty()) {
0016         qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Error: key empty";
0017         return false;
0018     }
0019     QString cleanKey = key;
0020     cleanKey.replace(QLatin1StringView("; "), QLatin1StringView(";"));
0021     const QStringList items = cleanKey.split(QLatin1Char(';'), Qt::SkipEmptyParts);
0022     for (int i = 0; i < items.count(); ++i) {
0023         const QString elem = items.at(i).trimmed();
0024         if (elem.startsWith(QLatin1StringView("v="))) {
0025             // v: Version (plain-text; REQUIRED).  Identifies the record retrieved
0026             //      as a DMARC record.  It MUST have the value of "DMARC1".  The value
0027             //      of this tag MUST match precisely; if it does not or it is absent,
0028             //      the entire retrieved record MUST be ignored.  It MUST be the first
0029             //      tag in the list.
0030             mVersion = elem.right(elem.length() - 2);
0031         } else if (elem.startsWith(QLatin1StringView("r="))) {
0032             // adkim:  (plain-text; OPTIONAL; default is "r".)  Indicates whether
0033             //   strict or relaxed DKIM Identifier Alignment mode is required by
0034             //   the Domain Owner.  See Section 3.1.1 for details.  Valid values
0035             //   are as follows:
0036             //      r: relaxed mode
0037             //      s: strict mode
0038             mAdkim = elem.right(elem.length() - 2);
0039         } else if (elem.startsWith(QLatin1StringView("p="))) {
0040             // p: Requested Mail Receiver policy (plain-text; REQUIRED for policy
0041             //                    records).  Indicates the policy to be enacted by the Receiver at
0042             //                    the request of the Domain Owner.  Policy applies to the domain
0043             //                    queried and to subdomains, unless subdomain policy is explicitly
0044             //                    described using the "sp" tag.  This tag is mandatory for policy
0045             //                    records only, but not for third-party reporting records (see
0046             //                                                                             Section 7.1).  Possible values are as follows:
0047 
0048             //                none:  The Domain Owner requests no specific action be taken
0049             //                regarding delivery of messages.
0050 
0051             //                quarantine:  The Domain Owner wishes to have email that fails the
0052             //                DMARC mechanism check be treated by Mail Receivers as
0053             //                suspicious.  Depending on the capabilities of the Mail
0054             //                Receiver, this can mean "place into spam folder", "scrutinize
0055             //                with additional intensity", and/or "flag as suspicious".
0056 
0057             //                reject:  The Domain Owner wishes for Mail Receivers to reject
0058             //                email that fails the DMARC mechanism check.  Rejection SHOULD
0059             //                occur during the SMTP transaction.  See Section 10.3 for some
0060             //                discussion of SMTP rejection methods and their implications.
0061             mPolicy = elem.right(elem.length() - 2);
0062         } else if (elem.startsWith(QLatin1StringView("ptc="))) {
0063             // pct:  (plain-text integer between 0 and 100, inclusive; OPTIONAL;
0064             //      default is 100).  Percentage of messages from the Domain Owner's
0065             //      mail stream to which the DMARC policy is to be applied.  However,
0066             //      this MUST NOT be applied to the DMARC-generated reports, all of
0067             //      which must be sent and received unhindered.  The purpose of the
0068             //      "pct" tag is to allow Domain Owners to enact a slow rollout
0069             //      enforcement of the DMARC mechanism.  The prospect of "all or
0070             //      nothing" is recognized as preventing many organizations from
0071             //      experimenting with strong authentication-based mechanisms.  See
0072             //      Section 6.6.4 for details.  Note that random selection based on
0073             //      this percentage, such as the following pseudocode, is adequate:
0074 
0075             //       if (random mod 100) < pct then
0076             //         selected = true
0077             //       else
0078             //         selected = false
0079             // TODO verify if it's a percentage
0080             mPercentage = QStringView(elem).right(elem.length() - 4).toInt();
0081         } else if (elem.startsWith(QLatin1StringView("sp="))) {
0082             // sp:  Requested Mail Receiver policy for all subdomains (plain-text;
0083             //   OPTIONAL).  Indicates the policy to be enacted by the Receiver at
0084             //   the request of the Domain Owner.  It applies only to subdomains of
0085             //   the domain queried and not to the domain itself.  Its syntax is
0086             //   identical to that of the "p" tag defined above.  If absent, the
0087             //   policy specified by the "p" tag MUST be applied for subdomains.
0088             //   Note that "sp" will be ignored for DMARC records published on
0089             //   subdomains of Organizational Domains due to the effect of the
0090             //   DMARC policy discovery mechanism described in Section 6.6.3.
0091             mSubDomainPolicy = elem.right(elem.length() - 3);
0092         }
0093     }
0094     if (mAdkim.isEmpty() && mVersion == QLatin1StringView("DMARC1")) {
0095         mAdkim = QLatin1Char('r');
0096     }
0097 
0098     return true;
0099 }
0100 
0101 QString DMARCInfo::version() const
0102 {
0103     return mVersion;
0104 }
0105 
0106 void DMARCInfo::setVersion(const QString &version)
0107 {
0108     mVersion = version;
0109 }
0110 
0111 QString DMARCInfo::adkim() const
0112 {
0113     return mAdkim;
0114 }
0115 
0116 void DMARCInfo::setAdkim(const QString &adkim)
0117 {
0118     mAdkim = adkim;
0119 }
0120 
0121 QString DMARCInfo::policy() const
0122 {
0123     return mPolicy;
0124 }
0125 
0126 void DMARCInfo::setPolicy(const QString &policy)
0127 {
0128     mPolicy = policy;
0129 }
0130 
0131 int DMARCInfo::percentage() const
0132 {
0133     return mPercentage;
0134 }
0135 
0136 void DMARCInfo::setPercentage(int percentage)
0137 {
0138     mPercentage = percentage;
0139 }
0140 
0141 QString DMARCInfo::subDomainPolicy() const
0142 {
0143     return mSubDomainPolicy;
0144 }
0145 
0146 void DMARCInfo::setSubDomainPolicy(const QString &subDomainPolicy)
0147 {
0148     mSubDomainPolicy = subDomainPolicy;
0149 }
0150 
0151 bool DMARCInfo::operator==(const DMARCInfo &other) const
0152 {
0153     return mVersion == other.version() && mAdkim == other.adkim() && mPolicy == other.policy() && mSubDomainPolicy == other.subDomainPolicy()
0154         && mPercentage == other.percentage();
0155 }
0156 
0157 QDebug operator<<(QDebug d, const DMARCInfo &t)
0158 {
0159     d << " mVersion " << t.version();
0160     d << " mAdkim " << t.adkim();
0161     d << " mPolicy " << t.policy();
0162     d << " mSubDomainPolicy " << t.subDomainPolicy();
0163     d << " mPercentage " << t.percentage();
0164     return d;
0165 }