File indexing completed on 2025-01-05 04:49:33

0001 /*
0002     This file is part of KMail.
0003     SPDX-FileCopyrightText: 2003 Andreas Gungl <a.gungl@gmx.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only
0006 */
0007 
0008 #include "antispamwizard.h"
0009 #include <KCursorSaver>
0010 #include <MailCommon/FilterAction>
0011 #include <MailCommon/FilterActionDict>
0012 #include <MailCommon/FilterManager>
0013 #include <MailCommon/FolderRequester>
0014 #include <MailCommon/FolderTreeView>
0015 #include <MailCommon/FolderTreeWidget>
0016 #include <MailCommon/FolderTreeWidgetProxyModel>
0017 #include <MailCommon/MailFilter>
0018 #include <MailCommon/MailKernel>
0019 #include <MailCommon/MailUtil>
0020 
0021 #include <PimCommon/PimUtil>
0022 
0023 #include <Akonadi/AgentInstance>
0024 
0025 #include <KConfigGroup>
0026 #include <KLocalizedString>
0027 #include <KProcess>
0028 #include <QDialog>
0029 
0030 #include <KConfigGroup>
0031 #include <MailCommon/ResourceReadConfigFile>
0032 #include <QApplication>
0033 #include <QBoxLayout>
0034 #include <QCheckBox>
0035 #include <QEventLoop>
0036 #include <QGridLayout>
0037 #include <QHBoxLayout>
0038 #include <QLabel>
0039 #include <QListWidget>
0040 #include <QPushButton>
0041 #include <QTextEdit>
0042 #include <QTimer>
0043 #include <QVBoxLayout>
0044 using namespace KMail;
0045 using namespace MailCommon;
0046 
0047 AntiSpamWizard::AntiSpamWizard(WizardMode mode, QWidget *parent)
0048     : KAssistantDialog(parent)
0049     , mInfoPage(nullptr)
0050     , mSpamRulesPage(nullptr)
0051     , mVirusRulesPage(nullptr)
0052     , mSummaryPage(nullptr)
0053     , mSpamToolsUsed(false)
0054     , mVirusToolsUsed(false)
0055     , mMode(mode)
0056 {
0057     // read the configuration for the anti-spam tools
0058     ConfigReader reader(mMode, mToolList);
0059     reader.readAndMergeConfig();
0060     mToolList = reader.getToolList();
0061 #ifndef NDEBUG
0062     if (mMode == AntiSpam) {
0063         qDebug() << "\nConsidered anti-spam tools:";
0064     } else {
0065         qDebug() << "\nConsidered anti-virus tools:";
0066     }
0067     QList<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
0068     for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0069         qDebug() << "Predefined tool:" << (*it).getId();
0070         qDebug() << "Config version:" << (*it).getVersion();
0071         qDebug() << "Selection priority:" << (*it).getPrio();
0072         qDebug() << "Displayed name:" << (*it).getVisibleName();
0073         qDebug() << "Executable:" << (*it).getExecutable();
0074         qDebug() << "WhatsThis URL:" << (*it).getWhatsThisText();
0075         qDebug() << "Filter name:" << (*it).getFilterName();
0076         qDebug() << "Detection command:" << (*it).getDetectCmd();
0077         qDebug() << "Learn spam command:" << (*it).getSpamCmd();
0078         qDebug() << "Learn ham command:" << (*it).getHamCmd();
0079         qDebug() << "Detection header:" << (*it).getDetectionHeader();
0080         qDebug() << "Detection pattern:" << (*it).getDetectionPattern();
0081         qDebug() << "Use as RegExp:" << (*it).isUseRegExp();
0082         qDebug() << "Supports Bayes Filter:" << (*it).useBayesFilter();
0083         qDebug() << "Type:" << (*it).getType();
0084     }
0085 #endif
0086 
0087     const bool isAntiSpam = (mMode == AntiSpam);
0088     setWindowTitle(isAntiSpam ? i18nc("@title:window", "Anti-Spam Wizard") : i18nc("@title:window", "Anti-Virus Wizard"));
0089     mInfoPage = new ASWizInfoPage(mMode, nullptr, QString());
0090     mInfoPageItem = addPage(mInfoPage, isAntiSpam ? i18n("Welcome to the KMail Anti-Spam Wizard") : i18n("Welcome to the KMail Anti-Virus Wizard"));
0091     connect(mInfoPage, &ASWizInfoPage::selectionChanged, this, &AntiSpamWizard::checkProgramsSelections);
0092 
0093     if (isAntiSpam) {
0094         mSpamRulesPage = new ASWizSpamRulesPage(nullptr, QString());
0095         mSpamRulesPageItem = addPage(mSpamRulesPage, i18n("Options to fine-tune the handling of spam messages"));
0096         connect(mSpamRulesPage, &ASWizSpamRulesPage::selectionChanged, this, &AntiSpamWizard::slotBuildSummary);
0097 
0098         mSummaryPage = new ASWizSummaryPage(nullptr, QString());
0099         mSummaryPageItem = addPage(mSummaryPage, i18n("Summary of changes to be made by this wizard"));
0100     } else {
0101         mVirusRulesPage = new ASWizVirusRulesPage(nullptr, QString());
0102         mVirusRulesPageItem = addPage(mVirusRulesPage, i18n("Options to fine-tune the handling of virus messages"));
0103         connect(mVirusRulesPage, &ASWizVirusRulesPage::selectionChanged, this, &AntiSpamWizard::checkVirusRulesSelections);
0104     }
0105 
0106     connect(button(QDialogButtonBox::Help), &QPushButton::clicked, this, &AntiSpamWizard::slotHelpClicked);
0107 
0108     QTimer::singleShot(0, this, &AntiSpamWizard::checkToolAvailability);
0109 }
0110 
0111 void AntiSpamWizard::accept()
0112 {
0113     if (mSpamRulesPage) {
0114         qDebug() << "Folder name for messages classified as spam is" << mSpamRulesPage->selectedSpamCollectionId();
0115         qDebug() << "Folder name for messages classified as unsure is" << mSpamRulesPage->selectedUnsureCollectionId();
0116     }
0117     if (mVirusRulesPage) {
0118         qDebug() << "Folder name for viruses is" << mVirusRulesPage->selectedFolderName();
0119     }
0120 
0121     FilterActionDict dict;
0122     QList<MailFilter *> filterList;
0123     bool replaceExistingFilters = false;
0124 
0125     // Let's start with virus detection and handling,
0126     // so we can avoid spam checks for viral messages
0127     if (mMode == AntiVirus) {
0128         if (!mVirusToolsUsed) {
0129             QDialog::accept();
0130             return;
0131         }
0132         QList<SpamToolConfig>::const_iterator end(mToolList.constEnd());
0133         for (QList<SpamToolConfig>::const_iterator it = mToolList.constBegin(); it != end; ++it) {
0134             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (mVirusRulesPage->pipeRulesSelected() && (*it).isVirusTool())) {
0135                 // pipe messages through the anti-virus tools,
0136                 // one single filter for each tool
0137                 // (could get combined but so it's easier to understand for the user)
0138                 auto pipeFilter = new MailFilter();
0139                 QList<FilterAction *> *pipeFilterActions = pipeFilter->actions();
0140                 FilterAction *pipeFilterAction = dict.value(QStringLiteral("filter app"))->create();
0141                 pipeFilterAction->argsFromString((*it).getDetectCmd());
0142                 pipeFilterActions->append(pipeFilterAction);
0143                 SearchPattern *pipeFilterPattern = pipeFilter->pattern();
0144                 pipeFilterPattern->setName(uniqueNameFor((*it).getFilterName()));
0145                 pipeFilterPattern->append(SearchRule::createInstance("<size>", SearchRule::FuncIsGreaterOrEqual, QStringLiteral("0")));
0146                 pipeFilter->setApplyOnOutbound(false);
0147                 pipeFilter->setApplyOnInbound(true);
0148                 pipeFilter->setApplyOnExplicit(true);
0149                 pipeFilter->setStopProcessingHere(false);
0150                 pipeFilter->setConfigureShortcut(false);
0151 
0152                 filterList.append(pipeFilter);
0153             }
0154         }
0155 
0156         if (mVirusRulesPage->moveRulesSelected()) {
0157             // Sort out viruses depending on header fields set by the tools
0158             auto virusFilter = new MailFilter();
0159             QList<FilterAction *> *virusFilterActions = virusFilter->actions();
0160             FilterAction *virusFilterAction1 = dict.value(QStringLiteral("transfer"))->create();
0161             virusFilterAction1->argsFromString(mVirusRulesPage->selectedFolderName());
0162             virusFilterActions->append(virusFilterAction1);
0163             if (mVirusRulesPage->markReadRulesSelected()) {
0164                 FilterAction *virusFilterAction2 = dict.value(QStringLiteral("set status"))->create();
0165                 virusFilterAction2->argsFromString(QStringLiteral("R")); // Read
0166                 virusFilterActions->append(virusFilterAction2);
0167             }
0168             SearchPattern *virusFilterPattern = virusFilter->pattern();
0169             virusFilterPattern->setName(uniqueNameFor(i18n("Virus handling")));
0170             virusFilterPattern->setOp(SearchPattern::OpOr);
0171             QList<SpamToolConfig>::ConstIterator endSpamTool(mToolList.constEnd());
0172             for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != endSpamTool; ++it) {
0173                 if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
0174                     if ((*it).isVirusTool()) {
0175                         const QByteArray header = (*it).getDetectionHeader().toLatin1();
0176                         const QString &pattern = (*it).getDetectionPattern();
0177                         if ((*it).isUseRegExp()) {
0178                             virusFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncRegExp, pattern));
0179                         } else {
0180                             virusFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncContains, pattern));
0181                         }
0182                     }
0183                 }
0184             }
0185             virusFilter->setApplyOnOutbound(false);
0186             virusFilter->setApplyOnInbound(true);
0187             virusFilter->setApplyOnExplicit(true);
0188             virusFilter->setStopProcessingHere(true);
0189             virusFilter->setConfigureShortcut(false);
0190 
0191             filterList.append(virusFilter);
0192         }
0193     } else { // AntiSpam mode
0194         if (!mSpamToolsUsed) {
0195             QDialog::accept();
0196             return;
0197         }
0198         // TODO Existing filters with same name are replaced. This is hardcoded
0199         // ATM and needs to be replaced with a value from a (still missing)
0200         // checkbox in the GUI. At least, the replacement is announced in the GUI.
0201         replaceExistingFilters = true;
0202         QList<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
0203         for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0204             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).isSpamTool() && !(*it).isDetectionOnly()) {
0205                 // pipe messages through the anti-spam tools,
0206                 // one single filter for each tool
0207                 // (could get combined but so it's easier to understand for the user)
0208                 auto pipeFilter = new MailFilter();
0209                 QList<FilterAction *> *pipeFilterActions = pipeFilter->actions();
0210                 FilterAction *pipeFilterAction = dict.value(QStringLiteral("filter app"))->create();
0211                 pipeFilterAction->argsFromString((*it).getDetectCmd());
0212                 pipeFilterActions->append(pipeFilterAction);
0213                 SearchPattern *pipeFilterPattern = pipeFilter->pattern();
0214                 if (replaceExistingFilters) {
0215                     pipeFilterPattern->setName((*it).getFilterName());
0216                 } else {
0217                     pipeFilterPattern->setName(uniqueNameFor((*it).getFilterName()));
0218                 }
0219                 pipeFilterPattern->append(SearchRule::createInstance("<size>", SearchRule::FuncIsLessOrEqual, QStringLiteral("256000")));
0220                 pipeFilter->setApplyOnOutbound(false);
0221                 pipeFilter->setApplyOnInbound(true);
0222                 pipeFilter->setApplyOnExplicit(true);
0223                 pipeFilter->setStopProcessingHere(false);
0224                 pipeFilter->setConfigureShortcut(false);
0225 
0226                 filterList.append(pipeFilter);
0227             }
0228         }
0229 
0230         // Sort out spam depending on header fields set by the tools
0231         auto spamFilter = new MailFilter();
0232         QList<FilterAction *> *spamFilterActions = spamFilter->actions();
0233         if (mSpamRulesPage->moveSpamSelected()) {
0234             FilterAction *spamFilterAction1 = dict.value(QStringLiteral("transfer"))->create();
0235             spamFilterAction1->argsFromString(mSpamRulesPage->selectedSpamCollectionId());
0236             spamFilterActions->append(spamFilterAction1);
0237         }
0238         FilterAction *spamFilterAction2 = dict.value(QStringLiteral("set status"))->create();
0239         spamFilterAction2->argsFromString(QStringLiteral("P")); // Spam
0240         spamFilterActions->append(spamFilterAction2);
0241         if (mSpamRulesPage->markAsReadSelected()) {
0242             FilterAction *spamFilterAction3 = dict.value(QStringLiteral("set status"))->create();
0243             spamFilterAction3->argsFromString(QStringLiteral("R")); // Read
0244             spamFilterActions->append(spamFilterAction3);
0245         }
0246         SearchPattern *spamFilterPattern = spamFilter->pattern();
0247         if (replaceExistingFilters) {
0248             spamFilterPattern->setName(i18n("Spam Handling"));
0249         } else {
0250             spamFilterPattern->setName(uniqueNameFor(i18n("Spam Handling")));
0251         }
0252         spamFilterPattern->setOp(SearchPattern::OpOr);
0253         QList<SpamToolConfig>::ConstIterator endToolList(mToolList.constEnd());
0254         for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != endToolList; ++it) {
0255             if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
0256                 if ((*it).isSpamTool()) {
0257                     const QByteArray header = (*it).getDetectionHeader().toLatin1();
0258                     const QString &pattern = (*it).getDetectionPattern();
0259                     if ((*it).isUseRegExp()) {
0260                         spamFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncRegExp, pattern));
0261                     } else {
0262                         spamFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncContains, pattern));
0263                     }
0264                 }
0265             }
0266         }
0267         spamFilter->setApplyOnOutbound(false);
0268         spamFilter->setApplyOnInbound(true);
0269         spamFilter->setApplyOnExplicit(true);
0270         spamFilter->setStopProcessingHere(true);
0271         spamFilter->setConfigureShortcut(false);
0272         filterList.append(spamFilter);
0273 
0274         if (mSpamRulesPage->moveUnsureSelected()) {
0275             // Sort out messages classified as unsure
0276             bool atLeastOneUnsurePattern = false;
0277             auto unsureFilter = new MailFilter();
0278             QList<FilterAction *> *unsureFilterActions = unsureFilter->actions();
0279             FilterAction *unsureFilterAction1 = dict.value(QStringLiteral("transfer"))->create();
0280             unsureFilterAction1->argsFromString(mSpamRulesPage->selectedUnsureCollectionId());
0281             unsureFilterActions->append(unsureFilterAction1);
0282             SearchPattern *unsureFilterPattern = unsureFilter->pattern();
0283             if (replaceExistingFilters) {
0284                 unsureFilterPattern->setName(i18n("Semi spam (unsure) handling"));
0285             } else {
0286                 unsureFilterPattern->setName(uniqueNameFor(i18n("Semi spam (unsure) handling")));
0287             }
0288             unsureFilterPattern->setOp(SearchPattern::OpOr);
0289             QList<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
0290             for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0291                 if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
0292                     if ((*it).isSpamTool() && (*it).hasTristateDetection()) {
0293                         atLeastOneUnsurePattern = true;
0294                         const QByteArray header = (*it).getDetectionHeader().toLatin1();
0295                         const QString &pattern = (*it).getDetectionPattern2();
0296                         if ((*it).isUseRegExp()) {
0297                             unsureFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncRegExp, pattern));
0298                         } else {
0299                             unsureFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncContains, pattern));
0300                         }
0301                     }
0302                 }
0303             }
0304             unsureFilter->setApplyOnOutbound(false);
0305             unsureFilter->setApplyOnInbound(true);
0306             unsureFilter->setApplyOnExplicit(true);
0307             unsureFilter->setStopProcessingHere(true);
0308             unsureFilter->setConfigureShortcut(false);
0309 
0310             if (atLeastOneUnsurePattern) {
0311                 filterList.append(unsureFilter);
0312             } else {
0313                 delete unsureFilter;
0314             }
0315         }
0316 
0317         // Classify messages manually as Spam
0318         auto classSpamFilter = new MailFilter();
0319         classSpamFilter->setIcon(QStringLiteral("mail-mark-junk"));
0320         QList<FilterAction *> *classSpamFilterActions = classSpamFilter->actions();
0321         FilterAction *classSpamFilterActionFirst = dict.value(QStringLiteral("set status"))->create();
0322         classSpamFilterActionFirst->argsFromString(QStringLiteral("P"));
0323         classSpamFilterActions->append(classSpamFilterActionFirst);
0324         QList<SpamToolConfig>::ConstIterator endToolList2(mToolList.constEnd());
0325         for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != endToolList2; ++it) {
0326             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).useBayesFilter() && !(*it).isDetectionOnly()) {
0327                 FilterAction *classSpamFilterAction = dict.value(QStringLiteral("execute"))->create();
0328                 classSpamFilterAction->argsFromString((*it).getSpamCmd());
0329                 classSpamFilterActions->append(classSpamFilterAction);
0330             }
0331         }
0332         if (mSpamRulesPage->moveSpamSelected()) {
0333             FilterAction *classSpamFilterActionLast = dict.value(QStringLiteral("transfer"))->create();
0334             classSpamFilterActionLast->argsFromString(mSpamRulesPage->selectedSpamCollectionId());
0335             classSpamFilterActions->append(classSpamFilterActionLast);
0336         }
0337 
0338         SearchPattern *classSpamFilterPattern = classSpamFilter->pattern();
0339         if (replaceExistingFilters) {
0340             classSpamFilterPattern->setName(i18n("Classify as Spam"));
0341         } else {
0342             classSpamFilterPattern->setName(uniqueNameFor(i18n("Classify as Spam")));
0343         }
0344         classSpamFilterPattern->append(SearchRule::createInstance("<size>", SearchRule::FuncIsGreaterOrEqual, QStringLiteral("0")));
0345         classSpamFilter->setApplyOnOutbound(false);
0346         classSpamFilter->setApplyOnInbound(false);
0347         classSpamFilter->setApplyOnExplicit(false);
0348         classSpamFilter->setStopProcessingHere(true);
0349         classSpamFilter->setConfigureShortcut(true);
0350         classSpamFilter->setConfigureToolbar(true);
0351         classSpamFilter->setToolbarName(i18n("Spam"));
0352         filterList.append(classSpamFilter);
0353 
0354         // Classify messages manually as not Spam / as Ham
0355         auto classHamFilter = new MailFilter();
0356         classHamFilter->setIcon(QStringLiteral("mail-mark-notjunk"));
0357         QList<FilterAction *> *classHamFilterActions = classHamFilter->actions();
0358         FilterAction *classHamFilterActionFirst = dict.value(QStringLiteral("set status"))->create();
0359         classHamFilterActionFirst->argsFromString(QStringLiteral("H"));
0360         classHamFilterActions->append(classHamFilterActionFirst);
0361         end = mToolList.constEnd();
0362         for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0363             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).useBayesFilter() && !(*it).isDetectionOnly()) {
0364                 FilterAction *classHamFilterAction = dict.value(QStringLiteral("execute"))->create();
0365                 classHamFilterAction->argsFromString((*it).getHamCmd());
0366                 classHamFilterActions->append(classHamFilterAction);
0367             }
0368         }
0369         end = mToolList.constEnd();
0370         for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0371             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).useBayesFilter() && !(*it).isDetectionOnly()) {
0372                 FilterAction *classHamFilterAction = dict.value(QStringLiteral("filter app"))->create();
0373                 classHamFilterAction->argsFromString((*it).getNoSpamCmd());
0374                 classHamFilterActions->append(classHamFilterAction);
0375             }
0376         }
0377         SearchPattern *classHamFilterPattern = classHamFilter->pattern();
0378         if (replaceExistingFilters) {
0379             classHamFilterPattern->setName(i18n("Classify as NOT Spam"));
0380         } else {
0381             classHamFilterPattern->setName(uniqueNameFor(i18n("Classify as NOT Spam")));
0382         }
0383         classHamFilterPattern->append(SearchRule::createInstance("<size>", SearchRule::FuncIsGreaterOrEqual, QStringLiteral("0")));
0384         classHamFilter->setApplyOnOutbound(false);
0385         classHamFilter->setApplyOnInbound(false);
0386         classHamFilter->setApplyOnExplicit(false);
0387         classHamFilter->setStopProcessingHere(true);
0388         classHamFilter->setConfigureShortcut(true);
0389         classHamFilter->setConfigureToolbar(true);
0390         classHamFilter->setToolbarName(i18n("Ham"));
0391         filterList.append(classHamFilter);
0392     }
0393 
0394     /* Now that all the filters have been added to the list, tell
0395      * the filter manager about it. That will Q_EMIT filterListUpdate
0396      * which will result in the filter list in kmmainwidget being
0397      * initialized. This should happened only once. */
0398     if (!filterList.isEmpty()) {
0399         MailCommon::FilterManager::instance()->appendFilters(filterList, replaceExistingFilters);
0400     }
0401 
0402     QDialog::accept();
0403 }
0404 
0405 void AntiSpamWizard::checkProgramsSelections()
0406 {
0407     bool supportUnsure = false;
0408 
0409     mSpamToolsUsed = false;
0410     mVirusToolsUsed = false;
0411     QList<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
0412     for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0413         if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
0414             if ((*it).isSpamTool()) {
0415                 mSpamToolsUsed = true;
0416                 if ((*it).hasTristateDetection()) {
0417                     supportUnsure = true;
0418                 }
0419             }
0420             if ((*it).isVirusTool()) {
0421                 mVirusToolsUsed = true;
0422             }
0423 
0424             if (mSpamToolsUsed && mVirusToolsUsed && supportUnsure) {
0425                 break;
0426             }
0427         }
0428     }
0429 
0430     if (mMode == AntiSpam) {
0431         mSpamRulesPage->allowUnsureFolderSelection(supportUnsure);
0432         mSpamRulesPage->allowMoveSpam(mSpamToolsUsed);
0433         slotBuildSummary();
0434         setAppropriate(mSpamRulesPageItem, mSpamToolsUsed);
0435         setAppropriate(mSummaryPageItem, mSpamToolsUsed);
0436     } else if (mMode == AntiVirus) {
0437         if (mVirusToolsUsed) {
0438             checkVirusRulesSelections();
0439         }
0440         setAppropriate(mVirusRulesPageItem, mVirusToolsUsed);
0441     }
0442 }
0443 
0444 void AntiSpamWizard::checkVirusRulesSelections()
0445 {
0446     // setFinishEnabled( mVirusRulesPage, anyVirusOptionChecked() );
0447 }
0448 
0449 void AntiSpamWizard::checkToolAvailability()
0450 {
0451     // this can take some time to find the tools
0452     KCursorSaver saver(Qt::WaitCursor);
0453     bool found = false;
0454     QList<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
0455     for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0456         const QString text(i18n("Scanning for %1...", (*it).getId()));
0457         mInfoPage->setScanProgressText(text);
0458         if ((*it).isSpamTool() && (*it).isServerBased()) {
0459             // check the configured account for pattern in <server>
0460             const QString pattern = (*it).getServerPattern();
0461             qDebug() << "Testing for server pattern:" << pattern;
0462             const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances();
0463             for (const Akonadi::AgentInstance &type : lst) {
0464                 if (type.status() == Akonadi::AgentInstance::Broken) {
0465                     continue;
0466                 }
0467                 const QString typeIdentifier(type.identifier());
0468                 if (PimCommon::Util::isImapResource(typeIdentifier)) {
0469                     MailCommon::ResourceReadConfigFile resourceFile(typeIdentifier);
0470                     const KConfigGroup grp = resourceFile.group(QStringLiteral("network"));
0471                     if (grp.isValid()) {
0472                         const QString host = grp.readEntry(QStringLiteral("ImapServer"));
0473                         if (host.contains(pattern.toLower(), Qt::CaseInsensitive)) {
0474                             mInfoPage->addAvailableTool((*it).getVisibleName());
0475                             found = true;
0476                         }
0477                     }
0478                 } else if (type.identifier().contains(POP3_RESOURCE_IDENTIFIER)) {
0479                     MailCommon::ResourceReadConfigFile resourceFile(typeIdentifier);
0480                     const KConfigGroup grp = resourceFile.group(QStringLiteral("General"));
0481                     if (grp.isValid()) {
0482                         const QString host = grp.readEntry(QStringLiteral("host"));
0483                         if (host.contains(pattern.toLower(), Qt::CaseInsensitive)) {
0484                             mInfoPage->addAvailableTool((*it).getVisibleName());
0485                             found = true;
0486                         }
0487                     }
0488                 }
0489             }
0490         } else {
0491             // check the availability of the application
0492             qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 200);
0493             if (!checkForProgram((*it).getExecutable())) {
0494                 mInfoPage->addAvailableTool((*it).getVisibleName());
0495                 found = true;
0496             }
0497         }
0498     }
0499     if (found) {
0500         mInfoPage->setScanProgressText((mMode == AntiSpam) ? i18n("Scanning for anti-spam tools finished.") : i18n("Scanning for anti-virus tools finished."));
0501     } else {
0502         mInfoPage->setScanProgressText((mMode == AntiSpam) ? i18n("<p>Sorry, no spam detection tools have been found. "
0503                                                                   "Install your spam detection software and "
0504                                                                   "re-run this wizard.</p>")
0505                                                            : i18n("Scanning complete. No anti-virus tools found."));
0506     }
0507     checkProgramsSelections();
0508 }
0509 
0510 void AntiSpamWizard::slotHelpClicked()
0511 {
0512     PimCommon::Util::invokeHelp((mMode == AntiSpam) ? QStringLiteral("kmail2/the-anti-spam-wizard.html") : QStringLiteral("kmail2/the-anti-virus-wizard.html"));
0513 }
0514 
0515 void AntiSpamWizard::slotBuildSummary()
0516 {
0517     QString text;
0518     QString newFilters;
0519     QString replaceFilters;
0520 
0521     if (mMode == AntiVirus) {
0522         text.clear(); // TODO add summary for the virus part
0523     } else { // AntiSpam mode
0524         if (mSpamRulesPage->markAsReadSelected()) {
0525             if (mSpamRulesPage->moveSpamSelected()) {
0526                 text = i18n(
0527                     "<p>Messages classified as spam are marked as read."
0528                     "<br />Spam messages are moved into the folder named <i>%1</i>.</p>",
0529                     mSpamRulesPage->selectedSpamCollectionName());
0530             } else {
0531                 text = i18n(
0532                     "<p>Messages classified as spam are marked as read."
0533                     "<br />Spam messages are not moved into a certain folder.</p>");
0534             }
0535         } else {
0536             if (mSpamRulesPage->moveSpamSelected()) {
0537                 text = i18n(
0538                     "<p>Messages classified as spam are not marked as read."
0539                     "<br />Spam messages are moved into the folder named <i>%1</i>.</p>",
0540                     mSpamRulesPage->selectedSpamCollectionName());
0541             } else {
0542                 text = i18n(
0543                     "<p>Messages classified as spam are not marked as read."
0544                     "<br />Spam messages are not moved into a certain folder.</p>");
0545             }
0546         }
0547         QList<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
0548         for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0549             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).isSpamTool() && !(*it).isDetectionOnly()) {
0550                 sortFilterOnExistance((*it).getFilterName(), newFilters, replaceFilters);
0551             }
0552         }
0553         sortFilterOnExistance(i18n("Spam Handling"), newFilters, replaceFilters);
0554 
0555         // The need for a handling of status "probably spam" depends on the tools chosen
0556         if (mSpamRulesPage->moveUnsureSelected()) {
0557             bool atLeastOneUnsurePattern = false;
0558             end = mToolList.constEnd();
0559             for (QList<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
0560                 if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
0561                     if ((*it).isSpamTool() && (*it).hasTristateDetection()) {
0562                         atLeastOneUnsurePattern = true;
0563                         break;
0564                     }
0565                 }
0566             }
0567             if (atLeastOneUnsurePattern) {
0568                 sortFilterOnExistance(i18n("Semi spam (unsure) handling"), newFilters, replaceFilters);
0569                 text +=
0570                     i18n("<p>The folder for messages classified as unsure (probably spam) is <i>%1</i>.</p>", mSpamRulesPage->selectedUnsureCollectionName());
0571             }
0572         }
0573 
0574         // Manual classification via toolbar icon / manually applied filter action
0575         sortFilterOnExistance(i18n("Classify as Spam"), newFilters, replaceFilters);
0576         sortFilterOnExistance(i18n("Classify as NOT Spam"), newFilters, replaceFilters);
0577 
0578         // Show the filters in the summary
0579         if (!newFilters.isEmpty()) {
0580             text += i18n("<p>The wizard will create the following filters:<ul>%1</ul></p>", newFilters);
0581         }
0582         if (!replaceFilters.isEmpty()) {
0583             text += i18n("<p>The wizard will replace the following filters:<ul>%1</ul></p>", replaceFilters);
0584         }
0585     }
0586 
0587     mSummaryPage->setSummaryText(text);
0588 }
0589 
0590 int AntiSpamWizard::checkForProgram(const QString &executable) const
0591 {
0592     qDebug() << "Testing for executable:" << executable;
0593     KProcess process;
0594     process.setShellCommand(executable);
0595     return process.execute();
0596 }
0597 
0598 bool AntiSpamWizard::anyVirusOptionChecked() const
0599 {
0600     return mVirusRulesPage->moveRulesSelected() || mVirusRulesPage->pipeRulesSelected();
0601 }
0602 
0603 const QString AntiSpamWizard::uniqueNameFor(const QString &name)
0604 {
0605     return MailCommon::FilterManager::instance()->createUniqueFilterName(name);
0606 }
0607 
0608 void AntiSpamWizard::sortFilterOnExistance(const QString &intendedFilterName, QString &newFilters, QString &replaceFilters)
0609 {
0610     if (uniqueNameFor(intendedFilterName) == intendedFilterName) {
0611         newFilters += QLatin1StringView("<li>") + intendedFilterName + QLatin1StringView("</li>");
0612     } else {
0613         replaceFilters += QLatin1StringView("<li>") + intendedFilterName + QLatin1StringView("</li>");
0614     }
0615 }
0616 
0617 //---------------------------------------------------------------------------
0618 AntiSpamWizard::SpamToolConfig::SpamToolConfig(const QString &toolId,
0619                                                int configVersion,
0620                                                int prio,
0621                                                const QString &name,
0622                                                const QString &exec,
0623                                                const QString &url,
0624                                                const QString &filter,
0625                                                const QString &detection,
0626                                                const QString &spam,
0627                                                const QString &ham,
0628                                                const QString &noSpam,
0629                                                const QString &header,
0630                                                const QString &pattern,
0631                                                const QString &pattern2,
0632                                                const QString &serverPattern,
0633                                                bool detectionOnly,
0634                                                bool regExp,
0635                                                bool bayesFilter,
0636                                                bool tristateDetection,
0637                                                WizardMode type)
0638     : mId(toolId)
0639     , mVersion(configVersion)
0640     , mPrio(prio)
0641     , mVisibleName(name)
0642     , mExecutable(exec)
0643     , mWhatsThisText(url)
0644     , mFilterName(filter)
0645     , mDetectCmd(detection)
0646     , mSpamCmd(spam)
0647     , mHamCmd(ham)
0648     , mNoSpamCmd(noSpam)
0649     , mDetectionHeader(header)
0650     , mDetectionPattern(pattern)
0651     , mDetectionPattern2(pattern2)
0652     , mServerPattern(serverPattern)
0653     , mDetectionOnly(detectionOnly)
0654     , mUseRegExp(regExp)
0655     , mSupportsBayesFilter(bayesFilter)
0656     , mSupportsUnsure(tristateDetection)
0657     , mType(type)
0658 {
0659 }
0660 
0661 int AntiSpamWizard::SpamToolConfig::getVersion() const
0662 {
0663     return mVersion;
0664 }
0665 
0666 int AntiSpamWizard::SpamToolConfig::getPrio() const
0667 {
0668     return mPrio;
0669 }
0670 
0671 QString AntiSpamWizard::SpamToolConfig::getId() const
0672 {
0673     return mId;
0674 }
0675 
0676 QString AntiSpamWizard::SpamToolConfig::getVisibleName() const
0677 {
0678     return mVisibleName;
0679 }
0680 
0681 QString AntiSpamWizard::SpamToolConfig::getExecutable() const
0682 {
0683     return mExecutable;
0684 }
0685 
0686 QString AntiSpamWizard::SpamToolConfig::getWhatsThisText() const
0687 {
0688     return mWhatsThisText;
0689 }
0690 
0691 QString AntiSpamWizard::SpamToolConfig::getFilterName() const
0692 {
0693     return mFilterName;
0694 }
0695 
0696 QString AntiSpamWizard::SpamToolConfig::getDetectCmd() const
0697 {
0698     return mDetectCmd;
0699 }
0700 
0701 QString AntiSpamWizard::SpamToolConfig::getSpamCmd() const
0702 {
0703     return mSpamCmd;
0704 }
0705 
0706 QString AntiSpamWizard::SpamToolConfig::getHamCmd() const
0707 {
0708     return mHamCmd;
0709 }
0710 
0711 QString AntiSpamWizard::SpamToolConfig::getNoSpamCmd() const
0712 {
0713     return mNoSpamCmd;
0714 }
0715 
0716 QString AntiSpamWizard::SpamToolConfig::getDetectionHeader() const
0717 {
0718     return mDetectionHeader;
0719 }
0720 
0721 QString AntiSpamWizard::SpamToolConfig::getDetectionPattern() const
0722 {
0723     return mDetectionPattern;
0724 }
0725 
0726 QString AntiSpamWizard::SpamToolConfig::getDetectionPattern2() const
0727 {
0728     return mDetectionPattern2;
0729 }
0730 
0731 QString AntiSpamWizard::SpamToolConfig::getServerPattern() const
0732 {
0733     return mServerPattern;
0734 }
0735 
0736 bool AntiSpamWizard::SpamToolConfig::isServerBased() const
0737 {
0738     return !mServerPattern.isEmpty();
0739 }
0740 
0741 bool AntiSpamWizard::SpamToolConfig::isDetectionOnly() const
0742 {
0743     return mDetectionOnly;
0744 }
0745 
0746 bool AntiSpamWizard::SpamToolConfig::isUseRegExp() const
0747 {
0748     return mUseRegExp;
0749 }
0750 
0751 bool AntiSpamWizard::SpamToolConfig::useBayesFilter() const
0752 {
0753     return mSupportsBayesFilter;
0754 }
0755 
0756 bool AntiSpamWizard::SpamToolConfig::hasTristateDetection() const
0757 {
0758     return mSupportsUnsure;
0759 }
0760 
0761 AntiSpamWizard::WizardMode AntiSpamWizard::SpamToolConfig::getType() const
0762 {
0763     return mType;
0764 }
0765 
0766 bool AntiSpamWizard::SpamToolConfig::isSpamTool() const
0767 {
0768     return mType == AntiSpam;
0769 }
0770 
0771 bool AntiSpamWizard::SpamToolConfig::isVirusTool() const
0772 {
0773     return mType == AntiVirus;
0774 }
0775 
0776 //---------------------------------------------------------------------------
0777 AntiSpamWizard::ConfigReader::ConfigReader(WizardMode mode, QList<SpamToolConfig> &configList)
0778     : mToolList(configList)
0779     , mMode(mode)
0780 {
0781     if (mMode == AntiSpam) {
0782         mConfig = KSharedConfig::openConfig(QStringLiteral("kmail.antispamrc"));
0783     } else {
0784         mConfig = KSharedConfig::openConfig(QStringLiteral("kmail.antivirusrc"));
0785     }
0786 }
0787 
0788 AntiSpamWizard::ConfigReader::~ConfigReader() = default;
0789 
0790 void AntiSpamWizard::ConfigReader::readAndMergeConfig()
0791 {
0792     QString groupName = (mMode == AntiSpam) ? QStringLiteral("Spamtool #%1") : QStringLiteral("Virustool #%1");
0793     // read the configuration from the global config file
0794     mConfig->setReadDefaults(true);
0795     KConfigGroup general(mConfig, QStringLiteral("General"));
0796     const int registeredTools = general.readEntry("tools", 0);
0797     for (int i = 1; i <= registeredTools; ++i) {
0798         KConfigGroup toolConfig(mConfig, groupName.arg(i));
0799         if (!toolConfig.readEntry("HeadersOnly", false)) {
0800             mToolList.append(readToolConfig(toolConfig));
0801         }
0802     }
0803 
0804     // read the configuration from the user config file
0805     // and merge newer config data
0806     mConfig->setReadDefaults(false);
0807     KConfigGroup user_general(mConfig, QStringLiteral("General"));
0808     const int user_registeredTools = user_general.readEntry("tools", 0);
0809     for (int i = 1; i <= user_registeredTools; ++i) {
0810         KConfigGroup toolConfig(mConfig, groupName.arg(i));
0811         if (!toolConfig.readEntry("HeadersOnly", false)) {
0812             mergeToolConfig(readToolConfig(toolConfig));
0813         }
0814     }
0815     // Make sure to have add least one tool listed even when the
0816     // config file was not found or whatever went wrong
0817     // Currently only works for spam tools
0818     if (mMode == AntiSpam) {
0819         if (registeredTools < 1 && user_registeredTools < 1) {
0820             mToolList.append(createDummyConfig());
0821         }
0822         sortToolList();
0823     }
0824 }
0825 
0826 AntiSpamWizard::SpamToolConfig AntiSpamWizard::ConfigReader::readToolConfig(KConfigGroup &configGroup)
0827 {
0828     const QString id = configGroup.readEntry("Ident");
0829     const int version = configGroup.readEntry("Version", 0);
0830 #ifndef NDEBUG
0831     qDebug() << "Found predefined tool:" << id;
0832     qDebug() << "With config version  :" << version;
0833 #endif
0834     const int prio = configGroup.readEntry("Priority", 1);
0835     const QString name = configGroup.readEntry("VisibleName");
0836     const QString executable = configGroup.readEntry("Executable");
0837     const QString url = configGroup.readEntry("URL");
0838     const QString filterName = configGroup.readEntry("PipeFilterName");
0839     const QString detectCmd = configGroup.readEntry("PipeCmdDetect");
0840     const QString spamCmd = configGroup.readEntry("ExecCmdSpam");
0841     const QString hamCmd = configGroup.readEntry("ExecCmdHam");
0842     const QString noSpamCmd = configGroup.readEntry("PipeCmdNoSpam");
0843     const QString header = configGroup.readEntry("DetectionHeader");
0844     const QString pattern = configGroup.readEntry("DetectionPattern");
0845     const QString pattern2 = configGroup.readEntry("DetectionPattern2");
0846     const QString serverPattern = configGroup.readEntry("ServerPattern");
0847     const bool detectionOnly = configGroup.readEntry("DetectionOnly", false);
0848     const bool useRegExp = configGroup.readEntry("UseRegExp", false);
0849     const bool supportsBayes = configGroup.readEntry("SupportsBayes", false);
0850     const bool supportsUnsure = configGroup.readEntry("SupportsUnsure", false);
0851     return SpamToolConfig(id,
0852                           version,
0853                           prio,
0854                           name,
0855                           executable,
0856                           url,
0857                           filterName,
0858                           detectCmd,
0859                           spamCmd,
0860                           hamCmd,
0861                           noSpamCmd,
0862                           header,
0863                           pattern,
0864                           pattern2,
0865                           serverPattern,
0866                           detectionOnly,
0867                           useRegExp,
0868                           supportsBayes,
0869                           supportsUnsure,
0870                           mMode);
0871 }
0872 
0873 AntiSpamWizard::SpamToolConfig AntiSpamWizard::ConfigReader::createDummyConfig()
0874 {
0875     return SpamToolConfig(QStringLiteral("spamassassin"),
0876                           0,
0877                           1,
0878                           QStringLiteral("SpamAssassin"),
0879                           QStringLiteral("spamassassin -V"),
0880                           QStringLiteral("https://spamassassin.apache.org/"),
0881                           QStringLiteral("SpamAssassin Check"),
0882                           QStringLiteral("spamassassin -L"),
0883                           QStringLiteral("sa-learn -L --spam --no-sync --single"),
0884                           QStringLiteral("sa-learn -L --ham --no-sync --single"),
0885                           QStringLiteral("spamassassin -d"),
0886                           QStringLiteral("X-Spam-Status"),
0887                           QStringLiteral("yes"),
0888                           QString(),
0889                           QString(),
0890                           false,
0891                           false,
0892                           true,
0893                           false,
0894                           AntiSpam);
0895 }
0896 
0897 void AntiSpamWizard::ConfigReader::mergeToolConfig(const AntiSpamWizard::SpamToolConfig &config)
0898 {
0899     bool found = false;
0900     QList<SpamToolConfig>::Iterator end(mToolList.end());
0901     for (QList<SpamToolConfig>::Iterator it = mToolList.begin(); it != end; ++it) {
0902 #ifndef NDEBUG
0903         qDebug() << "Check against tool:" << (*it).getId();
0904         qDebug() << "Against version   :" << (*it).getVersion();
0905 #endif
0906         if ((*it).getId() == config.getId()) {
0907             found = true;
0908             if ((*it).getVersion() < config.getVersion()) {
0909 #ifndef NDEBUG
0910                 qDebug() << "Replacing config ...";
0911 #endif
0912                 mToolList.erase(it);
0913                 mToolList.append(config);
0914             }
0915             break;
0916         }
0917     }
0918     if (!found) {
0919         mToolList.append(config);
0920     }
0921 }
0922 
0923 void AntiSpamWizard::ConfigReader::sortToolList()
0924 {
0925     QList<SpamToolConfig> tmpList;
0926     SpamToolConfig config;
0927 
0928     while (!mToolList.isEmpty()) {
0929         QList<SpamToolConfig>::Iterator highest;
0930         int priority = 0; // ascending
0931         QList<SpamToolConfig>::Iterator end(mToolList.end());
0932         for (QList<SpamToolConfig>::Iterator it = mToolList.begin(); it != end; ++it) {
0933             if ((*it).getPrio() > priority) {
0934                 priority = (*it).getPrio();
0935                 highest = it;
0936             }
0937         }
0938         config = (*highest);
0939         tmpList.append(config);
0940         mToolList.erase(highest);
0941     }
0942     QList<SpamToolConfig>::ConstIterator end(tmpList.constEnd());
0943     for (QList<SpamToolConfig>::ConstIterator it = tmpList.constBegin(); it != end; ++it) {
0944         mToolList.append((*it));
0945     }
0946 }
0947 
0948 //---------------------------------------------------------------------------
0949 ASWizPage::ASWizPage(QWidget *parent, const QString &name)
0950     : QWidget(parent)
0951 {
0952     setObjectName(name);
0953     mLayout = new QHBoxLayout(this);
0954 
0955     auto sideLayout = new QVBoxLayout();
0956     mLayout->addItem(sideLayout);
0957     mLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Minimum, QSizePolicy::Expanding));
0958 
0959     QPixmap banner;
0960     banner.load(QStringLiteral(":/org/kde/kmail/pics/kmwizard.png"));
0961     auto bannerLabel = new QLabel(this);
0962     bannerLabel->setPixmap(banner);
0963     bannerLabel->setScaledContents(false);
0964     bannerLabel->setFrameShape(QFrame::StyledPanel);
0965     bannerLabel->setFrameShadow(QFrame::Sunken);
0966     bannerLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0967 
0968     sideLayout->addWidget(bannerLabel);
0969     sideLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Minimum, QSizePolicy::Expanding));
0970 }
0971 
0972 //---------------------------------------------------------------------------
0973 ASWizInfoPage::ASWizInfoPage(AntiSpamWizard::WizardMode mode, QWidget *parent, const QString &name)
0974     : ASWizPage(parent, name)
0975 {
0976     QBoxLayout *layout = new QVBoxLayout();
0977     mLayout->addItem(layout);
0978 
0979     auto introText = new QTextEdit(this);
0980     introText->setText((mode == AntiSpamWizard::AntiSpam) ? i18n("The wizard will search for any tools to do spam detection\n"
0981                                                                  "and setup KMail to work with them.")
0982                                                           : i18n("<p>Here you can get some assistance in setting up KMail's filter "
0983                                                                  "rules to use some commonly-known anti-virus tools.</p>"
0984                                                                  "<p>The wizard can detect those tools on your computer as "
0985                                                                  "well as create filter rules to classify messages using these "
0986                                                                  "tools and to separate messages containing viruses. "
0987                                                                  "The wizard will not take any existing filter "
0988                                                                  "rules into consideration: it will always append the new rules.</p>"
0989                                                                  "<p><b>Warning:</b> As KMail appears to be frozen during the scan of the "
0990                                                                  "messages for viruses, you may encounter problems with "
0991                                                                  "the responsiveness of KMail because anti-virus tool "
0992                                                                  "operations are usually time consuming; please consider "
0993                                                                  "deleting the filter rules created by the wizard to get "
0994                                                                  "back to the former behavior.</p>"));
0995     introText->setReadOnly(true);
0996     introText->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
0997     layout->addWidget(introText);
0998 
0999     mScanProgressText = new QLabel(this);
1000     mScanProgressText->clear();
1001     mScanProgressText->setWordWrap(true);
1002     layout->addWidget(mScanProgressText);
1003 
1004     mToolsList = new QListWidget(this);
1005     mToolsList->hide();
1006     mToolsList->setSelectionMode(QAbstractItemView::MultiSelection);
1007     mToolsList->setLayoutMode(QListView::Batched);
1008     mToolsList->setBatchSize(10);
1009     mToolsList->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum));
1010     layout->addWidget(mToolsList);
1011     connect(mToolsList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ASWizInfoPage::processSelectionChange);
1012 
1013     mSelectionHint = new QLabel(this);
1014     mSelectionHint->clear();
1015     mSelectionHint->setWordWrap(true);
1016     layout->addWidget(mSelectionHint);
1017 }
1018 
1019 void ASWizInfoPage::setScanProgressText(const QString &toolName)
1020 {
1021     mScanProgressText->setText(toolName);
1022 }
1023 
1024 void ASWizInfoPage::addAvailableTool(const QString &visibleName)
1025 {
1026     mToolsList->addItem(visibleName);
1027     if (!mToolsList->isVisible()) {
1028         mToolsList->show();
1029         mToolsList->selectionModel()->clearSelection();
1030         mToolsList->setCurrentRow(0);
1031         mSelectionHint->setText(
1032             i18n("<p>Please select the tools to be used "
1033                  "for the detection and go "
1034                  "to the next page.</p>"));
1035     }
1036 }
1037 
1038 bool ASWizInfoPage::isProgramSelected(const QString &visibleName) const
1039 {
1040     const QList<QListWidgetItem *> foundItems = mToolsList->findItems(visibleName, Qt::MatchFixedString);
1041     return !foundItems.isEmpty() && foundItems[0]->isSelected();
1042 }
1043 
1044 void ASWizInfoPage::processSelectionChange()
1045 {
1046     Q_EMIT selectionChanged();
1047 }
1048 
1049 //---------------------------------------------------------------------------
1050 ASWizSpamRulesPage::ASWizSpamRulesPage(QWidget *parent, const QString &name)
1051     : ASWizPage(parent, name)
1052 {
1053     auto layout = new QVBoxLayout();
1054     mLayout->addItem(layout);
1055 
1056     mMarkRules = new QCheckBox(i18n("&Mark detected spam messages as read"), this);
1057     mMarkRules->setWhatsThis(i18n("Mark messages which have been classified as spam as read."));
1058     layout->addWidget(mMarkRules);
1059 
1060     mMoveSpamRules = new QCheckBox(i18n("Move &known spam to:"), this);
1061     mMoveSpamRules->setWhatsThis(
1062         i18n("The default folder for spam messages is the trash folder, "
1063              "but you may change that in the folder view below."));
1064     layout->addWidget(mMoveSpamRules);
1065 
1066     mFolderReqForSpamFolder = new FolderRequester(this);
1067     mFolderReqForSpamFolder->setCollection(CommonKernel->trashCollectionFolder());
1068     mFolderReqForSpamFolder->setMustBeReadWrite(true);
1069     mFolderReqForSpamFolder->setShowOutbox(false);
1070 
1071     auto hLayout1 = new QHBoxLayout();
1072     layout->addItem(hLayout1);
1073     hLayout1->addWidget(mFolderReqForSpamFolder);
1074 
1075     mMoveUnsureRules = new QCheckBox(i18n("Move &probable spam to:"), this);
1076     mMoveUnsureRules->setWhatsThis(
1077         i18n("The default folder is the inbox folder, but you may change that "
1078              "in the folder view below.<p>"
1079              "Not all tools support a classification as unsure. If you have not "
1080              "selected a capable tool, you cannot select a folder as well.</p>"));
1081     layout->addWidget(mMoveUnsureRules);
1082 
1083     mFolderReqForUnsureFolder = new FolderRequester(this);
1084     mFolderReqForUnsureFolder->setCollection(CommonKernel->inboxCollectionFolder());
1085     mFolderReqForUnsureFolder->setMustBeReadWrite(true);
1086     mFolderReqForUnsureFolder->setShowOutbox(false);
1087 
1088     auto hLayout2 = new QHBoxLayout();
1089     layout->addItem(hLayout2);
1090     hLayout2->addWidget(mFolderReqForUnsureFolder);
1091 
1092     layout->addStretch();
1093 
1094     connect(mMarkRules, &QAbstractButton::clicked, this, &ASWizSpamRulesPage::processSelectionChange);
1095     connect(mMoveSpamRules, &QAbstractButton::clicked, this, &ASWizSpamRulesPage::processSelectionChange);
1096     connect(mMoveUnsureRules, &QAbstractButton::clicked, this, &ASWizSpamRulesPage::processSelectionChange);
1097     connect(mFolderReqForSpamFolder, &FolderRequester::folderChanged, this, &ASWizSpamRulesPage::processSelectionChange);
1098     connect(mFolderReqForUnsureFolder, &FolderRequester::folderChanged, this, &ASWizSpamRulesPage::processSelectionChange);
1099 
1100     mMarkRules->setChecked(true);
1101     mMoveSpamRules->setChecked(true);
1102 }
1103 
1104 bool ASWizSpamRulesPage::markAsReadSelected() const
1105 {
1106     return mMarkRules->isChecked();
1107 }
1108 
1109 bool ASWizSpamRulesPage::moveSpamSelected() const
1110 {
1111     return mMoveSpamRules->isChecked();
1112 }
1113 
1114 bool ASWizSpamRulesPage::moveUnsureSelected() const
1115 {
1116     return mMoveUnsureRules->isChecked();
1117 }
1118 
1119 QString ASWizSpamRulesPage::selectedSpamCollectionId() const
1120 {
1121     return QString::number(selectedSpamCollection().id());
1122 }
1123 
1124 QString ASWizSpamRulesPage::selectedSpamCollectionName() const
1125 {
1126     return selectedSpamCollection().name();
1127 }
1128 
1129 Akonadi::Collection ASWizSpamRulesPage::selectedSpamCollection() const
1130 {
1131     if (mFolderReqForSpamFolder->hasCollection()) {
1132         return mFolderReqForSpamFolder->collection();
1133     } else {
1134         return CommonKernel->trashCollectionFolder();
1135     }
1136 }
1137 
1138 Akonadi::Collection ASWizSpamRulesPage::selectedUnsureCollection() const
1139 {
1140     if (mFolderReqForUnsureFolder->hasCollection()) {
1141         return mFolderReqForUnsureFolder->collection();
1142     } else {
1143         return CommonKernel->inboxCollectionFolder();
1144     }
1145 }
1146 
1147 QString ASWizSpamRulesPage::selectedUnsureCollectionName() const
1148 {
1149     return selectedUnsureCollection().name();
1150 }
1151 
1152 QString ASWizSpamRulesPage::selectedUnsureCollectionId() const
1153 {
1154     return QString::number(selectedUnsureCollection().id());
1155 }
1156 
1157 void ASWizSpamRulesPage::processSelectionChange()
1158 {
1159     mFolderReqForSpamFolder->setEnabled(mMoveSpamRules->isChecked());
1160     mFolderReqForUnsureFolder->setEnabled(mMoveUnsureRules->isChecked());
1161     Q_EMIT selectionChanged();
1162 }
1163 
1164 void ASWizSpamRulesPage::allowUnsureFolderSelection(bool enabled)
1165 {
1166     mMoveUnsureRules->setEnabled(enabled);
1167     mMoveUnsureRules->setVisible(enabled);
1168     mFolderReqForUnsureFolder->setEnabled(enabled);
1169     mFolderReqForUnsureFolder->setVisible(enabled);
1170 }
1171 
1172 void ASWizSpamRulesPage::allowMoveSpam(bool enabled)
1173 {
1174     mMarkRules->setEnabled(enabled);
1175     mMarkRules->setChecked(enabled);
1176     mMoveSpamRules->setEnabled(enabled);
1177     mMoveSpamRules->setChecked(enabled);
1178 }
1179 
1180 //---------------------------------------------------------------------------
1181 ASWizVirusRulesPage::ASWizVirusRulesPage(QWidget *parent, const QString &name)
1182     : ASWizPage(parent, name)
1183 {
1184     auto grid = new QGridLayout();
1185     mLayout->addItem(grid);
1186 
1187     mPipeRules = new QCheckBox(i18n("Check messages using the anti-virus tools"), this);
1188     mPipeRules->setWhatsThis(
1189         i18n("Let the anti-virus tools check your messages. The wizard "
1190              "will create appropriate filters. The messages are usually "
1191              "marked by the tools so that following filters can react "
1192              "on this and, for example, move virus messages to a special folder."));
1193     grid->addWidget(mPipeRules, 0, 0);
1194 
1195     mMoveRules = new QCheckBox(i18n("Move detected viral messages to the selected folder"), this);
1196     mMoveRules->setWhatsThis(
1197         i18n("A filter to detect messages classified as virus-infected and to move "
1198              "those messages into a predefined folder is created. The "
1199              "default folder is the trash folder, but you may change that "
1200              "in the folder view."));
1201     grid->addWidget(mMoveRules, 1, 0);
1202 
1203     mMarkRules = new QCheckBox(i18n("Additionally, mark detected viral messages as read"), this);
1204     mMarkRules->setEnabled(false);
1205     mMarkRules->setWhatsThis(
1206         i18n("Mark messages which have been classified as "
1207              "virus-infected as read, as well as moving them "
1208              "to the selected folder."));
1209     grid->addWidget(mMarkRules, 2, 0);
1210     FolderTreeWidget::TreeViewOptions opt = FolderTreeWidget::None;
1211     opt |= FolderTreeWidget::UseDistinctSelectionModel;
1212 
1213     FolderTreeWidgetProxyModel::FolderTreeWidgetProxyModelOptions optReadableProxy = FolderTreeWidgetProxyModel::None;
1214     optReadableProxy |= FolderTreeWidgetProxyModel::HideVirtualFolder;
1215     optReadableProxy |= FolderTreeWidgetProxyModel::HideOutboxFolder;
1216 
1217     mFolderTree = new FolderTreeWidget(this, nullptr, opt, optReadableProxy);
1218     mFolderTree->readConfig();
1219     mFolderTree->folderTreeView()->expandAll();
1220     mFolderTree->folderTreeWidgetProxyModel()->setAccessRights(Akonadi::Collection::CanCreateCollection);
1221 
1222     mFolderTree->selectCollectionFolder(CommonKernel->trashCollectionFolder());
1223     mFolderTree->folderTreeView()->setDragDropMode(QAbstractItemView::NoDragDrop);
1224 
1225     mFolderTree->disableContextMenuAndExtraColumn();
1226     grid->addWidget(mFolderTree, 3, 0);
1227 
1228     connect(mPipeRules, &QCheckBox::clicked, this, &ASWizVirusRulesPage::processSelectionChange);
1229     connect(mMoveRules, &QCheckBox::clicked, this, &ASWizVirusRulesPage::processSelectionChange);
1230     connect(mMarkRules, &QCheckBox::clicked, this, &ASWizVirusRulesPage::processSelectionChange);
1231     connect(mMoveRules, &QCheckBox::toggled, mMarkRules, &QCheckBox::setEnabled);
1232 }
1233 
1234 bool ASWizVirusRulesPage::pipeRulesSelected() const
1235 {
1236     return mPipeRules->isChecked();
1237 }
1238 
1239 bool ASWizVirusRulesPage::moveRulesSelected() const
1240 {
1241     return mMoveRules->isChecked();
1242 }
1243 
1244 bool ASWizVirusRulesPage::markReadRulesSelected() const
1245 {
1246     return mMarkRules->isChecked();
1247 }
1248 
1249 QString ASWizVirusRulesPage::selectedFolderName() const
1250 {
1251     if (mFolderTree->selectedCollection().isValid()) {
1252         return QString::number(mFolderTree->selectedCollection().id());
1253     } else {
1254         return QString::number(CommonKernel->trashCollectionFolder().id());
1255     }
1256 }
1257 
1258 void ASWizVirusRulesPage::processSelectionChange()
1259 {
1260     Q_EMIT selectionChanged();
1261 }
1262 
1263 //---------------------------------------------------------------------------
1264 ASWizSummaryPage::ASWizSummaryPage(QWidget *parent, const QString &name)
1265     : ASWizPage(parent, name)
1266     , mSummaryText(new QLabel(this))
1267 {
1268     QBoxLayout *layout = new QVBoxLayout();
1269     mLayout->addItem(layout);
1270 
1271     layout->addWidget(mSummaryText);
1272     layout->addStretch();
1273 }
1274 
1275 void ASWizSummaryPage::setSummaryText(const QString &text)
1276 {
1277     mSummaryText->setText(text);
1278 }
1279 
1280 #include "moc_antispamwizard.cpp"