1 /*
2   SPDX-FileCopyrightText: 2015-2021 Laurent Montel <montel@kde.org>
3 
4   SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 #pragma once
7 
8 #include "mailcommon_export.h"
9 #include <Akonadi/SearchQuery>
10 
11 #include <Akonadi/Item>
12 
13 class KConfigGroup;
14 namespace MailCommon
15 {
16 /**
17  * @short This class represents one search pattern rule.
18  * Incoming mail is sent through the list of mail filter
19  * rules before it is placed in the associated mail folder (usually "inbox").
20  * This class represents one mail filter rule. It is also used to represent
21  * a search rule as used by the search dialog and folders.
22  */
23 class MAILCOMMON_EXPORT SearchRule
24 {
25 public:
26     /**
27      * Defines a pointer to a search rule.
28      */
29     using Ptr = std::shared_ptr<SearchRule>;
30 
31     /**
32      * Describes operators for comparison of field and contents.
33      *
34      * If you change the order or contents of the enum: do not forget
35      * to change funcConfigNames[], sFilterFuncList and matches()
36      * in SearchRule, too.
37      * Also, it is assumed that these functions come in pairs of logical
38      * opposites (ie. "=" <-> "!=", ">" <-> "<=", etc.).
39      */
40     enum Function {
41         FuncNone = -1,
42         FuncContains = 0,
43         FuncContainsNot,
44         FuncEquals,
45         FuncNotEqual,
46         FuncRegExp,
47         FuncNotRegExp,
48         FuncIsGreater,
49         FuncIsLessOrEqual,
50         FuncIsLess,
51         FuncIsGreaterOrEqual,
52         FuncIsInAddressbook,
53         FuncIsNotInAddressbook,
54         FuncIsInCategory,
55         FuncIsNotInCategory,
56         FuncHasAttachment,
57         FuncHasNoAttachment,
58         FuncStartWith,
59         FuncNotStartWith,
60         FuncEndWith,
61         FuncNotEndWith
62     };
63 
64     /**
65      * @enum RequiredPart
66      * @brief Possible required parts.
67      */
68     enum RequiredPart {
69         Envelope = 0, ///< Envelope
70         Header, ///< Header
71         CompleteMessage ///< Whole message
72     };
73 
74     /**
75      * Creates new new search rule.
76      *
77      * @param field The field to search in.
78      * @param function The function to use for searching.
79      * @param contents The contents to search for.
80      */
81     explicit SearchRule(const QByteArray &field = QByteArray(), Function function = FuncContains, const QString &contents = QString());
82 
83     /**
84      * Creates a new search rule from an @p other rule.
85      */
86     SearchRule(const SearchRule &other);
87 
88     /**
89      * Initializes this rule with an @p other rule.
90      */
91     const SearchRule &operator=(const SearchRule &other);
92 
93     /**
94      * Creates a new search rule of a certain type by instantiating the
95      * appropriate subclass depending on the @p field.
96      *
97      * @param field The field to search in.
98      * @param function The function to use for searching.
99      * @param contents The contents to search for.
100      */
101     static SearchRule::Ptr createInstance(const QByteArray &field = QByteArray(), Function function = FuncContains, const QString &contents = QString());
102 
103     /**
104      * Creates a new search rule of a certain type by instantiating the
105      * appropriate subclass depending on the @p field.
106      *
107      * @param field The field to search in.
108      * @param function The name of the function to use for searching.
109      * @param contents The contents to search for.
110      */
111     static SearchRule::Ptr createInstance(const QByteArray &field, const char *function, const QString &contents);
112 
113     /**
114      * Creates a new search rule by cloning an @p other rule.
115      */
116     static SearchRule::Ptr createInstance(const SearchRule &other);
117 
118     /**
119      * Creates a new search rule by deseralizing its structure from a data @p stream.
120      */
121     static SearchRule::Ptr createInstance(QDataStream &stream);
122 
123     /**
124      * Creates a new search rule from a given config @p group.
125      *
126      * @param group The config group to read the structure from.
127      * @param index The identifier that is used to distinguish
128      *              rules within a single config group.
129      *
130      * @note This function does no validation of the data obtained
131      *       from the config file. You should call isEmpty yourself
132      *       if you need valid rules.
133      */
134     static SearchRule::Ptr createInstanceFromConfig(const KConfigGroup &group, int index);
135 
136     /**
137      * Destroys the search rule.
138      */
139     virtual ~SearchRule();
140 
141     /**
142      * Tries to match the rule against the KMime::Message in the
143      * given @p item.
144      *
145      * @return true if the rule matched, false otherwise.
146      *
147      * @note Must be implemented by subclasses.
148      */
149     virtual bool matches(const Akonadi::Item &item) const = 0;
150 
151     /**
152      * Determines whether the rule is worth considering.
153      * It isn't if either the field is not set or the contents is empty.
154      * The calling code should make sure that it's rule list contains
155      * only non-empty rules, as matches doesn't check this.
156      */
157     virtual bool isEmpty() const = 0;
158 
159     /**
160      * Returns the required part from the item that is needed for the search to
161      * operate. See @ref RequiredPart */
162     virtual SearchRule::RequiredPart requiredPart() const = 0;
163 
164     /**
165      * Saves the object into a given config @p group.
166      *
167      * @param group The config group.
168      * @param index The identifier that is used to distinguish
169      *              rules within a single config group.
170      *
171      * @note This function will happily write itself even when it's
172      *       not valid, assuming higher layers to Do The Right Thing(TM).
173      */
174     void writeConfig(KConfigGroup &group, int index) const;
175 
176     void generateSieveScript(QStringList &requireModules, QString &code);
177 
178     /**
179      * Sets the filter @p function of the rule.
180      */
181     void setFunction(Function function);
182 
183     /**
184      * Returns the filter function of the rule.
185      */
186     Function function() const;
187 
188     /**
189      * Sets the message header field @p name.
190      *
191      * @note Make sure the name contains no trailing ':'.
192      */
193     void setField(const QByteArray &name);
194 
195     /**
196      * Returns the message header field name (without the trailing ':').
197      *
198      * There are also six pseudo-headers:
199      * @li \<message\>: Try to match against the whole message.
200      * @li \<body\>: Try to match against the body of the message.
201      * @li \<any header\>: Try to match against any header field.
202      * @li \<recipients\>: Try to match against both To: and Cc: header fields.
203      * @li \<size\>: Try to match against size of message (numerical).
204      * @li \<age in days\>: Try to match against age of message (numerical).
205      * @li \<status\>: Try to match against status of message (status).
206      * @li \<tag\>: Try to match against message tags.
207      */
208     QByteArray field() const;
209 
210     /**
211      * Set the @p contents of the rule.
212      *
213      * This can be either a substring to search for in
214      * or a regexp pattern to match against the header.
215      */
216     void setContents(const QString &contents);
217 
218     /**
219      * Returns the contents of the rule.
220      */
221     QString contents() const;
222 
223     /**
224      * Returns the rule as string for debugging purpose
225      */
226     const QString asString() const;
227 
228     /**
229      * Adds query terms to the given term group.
230      */
addQueryTerms(Akonadi::SearchTerm & groupTerm,bool & emptyIsNotAnError)231     virtual void addQueryTerms(Akonadi::SearchTerm &groupTerm, bool &emptyIsNotAnError) const
232     {
233         Q_UNUSED(groupTerm)
234         Q_UNUSED(emptyIsNotAnError)
235     }
236 
237     QDataStream &operator>>(QDataStream &) const;
informationAboutNotValidRules()238     virtual QString informationAboutNotValidRules() const
239     {
240         return QString();
241     }
242 
243 protected:
244     /**
245      * Helper that returns whether the rule has a negated function.
246      */
247     bool isNegated() const;
248 
249     /**
250      * Converts the rule function into the corresponding Akonadi query operator.
251      */
252     Akonadi::SearchTerm::Condition akonadiComparator() const;
253 
254 private:
255     static Function configValueToFunc(const char *);
256     static QString functionToString(Function);
257     QString conditionToString(Function function);
258 
259     QByteArray mField;
260     Function mFunction;
261     QString mContents;
262 };
263 }
264 
265