File indexing completed on 2024-09-22 04:50:06

0001 /*
0002  * kmail: KDE mail client
0003  * SPDX-FileCopyrightText: 1996-1998 Stefan Taferner <taferner@kde.org>
0004  * SPDX-FileCopyrightText: 2012 Andras Mantia <amantia@kde.org>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  *
0008  */
0009 
0010 // my header
0011 #include "mailfilter.h"
0012 
0013 // other kmail headers
0014 #include "dialog/filteractionmissingaccountdialog.h"
0015 #include "filteractions/filteraction.h"
0016 #include "filteractions/filteractiondict.h"
0017 #include "filterlog.h"
0018 #include "filtermanager.h"
0019 using MailCommon::FilterLog;
0020 
0021 #include <PimCommon/PimUtil>
0022 
0023 // KDEPIMLIBS headers
0024 #include <Akonadi/AgentManager>
0025 
0026 // other KDE headers
0027 #include <KConfig>
0028 #include <KConfigGroup>
0029 #include <KLocalizedString>
0030 #include <KMessageBox>
0031 #include <KRandom>
0032 #include <QPointer>
0033 
0034 #include <algorithm>
0035 
0036 using namespace MailCommon;
0037 
0038 MailFilter::MailFilter()
0039 {
0040     generateRandomIdentifier();
0041     bApplyOnInbound = true;
0042     bApplyBeforeOutbound = false;
0043     bApplyOnOutbound = false;
0044     bApplyOnExplicit = true;
0045     bApplyOnAllFolders = false;
0046     bStopProcessingHere = true;
0047     bConfigureShortcut = false;
0048     bConfigureToolbar = false;
0049     bAutoNaming = true;
0050     mApplicability = All;
0051     bEnabled = true;
0052 }
0053 
0054 MailFilter::MailFilter(const KConfigGroup &aConfig, bool interactive, bool &needUpdate)
0055 {
0056     needUpdate = readConfig(aConfig, interactive);
0057 }
0058 
0059 MailFilter::MailFilter(const MailFilter &aFilter)
0060 {
0061     mIdentifier = aFilter.mIdentifier;
0062     mPattern = aFilter.mPattern;
0063 
0064     bApplyOnInbound = aFilter.applyOnInbound();
0065     bApplyBeforeOutbound = aFilter.applyBeforeOutbound();
0066     bApplyOnOutbound = aFilter.applyOnOutbound();
0067     bApplyOnExplicit = aFilter.applyOnExplicit();
0068     bApplyOnAllFolders = aFilter.applyOnAllFoldersInbound();
0069     bStopProcessingHere = aFilter.stopProcessingHere();
0070     bConfigureShortcut = aFilter.configureShortcut();
0071     bConfigureToolbar = aFilter.configureToolbar();
0072     mToolbarName = aFilter.toolbarName();
0073     mApplicability = aFilter.applicability();
0074     bAutoNaming = aFilter.isAutoNaming();
0075     bEnabled = aFilter.isEnabled();
0076     mIcon = aFilter.icon();
0077     mShortcut = aFilter.shortcut();
0078 
0079     QListIterator<FilterAction *> it(aFilter.mActions);
0080     while (it.hasNext()) {
0081         FilterAction *action = it.next();
0082         FilterActionDesc *desc = FilterManager::filterActionDict()->value(action->name());
0083         if (desc) {
0084             FilterAction *f = desc->create();
0085             if (f) {
0086                 f->argsFromString(action->argsAsString());
0087                 mActions.append(f);
0088             }
0089         }
0090     }
0091 
0092     mAccounts.clear();
0093     QStringList::ConstIterator it2;
0094     QStringList::ConstIterator end2 = aFilter.mAccounts.constEnd();
0095     for (it2 = aFilter.mAccounts.constBegin(); it2 != end2; ++it2) {
0096         mAccounts.append(*it2);
0097     }
0098 }
0099 
0100 MailFilter::~MailFilter()
0101 {
0102     qDeleteAll(mActions);
0103 }
0104 
0105 int MailFilter::filterActionsMaximumSize()
0106 {
0107     return 8;
0108 }
0109 
0110 void MailFilter::generateRandomIdentifier()
0111 {
0112     mIdentifier = KRandom::randomString(16);
0113 }
0114 
0115 QString MailFilter::identifier() const
0116 {
0117     return mIdentifier;
0118 }
0119 
0120 QString MailFilter::name() const
0121 {
0122     return mPattern.name();
0123 }
0124 
0125 MailFilter::ReturnCode MailFilter::execActions(ItemContext &context, bool &stopIt, bool applyOnOutbound) const
0126 {
0127     QList<FilterAction *>::const_iterator it(mActions.constBegin());
0128     QList<FilterAction *>::const_iterator end(mActions.constEnd());
0129     for (; it != end; ++it) {
0130         if (FilterLog::instance()->isLogging()) {
0131             const QString logText(i18n("<b>Applying filter action:</b> %1", (*it)->displayString()));
0132             FilterLog::instance()->add(logText, FilterLog::AppliedAction);
0133         }
0134 
0135         const FilterAction::ReturnCode result = (*it)->process(context, applyOnOutbound);
0136 
0137         switch (result) {
0138         case FilterAction::CriticalError:
0139             if (FilterLog::instance()->isLogging()) {
0140                 const QString logText = QStringLiteral("<font color=#FF0000>%1</font>").arg(i18n("A critical error occurred. Processing stops here."));
0141                 FilterLog::instance()->add(logText, FilterLog::AppliedAction);
0142             }
0143             // in case it's a critical error: return immediately!
0144             return CriticalError;
0145         case FilterAction::ErrorButGoOn:
0146             if (FilterLog::instance()->isLogging()) {
0147                 const QString logText = QStringLiteral("<font color=#FF0000>%1</font>").arg(i18n("A problem was found while applying this action."));
0148                 FilterLog::instance()->add(logText, FilterLog::AppliedAction);
0149             }
0150         case FilterAction::GoOn:
0151         case FilterAction::ErrorNeedComplete:
0152             break;
0153         }
0154     }
0155 
0156     stopIt = stopProcessingHere();
0157 
0158     return GoOn;
0159 }
0160 
0161 QList<FilterAction *> *MailFilter::actions()
0162 {
0163     return &mActions;
0164 }
0165 
0166 const QList<FilterAction *> *MailFilter::actions() const
0167 {
0168     return &mActions;
0169 }
0170 
0171 SearchPattern *MailFilter::pattern()
0172 {
0173     return &mPattern;
0174 }
0175 
0176 const SearchPattern *MailFilter::pattern() const
0177 {
0178     return &mPattern;
0179 }
0180 
0181 void MailFilter::setApplyOnOutbound(bool aApply)
0182 {
0183     bApplyOnOutbound = aApply;
0184 }
0185 
0186 void MailFilter::setApplyBeforeOutbound(bool aApply)
0187 {
0188     bApplyBeforeOutbound = aApply;
0189 }
0190 
0191 bool MailFilter::applyOnOutbound() const
0192 {
0193     return bApplyOnOutbound;
0194 }
0195 
0196 bool MailFilter::applyBeforeOutbound() const
0197 {
0198     return bApplyBeforeOutbound;
0199 }
0200 
0201 void MailFilter::setApplyOnInbound(bool aApply)
0202 {
0203     bApplyOnInbound = aApply;
0204 }
0205 
0206 bool MailFilter::applyOnInbound() const
0207 {
0208     return bApplyOnInbound;
0209 }
0210 
0211 void MailFilter::setApplyOnExplicit(bool aApply)
0212 {
0213     bApplyOnExplicit = aApply;
0214 }
0215 
0216 bool MailFilter::applyOnExplicit() const
0217 {
0218     return bApplyOnExplicit;
0219 }
0220 
0221 void MailFilter::setApplyOnAllFoldersInbound(bool aApply)
0222 {
0223     bApplyOnAllFolders = aApply;
0224 }
0225 
0226 bool MailFilter::applyOnAllFoldersInbound() const
0227 {
0228     return bApplyOnAllFolders;
0229 }
0230 
0231 void MailFilter::setApplicability(AccountType aApply)
0232 {
0233     mApplicability = aApply;
0234 }
0235 
0236 MailFilter::AccountType MailFilter::applicability() const
0237 {
0238     return mApplicability;
0239 }
0240 
0241 SearchRule::RequiredPart MailFilter::requiredPart(const QString &id) const
0242 {
0243     // find the required message part needed for the filter
0244     // this can be either only the Envelope, all Header or the CompleteMessage
0245     // Makes the assumption that  Envelope < Header < CompleteMessage
0246     int requiredPart = SearchRule::Envelope;
0247 
0248     if (!bEnabled || !applyOnAccount(id)) {
0249         return static_cast<SearchRule::RequiredPart>(requiredPart);
0250     }
0251 
0252     if (pattern()) {
0253         requiredPart = qMax(requiredPart, static_cast<int>(pattern()->requiredPart())); // no pattern means always matches?
0254     }
0255 
0256     int requiredPartByActions = SearchRule::Envelope;
0257 
0258     QList<FilterAction *> actionList = *actions();
0259     if (!actionList.isEmpty()) {
0260         requiredPartByActions = (*std::max_element(actionList.constBegin(), actionList.constEnd(), [](auto lhs, auto rhs) {
0261                                     return lhs->requiredPart() < rhs->requiredPart();
0262                                 }))->requiredPart();
0263     }
0264     requiredPart = qMax(requiredPart, requiredPartByActions);
0265 
0266     return static_cast<SearchRule::RequiredPart>(requiredPart);
0267 }
0268 
0269 void MailFilter::agentRemoved(const QString &identifier)
0270 {
0271     mAccounts.removeAll(identifier);
0272 }
0273 
0274 void MailFilter::folderRemoved(const Akonadi::Collection &aFolder, const Akonadi::Collection &aNewFolder)
0275 {
0276     QListIterator<FilterAction *> it(mActions);
0277     while (it.hasNext()) {
0278         it.next()->folderRemoved(aFolder, aNewFolder);
0279     }
0280 }
0281 
0282 void MailFilter::clearApplyOnAccount()
0283 {
0284     mAccounts.clear();
0285 }
0286 
0287 void MailFilter::setApplyOnAccount(const QString &id, bool aApply)
0288 {
0289     if (aApply && !mAccounts.contains(id)) {
0290         mAccounts.append(id);
0291     } else if (!aApply && mAccounts.contains(id)) {
0292         mAccounts.removeAll(id);
0293     }
0294 }
0295 
0296 bool MailFilter::applyOnAccount(const QString &id) const
0297 {
0298     if (applicability() == All) {
0299         return true;
0300     }
0301     if (applicability() == ButImap) {
0302         Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(id);
0303         if (instance.isValid()) {
0304             return !PimCommon::Util::isImapResource(instance.type().identifier());
0305         } else {
0306             return false;
0307         }
0308     }
0309     if (applicability() == Checked) {
0310         return mAccounts.contains(id);
0311     }
0312 
0313     return false;
0314 }
0315 
0316 void MailFilter::setStopProcessingHere(bool aStop)
0317 {
0318     bStopProcessingHere = aStop;
0319 }
0320 
0321 bool MailFilter::stopProcessingHere() const
0322 {
0323     return bStopProcessingHere;
0324 }
0325 
0326 void MailFilter::setConfigureShortcut(bool aShort)
0327 {
0328     bConfigureShortcut = aShort;
0329     bConfigureToolbar = (bConfigureToolbar && bConfigureShortcut);
0330 }
0331 
0332 bool MailFilter::configureShortcut() const
0333 {
0334     return bConfigureShortcut;
0335 }
0336 
0337 void MailFilter::setConfigureToolbar(bool aTool)
0338 {
0339     bConfigureToolbar = (aTool && bConfigureShortcut);
0340 }
0341 
0342 bool MailFilter::configureToolbar() const
0343 {
0344     return bConfigureToolbar;
0345 }
0346 
0347 void MailFilter::setToolbarName(const QString &toolbarName)
0348 {
0349     mToolbarName = toolbarName;
0350 }
0351 
0352 void MailFilter::setShortcut(const QKeySequence &shortcut)
0353 {
0354     mShortcut = shortcut;
0355 }
0356 
0357 const QKeySequence &MailFilter::shortcut() const
0358 {
0359     return mShortcut;
0360 }
0361 
0362 void MailFilter::setIcon(const QString &icon)
0363 {
0364     mIcon = icon;
0365 }
0366 
0367 QString MailFilter::icon() const
0368 {
0369     return mIcon;
0370 }
0371 
0372 void MailFilter::setAutoNaming(bool useAutomaticNames)
0373 {
0374     bAutoNaming = useAutomaticNames;
0375 }
0376 
0377 bool MailFilter::isAutoNaming() const
0378 {
0379     return bAutoNaming;
0380 }
0381 
0382 //-----------------------------------------------------------------------------
0383 bool MailFilter::readConfig(const KConfigGroup &config, bool interactive)
0384 {
0385     bool needUpdate = false;
0386     // MKSearchPattern::readConfig ensures
0387     // that the pattern is purified.
0388     mPattern.readConfig(config);
0389     mIdentifier = config.readEntry("identifier", KRandom::randomString(16));
0390 
0391     const QStringList sets = config.readEntry("apply-on", QStringList());
0392     if (sets.isEmpty() && !config.hasKey("apply-on")) {
0393         bApplyBeforeOutbound = false;
0394         bApplyOnOutbound = false;
0395         bApplyOnInbound = true;
0396         bApplyOnExplicit = true;
0397         bApplyOnAllFolders = false;
0398         mApplicability = ButImap;
0399     } else {
0400         bApplyBeforeOutbound = bool(sets.contains(QLatin1StringView("before-send-mail")));
0401         bApplyOnInbound = bool(sets.contains(QLatin1StringView("check-mail")));
0402         bApplyOnOutbound = bool(sets.contains(QLatin1StringView("send-mail")));
0403         bApplyOnExplicit = bool(sets.contains(QLatin1StringView("manual-filtering")));
0404         bApplyOnAllFolders = bool(sets.contains(QLatin1StringView("all-folders")));
0405         mApplicability = static_cast<AccountType>(config.readEntry("Applicability", static_cast<int>(ButImap)));
0406     }
0407 
0408     bStopProcessingHere = config.readEntry("StopProcessingHere", true);
0409     bConfigureShortcut = config.readEntry("ConfigureShortcut", false);
0410     QString shortcut(config.readEntry("Shortcut", QString()));
0411     if (!shortcut.isEmpty()) {
0412         QKeySequence sc(shortcut);
0413         setShortcut(sc);
0414     }
0415     bConfigureToolbar = config.readEntry("ConfigureToolbar", false);
0416     bConfigureToolbar = bConfigureToolbar && bConfigureShortcut;
0417     mToolbarName = config.readEntry("ToolbarName", name());
0418     mIcon = config.readEntry("Icon", "system-run");
0419     bAutoNaming = config.readEntry("AutomaticName", false);
0420     bEnabled = config.readEntry("Enabled", true);
0421 
0422     mActions.clear();
0423 
0424     int numActions = config.readEntry("actions", 0);
0425     if (numActions > filterActionsMaximumSize()) {
0426         numActions = filterActionsMaximumSize();
0427         KMessageBox::information(nullptr, i18n("<qt>Too many filter actions in filter rule <b>%1</b>.</qt>", mPattern.name()));
0428     }
0429 
0430     for (int i = 0; i < numActions; ++i) {
0431         const QString actName = QStringLiteral("action-name-%1").arg(i);
0432         const QString argsName = QStringLiteral("action-args-%1").arg(i);
0433         // get the action description...
0434         const QString resultActName = config.readEntry(actName, QString());
0435         FilterActionDesc *desc = FilterManager::filterActionDict()->value(resultActName);
0436         if (desc) {
0437             //...create an instance...
0438             FilterAction *fa = desc->create();
0439             if (fa) {
0440                 //...load it with it's parameter...
0441                 if (interactive) {
0442                     const bool ret = fa->argsFromStringInteractive(config.readEntry(argsName, QString()), name());
0443                     if (ret) {
0444                         needUpdate = true;
0445                     }
0446                 } else {
0447                     fa->argsFromString(config.readEntry(argsName, QString()));
0448                 }
0449                 //...check if it's empty and...
0450                 if (!fa->isEmpty()) {
0451                     //...append it if it's not and...
0452                     mActions.append(fa);
0453                 } else {
0454                     //...delete is else.
0455                     delete fa;
0456                 }
0457             }
0458         } else {
0459             KMessageBox::information(
0460                 nullptr /* app-global modal dialog box */,
0461                 i18n("<qt>Unknown filter action <b>%1</b><br />in filter rule <b>%2</b>.<br />Ignoring it.</qt>", resultActName, mPattern.name()));
0462         }
0463     }
0464 
0465     mAccounts = config.readEntry("accounts-set", QStringList());
0466     if (!mAccounts.isEmpty() && interactive) {
0467         if (!MailCommon::FilterActionMissingAccountDialog::allAccountExist(mAccounts)) {
0468             QPointer<MailCommon::FilterActionMissingAccountDialog> dlg = new MailCommon::FilterActionMissingAccountDialog(mAccounts, name());
0469             if (dlg->exec()) {
0470                 mAccounts = dlg->selectedAccount();
0471                 needUpdate = true;
0472             }
0473             delete dlg;
0474         }
0475     }
0476     return needUpdate;
0477 }
0478 
0479 void MailFilter::generateSieveScript(QStringList &requiresModules, QString &code)
0480 {
0481     mPattern.generateSieveScript(requiresModules, code);
0482 
0483     QList<FilterAction *>::const_iterator it;
0484     QList<FilterAction *>::const_iterator end(mActions.constEnd());
0485 
0486     const QString indentationStr{QStringLiteral("    ")};
0487     code += QLatin1StringView(")\n{\n");
0488     bool firstAction = true;
0489     for (it = mActions.constBegin(); it != end; ++it) {
0490         // Add endline here.
0491         if (firstAction) {
0492             firstAction = false;
0493         } else {
0494             code += QLatin1Char('\n');
0495         }
0496         code += indentationStr + (*it)->sieveCode();
0497         const QStringList lstRequires = (*it)->sieveRequires();
0498         for (const QString &str : lstRequires) {
0499             if (!requiresModules.contains(str)) {
0500                 requiresModules.append(str);
0501             }
0502         }
0503     }
0504     if (bStopProcessingHere) {
0505         code += QLatin1Char('\n') + indentationStr + QStringLiteral("stop;");
0506     }
0507     code += QLatin1StringView("\n}\n");
0508 }
0509 
0510 void MailFilter::writeConfig(KConfigGroup &config, bool exportFilter) const
0511 {
0512     mPattern.writeConfig(config);
0513     config.writeEntry("identifier", mIdentifier);
0514 
0515     QStringList sets;
0516     if (bApplyOnInbound) {
0517         sets.append(QStringLiteral("check-mail"));
0518     }
0519     if (bApplyBeforeOutbound) {
0520         sets.append(QStringLiteral("before-send-mail"));
0521     }
0522     if (bApplyOnOutbound) {
0523         sets.append(QStringLiteral("send-mail"));
0524     }
0525     if (bApplyOnExplicit) {
0526         sets.append(QStringLiteral("manual-filtering"));
0527     }
0528     if (bApplyOnAllFolders) {
0529         sets.append(QStringLiteral("all-folders"));
0530     }
0531     config.writeEntry("apply-on", sets);
0532 
0533     config.writeEntry("StopProcessingHere", bStopProcessingHere);
0534     config.writeEntry("ConfigureShortcut", bConfigureShortcut);
0535     if (!mShortcut.isEmpty()) {
0536         config.writeEntry("Shortcut", mShortcut.toString());
0537     }
0538     config.writeEntry("ConfigureToolbar", bConfigureToolbar);
0539     config.writeEntry("ToolbarName", mToolbarName);
0540     if (!mIcon.isEmpty()) {
0541         config.writeEntry("Icon", mIcon);
0542     }
0543     config.writeEntry("AutomaticName", bAutoNaming);
0544     config.writeEntry("Applicability", static_cast<int>(mApplicability));
0545     config.writeEntry("Enabled", bEnabled);
0546     int i;
0547 
0548     QList<FilterAction *>::const_iterator it;
0549     QList<FilterAction *>::const_iterator end(mActions.constEnd());
0550 
0551     for (i = 0, it = mActions.constBegin(); it != end; ++it, ++i) {
0552         config.writeEntry(QStringLiteral("action-name-%1").arg(i), (*it)->name());
0553         config.writeEntry(QStringLiteral("action-args-%1").arg(i), exportFilter ? (*it)->argsAsStringReal() : (*it)->argsAsString());
0554     }
0555     config.writeEntry("actions", i);
0556     if (!mAccounts.isEmpty()) {
0557         config.writeEntry("accounts-set", mAccounts);
0558     }
0559 }
0560 
0561 QString MailFilter::purify(bool removeAction)
0562 {
0563     QString informationAboutNotValidAction = mPattern.purify(removeAction);
0564 
0565     if (mActions.isEmpty()) {
0566         if (!informationAboutNotValidAction.isEmpty()) {
0567             informationAboutNotValidAction += QLatin1Char('\n');
0568         }
0569         informationAboutNotValidAction += i18n("Any action defined.");
0570     } else {
0571         QListIterator<FilterAction *> it(mActions);
0572         it.toBack();
0573         while (it.hasPrevious()) {
0574             FilterAction *action = it.previous();
0575             if (action->isEmpty()) {
0576                 if (!informationAboutNotValidAction.isEmpty()) {
0577                     informationAboutNotValidAction += QLatin1Char('\n');
0578                 }
0579                 informationAboutNotValidAction += action->informationAboutNotValidAction();
0580                 if (removeAction) {
0581                     mActions.removeAll(action);
0582                 }
0583             }
0584         }
0585     }
0586 
0587     if (!Akonadi::AgentManager::self()->instances().isEmpty()) { // safety test to ensure that Akonadi system is ready
0588         // Remove invalid accounts from mAccounts - just to be tidy
0589         QStringList::Iterator it2 = mAccounts.begin();
0590         while (it2 != mAccounts.end()) {
0591             if (!Akonadi::AgentManager::self()->instance(*it2).isValid()) {
0592                 it2 = mAccounts.erase(it2);
0593             } else {
0594                 ++it2;
0595             }
0596         }
0597     }
0598     return informationAboutNotValidAction;
0599 }
0600 
0601 bool MailFilter::isEmpty() const
0602 {
0603     return (mPattern.isEmpty() && mActions.isEmpty()) || ((applicability() == Checked) && (bApplyOnInbound && mAccounts.isEmpty()));
0604 }
0605 
0606 QString MailFilter::toolbarName() const
0607 {
0608     if (mToolbarName.isEmpty()) {
0609         return name();
0610     } else {
0611         return mToolbarName;
0612     }
0613 }
0614 
0615 const QString MailFilter::asString() const
0616 {
0617     QString result;
0618 
0619     result += QLatin1StringView("Filter name: ") + name() + QLatin1StringView(" (") + mIdentifier + QLatin1StringView(")\n");
0620     result += mPattern.asString() + QLatin1Char('\n');
0621 
0622     result += QStringLiteral("Filter is %1\n").arg(bEnabled ? QStringLiteral("enabled") : QStringLiteral("disabled"));
0623 
0624     QList<FilterAction *>::const_iterator it(mActions.constBegin());
0625     QList<FilterAction *>::const_iterator end(mActions.constEnd());
0626     for (; it != end; ++it) {
0627         result += QStringLiteral("    action: ");
0628         result += (*it)->label();
0629         result += QLatin1Char(' ');
0630         result += (*it)->argsAsString();
0631         result += QLatin1Char('\n');
0632     }
0633     result += QStringLiteral("This filter belongs to the following sets:");
0634     if (bApplyOnInbound) {
0635         result += QStringLiteral(" Inbound");
0636     }
0637     if (bApplyBeforeOutbound) {
0638         result += QStringLiteral(" before-Outbound");
0639     }
0640     if (bApplyOnOutbound) {
0641         result += QStringLiteral(" Outbound");
0642     }
0643     if (bApplyOnExplicit) {
0644         result += QStringLiteral(" Explicit");
0645     }
0646     if (bApplyOnAllFolders) {
0647         result += QStringLiteral(" All Folders");
0648     }
0649     result += QLatin1Char('\n');
0650     if (bApplyOnInbound && mApplicability == All) {
0651         result += QStringLiteral("This filter applies to all accounts.\n");
0652     } else if (bApplyOnInbound && mApplicability == ButImap) {
0653         result += QStringLiteral("This filter applies to all but IMAP accounts.\n");
0654     } else if (bApplyOnInbound) {
0655         result += QStringLiteral("This filter applies to the following accounts:");
0656         if (mAccounts.isEmpty()) {
0657             result += QStringLiteral(" None");
0658         } else {
0659             for (QStringList::ConstIterator it2 = mAccounts.begin(), it2End = mAccounts.end(); it2 != it2End; ++it2) {
0660                 if (Akonadi::AgentManager::self()->instance(*it2).isValid()) {
0661                     result += QLatin1Char(' ') + Akonadi::AgentManager::self()->instance(*it2).name();
0662                 }
0663             }
0664         }
0665         result += QLatin1Char('\n');
0666     }
0667     if (bStopProcessingHere) {
0668         result += QStringLiteral("If it matches, processing stops at this filter.\n");
0669     }
0670 
0671     return result;
0672 }
0673 
0674 QDataStream &MailCommon::operator<<(QDataStream &stream, const MailCommon::MailFilter &filter)
0675 {
0676     stream << filter.mIdentifier;
0677     stream << filter.mPattern.serialize();
0678 
0679     stream << filter.mActions.count();
0680     QListIterator<FilterAction *> it(filter.mActions);
0681     while (it.hasNext()) {
0682         const FilterAction *action = it.next();
0683         stream << action->name();
0684         stream << action->argsAsString();
0685     }
0686 
0687     stream << filter.mAccounts;
0688     stream << filter.mIcon;
0689     stream << filter.mToolbarName;
0690     stream << filter.mShortcut;
0691     stream << filter.bApplyOnInbound;
0692     stream << filter.bApplyBeforeOutbound;
0693     stream << filter.bApplyOnOutbound;
0694     stream << filter.bApplyOnExplicit;
0695     stream << filter.bApplyOnAllFolders;
0696     stream << filter.bStopProcessingHere;
0697     stream << filter.bConfigureShortcut;
0698     stream << filter.bConfigureToolbar;
0699     stream << filter.bAutoNaming;
0700     stream << filter.mApplicability;
0701     stream << filter.bEnabled;
0702 
0703     return stream;
0704 }
0705 
0706 QDataStream &MailCommon::operator>>(QDataStream &stream, MailCommon::MailFilter &filter)
0707 {
0708     QByteArray pattern;
0709     int numberOfActions;
0710     QKeySequence shortcut;
0711     bool bApplyOnInbound;
0712     bool bApplyBeforeOutbound;
0713     bool bApplyOnOutbound;
0714     bool bApplyOnExplicit;
0715     bool bApplyOnAllFolders;
0716     bool bStopProcessingHere;
0717     bool bConfigureShortcut;
0718     bool bConfigureToolbar;
0719     bool bAutoNaming;
0720     int applicability;
0721     bool bEnabled;
0722 
0723     stream >> filter.mIdentifier;
0724     stream >> pattern;
0725 
0726     stream >> numberOfActions;
0727     qDeleteAll(filter.mActions);
0728     filter.mActions.clear();
0729 
0730     for (int i = 0; i < numberOfActions; ++i) {
0731         QString actionName;
0732         QString actionArguments;
0733 
0734         stream >> actionName;
0735         stream >> actionArguments;
0736 
0737         FilterActionDesc *description = FilterManager::filterActionDict()->value(actionName);
0738         if (description) {
0739             FilterAction *filterAction = description->create();
0740             if (filterAction) {
0741                 filterAction->argsFromString(actionArguments);
0742                 filter.mActions.append(filterAction);
0743             }
0744         }
0745     }
0746 
0747     stream >> filter.mAccounts;
0748     stream >> filter.mIcon;
0749     stream >> filter.mToolbarName;
0750     stream >> shortcut;
0751     stream >> bApplyOnInbound;
0752     stream >> bApplyBeforeOutbound;
0753     stream >> bApplyOnOutbound;
0754     stream >> bApplyOnExplicit;
0755     stream >> bApplyOnAllFolders;
0756     stream >> bStopProcessingHere;
0757     stream >> bConfigureShortcut;
0758     stream >> bConfigureToolbar;
0759     stream >> bAutoNaming;
0760     stream >> applicability;
0761     stream >> bEnabled;
0762 
0763     filter.mPattern.deserialize(pattern);
0764     filter.mShortcut = shortcut;
0765     filter.bApplyOnInbound = bApplyOnInbound;
0766     filter.bApplyBeforeOutbound = bApplyBeforeOutbound;
0767     filter.bApplyOnOutbound = bApplyOnOutbound;
0768     filter.bApplyOnExplicit = bApplyOnExplicit;
0769     filter.bApplyOnAllFolders = bApplyOnAllFolders;
0770     filter.bStopProcessingHere = bStopProcessingHere;
0771     filter.bConfigureShortcut = bConfigureShortcut;
0772     filter.bConfigureToolbar = bConfigureToolbar;
0773     filter.bAutoNaming = bAutoNaming;
0774     filter.bEnabled = bEnabled;
0775     filter.mApplicability = static_cast<MailCommon::MailFilter::AccountType>(applicability);
0776 
0777     return stream;
0778 }
0779 
0780 bool MailFilter::isEnabled() const
0781 {
0782     return bEnabled;
0783 }
0784 
0785 void MailFilter::setEnabled(bool enabled)
0786 {
0787     bEnabled = enabled;
0788 }