1 /*
2   SPDX-FileCopyrightText: 2015-2021 Laurent Montel <montel@kde.org>
3 
4   SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "searchrulenumerical.h"
8 
9 #include "filter/filterlog.h"
10 using MailCommon::FilterLog;
11 
12 #include <KMime/KMimeMessage>
13 #include <QDateTime>
14 
15 #include <QRegularExpression>
16 
17 #include <algorithm>
18 
19 using namespace MailCommon;
20 
SearchRuleNumerical(const QByteArray & field,Function func,const QString & contents)21 SearchRuleNumerical::SearchRuleNumerical(const QByteArray &field, Function func, const QString &contents)
22     : SearchRule(field, func, contents)
23 {
24 }
25 
isEmpty() const26 bool SearchRuleNumerical::isEmpty() const
27 {
28     bool ok = false;
29     contents().toLongLong(&ok);
30 
31     return !ok;
32 }
33 
matches(const Akonadi::Item & item) const34 bool SearchRuleNumerical::matches(const Akonadi::Item &item) const
35 {
36     if (!item.hasPayload<KMime::Message::Ptr>()) {
37         return false;
38     }
39 
40     const auto msg = item.payload<KMime::Message::Ptr>();
41 
42     QString msgContents;
43     qint64 numericalMsgContents = 0;
44     qint64 numericalValue = 0;
45 
46     if (qstricmp(field().constData(), "<size>") == 0) {
47         numericalMsgContents = item.size();
48         numericalValue = contents().toLongLong();
49         msgContents.setNum(numericalMsgContents);
50     } else if (qstricmp(field().constData(), "<age in days>") == 0) {
51         QDateTime msgDateTime = msg->date()->dateTime();
52         numericalMsgContents = msgDateTime.daysTo(QDateTime::currentDateTime());
53         numericalValue = contents().toInt();
54         msgContents.setNum(numericalMsgContents);
55     } else {
56         return false;
57     }
58     bool rc = matchesInternal(numericalValue, numericalMsgContents, msgContents);
59     if (FilterLog::instance()->isLogging()) {
60         QString msg = (rc ? QStringLiteral("<font color=#00FF00>1 = </font>") : QStringLiteral("<font color=#FF0000>0 = </font>"));
61         msg += FilterLog::recode(asString());
62         msg += QLatin1String(" ( <i>") + QString::number(numericalMsgContents) + QLatin1String("</i> )");
63         FilterLog::instance()->add(msg, FilterLog::RuleResult);
64     }
65     return rc;
66 }
67 
requiredPart() const68 SearchRule::RequiredPart SearchRuleNumerical::requiredPart() const
69 {
70     return SearchRule::Envelope;
71 }
72 
matchesInternal(long numericalValue,long numericalMsgContents,const QString & msgContents) const73 bool SearchRuleNumerical::matchesInternal(long numericalValue, long numericalMsgContents, const QString &msgContents) const
74 {
75     switch (function()) {
76     case SearchRule::FuncEquals:
77         return numericalValue == numericalMsgContents;
78 
79     case SearchRule::FuncNotEqual:
80         return numericalValue != numericalMsgContents;
81 
82     case SearchRule::FuncContains:
83         return msgContents.contains(contents(), Qt::CaseInsensitive);
84 
85     case SearchRule::FuncContainsNot:
86         return !msgContents.contains(contents(), Qt::CaseInsensitive);
87 
88     case SearchRule::FuncRegExp:
89         return msgContents.contains(QRegularExpression(contents(), QRegularExpression::CaseInsensitiveOption));
90 
91     case SearchRule::FuncNotRegExp:
92         return !msgContents.contains(QRegularExpression(contents(), QRegularExpression::CaseInsensitiveOption));
93 
94     case FuncIsGreater:
95         return numericalMsgContents > numericalValue;
96 
97     case FuncIsLessOrEqual:
98         return numericalMsgContents <= numericalValue;
99 
100     case FuncIsLess:
101         return numericalMsgContents < numericalValue;
102 
103     case FuncIsGreaterOrEqual:
104         return numericalMsgContents >= numericalValue;
105 
106     case FuncIsInAddressbook: // since email-addresses are not numerical, I settle for false here
107         return false;
108 
109     case FuncIsNotInAddressbook:
110         return false;
111 
112     default:;
113     }
114 
115     return false;
116 }
117 
addQueryTerms(Akonadi::SearchTerm & groupTerm,bool & emptyIsNotAnError) const118 void SearchRuleNumerical::addQueryTerms(Akonadi::SearchTerm &groupTerm, bool &emptyIsNotAnError) const
119 {
120     using namespace Akonadi;
121     emptyIsNotAnError = false;
122     if (qstricmp(field().constData(), "<size>") == 0) {
123         EmailSearchTerm term(EmailSearchTerm::ByteSize, contents().toInt(), akonadiComparator());
124         term.setIsNegated(isNegated());
125         groupTerm.addSubTerm(term);
126     } else if (qstricmp(field().constData(), "<age in days>") == 0) {
127         QDate date(QDate::currentDate());
128         date = date.addDays(contents().toInt());
129         EmailSearchTerm term(EmailSearchTerm::HeaderOnlyDate, date, akonadiComparator());
130         term.setIsNegated(isNegated());
131         groupTerm.addSubTerm(term);
132     }
133 }
134 
informationAboutNotValidRules() const135 QString SearchRuleNumerical::informationAboutNotValidRules() const
136 {
137     return i18n("Content is not a number.");
138 }
139