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 <paudley@blackcat.ca>
6 SPDX-FileCopyrightText: 2004 Ingo Kloecker <kloecker@kde.org>
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/Message>
17
18#include <QRegularExpression>
19
20using 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}
virtual QString asUnicodeString() const=0
static SpamScores getSpamScores(KMime::Message *message)
Extract scores from known anti-spam headers.
A simple tuple of error, agent, score, confidence and header.
const char * constData() const const
bool isEmpty() const const
void append(QList< T > &&value)
const_iterator constBegin() const const
const_iterator constEnd() const const
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, MatchType matchType, MatchOptions matchOptions) const const
QString captured(QStringView name) const const
bool hasMatch() const const
bool isEmpty() const const
float toFloat(bool *ok) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:33:26 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.