Messagelib

dkimauthenticationstatusinfo.cpp
1 /*
2  SPDX-FileCopyrightText: 2018-2023 Laurent Montel <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "dkimauthenticationstatusinfo.h"
8 #include "dkimauthenticationstatusinfoutil.h"
9 #include "messageviewer_dkimcheckerdebug.h"
10 
11 #include <QRegularExpressionMatch>
12 using namespace MessageViewer;
13 // see https://tools.ietf.org/html/rfc7601
14 DKIMAuthenticationStatusInfo::DKIMAuthenticationStatusInfo() = default;
15 
16 bool DKIMAuthenticationStatusInfo::parseAuthenticationStatus(const QString &key, bool relaxingParsing)
17 {
18  QString valueKey = key;
19  // kmime remove extra \r\n but we need it for regexp at the end.
20  if (!valueKey.endsWith(QLatin1String("\r\n"))) {
21  valueKey += QLatin1String("\r\n");
22  }
23  // https://tools.ietf.org/html/rfc7601#section-2.2
24  // authres-header = "Authentication-Results:" [CFWS] authserv-id
25  // [ CFWS authres-version ]
26  // ( no-result / 1*resinfo ) [CFWS] CRLF
27 
28  // 1) extract AuthservId and AuthVersion
30  const QString regStr = DKIMAuthenticationStatusInfoUtil::value_cp() + QLatin1String("(?:") + DKIMAuthenticationStatusInfoUtil::cfws_p()
31  + QLatin1String("([0-9]+)") + DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1String(" )?");
32  // qDebug() << " regStr" << regStr;
33  static const QRegularExpression regular1(regStr);
34  int index = valueKey.indexOf(regular1, 0, &match);
35  if (index != -1) {
36  mAuthservId = match.captured(1);
37  const QString authVersionStr = match.captured(2);
38  if (!authVersionStr.isEmpty()) {
39  mAuthVersion = authVersionStr.toInt();
40  } else {
41  mAuthVersion = 1;
42  }
43  valueKey = valueKey.right(valueKey.length() - (index + match.capturedLength(0)));
44  // qDebug() << " match.captured(0)"<<match.captured(0)<<"match.captured(1)" <<match.captured(1) << authVersionStr;
45  // qDebug() << " valueKey" << valueKey;
46  } else {
47  return false;
48  }
49  // check if message authentication was performed
50  const QString authResultStr = DKIMAuthenticationStatusInfoUtil::regexMatchO(DKIMAuthenticationStatusInfoUtil::value_cp() + QLatin1String(";")
51  + DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1String("?none"));
52  // qDebug() << "authResultStr "<<authResultStr;
53  static const QRegularExpression regular2(authResultStr);
54  index = valueKey.indexOf(regular2, 0, &match);
55  if (index != -1) {
56  // no result
57  return false;
58  }
59  while (!valueKey.isEmpty()) {
60  // qDebug() << "valueKey LOOP" << valueKey;
61  const AuthStatusInfo resultInfo = parseAuthResultInfo(valueKey, relaxingParsing);
62  if (resultInfo.isValid()) {
63  mListAuthStatusInfo.append(resultInfo);
64  }
65  }
66  return true;
67 }
68 
69 DKIMAuthenticationStatusInfo::AuthStatusInfo DKIMAuthenticationStatusInfo::parseAuthResultInfo(QString &valueKey, bool relaxingParsing)
70 {
71  DKIMAuthenticationStatusInfo::AuthStatusInfo authStatusInfo;
72  // 2) extract methodspec
73  const QString methodVersionp =
74  DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1Char('/') + DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1String("([0-9]+)");
75  const QString method_p = QLatin1Char('(') + DKIMAuthenticationStatusInfoUtil::keyword_p() + QLatin1String(")(?:") + methodVersionp + QLatin1String(")?");
76  QString Keyword_result_p = QStringLiteral("none|pass|fail|softfail|policy|neutral|temperror|permerror");
77  // older SPF specs (e.g. RFC 4408) use mixed case
78  Keyword_result_p += QLatin1String("|None|Pass|Fail|SoftFail|Neutral|TempError|PermError");
79  const QString result_p = QLatin1Char('=') + DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1Char('(') + Keyword_result_p + QLatin1Char(')');
80  const QString methodspec_p =
81  QLatin1Char(';') + DKIMAuthenticationStatusInfoUtil::cfws_op() + method_p + DKIMAuthenticationStatusInfoUtil::cfws_op() + result_p;
82 
83  // qDebug() << "methodspec_p " << methodspec_p;
85  static const QRegularExpression reg2(methodspec_p);
86  int index = valueKey.indexOf(reg2, 0, &match);
87  if (index == -1) {
88  valueKey = QString(); // remove it !
89  qCDebug(MESSAGEVIEWER_DKIMCHECKER_LOG) << "methodspec not found ";
90  // no result
91  return authStatusInfo;
92  }
93  // qDebug() << " match" << match.captured(0) << match.captured(1) << match.capturedTexts();
94  authStatusInfo.method = match.captured(1);
95  const QString authVersionStr = match.captured(2);
96  if (!authVersionStr.isEmpty()) {
97  authStatusInfo.methodVersion = authVersionStr.toInt();
98  } else {
99  authStatusInfo.methodVersion = 1;
100  }
101  authStatusInfo.result = match.captured(3);
102 
103  valueKey = valueKey.right(valueKey.length() - (index + match.capturedLength(0))); // Improve it!
104 
105  // 3) extract reasonspec (optional)
106  const QString reasonspec_p =
107  DKIMAuthenticationStatusInfoUtil::regexMatchO(QLatin1String("reason") + DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1Char('=')
108  + DKIMAuthenticationStatusInfoUtil::cfws_op() + DKIMAuthenticationStatusInfoUtil::value_cp());
109  static const QRegularExpression reg31(reasonspec_p);
110  index = valueKey.indexOf(reg31, 0, &match);
111  if (index != -1) {
112  // qDebug() << " reason " << match.capturedTexts();
113  authStatusInfo.reason = match.captured(2);
114  valueKey = valueKey.right(valueKey.length() - (index + match.capturedLength(0))); // Improve it!
115  }
116  // 4) extract propspec (optional)
117  QString pvalue_p = DKIMAuthenticationStatusInfoUtil::value_p() + QLatin1String("|(?:(?:") + DKIMAuthenticationStatusInfoUtil::localPart_p()
118  + QLatin1String("[email protected])?") + DKIMAuthenticationStatusInfoUtil::domainName_p() + QLatin1Char(')');
119  if (relaxingParsing) {
120  // Allow "/" in the header.b (or other) property, even if it is not in a quoted-string
121  pvalue_p += QStringLiteral("|[^ \\x00-\\x1F\\x7F()<>@,;:\\\\\"[\\]?=]+");
122  }
123 
124  const QString property_p = QLatin1String("mailfrom|rcptto") + QLatin1Char('|') + DKIMAuthenticationStatusInfoUtil::keyword_p();
125  const QString propspec_p = QLatin1Char('(') + DKIMAuthenticationStatusInfoUtil::keyword_p() + QLatin1Char(')') + DKIMAuthenticationStatusInfoUtil::cfws_op()
126  + QLatin1String("\\.") + DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1Char('(') + property_p + QLatin1Char(')')
127  + DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1Char('=') + DKIMAuthenticationStatusInfoUtil::cfws_op() + QLatin1Char('(')
128  + pvalue_p /*+ QLatin1Char(')')*/;
129 
130  // qDebug() << "propspec_p " << propspec_p;
131 
132  const QString regexp = DKIMAuthenticationStatusInfoUtil::regexMatchO(propspec_p);
133  static const QRegularExpression reg(regexp);
134  if (!reg.isValid()) {
135  qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << " reg error : " << reg.errorString();
136  } else {
137  index = valueKey.indexOf(reg, 0, &match);
138  while (index != -1) {
139  // qDebug() << " propspec " << match.capturedTexts();
140  valueKey = valueKey.right(valueKey.length() - (index + match.capturedLength(0))); // Improve it!
141  // qDebug() << " value KEy " << valueKey;
142  const QString &captured1 = match.captured(1);
143  // qDebug() << " captured1 " << captured1;
144  if (captured1 == QLatin1String("header")) {
145  AuthStatusInfo::Property prop;
146  prop.type = match.captured(2);
147  prop.value = match.captured(3);
148  authStatusInfo.header.append(prop);
149  } else if (captured1 == QLatin1String("smtp")) {
150  AuthStatusInfo::Property prop;
151  prop.type = match.captured(2);
152  prop.value = match.captured(3);
153  authStatusInfo.smtp.append(prop);
154  } else if (captured1 == QLatin1String("body")) {
155  AuthStatusInfo::Property prop;
156  prop.type = match.captured(2);
157  prop.value = match.captured(3);
158  authStatusInfo.body.append(prop);
159  } else if (captured1 == QLatin1String("policy")) {
160  AuthStatusInfo::Property prop;
161  prop.type = match.captured(2);
162  prop.value = match.captured(3);
163  authStatusInfo.policy.append(prop);
164  } else {
165  qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Unknown type found " << captured1;
166  }
167  index = valueKey.indexOf(reg, 0, &match);
168  }
169  }
170  return authStatusInfo;
171 }
172 
173 int DKIMAuthenticationStatusInfo::authVersion() const
174 {
175  return mAuthVersion;
176 }
177 
178 void DKIMAuthenticationStatusInfo::setAuthVersion(int authVersion)
179 {
180  mAuthVersion = authVersion;
181 }
182 
183 QString DKIMAuthenticationStatusInfo::reasonSpec() const
184 {
185  return mReasonSpec;
186 }
187 
188 void DKIMAuthenticationStatusInfo::setReasonSpec(const QString &reasonSpec)
189 {
190  mReasonSpec = reasonSpec;
191 }
192 
193 bool DKIMAuthenticationStatusInfo::operator==(const DKIMAuthenticationStatusInfo &other) const
194 {
195  return mAuthservId == other.authservId() && mAuthVersion == other.authVersion() && mReasonSpec == other.reasonSpec()
196  && mListAuthStatusInfo == other.listAuthStatusInfo();
197 }
198 
199 QVector<DKIMAuthenticationStatusInfo::AuthStatusInfo> DKIMAuthenticationStatusInfo::listAuthStatusInfo() const
200 {
201  return mListAuthStatusInfo;
202 }
203 
204 void DKIMAuthenticationStatusInfo::setListAuthStatusInfo(const QVector<AuthStatusInfo> &listAuthStatusInfo)
205 {
206  mListAuthStatusInfo = listAuthStatusInfo;
207 }
208 
209 QString DKIMAuthenticationStatusInfo::authservId() const
210 {
211  return mAuthservId;
212 }
213 
214 void DKIMAuthenticationStatusInfo::setAuthservId(const QString &authservId)
215 {
216  mAuthservId = authservId;
217 }
218 
219 QDebug operator<<(QDebug d, const DKIMAuthenticationStatusInfo &t)
220 {
221  d << "mAuthservId: " << t.authservId();
222  d << "mReasonSpec: " << t.reasonSpec();
223  d << "mAuthVersion: " << t.authVersion() << '\n';
224  const auto listAuthStatusInfo = t.listAuthStatusInfo();
225  for (const DKIMAuthenticationStatusInfo::AuthStatusInfo &info : listAuthStatusInfo) {
226  d << "mListAuthStatusInfo: " << info.method << " : " << info.result << " : " << info.methodVersion << " : " << info.reason << '\n';
227  d << "Property:" << '\n';
228  if (!info.smtp.isEmpty()) {
229  for (const DKIMAuthenticationStatusInfo::AuthStatusInfo::Property &prop : info.smtp) {
230  d << " smtp " << prop.type << " : " << prop.value << '\n';
231  }
232  }
233  if (!info.header.isEmpty()) {
234  for (const DKIMAuthenticationStatusInfo::AuthStatusInfo::Property &prop : info.header) {
235  d << " header " << prop.type << " : " << prop.value << '\n';
236  }
237  }
238  if (!info.body.isEmpty()) {
239  for (const DKIMAuthenticationStatusInfo::AuthStatusInfo::Property &prop : info.body) {
240  d << " body " << prop.type << " : " << prop.value << '\n';
241  }
242  }
243  if (!info.policy.isEmpty()) {
244  for (const DKIMAuthenticationStatusInfo::AuthStatusInfo::Property &prop : info.policy) {
245  d << " policy " << prop.type << " : " << prop.value << '\n';
246  }
247  }
248  }
249  return d;
250 }
251 
252 bool DKIMAuthenticationStatusInfo::AuthStatusInfo::operator==(const DKIMAuthenticationStatusInfo::AuthStatusInfo &other) const
253 {
254  return other.method == method && other.result == result && other.methodVersion == methodVersion && other.reason == reason && other.policy == policy
255  && other.smtp == smtp && other.header == header && other.body == body;
256 }
257 
258 bool DKIMAuthenticationStatusInfo::AuthStatusInfo::isValid() const
259 {
260  return !method.isEmpty();
261 }
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QDataStream & operator<<(QDataStream &out, const KDateTime &dateTime)
bool isEmpty() const const
int length() const const
int toInt(bool *ok, int base) const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString right(int n) const const
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Mar 22 2023 04:07:14 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.