Messagelib

dkimutil.cpp
1 /*
2  SPDX-FileCopyrightText: 2018-2021 Laurent Montel <[email protected]>
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 
11 QString 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 == QLatin1String("\r\n")) {
41  body.clear();
42  }
43  return body;
44 }
45 
46 QString 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(QLatin1String("\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 
70 QByteArray MessageViewer::DKIMUtil::generateHash(const QByteArray &body, QCryptographicHash::Algorithm algo)
71 {
72  return QCryptographicHash::hash(body, algo).toBase64();
73 }
74 
75 QString MessageViewer::DKIMUtil::headerCanonizationSimple(const QString &headerName, const QString &headerValue)
76 {
77  // TODO verify it lower it ?
78  return headerName + QLatin1Char(':') + headerValue;
79 }
80 
81 QString 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 == QLatin1String("content-type") && removeQuoteOnContentType) { // Remove quote in charset
116  if (newHeaderValue.contains(QLatin1String("charset=\""))) {
117  newHeaderValue.remove(QLatin1Char('"'));
118  }
119  }
120  // Remove extra space.
121  newHeaderValue = newHeaderValue.trimmed();
122  return newHeaderName + QLatin1Char(':') + newHeaderValue;
123 }
124 
125 QString 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 
136 QString MessageViewer::DKIMUtil::emailDomain(const QString &emailDomain)
137 {
138  return emailDomain.right(emailDomain.length() - emailDomain.indexOf(QLatin1Char('@')) - 1);
139 }
140 
141 QString 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 
155 QString MessageViewer::DKIMUtil::defaultConfigFileName()
156 {
157  return QStringLiteral("dkimsettingsrc");
158 }
159 
160 QString 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 
186 MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod MessageViewer::DKIMUtil::convertAuthenticationMethodStringToEnum(const QString &str)
187 {
188  if (str == QLatin1String("dkim")) {
189  return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dkim;
190  } else if (str == QLatin1String("spf")) {
191  return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Spf;
192  } else if (str == QLatin1String("dmarc")) {
193  return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dmarc;
194  } else if (str == QLatin1String("dkim-atps")) {
195  return MessageViewer::DKIMCheckSignatureJob::AuthenticationMethod::Dkimatps;
196  } else if (str == QLatin1String("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 }
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString & remove(int position, int n)
void chop(int n)
void clear()
bool isEmpty() const const
QString trimmed() const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString right(int n) const const
QString toLower() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString & replace(int position, int n, QChar after)
const QChar at(int position) const const
int length() const const
QByteArray hash(const QByteArray &data, QCryptographicHash::Algorithm method)
QByteArray toBase64(QByteArray::Base64Options options) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Dec 5 2021 23:04:53 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.