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/KMimeMessage>
17#include <KMime/Message>
18
19#include <QRegularExpression>
20
21using namespace MessageViewer;
22
23// static
25{
26 SpamScores scores;
27 const SpamAgents agents = AntiSpamConfig::instance()->uniqueAgents();
29 for (SpamAgents::const_iterator it = agents.constBegin(); it != end; ++it) {
30 float score = -2.0;
31
32 SpamError spamError = noError;
33
34 // Skip bogus agents
35 if ((*it).scoreType() == SpamAgentNone) {
36 continue;
37 }
38
39 // Do we have the needed score field for this agent?
40 KMime::Headers::Base *header = message->headerByType((*it).header().constData());
41 if (!header) {
42 continue;
43 }
44
45 const QString mField = header->asUnicodeString();
46
47 if (mField.isEmpty()) {
48 continue;
49 }
50
51 QString scoreString;
52 bool scoreValid = false;
53
54 if ((*it).scoreType() != SpamAgentBool) {
55 // Can we extract the score?
56 QRegularExpression scorePattern = (*it).scorePattern();
57 if (scorePattern.match(mField).hasMatch()) {
58 scoreString = scorePattern.match(mField).captured(1);
59 scoreValid = true;
60 }
61 } else {
62 scoreValid = true;
63 }
64
65 if (!scoreValid) {
66 spamError = couldNotFindTheScoreField;
67 qCDebug(MESSAGEVIEWER_LOG) << "Score could not be extracted from header '" << mField << "'";
68 } else {
69 bool floatValid = false;
70 switch ((*it).scoreType()) {
71 case SpamAgentNone:
72 spamError = errorExtractingAgentString;
73 break;
74
75 case SpamAgentBool:
76 if ((*it).scorePattern().match(mField).hasMatch()) {
77 score = 0.0;
78 } else {
79 score = 100.0;
80 }
81 break;
82
83 case SpamAgentFloat:
84 score = scoreString.toFloat(&floatValid);
85 if (!floatValid) {
86 spamError = couldNotConverScoreToFloat;
87 qCDebug(MESSAGEVIEWER_LOG) << "Score (" << scoreString << ") is no number";
88 } else {
89 score *= 100.0;
90 }
91 break;
92
93 case SpamAgentFloatLarge:
94 score = scoreString.toFloat(&floatValid);
95 if (!floatValid) {
96 spamError = couldNotConverScoreToFloat;
97 qCDebug(MESSAGEVIEWER_LOG) << "Score (" << scoreString << ") is no number";
98 }
99 break;
100
101 case SpamAgentAdjustedFloat:
102 score = scoreString.toFloat(&floatValid);
103 if (!floatValid) {
104 spamError = couldNotConverScoreToFloat;
105 qCDebug(MESSAGEVIEWER_LOG) << "Score (" << scoreString << ") is no number";
106 break;
107 }
108
109 // Find the threshold value.
110 QString thresholdString;
111 const QRegularExpression thresholdPattern = (*it).thresholdPattern();
112 if (thresholdPattern.match(mField).hasMatch()) {
113 thresholdString = thresholdPattern.match(mField).captured(1);
114 } else {
115 spamError = couldNotFindTheThresholdField;
116 qCDebug(MESSAGEVIEWER_LOG) << "Threshold could not be extracted from header '" << mField << "'";
117 break;
118 }
119 const float threshold = thresholdString.toFloat(&floatValid);
120 if (!floatValid || (threshold <= 0.0)) {
121 spamError = couldNotConvertThresholdToFloatOrThresholdIsNegative;
122 qCDebug(MESSAGEVIEWER_LOG) << "Threshold (" << thresholdString << ") is no"
123 << "number or is negative";
124 break;
125 }
126
127 // Normalize the score. Anything below 0 means 0%, anything above
128 // threshold mean 100%. Values between 0 and threshold are mapped
129 // linearly to 0% - 100%.
130 if (score < 0.0) {
131 score = 0.0;
132 } else if (score > threshold) {
133 score = 100.0;
134 } else {
135 score = score / threshold * 100.0;
136 }
137 break;
138 }
139 }
140 // Find the confidence
141 float confidence = -2.0;
142 QString confidenceString = QStringLiteral("-2.0");
143 bool confidenceValid = false;
144 // Do we have the needed confidence field for this agent?
145 const QByteArray confidenceHeaderName = (*it).confidenceHeader();
146 QString mCField;
147 if (!confidenceHeaderName.isEmpty()) {
148 KMime::Headers::Base *cHeader = message->headerByType(confidenceHeaderName.constData());
149 if (cHeader) {
150 mCField = cHeader->asUnicodeString();
151 if (!mCField.isEmpty()) {
152 // Can we extract the confidence?
153 QRegularExpression cScorePattern = (*it).confidencePattern();
154 if (cScorePattern.match(mCField).hasMatch()) {
155 confidenceString = cScorePattern.match(mCField).captured(1);
156 }
157 confidence = confidenceString.toFloat(&confidenceValid);
158 if (!confidenceValid) {
159 spamError = couldNotConvertConfidenceToFloat;
160 qCDebug(MESSAGEVIEWER_LOG) << "Unable to convert confidence to float:" << confidenceString;
161 }
162 }
163 }
164 }
165 scores.append(SpamScore((*it).name(), spamError, score, confidence * 100, mField, mCField));
166 }
167
168 return scores;
169}
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 Tue Mar 26 2024 11:12:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.