Messagelib

spamheaderanalyzer.cpp
1 /*
2  spamheaderanalyzer.cpp
3 
4  This file is part of KMail, the KDE mail client.
5  SPDX-FileCopyrightText: 2004 Patrick Audley <[email protected]>
6  SPDX-FileCopyrightText: 2004 Ingo Kloecker <[email protected]>
7 
8  SPDX-License-Identifier: GPL-2.0-or-later
9 */
10 
11 #include "spamheaderanalyzer.h"
12 #include "antispamconfig.h"
13 #include "messageviewer_debug.h"
14 
15 #include <KMime/Headers>
16 #include <KMime/KMimeMessage>
17 
18 #include <QRegularExpression>
19 
20 using namespace MessageViewer;
21 
22 // static
24 {
25  SpamScores scores;
26  const SpamAgents agents = AntiSpamConfig::instance()->uniqueAgents();
28  for (SpamAgents::const_iterator it = agents.constBegin(); it != end; ++it) {
29  float score = -2.0;
30 
31  SpamError spamError = noError;
32 
33  // Skip bogus agents
34  if ((*it).scoreType() == SpamAgentNone) {
35  continue;
36  }
37 
38  // Do we have the needed score field for this agent?
39  KMime::Headers::Base *header = message->headerByType((*it).header().constData());
40  if (!header) {
41  continue;
42  }
43 
44  const QString mField = header->asUnicodeString();
45 
46  if (mField.isEmpty()) {
47  continue;
48  }
49 
50  QString scoreString;
51  bool scoreValid = false;
52 
53  if ((*it).scoreType() != SpamAgentBool) {
54  // Can we extract the score?
55  QRegularExpression scorePattern = (*it).scorePattern();
56  if (scorePattern.match(mField).hasMatch()) {
57  scoreString = scorePattern.match(mField).captured(1);
58  scoreValid = true;
59  }
60  } else {
61  scoreValid = true;
62  }
63 
64  if (!scoreValid) {
65  spamError = couldNotFindTheScoreField;
66  qCDebug(MESSAGEVIEWER_LOG) << "Score could not be extracted from header '" << mField << "'";
67  } else {
68  bool floatValid = false;
69  switch ((*it).scoreType()) {
70  case SpamAgentNone:
71  spamError = errorExtractingAgentString;
72  break;
73 
74  case SpamAgentBool:
75  if ((*it).scorePattern().match(mField).hasMatch()) {
76  score = 0.0;
77  } else {
78  score = 100.0;
79  }
80  break;
81 
82  case SpamAgentFloat:
83  score = scoreString.toFloat(&floatValid);
84  if (!floatValid) {
85  spamError = couldNotConverScoreToFloat;
86  qCDebug(MESSAGEVIEWER_LOG) << "Score (" << scoreString << ") is no number";
87  } else {
88  score *= 100.0;
89  }
90  break;
91 
92  case SpamAgentFloatLarge:
93  score = scoreString.toFloat(&floatValid);
94  if (!floatValid) {
95  spamError = couldNotConverScoreToFloat;
96  qCDebug(MESSAGEVIEWER_LOG) << "Score (" << scoreString << ") is no number";
97  }
98  break;
99 
100  case SpamAgentAdjustedFloat:
101  score = scoreString.toFloat(&floatValid);
102  if (!floatValid) {
103  spamError = couldNotConverScoreToFloat;
104  qCDebug(MESSAGEVIEWER_LOG) << "Score (" << scoreString << ") is no number";
105  break;
106  }
107 
108  // Find the threshold value.
109  QString thresholdString;
110  const QRegularExpression thresholdPattern = (*it).thresholdPattern();
111  if (thresholdPattern.match(mField).hasMatch()) {
112  thresholdString = thresholdPattern.match(mField).captured(1);
113  } else {
114  spamError = couldNotFindTheThresholdField;
115  qCDebug(MESSAGEVIEWER_LOG) << "Threshold could not be extracted from header '" << mField << "'";
116  break;
117  }
118  const float threshold = thresholdString.toFloat(&floatValid);
119  if (!floatValid || (threshold <= 0.0)) {
120  spamError = couldNotConvertThresholdToFloatOrThresholdIsNegative;
121  qCDebug(MESSAGEVIEWER_LOG) << "Threshold (" << thresholdString << ") is no"
122  << "number or is negative";
123  break;
124  }
125 
126  // Normalize the score. Anything below 0 means 0%, anything above
127  // threshold mean 100%. Values between 0 and threshold are mapped
128  // linearly to 0% - 100%.
129  if (score < 0.0) {
130  score = 0.0;
131  } else if (score > threshold) {
132  score = 100.0;
133  } else {
134  score = score / threshold * 100.0;
135  }
136  break;
137  }
138  }
139  // Find the confidence
140  float confidence = -2.0;
141  QString confidenceString = QStringLiteral("-2.0");
142  bool confidenceValid = false;
143  // Do we have the needed confidence field for this agent?
144  const QByteArray confidenceHeaderName = (*it).confidenceHeader();
145  QString mCField;
146  if (!confidenceHeaderName.isEmpty()) {
147  KMime::Headers::Base *cHeader = message->headerByType(confidenceHeaderName.constData());
148  if (cHeader) {
149  mCField = cHeader->asUnicodeString();
150  if (!mCField.isEmpty()) {
151  // Can we extract the confidence?
152  QRegularExpression cScorePattern = (*it).confidencePattern();
153  if (cScorePattern.match(mCField).hasMatch()) {
154  confidenceString = cScorePattern.match(mCField).captured(1);
155  }
156  confidence = confidenceString.toFloat(&confidenceValid);
157  if (!confidenceValid) {
158  spamError = couldNotConvertConfidenceToFloat;
159  qCDebug(MESSAGEVIEWER_LOG) << "Unable to convert confidence to float:" << confidenceString;
160  }
161  }
162  }
163  }
164  scores.append(SpamScore((*it).name(), spamError, score, confidence * 100, mField, mCField));
165  }
166 
167  return scores;
168 }
void append(const T &value)
QList::const_iterator constBegin() const const
bool isEmpty() const const
static SpamScores getSpamScores(KMime::Message *message)
Extract scores from known anti-spam headers.
float toFloat(bool *ok) const const
bool hasMatch() const const
QRegularExpressionMatch match(const QString &subject, int offset, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions) const const
A simple tuple of error, agent, score, confidence and header.
bool isEmpty() const const
QList::const_iterator constEnd() const const
const char * constData() const const
virtual QString asUnicodeString() const=0
QString message
QString captured(int nth) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sat Sep 23 2023 03:56:11 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.