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 
0007 #include "searchrulestatus.h"
0008 #include "filter/filterlog.h"
0009 using MailCommon::FilterLog;
0010 
0011 using namespace MailCommon;
0012 
0013 struct _statusNames {
0014     const char *name;
0015     Akonadi::MessageStatus status;
0016 };
0017 
0018 static struct _statusNames statusNames[] = {{"Important", Akonadi::MessageStatus::statusImportant()},
0019                                             {"Unread", Akonadi::MessageStatus::statusUnread()},
0020                                             {"Read", Akonadi::MessageStatus::statusRead()},
0021                                             {"Deleted", Akonadi::MessageStatus::statusDeleted()},
0022                                             {"Replied", Akonadi::MessageStatus::statusReplied()},
0023                                             {"Forwarded", Akonadi::MessageStatus::statusForwarded()},
0024                                             {"Queued", Akonadi::MessageStatus::statusQueued()},
0025                                             {"Sent", Akonadi::MessageStatus::statusSent()},
0026                                             {"Watched", Akonadi::MessageStatus::statusWatched()},
0027                                             {"Ignored", Akonadi::MessageStatus::statusIgnored()},
0028                                             {"Action Item", Akonadi::MessageStatus::statusToAct()},
0029                                             {"Spam", Akonadi::MessageStatus::statusSpam()},
0030                                             {"Ham", Akonadi::MessageStatus::statusHam()},
0031                                             {"Has Attachment", Akonadi::MessageStatus::statusHasAttachment()}};
0032 
0033 QString englishNameForStatus(Akonadi::MessageStatus status)
0034 {
0035     for (const _statusNames &statusName : statusNames) {
0036         if (statusName.status == status) {
0037             return QString::fromLatin1(statusName.name);
0038         }
0039     }
0040     return {};
0041 }
0042 
0043 SearchRuleStatus::SearchRuleStatus(const QByteArray &field, Function func, const QString &aContents)
0044     : SearchRule(field, func, aContents)
0045 {
0046     // the values are always in english, both from the conf file as well as
0047     // the patternedit gui
0048     mStatus = statusFromEnglishName(aContents);
0049 }
0050 
0051 SearchRuleStatus::SearchRuleStatus(Akonadi::MessageStatus status, Function func)
0052     : SearchRule("<status>", func, englishNameForStatus(status))
0053 {
0054     mStatus = status;
0055 }
0056 
0057 Akonadi::MessageStatus SearchRuleStatus::statusFromEnglishName(const QString &aStatusString)
0058 {
0059     for (const _statusNames &statusName : statusNames) {
0060         if (!aStatusString.compare(QString::fromLatin1(statusName.name))) {
0061             return statusName.status;
0062         }
0063     }
0064     Akonadi::MessageStatus unknown;
0065     return unknown;
0066 }
0067 
0068 QString SearchRuleStatus::informationAboutNotValidRules() const
0069 {
0070     // TODO
0071     return {};
0072 }
0073 
0074 bool SearchRuleStatus::isEmpty() const
0075 {
0076     return field().trimmed().isEmpty() || contents().isEmpty();
0077 }
0078 
0079 bool SearchRuleStatus::matches(const Akonadi::Item &item) const
0080 {
0081     Akonadi::MessageStatus status;
0082     status.setStatusFromFlags(item.flags());
0083     bool rc = false;
0084     switch (function()) {
0085     case FuncEquals: // fallthrough. So that "<status> 'is' 'read'" works
0086     case FuncContains:
0087         if (status & mStatus) {
0088             rc = true;
0089         }
0090         break;
0091     case FuncNotEqual: // fallthrough. So that "<status> 'is not' 'read'" works
0092     case FuncContainsNot:
0093         if (!(status & mStatus)) {
0094             rc = true;
0095         }
0096         break;
0097     // FIXME what about the remaining funcs, how can they make sense for
0098     // statuses?
0099     default:
0100         break;
0101     }
0102     if (FilterLog::instance()->isLogging()) {
0103         QString msg = (rc ? QStringLiteral("<font color=#00FF00>1 = </font>") : QStringLiteral("<font color=#FF0000>0 = </font>"));
0104         msg += FilterLog::recode(asString());
0105         FilterLog::instance()->add(msg, FilterLog::RuleResult);
0106     }
0107     return rc;
0108 }
0109 
0110 SearchRule::RequiredPart SearchRuleStatus::requiredPart() const
0111 {
0112     return SearchRule::Envelope;
0113 }
0114 
0115 void SearchRuleStatus::addQueryTerms(Akonadi::SearchTerm &groupTerm, bool &emptyIsNotAnError) const
0116 {
0117     using namespace Akonadi;
0118     emptyIsNotAnError = true;
0119     // TODO double check that isRead also works
0120     if (!mStatus.statusFlags().isEmpty()) {
0121         EmailSearchTerm term(EmailSearchTerm::MessageStatus, mStatus.statusFlags().values().first(), akonadiComparator());
0122         term.setIsNegated(isNegated());
0123         groupTerm.addSubTerm(term);
0124     } else {
0125         // Special case Unread
0126         Akonadi::MessageStatus status;
0127         status.setRead(true);
0128         EmailSearchTerm term(EmailSearchTerm::MessageStatus, status.statusFlags().values().first(), akonadiComparator());
0129         term.setIsNegated(!isNegated());
0130         groupTerm.addSubTerm(term);
0131     }
0132 }