File indexing completed on 2024-11-17 04:51:14

0001 /*
0002   SPDX-FileCopyrightText: 2015-2024 Laurent Montel <montel@kde.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #pragma once
0007 
0008 #include "mailcommon_export.h"
0009 #include <Akonadi/SearchQuery>
0010 
0011 #include <Akonadi/Item>
0012 
0013 class KConfigGroup;
0014 namespace MailCommon
0015 {
0016 /**
0017  * @short This class represents one search pattern rule.
0018  * Incoming mail is sent through the list of mail filter
0019  * rules before it is placed in the associated mail folder (usually "inbox").
0020  * This class represents one mail filter rule. It is also used to represent
0021  * a search rule as used by the search dialog and folders.
0022  */
0023 class MAILCOMMON_EXPORT SearchRule
0024 {
0025 public:
0026     /**
0027      * Defines a pointer to a search rule.
0028      */
0029     using Ptr = std::shared_ptr<SearchRule>;
0030 
0031     /**
0032      * Describes operators for comparison of field and contents.
0033      *
0034      * If you change the order or contents of the enum: do not forget
0035      * to change funcConfigNames[], sFilterFuncList and matches()
0036      * in SearchRule, too.
0037      * Also, it is assumed that these functions come in pairs of logical
0038      * opposites (ie. "=" <-> "!=", ">" <-> "<=", etc.).
0039      */
0040     enum Function {
0041         FuncNone = -1,
0042         FuncContains = 0,
0043         FuncContainsNot,
0044         FuncEquals,
0045         FuncNotEqual,
0046         FuncRegExp,
0047         FuncNotRegExp,
0048         FuncIsGreater,
0049         FuncIsLessOrEqual,
0050         FuncIsLess,
0051         FuncIsGreaterOrEqual,
0052         FuncIsInAddressbook,
0053         FuncIsNotInAddressbook,
0054         FuncIsInCategory,
0055         FuncIsNotInCategory,
0056         FuncHasAttachment,
0057         FuncHasNoAttachment,
0058         FuncStartWith,
0059         FuncNotStartWith,
0060         FuncEndWith,
0061         FuncNotEndWith
0062     };
0063 
0064     /**
0065      * @enum RequiredPart
0066      * @brief Possible required parts.
0067      */
0068     enum RequiredPart {
0069         Envelope = 0, ///< Envelope
0070         Header, ///< Header
0071         CompleteMessage ///< Whole message
0072     };
0073 
0074     /**
0075      * Creates new new search rule.
0076      *
0077      * @param field The field to search in.
0078      * @param function The function to use for searching.
0079      * @param contents The contents to search for.
0080      */
0081     explicit SearchRule(const QByteArray &field = QByteArray(), Function function = FuncContains, const QString &contents = QString());
0082 
0083     /**
0084      * Creates a new search rule from an @p other rule.
0085      */
0086     SearchRule(const SearchRule &other);
0087 
0088     /**
0089      * Initializes this rule with an @p other rule.
0090      */
0091     const SearchRule &operator=(const SearchRule &other);
0092 
0093     /**
0094      * Creates a new search rule of a certain type by instantiating the
0095      * appropriate subclass depending on the @p field.
0096      *
0097      * @param field The field to search in.
0098      * @param function The function to use for searching.
0099      * @param contents The contents to search for.
0100      */
0101     static SearchRule::Ptr createInstance(const QByteArray &field = QByteArray(), Function function = FuncContains, const QString &contents = QString());
0102 
0103     /**
0104      * Creates a new search rule of a certain type by instantiating the
0105      * appropriate subclass depending on the @p field.
0106      *
0107      * @param field The field to search in.
0108      * @param function The name of the function to use for searching.
0109      * @param contents The contents to search for.
0110      */
0111     static SearchRule::Ptr createInstance(const QByteArray &field, const char *function, const QString &contents);
0112 
0113     /**
0114      * Creates a new search rule by cloning an @p other rule.
0115      */
0116     static SearchRule::Ptr createInstance(const SearchRule &other);
0117 
0118     /**
0119      * Creates a new search rule by deseralizing its structure from a data @p stream.
0120      */
0121     static SearchRule::Ptr createInstance(QDataStream &stream);
0122 
0123     /**
0124      * Creates a new search rule from a given config @p group.
0125      *
0126      * @param group The config group to read the structure from.
0127      * @param index The identifier that is used to distinguish
0128      *              rules within a single config group.
0129      *
0130      * @note This function does no validation of the data obtained
0131      *       from the config file. You should call isEmpty yourself
0132      *       if you need valid rules.
0133      */
0134     static SearchRule::Ptr createInstanceFromConfig(const KConfigGroup &group, int index);
0135 
0136     /**
0137      * Destroys the search rule.
0138      */
0139     virtual ~SearchRule();
0140 
0141     /**
0142      * Tries to match the rule against the KMime::Message in the
0143      * given @p item.
0144      *
0145      * @return true if the rule matched, false otherwise.
0146      *
0147      * @note Must be implemented by subclasses.
0148      */
0149     virtual bool matches(const Akonadi::Item &item) const = 0;
0150 
0151     /**
0152      * Determines whether the rule is worth considering.
0153      * It isn't if either the field is not set or the contents is empty.
0154      * The calling code should make sure that it's rule list contains
0155      * only non-empty rules, as matches doesn't check this.
0156      */
0157     virtual bool isEmpty() const = 0;
0158 
0159     /**
0160      * Returns the required part from the item that is needed for the search to
0161      * operate. See @ref RequiredPart */
0162     virtual SearchRule::RequiredPart requiredPart() const = 0;
0163 
0164     /**
0165      * Saves the object into a given config @p group.
0166      *
0167      * @param group The config group.
0168      * @param index The identifier that is used to distinguish
0169      *              rules within a single config group.
0170      *
0171      * @note This function will happily write itself even when it's
0172      *       not valid, assuming higher layers to Do The Right Thing(TM).
0173      */
0174     void writeConfig(KConfigGroup &group, int index) const;
0175 
0176     void generateSieveScript(QStringList &requireModules, QString &code);
0177 
0178     /**
0179      * Sets the filter @p function of the rule.
0180      */
0181     void setFunction(Function function);
0182 
0183     /**
0184      * Returns the filter function of the rule.
0185      */
0186     Function function() const;
0187 
0188     /**
0189      * Sets the message header field @p name.
0190      *
0191      * @note Make sure the name contains no trailing ':'.
0192      */
0193     void setField(const QByteArray &name);
0194 
0195     /**
0196      * Returns the message header field name (without the trailing ':').
0197      *
0198      * There are also six pseudo-headers:
0199      * @li \<message\>: Try to match against the whole message.
0200      * @li \<body\>: Try to match against the body of the message.
0201      * @li \<any header\>: Try to match against any header field.
0202      * @li \<recipients\>: Try to match against both To: and Cc: header fields.
0203      * @li \<size\>: Try to match against size of message (numerical).
0204      * @li \<age in days\>: Try to match against age of message (numerical).
0205      * @li \<status\>: Try to match against status of message (status).
0206      * @li \<tag\>: Try to match against message tags.
0207      */
0208     QByteArray field() const;
0209 
0210     /**
0211      * Set the @p contents of the rule.
0212      *
0213      * This can be either a substring to search for in
0214      * or a regexp pattern to match against the header.
0215      */
0216     void setContents(const QString &contents);
0217 
0218     /**
0219      * Returns the contents of the rule.
0220      */
0221     QString contents() const;
0222 
0223     /**
0224      * Returns the rule as string for debugging purpose
0225      */
0226     const QString asString() const;
0227 
0228     /**
0229      * Adds query terms to the given term group.
0230      */
0231     virtual void addQueryTerms(Akonadi::SearchTerm &groupTerm, bool &emptyIsNotAnError) const
0232     {
0233         Q_UNUSED(groupTerm)
0234         Q_UNUSED(emptyIsNotAnError)
0235     }
0236 
0237     QDataStream &operator>>(QDataStream &) const;
0238     virtual QString informationAboutNotValidRules() const
0239     {
0240         return {};
0241     }
0242 
0243 protected:
0244     /**
0245      * Helper that returns whether the rule has a negated function.
0246      */
0247     bool isNegated() const;
0248 
0249     /**
0250      * Converts the rule function into the corresponding Akonadi query operator.
0251      */
0252     Akonadi::SearchTerm::Condition akonadiComparator() const;
0253 
0254 private:
0255     static Function configValueToFunc(const char *);
0256     static QString functionToString(Function);
0257     MAILCOMMON_NO_EXPORT QString conditionToString(Function function);
0258 
0259     QByteArray mField;
0260     Function mFunction;
0261     QString mContents;
0262 };
0263 }