Messagelib

dkimutil.cpp
1/*
2 SPDX-FileCopyrightText: 2018-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "dkimutil.h"
8#include "messageviewer_dkimcheckerdebug.h"
9#include <QRegularExpression>
10
11QString MessageViewer::DKIMUtil::bodyCanonizationRelaxed(QString body)
12{
13 /*
14 * canonicalize the body using the relaxed algorithm
15 * specified in Section 3.4.4 of RFC 6376
16 */
17 /*
18 a. Reduce whitespace:
19
20 * Ignore all whitespace at the end of lines. Implementations
21 MUST NOT remove the CRLF at the end of the line.
22
23 * Reduce all sequences of WSP within a line to a single SP
24 character.
25
26 b. Ignore all empty lines at the end of the message body. "Empty
27 line" is defined in Section 3.4.3. If the body is non-empty but
28 does not end with a CRLF, a CRLF is added. (For email, this is
29 only possible when using extensions to SMTP or non-SMTP transport
30 mechanisms.)
31 */
32
33 body.replace(QStringLiteral("\n"), QStringLiteral("\r\n"));
34 static const QRegularExpression reg1(QStringLiteral("[ \t]+\r\n"));
35 body.replace(reg1, QStringLiteral("\r\n"));
36 static const QRegularExpression reg2(QStringLiteral("[ \t]+"));
37 body.replace(reg2, QStringLiteral(" "));
38 static const QRegularExpression reg3(QStringLiteral("((\r\n)+?)$"));
39 body.replace(QRegularExpression(reg3), QStringLiteral("\r\n"));
40 if (body == QLatin1StringView("\r\n")) {
41 body.clear();
42 }
43 return body;
44}
45
46QString MessageViewer::DKIMUtil::bodyCanonizationSimple(QString body)
47{
48 // The "simple" body canonicalization algorithm ignores all empty lines
49 // at the end of the message body. An empty line is a line of zero
50 // length after removal of the line terminator. If there is no body or
51 // no trailing CRLF on the message body, a CRLF is added. It makes no
52 // other changes to the message body. In more formal terms, the
53 // "simple" body canonicalization algorithm converts "*CRLF" at the end
54 // of the body to a single "CRLF".
55
56 // Note that a completely empty or missing body is canonicalized as a
57 // single "CRLF"; that is, the canonicalized length will be 2 octets.
58 body.replace(QStringLiteral("\n"), QStringLiteral("\r\n"));
59 static const QRegularExpression reg(QStringLiteral("((\r\n)+)?$"));
60 body.replace(reg, QStringLiteral("\r\n"));
61 if (body.endsWith(QLatin1StringView("\r\n"))) { // Remove it from start
62 body.chop(2);
63 }
64 if (body.isEmpty()) {
65 body = QStringLiteral("\r\n");
66 }
67 return body;
68}
69
70QByteArray MessageViewer::DKIMUtil::generateHash(const QByteArray &body, QCryptographicHash::Algorithm algo)
71{
72 return QCryptographicHash::hash(body, algo).toBase64();
73}
74
75QString MessageViewer::DKIMUtil::headerCanonizationSimple(const QString &headerName, const QString &headerValue)
76{
77 // TODO verify it lower it ?
78 return headerName + QLatin1Char(':') + headerValue;
79}
80
81QString MessageViewer::DKIMUtil::headerCanonizationRelaxed(const QString &headerName, const QString &headerValue, bool removeQuoteOnContentType)
82{
83 // The "relaxed" header canonicalization algorithm MUST apply the
84 // following steps in order:
85
86 // o Convert all header field names (not the header field values) to
87 // lowercase. For example, convert "SUBJect: AbC" to "subject: AbC".
88
89 // o Unfold all header field continuation lines as described in
90 // [RFC5322]; in particular, lines with terminators embedded in
91 // continued header field values (that is, CRLF sequences followed by
92 // WSP) MUST be interpreted without the CRLF. Implementations MUST
93 // NOT remove the CRLF at the end of the header field value.
94
95 // o Convert all sequences of one or more WSP characters to a single SP
96 // character. WSP characters here include those before and after a
97 // line folding boundary.
98
99 // o Delete all WSP characters at the end of each unfolded header field
100 // value.
101
102 // o Delete any WSP characters remaining before and after the colon
103 // separating the header field name from the header field value. The
104 // colon separator MUST be retained.
105 QString newHeaderName = headerName.toLower();
106 QString newHeaderValue = headerValue;
107 static const QRegularExpression reg1(QStringLiteral("\r\n[ \t]+"));
108 newHeaderValue.replace(reg1, QStringLiteral(" "));
109 static const QRegularExpression reg2(QStringLiteral("[ \t]+"));
110 newHeaderValue.replace(reg2, QStringLiteral(" "));
111 static const QRegularExpression reg3(QStringLiteral("[ \t]+\r\n"));
112 newHeaderValue.replace(reg3, QStringLiteral("\r\n"));
113 // Perhaps remove tab after headername and before value name
114 // newHeaderValue.replace(QRegularExpression(QStringLiteral("[ \t]*:[ \t]")), QStringLiteral(":"));
115 if (newHeaderName == QLatin1StringView("content-type") && removeQuoteOnContentType) { // Remove quote in charset
116 if (newHeaderValue.contains(QLatin1StringView("charset=\""))) {
117 newHeaderValue.remove(QLatin1Char('"'));
118 }
119 }
120 // Remove extra space.
121 newHeaderValue = newHeaderValue.trimmed();
122 return newHeaderName + QLatin1Char(':') + newHeaderValue;
123}
124
125QString MessageViewer::DKIMUtil::cleanString(QString str)
126{
127 // Move as static ?
128 // WSP help pattern as specified in Section 2.8 of RFC 6376
129 const QString pattWSP = QStringLiteral("[ \t]");
130 // FWS help pattern as specified in Section 2.8 of RFC 6376
131 const QString pattFWS = QStringLiteral("(?:") + pattWSP + QStringLiteral("*(?:\r\n)?") + pattWSP + QStringLiteral("+)");
132 str.replace(QRegularExpression(pattFWS), QString());
133 return str;
134}
135
136QString MessageViewer::DKIMUtil::emailDomain(const QString &emailDomain)
137{
138 return emailDomain.right(emailDomain.length() - emailDomain.indexOf(QLatin1Char('@')) - 1);
139}
140
141QString MessageViewer::DKIMUtil::emailSubDomain(const QString &emailDomain)
142{
143 int dotNumber = 0;
144 for (int i = emailDomain.length() - 1; i >= 0; --i) {
145 if (emailDomain.at(i) == QLatin1Char('.')) {
146 dotNumber++;
147 if (dotNumber == 2) {
148 return emailDomain.right(emailDomain.length() - i - 1);
149 }
150 }
151 }
152 return emailDomain;
153}
154
155QString MessageViewer::DKIMUtil::defaultConfigFileName()
156{
157 return QStringLiteral("dkimsettingsrc");
158}
159
160QString MessageViewer::DKIMUtil::convertAuthenticationMethodEnumToString(MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod method)
161{
162 QString methodStr;
163 switch (method) {
164 case MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Unknown:
165 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Undefined type";
166 break;
167 case MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dkim:
168 methodStr = QStringLiteral("dkim");
169 break;
170 case MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Spf:
171 methodStr = QStringLiteral("spf");
172 break;
173 case MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dmarc:
174 methodStr = QStringLiteral("dmarc");
175 break;
176 case MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dkimatps:
177 methodStr = QStringLiteral("dkim-atps");
178 break;
179 case MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Auth:
180 methodStr = QStringLiteral("auth");
181 break;
182 }
183 return methodStr;
184}
185
186MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod MessageViewer::DKIMUtil::convertAuthenticationMethodStringToEnum(const QString &str)
187{
188 if (str == QLatin1StringView("dkim")) {
189 return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dkim;
190 } else if (str == QLatin1StringView("spf")) {
191 return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Spf;
192 } else if (str == QLatin1StringView("dmarc")) {
193 return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dmarc;
194 } else if (str == QLatin1StringView("dkim-atps")) {
195 return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dkimatps;
196 } else if (str == QLatin1StringView("auth")) {
197 return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Auth;
198 } else {
199 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Undefined type " << str;
200 return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Unknown;
201 }
202}
QByteArray toBase64(Base64Options options) const const
QByteArray hash(QByteArrayView data, Algorithm method)
const QChar at(qsizetype position) const const
void chop(qsizetype n)
void clear()
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString right(qsizetype n) const const
QString toLower() const const
QString trimmed() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 11:58:49 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.