1 /*
2     This file is part of KMail.
3     SPDX-FileCopyrightText: 2003 Andreas Gungl <a.gungl@gmx.de>
4 
5     SPDX-License-Identifier: GPL-2.0-only
6 */
7 
8 #include "antispamwizard.h"
9 #include <KCursorSaver>
10 #include <MailCommon/FilterAction>
11 #include <MailCommon/FilterActionDict>
12 #include <MailCommon/FilterManager>
13 #include <MailCommon/FolderRequester>
14 #include <MailCommon/FolderTreeView>
15 #include <MailCommon/FolderTreeWidget>
16 #include <MailCommon/FolderTreeWidgetProxyModel>
17 #include <MailCommon/MailFilter>
18 #include <MailCommon/MailKernel>
19 #include <MailCommon/MailUtil>
20 
21 #include <PimCommon/PimUtil>
22 
23 #include <Akonadi/AgentInstance>
24 
25 #include <KConfigGroup>
26 #include <KLocalizedString>
27 #include <KProcess>
28 #include <QDialog>
29 
30 #include <KConfigGroup>
31 #include <MailCommon/ResourceReadConfigFile>
32 #include <QApplication>
33 #include <QBoxLayout>
34 #include <QCheckBox>
35 #include <QEventLoop>
36 #include <QGridLayout>
37 #include <QHBoxLayout>
38 #include <QLabel>
39 #include <QListWidget>
40 #include <QPushButton>
41 #include <QTextEdit>
42 #include <QTimer>
43 #include <QVBoxLayout>
44 #include <QVector>
45 using namespace KMail;
46 using namespace MailCommon;
47 
AntiSpamWizard(WizardMode mode,QWidget * parent)48 AntiSpamWizard::AntiSpamWizard(WizardMode mode, QWidget *parent)
49     : KAssistantDialog(parent)
50     , mInfoPage(nullptr)
51     , mSpamRulesPage(nullptr)
52     , mVirusRulesPage(nullptr)
53     , mSummaryPage(nullptr)
54     , mSpamToolsUsed(false)
55     , mVirusToolsUsed(false)
56     , mMode(mode)
57 {
58     // read the configuration for the anti-spam tools
59     ConfigReader reader(mMode, mToolList);
60     reader.readAndMergeConfig();
61     mToolList = reader.getToolList();
62 #ifndef NDEBUG
63     if (mMode == AntiSpam) {
64         qDebug() << "\nConsidered anti-spam tools:";
65     } else {
66         qDebug() << "\nConsidered anti-virus tools:";
67     }
68     QVector<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
69     for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
70         qDebug() << "Predefined tool:" << (*it).getId();
71         qDebug() << "Config version:" << (*it).getVersion();
72         qDebug() << "Selection priority:" << (*it).getPrio();
73         qDebug() << "Displayed name:" << (*it).getVisibleName();
74         qDebug() << "Executable:" << (*it).getExecutable();
75         qDebug() << "WhatsThis URL:" << (*it).getWhatsThisText();
76         qDebug() << "Filter name:" << (*it).getFilterName();
77         qDebug() << "Detection command:" << (*it).getDetectCmd();
78         qDebug() << "Learn spam command:" << (*it).getSpamCmd();
79         qDebug() << "Learn ham command:" << (*it).getHamCmd();
80         qDebug() << "Detection header:" << (*it).getDetectionHeader();
81         qDebug() << "Detection pattern:" << (*it).getDetectionPattern();
82         qDebug() << "Use as RegExp:" << (*it).isUseRegExp();
83         qDebug() << "Supports Bayes Filter:" << (*it).useBayesFilter();
84         qDebug() << "Type:" << (*it).getType();
85     }
86 #endif
87 
88     const bool isAntiSpam = (mMode == AntiSpam);
89     setWindowTitle(isAntiSpam ? i18n("Anti-Spam Wizard") : i18n("Anti-Virus Wizard"));
90     mInfoPage = new ASWizInfoPage(mMode, nullptr, QString());
91     mInfoPageItem = addPage(mInfoPage, isAntiSpam ? i18n("Welcome to the KMail Anti-Spam Wizard") : i18n("Welcome to the KMail Anti-Virus Wizard"));
92     connect(mInfoPage, &ASWizInfoPage::selectionChanged, this, &AntiSpamWizard::checkProgramsSelections);
93 
94     if (isAntiSpam) {
95         mSpamRulesPage = new ASWizSpamRulesPage(nullptr, QString());
96         mSpamRulesPageItem = addPage(mSpamRulesPage, i18n("Options to fine-tune the handling of spam messages"));
97         connect(mSpamRulesPage, &ASWizSpamRulesPage::selectionChanged, this, &AntiSpamWizard::slotBuildSummary);
98 
99         mSummaryPage = new ASWizSummaryPage(nullptr, QString());
100         mSummaryPageItem = addPage(mSummaryPage, i18n("Summary of changes to be made by this wizard"));
101     } else {
102         mVirusRulesPage = new ASWizVirusRulesPage(nullptr, QString());
103         mVirusRulesPageItem = addPage(mVirusRulesPage, i18n("Options to fine-tune the handling of virus messages"));
104         connect(mVirusRulesPage, &ASWizVirusRulesPage::selectionChanged, this, &AntiSpamWizard::checkVirusRulesSelections);
105     }
106 
107     connect(button(QDialogButtonBox::Help), &QPushButton::clicked, this, &AntiSpamWizard::slotHelpClicked);
108 
109     QTimer::singleShot(0, this, &AntiSpamWizard::checkToolAvailability);
110 }
111 
accept()112 void AntiSpamWizard::accept()
113 {
114     if (mSpamRulesPage) {
115         qDebug() << "Folder name for messages classified as spam is" << mSpamRulesPage->selectedSpamCollectionId();
116         qDebug() << "Folder name for messages classified as unsure is" << mSpamRulesPage->selectedUnsureCollectionId();
117     }
118     if (mVirusRulesPage) {
119         qDebug() << "Folder name for viruses is" << mVirusRulesPage->selectedFolderName();
120     }
121 
122     FilterActionDict dict;
123     QVector<MailFilter *> filterList;
124     bool replaceExistingFilters = false;
125 
126     // Let's start with virus detection and handling,
127     // so we can avoid spam checks for viral messages
128     if (mMode == AntiVirus) {
129         if (!mVirusToolsUsed) {
130             QDialog::accept();
131             return;
132         }
133         QVector<SpamToolConfig>::const_iterator end(mToolList.constEnd());
134         for (QVector<SpamToolConfig>::const_iterator it = mToolList.constBegin(); it != end; ++it) {
135             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (mVirusRulesPage->pipeRulesSelected() && (*it).isVirusTool())) {
136                 // pipe messages through the anti-virus tools,
137                 // one single filter for each tool
138                 // (could get combined but so it's easier to understand for the user)
139                 auto pipeFilter = new MailFilter();
140                 QVector<FilterAction *> *pipeFilterActions = pipeFilter->actions();
141                 FilterAction *pipeFilterAction = dict.value(QStringLiteral("filter app"))->create();
142                 pipeFilterAction->argsFromString((*it).getDetectCmd());
143                 pipeFilterActions->append(pipeFilterAction);
144                 SearchPattern *pipeFilterPattern = pipeFilter->pattern();
145                 pipeFilterPattern->setName(uniqueNameFor((*it).getFilterName()));
146                 pipeFilterPattern->append(SearchRule::createInstance("<size>", SearchRule::FuncIsGreaterOrEqual, QStringLiteral("0")));
147                 pipeFilter->setApplyOnOutbound(false);
148                 pipeFilter->setApplyOnInbound(true);
149                 pipeFilter->setApplyOnExplicit(true);
150                 pipeFilter->setStopProcessingHere(false);
151                 pipeFilter->setConfigureShortcut(false);
152 
153                 filterList.append(pipeFilter);
154             }
155         }
156 
157         if (mVirusRulesPage->moveRulesSelected()) {
158             // Sort out viruses depending on header fields set by the tools
159             auto virusFilter = new MailFilter();
160             QVector<FilterAction *> *virusFilterActions = virusFilter->actions();
161             FilterAction *virusFilterAction1 = dict.value(QStringLiteral("transfer"))->create();
162             virusFilterAction1->argsFromString(mVirusRulesPage->selectedFolderName());
163             virusFilterActions->append(virusFilterAction1);
164             if (mVirusRulesPage->markReadRulesSelected()) {
165                 FilterAction *virusFilterAction2 = dict.value(QStringLiteral("set status"))->create();
166                 virusFilterAction2->argsFromString(QStringLiteral("R")); // Read
167                 virusFilterActions->append(virusFilterAction2);
168             }
169             SearchPattern *virusFilterPattern = virusFilter->pattern();
170             virusFilterPattern->setName(uniqueNameFor(i18n("Virus handling")));
171             virusFilterPattern->setOp(SearchPattern::OpOr);
172             QVector<SpamToolConfig>::ConstIterator endSpamTool(mToolList.constEnd());
173             for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != endSpamTool; ++it) {
174                 if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
175                     if ((*it).isVirusTool()) {
176                         const QByteArray header = (*it).getDetectionHeader().toLatin1();
177                         const QString &pattern = (*it).getDetectionPattern();
178                         if ((*it).isUseRegExp()) {
179                             virusFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncRegExp, pattern));
180                         } else {
181                             virusFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncContains, pattern));
182                         }
183                     }
184                 }
185             }
186             virusFilter->setApplyOnOutbound(false);
187             virusFilter->setApplyOnInbound(true);
188             virusFilter->setApplyOnExplicit(true);
189             virusFilter->setStopProcessingHere(true);
190             virusFilter->setConfigureShortcut(false);
191 
192             filterList.append(virusFilter);
193         }
194     } else { // AntiSpam mode
195         if (!mSpamToolsUsed) {
196             QDialog::accept();
197             return;
198         }
199         // TODO Existing filters with same name are replaced. This is hardcoded
200         // ATM and needs to be replaced with a value from a (still missing)
201         // checkbox in the GUI. At least, the replacement is announced in the GUI.
202         replaceExistingFilters = true;
203         QVector<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
204         for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
205             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).isSpamTool() && !(*it).isDetectionOnly()) {
206                 // pipe messages through the anti-spam tools,
207                 // one single filter for each tool
208                 // (could get combined but so it's easier to understand for the user)
209                 auto pipeFilter = new MailFilter();
210                 QVector<FilterAction *> *pipeFilterActions = pipeFilter->actions();
211                 FilterAction *pipeFilterAction = dict.value(QStringLiteral("filter app"))->create();
212                 pipeFilterAction->argsFromString((*it).getDetectCmd());
213                 pipeFilterActions->append(pipeFilterAction);
214                 SearchPattern *pipeFilterPattern = pipeFilter->pattern();
215                 if (replaceExistingFilters) {
216                     pipeFilterPattern->setName((*it).getFilterName());
217                 } else {
218                     pipeFilterPattern->setName(uniqueNameFor((*it).getFilterName()));
219                 }
220                 pipeFilterPattern->append(SearchRule::createInstance("<size>", SearchRule::FuncIsLessOrEqual, QStringLiteral("256000")));
221                 pipeFilter->setApplyOnOutbound(false);
222                 pipeFilter->setApplyOnInbound(true);
223                 pipeFilter->setApplyOnExplicit(true);
224                 pipeFilter->setStopProcessingHere(false);
225                 pipeFilter->setConfigureShortcut(false);
226 
227                 filterList.append(pipeFilter);
228             }
229         }
230 
231         // Sort out spam depending on header fields set by the tools
232         auto spamFilter = new MailFilter();
233         QVector<FilterAction *> *spamFilterActions = spamFilter->actions();
234         if (mSpamRulesPage->moveSpamSelected()) {
235             FilterAction *spamFilterAction1 = dict.value(QStringLiteral("transfer"))->create();
236             spamFilterAction1->argsFromString(mSpamRulesPage->selectedSpamCollectionId());
237             spamFilterActions->append(spamFilterAction1);
238         }
239         FilterAction *spamFilterAction2 = dict.value(QStringLiteral("set status"))->create();
240         spamFilterAction2->argsFromString(QStringLiteral("P")); // Spam
241         spamFilterActions->append(spamFilterAction2);
242         if (mSpamRulesPage->markAsReadSelected()) {
243             FilterAction *spamFilterAction3 = dict.value(QStringLiteral("set status"))->create();
244             spamFilterAction3->argsFromString(QStringLiteral("R")); // Read
245             spamFilterActions->append(spamFilterAction3);
246         }
247         SearchPattern *spamFilterPattern = spamFilter->pattern();
248         if (replaceExistingFilters) {
249             spamFilterPattern->setName(i18n("Spam Handling"));
250         } else {
251             spamFilterPattern->setName(uniqueNameFor(i18n("Spam Handling")));
252         }
253         spamFilterPattern->setOp(SearchPattern::OpOr);
254         QVector<SpamToolConfig>::ConstIterator endToolList(mToolList.constEnd());
255         for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != endToolList; ++it) {
256             if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
257                 if ((*it).isSpamTool()) {
258                     const QByteArray header = (*it).getDetectionHeader().toLatin1();
259                     const QString &pattern = (*it).getDetectionPattern();
260                     if ((*it).isUseRegExp()) {
261                         spamFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncRegExp, pattern));
262                     } else {
263                         spamFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncContains, pattern));
264                     }
265                 }
266             }
267         }
268         spamFilter->setApplyOnOutbound(false);
269         spamFilter->setApplyOnInbound(true);
270         spamFilter->setApplyOnExplicit(true);
271         spamFilter->setStopProcessingHere(true);
272         spamFilter->setConfigureShortcut(false);
273         filterList.append(spamFilter);
274 
275         if (mSpamRulesPage->moveUnsureSelected()) {
276             // Sort out messages classified as unsure
277             bool atLeastOneUnsurePattern = false;
278             auto unsureFilter = new MailFilter();
279             QVector<FilterAction *> *unsureFilterActions = unsureFilter->actions();
280             FilterAction *unsureFilterAction1 = dict.value(QStringLiteral("transfer"))->create();
281             unsureFilterAction1->argsFromString(mSpamRulesPage->selectedUnsureCollectionId());
282             unsureFilterActions->append(unsureFilterAction1);
283             SearchPattern *unsureFilterPattern = unsureFilter->pattern();
284             if (replaceExistingFilters) {
285                 unsureFilterPattern->setName(i18n("Semi spam (unsure) handling"));
286             } else {
287                 unsureFilterPattern->setName(uniqueNameFor(i18n("Semi spam (unsure) handling")));
288             }
289             unsureFilterPattern->setOp(SearchPattern::OpOr);
290             QVector<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
291             for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
292                 if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
293                     if ((*it).isSpamTool() && (*it).hasTristateDetection()) {
294                         atLeastOneUnsurePattern = true;
295                         const QByteArray header = (*it).getDetectionHeader().toLatin1();
296                         const QString &pattern = (*it).getDetectionPattern2();
297                         if ((*it).isUseRegExp()) {
298                             unsureFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncRegExp, pattern));
299                         } else {
300                             unsureFilterPattern->append(SearchRule::createInstance(header, SearchRule::FuncContains, pattern));
301                         }
302                     }
303                 }
304             }
305             unsureFilter->setApplyOnOutbound(false);
306             unsureFilter->setApplyOnInbound(true);
307             unsureFilter->setApplyOnExplicit(true);
308             unsureFilter->setStopProcessingHere(true);
309             unsureFilter->setConfigureShortcut(false);
310 
311             if (atLeastOneUnsurePattern) {
312                 filterList.append(unsureFilter);
313             } else {
314                 delete unsureFilter;
315             }
316         }
317 
318         // Classify messages manually as Spam
319         auto classSpamFilter = new MailFilter();
320         classSpamFilter->setIcon(QStringLiteral("mail-mark-junk"));
321         QVector<FilterAction *> *classSpamFilterActions = classSpamFilter->actions();
322         FilterAction *classSpamFilterActionFirst = dict.value(QStringLiteral("set status"))->create();
323         classSpamFilterActionFirst->argsFromString(QStringLiteral("P"));
324         classSpamFilterActions->append(classSpamFilterActionFirst);
325         QVector<SpamToolConfig>::ConstIterator endToolList2(mToolList.constEnd());
326         for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != endToolList2; ++it) {
327             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).useBayesFilter() && !(*it).isDetectionOnly()) {
328                 FilterAction *classSpamFilterAction = dict.value(QStringLiteral("execute"))->create();
329                 classSpamFilterAction->argsFromString((*it).getSpamCmd());
330                 classSpamFilterActions->append(classSpamFilterAction);
331             }
332         }
333         if (mSpamRulesPage->moveSpamSelected()) {
334             FilterAction *classSpamFilterActionLast = dict.value(QStringLiteral("transfer"))->create();
335             classSpamFilterActionLast->argsFromString(mSpamRulesPage->selectedSpamCollectionId());
336             classSpamFilterActions->append(classSpamFilterActionLast);
337         }
338 
339         SearchPattern *classSpamFilterPattern = classSpamFilter->pattern();
340         if (replaceExistingFilters) {
341             classSpamFilterPattern->setName(i18n("Classify as Spam"));
342         } else {
343             classSpamFilterPattern->setName(uniqueNameFor(i18n("Classify as Spam")));
344         }
345         classSpamFilterPattern->append(SearchRule::createInstance("<size>", SearchRule::FuncIsGreaterOrEqual, QStringLiteral("0")));
346         classSpamFilter->setApplyOnOutbound(false);
347         classSpamFilter->setApplyOnInbound(false);
348         classSpamFilter->setApplyOnExplicit(false);
349         classSpamFilter->setStopProcessingHere(true);
350         classSpamFilter->setConfigureShortcut(true);
351         classSpamFilter->setConfigureToolbar(true);
352         classSpamFilter->setToolbarName(i18n("Spam"));
353         filterList.append(classSpamFilter);
354 
355         // Classify messages manually as not Spam / as Ham
356         auto classHamFilter = new MailFilter();
357         classHamFilter->setIcon(QStringLiteral("mail-mark-notjunk"));
358         QVector<FilterAction *> *classHamFilterActions = classHamFilter->actions();
359         FilterAction *classHamFilterActionFirst = dict.value(QStringLiteral("set status"))->create();
360         classHamFilterActionFirst->argsFromString(QStringLiteral("H"));
361         classHamFilterActions->append(classHamFilterActionFirst);
362         end = mToolList.constEnd();
363         for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
364             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).useBayesFilter() && !(*it).isDetectionOnly()) {
365                 FilterAction *classHamFilterAction = dict.value(QStringLiteral("execute"))->create();
366                 classHamFilterAction->argsFromString((*it).getHamCmd());
367                 classHamFilterActions->append(classHamFilterAction);
368             }
369         }
370         end = mToolList.constEnd();
371         for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
372             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).useBayesFilter() && !(*it).isDetectionOnly()) {
373                 FilterAction *classHamFilterAction = dict.value(QStringLiteral("filter app"))->create();
374                 classHamFilterAction->argsFromString((*it).getNoSpamCmd());
375                 classHamFilterActions->append(classHamFilterAction);
376             }
377         }
378         SearchPattern *classHamFilterPattern = classHamFilter->pattern();
379         if (replaceExistingFilters) {
380             classHamFilterPattern->setName(i18n("Classify as NOT Spam"));
381         } else {
382             classHamFilterPattern->setName(uniqueNameFor(i18n("Classify as NOT Spam")));
383         }
384         classHamFilterPattern->append(SearchRule::createInstance("<size>", SearchRule::FuncIsGreaterOrEqual, QStringLiteral("0")));
385         classHamFilter->setApplyOnOutbound(false);
386         classHamFilter->setApplyOnInbound(false);
387         classHamFilter->setApplyOnExplicit(false);
388         classHamFilter->setStopProcessingHere(true);
389         classHamFilter->setConfigureShortcut(true);
390         classHamFilter->setConfigureToolbar(true);
391         classHamFilter->setToolbarName(i18n("Ham"));
392         filterList.append(classHamFilter);
393     }
394 
395     /* Now that all the filters have been added to the list, tell
396      * the filter manager about it. That will Q_EMIT filterListUpdate
397      * which will result in the filter list in kmmainwidget being
398      * initialized. This should happened only once. */
399     if (!filterList.isEmpty()) {
400         MailCommon::FilterManager::instance()->appendFilters(filterList, replaceExistingFilters);
401     }
402 
403     QDialog::accept();
404 }
405 
checkProgramsSelections()406 void AntiSpamWizard::checkProgramsSelections()
407 {
408     bool supportUnsure = false;
409 
410     mSpamToolsUsed = false;
411     mVirusToolsUsed = false;
412     QVector<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
413     for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
414         if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
415             if ((*it).isSpamTool()) {
416                 mSpamToolsUsed = true;
417                 if ((*it).hasTristateDetection()) {
418                     supportUnsure = true;
419                 }
420             }
421             if ((*it).isVirusTool()) {
422                 mVirusToolsUsed = true;
423             }
424 
425             if (mSpamToolsUsed && mVirusToolsUsed && supportUnsure) {
426                 break;
427             }
428         }
429     }
430 
431     if (mMode == AntiSpam) {
432         mSpamRulesPage->allowUnsureFolderSelection(supportUnsure);
433         mSpamRulesPage->allowMoveSpam(mSpamToolsUsed);
434         slotBuildSummary();
435         setAppropriate(mSpamRulesPageItem, mSpamToolsUsed);
436         setAppropriate(mSummaryPageItem, mSpamToolsUsed);
437     } else if (mMode == AntiVirus) {
438         if (mVirusToolsUsed) {
439             checkVirusRulesSelections();
440         }
441         setAppropriate(mVirusRulesPageItem, mVirusToolsUsed);
442     }
443 }
444 
checkVirusRulesSelections()445 void AntiSpamWizard::checkVirusRulesSelections()
446 {
447     // setFinishEnabled( mVirusRulesPage, anyVirusOptionChecked() );
448 }
449 
checkToolAvailability()450 void AntiSpamWizard::checkToolAvailability()
451 {
452     // this can take some time to find the tools
453     KCursorSaver saver(Qt::WaitCursor);
454     bool found = false;
455     QVector<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
456     for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
457         const QString text(i18n("Scanning for %1...", (*it).getId()));
458         mInfoPage->setScanProgressText(text);
459         if ((*it).isSpamTool() && (*it).isServerBased()) {
460             // check the configured account for pattern in <server>
461             const QString pattern = (*it).getServerPattern();
462             qDebug() << "Testing for server pattern:" << pattern;
463             const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances();
464             for (const Akonadi::AgentInstance &type : lst) {
465                 if (type.status() == Akonadi::AgentInstance::Broken) {
466                     continue;
467                 }
468                 const QString typeIdentifier(type.identifier());
469                 if (PimCommon::Util::isImapResource(typeIdentifier)) {
470                     MailCommon::ResourceReadConfigFile resourceFile(typeIdentifier);
471                     const KConfigGroup grp = resourceFile.group(QStringLiteral("network"));
472                     if (grp.isValid()) {
473                         const QString host = grp.readEntry(QStringLiteral("ImapServer"));
474                         if (host.contains(pattern.toLower(), Qt::CaseInsensitive)) {
475                             mInfoPage->addAvailableTool((*it).getVisibleName());
476                             found = true;
477                         }
478                     }
479                 } else if (type.identifier().contains(POP3_RESOURCE_IDENTIFIER)) {
480                     MailCommon::ResourceReadConfigFile resourceFile(typeIdentifier);
481                     const KConfigGroup grp = resourceFile.group(QStringLiteral("General"));
482                     if (grp.isValid()) {
483                         const QString host = grp.readEntry(QStringLiteral("host"));
484                         if (host.contains(pattern.toLower(), Qt::CaseInsensitive)) {
485                             mInfoPage->addAvailableTool((*it).getVisibleName());
486                             found = true;
487                         }
488                     }
489                 }
490             }
491         } else {
492             // check the availability of the application
493             qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 200);
494             if (!checkForProgram((*it).getExecutable())) {
495                 mInfoPage->addAvailableTool((*it).getVisibleName());
496                 found = true;
497             }
498         }
499     }
500     if (found) {
501         mInfoPage->setScanProgressText((mMode == AntiSpam) ? i18n("Scanning for anti-spam tools finished.") : i18n("Scanning for anti-virus tools finished."));
502     } else {
503         mInfoPage->setScanProgressText((mMode == AntiSpam) ? i18n("<p>Sorry, no spam detection tools have been found. "
504                                                                   "Install your spam detection software and "
505                                                                   "re-run this wizard.</p>")
506                                                            : i18n("Scanning complete. No anti-virus tools found."));
507     }
508     checkProgramsSelections();
509 }
510 
slotHelpClicked()511 void AntiSpamWizard::slotHelpClicked()
512 {
513     PimCommon::Util::invokeHelp((mMode == AntiSpam) ? QStringLiteral("kmail2/the-anti-spam-wizard.html") : QStringLiteral("kmail2/the-anti-virus-wizard.html"));
514 }
515 
slotBuildSummary()516 void AntiSpamWizard::slotBuildSummary()
517 {
518     QString text;
519     QString newFilters;
520     QString replaceFilters;
521 
522     if (mMode == AntiVirus) {
523         text.clear(); // TODO add summary for the virus part
524     } else { // AntiSpam mode
525         if (mSpamRulesPage->markAsReadSelected()) {
526             if (mSpamRulesPage->moveSpamSelected()) {
527                 text = i18n(
528                     "<p>Messages classified as spam are marked as read."
529                     "<br />Spam messages are moved into the folder named <i>%1</i>.</p>",
530                     mSpamRulesPage->selectedSpamCollectionName());
531             } else {
532                 text = i18n(
533                     "<p>Messages classified as spam are marked as read."
534                     "<br />Spam messages are not moved into a certain folder.</p>");
535             }
536         } else {
537             if (mSpamRulesPage->moveSpamSelected()) {
538                 text = i18n(
539                     "<p>Messages classified as spam are not marked as read."
540                     "<br />Spam messages are moved into the folder named <i>%1</i>.</p>",
541                     mSpamRulesPage->selectedSpamCollectionName());
542             } else {
543                 text = i18n(
544                     "<p>Messages classified as spam are not marked as read."
545                     "<br />Spam messages are not moved into a certain folder.</p>");
546             }
547         }
548         QVector<SpamToolConfig>::ConstIterator end(mToolList.constEnd());
549         for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
550             if (mInfoPage->isProgramSelected((*it).getVisibleName()) && (*it).isSpamTool() && !(*it).isDetectionOnly()) {
551                 sortFilterOnExistance((*it).getFilterName(), newFilters, replaceFilters);
552             }
553         }
554         sortFilterOnExistance(i18n("Spam Handling"), newFilters, replaceFilters);
555 
556         // The need for a handling of status "probably spam" depends on the tools chosen
557         if (mSpamRulesPage->moveUnsureSelected()) {
558             bool atLeastOneUnsurePattern = false;
559             end = mToolList.constEnd();
560             for (QVector<SpamToolConfig>::ConstIterator it = mToolList.constBegin(); it != end; ++it) {
561                 if (mInfoPage->isProgramSelected((*it).getVisibleName())) {
562                     if ((*it).isSpamTool() && (*it).hasTristateDetection()) {
563                         atLeastOneUnsurePattern = true;
564                         break;
565                     }
566                 }
567             }
568             if (atLeastOneUnsurePattern) {
569                 sortFilterOnExistance(i18n("Semi spam (unsure) handling"), newFilters, replaceFilters);
570                 text +=
571                     i18n("<p>The folder for messages classified as unsure (probably spam) is <i>%1</i>.</p>", mSpamRulesPage->selectedUnsureCollectionName());
572             }
573         }
574 
575         // Manual classification via toolbar icon / manually applied filter action
576         sortFilterOnExistance(i18n("Classify as Spam"), newFilters, replaceFilters);
577         sortFilterOnExistance(i18n("Classify as NOT Spam"), newFilters, replaceFilters);
578 
579         // Show the filters in the summary
580         if (!newFilters.isEmpty()) {
581             text += i18n("<p>The wizard will create the following filters:<ul>%1</ul></p>", newFilters);
582         }
583         if (!replaceFilters.isEmpty()) {
584             text += i18n("<p>The wizard will replace the following filters:<ul>%1</ul></p>", replaceFilters);
585         }
586     }
587 
588     mSummaryPage->setSummaryText(text);
589 }
590 
checkForProgram(const QString & executable) const591 int AntiSpamWizard::checkForProgram(const QString &executable) const
592 {
593     qDebug() << "Testing for executable:" << executable;
594     KProcess process;
595     process.setShellCommand(executable);
596     return process.execute();
597 }
598 
anyVirusOptionChecked() const599 bool AntiSpamWizard::anyVirusOptionChecked() const
600 {
601     return mVirusRulesPage->moveRulesSelected() || mVirusRulesPage->pipeRulesSelected();
602 }
603 
uniqueNameFor(const QString & name)604 const QString AntiSpamWizard::uniqueNameFor(const QString &name)
605 {
606     return MailCommon::FilterManager::instance()->createUniqueFilterName(name);
607 }
608 
sortFilterOnExistance(const QString & intendedFilterName,QString & newFilters,QString & replaceFilters)609 void AntiSpamWizard::sortFilterOnExistance(const QString &intendedFilterName, QString &newFilters, QString &replaceFilters)
610 {
611     if (uniqueNameFor(intendedFilterName) == intendedFilterName) {
612         newFilters += QLatin1String("<li>") + intendedFilterName + QLatin1String("</li>");
613     } else {
614         replaceFilters += QLatin1String("<li>") + intendedFilterName + QLatin1String("</li>");
615     }
616 }
617 
618 //---------------------------------------------------------------------------
SpamToolConfig(const QString & toolId,int configVersion,int prio,const QString & name,const QString & exec,const QString & url,const QString & filter,const QString & detection,const QString & spam,const QString & ham,const QString & noSpam,const QString & header,const QString & pattern,const QString & pattern2,const QString & serverPattern,bool detectionOnly,bool regExp,bool bayesFilter,bool tristateDetection,WizardMode type)619 AntiSpamWizard::SpamToolConfig::SpamToolConfig(const QString &toolId,
620                                                int configVersion,
621                                                int prio,
622                                                const QString &name,
623                                                const QString &exec,
624                                                const QString &url,
625                                                const QString &filter,
626                                                const QString &detection,
627                                                const QString &spam,
628                                                const QString &ham,
629                                                const QString &noSpam,
630                                                const QString &header,
631                                                const QString &pattern,
632                                                const QString &pattern2,
633                                                const QString &serverPattern,
634                                                bool detectionOnly,
635                                                bool regExp,
636                                                bool bayesFilter,
637                                                bool tristateDetection,
638                                                WizardMode type)
639     : mId(toolId)
640     , mVersion(configVersion)
641     , mPrio(prio)
642     , mVisibleName(name)
643     , mExecutable(exec)
644     , mWhatsThisText(url)
645     , mFilterName(filter)
646     , mDetectCmd(detection)
647     , mSpamCmd(spam)
648     , mHamCmd(ham)
649     , mNoSpamCmd(noSpam)
650     , mDetectionHeader(header)
651     , mDetectionPattern(pattern)
652     , mDetectionPattern2(pattern2)
653     , mServerPattern(serverPattern)
654     , mDetectionOnly(detectionOnly)
655     , mUseRegExp(regExp)
656     , mSupportsBayesFilter(bayesFilter)
657     , mSupportsUnsure(tristateDetection)
658     , mType(type)
659 {
660 }
661 
getVersion() const662 int AntiSpamWizard::SpamToolConfig::getVersion() const
663 {
664     return mVersion;
665 }
666 
getPrio() const667 int AntiSpamWizard::SpamToolConfig::getPrio() const
668 {
669     return mPrio;
670 }
671 
getId() const672 QString AntiSpamWizard::SpamToolConfig::getId() const
673 {
674     return mId;
675 }
676 
getVisibleName() const677 QString AntiSpamWizard::SpamToolConfig::getVisibleName() const
678 {
679     return mVisibleName;
680 }
681 
getExecutable() const682 QString AntiSpamWizard::SpamToolConfig::getExecutable() const
683 {
684     return mExecutable;
685 }
686 
getWhatsThisText() const687 QString AntiSpamWizard::SpamToolConfig::getWhatsThisText() const
688 {
689     return mWhatsThisText;
690 }
691 
getFilterName() const692 QString AntiSpamWizard::SpamToolConfig::getFilterName() const
693 {
694     return mFilterName;
695 }
696 
getDetectCmd() const697 QString AntiSpamWizard::SpamToolConfig::getDetectCmd() const
698 {
699     return mDetectCmd;
700 }
701 
getSpamCmd() const702 QString AntiSpamWizard::SpamToolConfig::getSpamCmd() const
703 {
704     return mSpamCmd;
705 }
706 
getHamCmd() const707 QString AntiSpamWizard::SpamToolConfig::getHamCmd() const
708 {
709     return mHamCmd;
710 }
711 
getNoSpamCmd() const712 QString AntiSpamWizard::SpamToolConfig::getNoSpamCmd() const
713 {
714     return mNoSpamCmd;
715 }
716 
getDetectionHeader() const717 QString AntiSpamWizard::SpamToolConfig::getDetectionHeader() const
718 {
719     return mDetectionHeader;
720 }
721 
getDetectionPattern() const722 QString AntiSpamWizard::SpamToolConfig::getDetectionPattern() const
723 {
724     return mDetectionPattern;
725 }
726 
getDetectionPattern2() const727 QString AntiSpamWizard::SpamToolConfig::getDetectionPattern2() const
728 {
729     return mDetectionPattern2;
730 }
731 
getServerPattern() const732 QString AntiSpamWizard::SpamToolConfig::getServerPattern() const
733 {
734     return mServerPattern;
735 }
736 
isServerBased() const737 bool AntiSpamWizard::SpamToolConfig::isServerBased() const
738 {
739     return !mServerPattern.isEmpty();
740 }
741 
isDetectionOnly() const742 bool AntiSpamWizard::SpamToolConfig::isDetectionOnly() const
743 {
744     return mDetectionOnly;
745 }
746 
isUseRegExp() const747 bool AntiSpamWizard::SpamToolConfig::isUseRegExp() const
748 {
749     return mUseRegExp;
750 }
751 
useBayesFilter() const752 bool AntiSpamWizard::SpamToolConfig::useBayesFilter() const
753 {
754     return mSupportsBayesFilter;
755 }
756 
hasTristateDetection() const757 bool AntiSpamWizard::SpamToolConfig::hasTristateDetection() const
758 {
759     return mSupportsUnsure;
760 }
761 
getType() const762 AntiSpamWizard::WizardMode AntiSpamWizard::SpamToolConfig::getType() const
763 {
764     return mType;
765 }
766 
isSpamTool() const767 bool AntiSpamWizard::SpamToolConfig::isSpamTool() const
768 {
769     return mType == AntiSpam;
770 }
771 
isVirusTool() const772 bool AntiSpamWizard::SpamToolConfig::isVirusTool() const
773 {
774     return mType == AntiVirus;
775 }
776 
777 //---------------------------------------------------------------------------
ConfigReader(WizardMode mode,QVector<SpamToolConfig> & configList)778 AntiSpamWizard::ConfigReader::ConfigReader(WizardMode mode, QVector<SpamToolConfig> &configList)
779     : mToolList(configList)
780     , mMode(mode)
781 {
782     if (mMode == AntiSpam) {
783         mConfig = KSharedConfig::openConfig(QStringLiteral("kmail.antispamrc"));
784     } else {
785         mConfig = KSharedConfig::openConfig(QStringLiteral("kmail.antivirusrc"));
786     }
787 }
788 
~ConfigReader()789 AntiSpamWizard::ConfigReader::~ConfigReader()
790 {
791 }
792 
readAndMergeConfig()793 void AntiSpamWizard::ConfigReader::readAndMergeConfig()
794 {
795     QString groupName = (mMode == AntiSpam) ? QStringLiteral("Spamtool #%1") : QStringLiteral("Virustool #%1");
796     // read the configuration from the global config file
797     mConfig->setReadDefaults(true);
798     KConfigGroup general(mConfig, "General");
799     const int registeredTools = general.readEntry("tools", 0);
800     for (int i = 1; i <= registeredTools; ++i) {
801         KConfigGroup toolConfig(mConfig, groupName.arg(i));
802         if (!toolConfig.readEntry("HeadersOnly", false)) {
803             mToolList.append(readToolConfig(toolConfig));
804         }
805     }
806 
807     // read the configuration from the user config file
808     // and merge newer config data
809     mConfig->setReadDefaults(false);
810     KConfigGroup user_general(mConfig, "General");
811     const int user_registeredTools = user_general.readEntry("tools", 0);
812     for (int i = 1; i <= user_registeredTools; ++i) {
813         KConfigGroup toolConfig(mConfig, groupName.arg(i));
814         if (!toolConfig.readEntry("HeadersOnly", false)) {
815             mergeToolConfig(readToolConfig(toolConfig));
816         }
817     }
818     // Make sure to have add least one tool listed even when the
819     // config file was not found or whatever went wrong
820     // Currently only works for spam tools
821     if (mMode == AntiSpam) {
822         if (registeredTools < 1 && user_registeredTools < 1) {
823             mToolList.append(createDummyConfig());
824         }
825         sortToolList();
826     }
827 }
828 
readToolConfig(KConfigGroup & configGroup)829 AntiSpamWizard::SpamToolConfig AntiSpamWizard::ConfigReader::readToolConfig(KConfigGroup &configGroup)
830 {
831     const QString id = configGroup.readEntry("Ident");
832     const int version = configGroup.readEntry("Version", 0);
833 #ifndef NDEBUG
834     qDebug() << "Found predefined tool:" << id;
835     qDebug() << "With config version  :" << version;
836 #endif
837     const int prio = configGroup.readEntry("Priority", 1);
838     const QString name = configGroup.readEntry("VisibleName");
839     const QString executable = configGroup.readEntry("Executable");
840     const QString url = configGroup.readEntry("URL");
841     const QString filterName = configGroup.readEntry("PipeFilterName");
842     const QString detectCmd = configGroup.readEntry("PipeCmdDetect");
843     const QString spamCmd = configGroup.readEntry("ExecCmdSpam");
844     const QString hamCmd = configGroup.readEntry("ExecCmdHam");
845     const QString noSpamCmd = configGroup.readEntry("PipeCmdNoSpam");
846     const QString header = configGroup.readEntry("DetectionHeader");
847     const QString pattern = configGroup.readEntry("DetectionPattern");
848     const QString pattern2 = configGroup.readEntry("DetectionPattern2");
849     const QString serverPattern = configGroup.readEntry("ServerPattern");
850     const bool detectionOnly = configGroup.readEntry("DetectionOnly", false);
851     const bool useRegExp = configGroup.readEntry("UseRegExp", false);
852     const bool supportsBayes = configGroup.readEntry("SupportsBayes", false);
853     const bool supportsUnsure = configGroup.readEntry("SupportsUnsure", false);
854     return SpamToolConfig(id,
855                           version,
856                           prio,
857                           name,
858                           executable,
859                           url,
860                           filterName,
861                           detectCmd,
862                           spamCmd,
863                           hamCmd,
864                           noSpamCmd,
865                           header,
866                           pattern,
867                           pattern2,
868                           serverPattern,
869                           detectionOnly,
870                           useRegExp,
871                           supportsBayes,
872                           supportsUnsure,
873                           mMode);
874 }
875 
createDummyConfig()876 AntiSpamWizard::SpamToolConfig AntiSpamWizard::ConfigReader::createDummyConfig()
877 {
878     return SpamToolConfig(QStringLiteral("spamassassin"),
879                           0,
880                           1,
881                           QStringLiteral("SpamAssassin"),
882                           QStringLiteral("spamassassin -V"),
883                           QStringLiteral("https://spamassassin.apache.org/"),
884                           QStringLiteral("SpamAssassin Check"),
885                           QStringLiteral("spamassassin -L"),
886                           QStringLiteral("sa-learn -L --spam --no-sync --single"),
887                           QStringLiteral("sa-learn -L --ham --no-sync --single"),
888                           QStringLiteral("spamassassin -d"),
889                           QStringLiteral("X-Spam-Status"),
890                           QStringLiteral("yes"),
891                           QString(),
892                           QString(),
893                           false,
894                           false,
895                           true,
896                           false,
897                           AntiSpam);
898 }
899 
mergeToolConfig(const AntiSpamWizard::SpamToolConfig & config)900 void AntiSpamWizard::ConfigReader::mergeToolConfig(const AntiSpamWizard::SpamToolConfig &config)
901 {
902     bool found = false;
903     QVector<SpamToolConfig>::Iterator end(mToolList.end());
904     for (QVector<SpamToolConfig>::Iterator it = mToolList.begin(); it != end; ++it) {
905 #ifndef NDEBUG
906         qDebug() << "Check against tool:" << (*it).getId();
907         qDebug() << "Against version   :" << (*it).getVersion();
908 #endif
909         if ((*it).getId() == config.getId()) {
910             found = true;
911             if ((*it).getVersion() < config.getVersion()) {
912 #ifndef NDEBUG
913                 qDebug() << "Replacing config ...";
914 #endif
915                 mToolList.erase(it);
916                 mToolList.append(config);
917             }
918             break;
919         }
920     }
921     if (!found) {
922         mToolList.append(config);
923     }
924 }
925 
sortToolList()926 void AntiSpamWizard::ConfigReader::sortToolList()
927 {
928     QVector<SpamToolConfig> tmpList;
929     SpamToolConfig config;
930 
931     while (!mToolList.isEmpty()) {
932         QVector<SpamToolConfig>::Iterator highest;
933         int priority = 0; // ascending
934         QVector<SpamToolConfig>::Iterator end(mToolList.end());
935         for (QVector<SpamToolConfig>::Iterator it = mToolList.begin(); it != end; ++it) {
936             if ((*it).getPrio() > priority) {
937                 priority = (*it).getPrio();
938                 highest = it;
939             }
940         }
941         config = (*highest);
942         tmpList.append(config);
943         mToolList.erase(highest);
944     }
945     QVector<SpamToolConfig>::ConstIterator end(tmpList.constEnd());
946     for (QVector<SpamToolConfig>::ConstIterator it = tmpList.constBegin(); it != end; ++it) {
947         mToolList.append((*it));
948     }
949 }
950 
951 //---------------------------------------------------------------------------
ASWizPage(QWidget * parent,const QString & name)952 ASWizPage::ASWizPage(QWidget *parent, const QString &name)
953     : QWidget(parent)
954 {
955     setObjectName(name);
956     mLayout = new QHBoxLayout(this);
957 
958     auto sideLayout = new QVBoxLayout();
959     mLayout->addItem(sideLayout);
960     mLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Minimum, QSizePolicy::Expanding));
961 
962     QPixmap banner;
963     banner.load(QStringLiteral(":/org/kde/kmail/pics/kmwizard.png"));
964     auto bannerLabel = new QLabel(this);
965     bannerLabel->setPixmap(banner);
966     bannerLabel->setScaledContents(false);
967     bannerLabel->setFrameShape(QFrame::StyledPanel);
968     bannerLabel->setFrameShadow(QFrame::Sunken);
969     bannerLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
970 
971     sideLayout->addWidget(bannerLabel);
972     sideLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Minimum, QSizePolicy::Expanding));
973 }
974 
975 //---------------------------------------------------------------------------
ASWizInfoPage(AntiSpamWizard::WizardMode mode,QWidget * parent,const QString & name)976 ASWizInfoPage::ASWizInfoPage(AntiSpamWizard::WizardMode mode, QWidget *parent, const QString &name)
977     : ASWizPage(parent, name)
978 {
979     QBoxLayout *layout = new QVBoxLayout();
980     mLayout->addItem(layout);
981 
982     auto introText = new QTextEdit(this);
983     introText->setText((mode == AntiSpamWizard::AntiSpam) ? i18n("The wizard will search for any tools to do spam detection\n"
984                                                                  "and setup KMail to work with them.")
985                                                           : i18n("<p>Here you can get some assistance in setting up KMail's filter "
986                                                                  "rules to use some commonly-known anti-virus tools.</p>"
987                                                                  "<p>The wizard can detect those tools on your computer as "
988                                                                  "well as create filter rules to classify messages using these "
989                                                                  "tools and to separate messages containing viruses. "
990                                                                  "The wizard will not take any existing filter "
991                                                                  "rules into consideration: it will always append the new rules.</p>"
992                                                                  "<p><b>Warning:</b> As KMail appears to be frozen during the scan of the "
993                                                                  "messages for viruses, you may encounter problems with "
994                                                                  "the responsiveness of KMail because anti-virus tool "
995                                                                  "operations are usually time consuming; please consider "
996                                                                  "deleting the filter rules created by the wizard to get "
997                                                                  "back to the former behavior.</p>"));
998     introText->setReadOnly(true);
999     introText->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
1000     layout->addWidget(introText);
1001 
1002     mScanProgressText = new QLabel(this);
1003     mScanProgressText->clear();
1004     mScanProgressText->setWordWrap(true);
1005     layout->addWidget(mScanProgressText);
1006 
1007     mToolsList = new QListWidget(this);
1008     mToolsList->hide();
1009     mToolsList->setSelectionMode(QAbstractItemView::MultiSelection);
1010     mToolsList->setLayoutMode(QListView::Batched);
1011     mToolsList->setBatchSize(10);
1012     mToolsList->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum));
1013     layout->addWidget(mToolsList);
1014     connect(mToolsList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ASWizInfoPage::processSelectionChange);
1015 
1016     mSelectionHint = new QLabel(this);
1017     mSelectionHint->clear();
1018     mSelectionHint->setWordWrap(true);
1019     layout->addWidget(mSelectionHint);
1020 }
1021 
setScanProgressText(const QString & toolName)1022 void ASWizInfoPage::setScanProgressText(const QString &toolName)
1023 {
1024     mScanProgressText->setText(toolName);
1025 }
1026 
addAvailableTool(const QString & visibleName)1027 void ASWizInfoPage::addAvailableTool(const QString &visibleName)
1028 {
1029     mToolsList->addItem(visibleName);
1030     if (!mToolsList->isVisible()) {
1031         mToolsList->show();
1032         mToolsList->selectionModel()->clearSelection();
1033         mToolsList->setCurrentRow(0);
1034         mSelectionHint->setText(
1035             i18n("<p>Please select the tools to be used "
1036                  "for the detection and go "
1037                  "to the next page.</p>"));
1038     }
1039 }
1040 
isProgramSelected(const QString & visibleName) const1041 bool ASWizInfoPage::isProgramSelected(const QString &visibleName) const
1042 {
1043     const QList<QListWidgetItem *> foundItems = mToolsList->findItems(visibleName, Qt::MatchFixedString);
1044     return !foundItems.isEmpty() && foundItems[0]->isSelected();
1045 }
1046 
processSelectionChange()1047 void ASWizInfoPage::processSelectionChange()
1048 {
1049     Q_EMIT selectionChanged();
1050 }
1051 
1052 //---------------------------------------------------------------------------
ASWizSpamRulesPage(QWidget * parent,const QString & name)1053 ASWizSpamRulesPage::ASWizSpamRulesPage(QWidget *parent, const QString &name)
1054     : ASWizPage(parent, name)
1055 {
1056     auto layout = new QVBoxLayout();
1057     mLayout->addItem(layout);
1058 
1059     mMarkRules = new QCheckBox(i18n("&Mark detected spam messages as read"), this);
1060     mMarkRules->setWhatsThis(i18n("Mark messages which have been classified as spam as read."));
1061     layout->addWidget(mMarkRules);
1062 
1063     mMoveSpamRules = new QCheckBox(i18n("Move &known spam to:"), this);
1064     mMoveSpamRules->setWhatsThis(
1065         i18n("The default folder for spam messages is the trash folder, "
1066              "but you may change that in the folder view below."));
1067     layout->addWidget(mMoveSpamRules);
1068 
1069     mFolderReqForSpamFolder = new FolderRequester(this);
1070     mFolderReqForSpamFolder->setCollection(CommonKernel->trashCollectionFolder());
1071     mFolderReqForSpamFolder->setMustBeReadWrite(true);
1072     mFolderReqForSpamFolder->setShowOutbox(false);
1073 
1074     auto hLayout1 = new QHBoxLayout();
1075     layout->addItem(hLayout1);
1076     hLayout1->addWidget(mFolderReqForSpamFolder);
1077 
1078     mMoveUnsureRules = new QCheckBox(i18n("Move &probable spam to:"), this);
1079     mMoveUnsureRules->setWhatsThis(
1080         i18n("The default folder is the inbox folder, but you may change that "
1081              "in the folder view below.<p>"
1082              "Not all tools support a classification as unsure. If you have not "
1083              "selected a capable tool, you cannot select a folder as well.</p>"));
1084     layout->addWidget(mMoveUnsureRules);
1085 
1086     mFolderReqForUnsureFolder = new FolderRequester(this);
1087     mFolderReqForUnsureFolder->setCollection(CommonKernel->inboxCollectionFolder());
1088     mFolderReqForUnsureFolder->setMustBeReadWrite(true);
1089     mFolderReqForUnsureFolder->setShowOutbox(false);
1090 
1091     auto hLayout2 = new QHBoxLayout();
1092     layout->addItem(hLayout2);
1093     hLayout2->addWidget(mFolderReqForUnsureFolder);
1094 
1095     layout->addStretch();
1096 
1097     connect(mMarkRules, &QAbstractButton::clicked, this, &ASWizSpamRulesPage::processSelectionChange);
1098     connect(mMoveSpamRules, &QAbstractButton::clicked, this, &ASWizSpamRulesPage::processSelectionChange);
1099     connect(mMoveUnsureRules, &QAbstractButton::clicked, this, &ASWizSpamRulesPage::processSelectionChange);
1100     connect(mFolderReqForSpamFolder, &FolderRequester::folderChanged, this, &ASWizSpamRulesPage::processSelectionChange);
1101     connect(mFolderReqForUnsureFolder, &FolderRequester::folderChanged, this, &ASWizSpamRulesPage::processSelectionChange);
1102 
1103     mMarkRules->setChecked(true);
1104     mMoveSpamRules->setChecked(true);
1105 }
1106 
markAsReadSelected() const1107 bool ASWizSpamRulesPage::markAsReadSelected() const
1108 {
1109     return mMarkRules->isChecked();
1110 }
1111 
moveSpamSelected() const1112 bool ASWizSpamRulesPage::moveSpamSelected() const
1113 {
1114     return mMoveSpamRules->isChecked();
1115 }
1116 
moveUnsureSelected() const1117 bool ASWizSpamRulesPage::moveUnsureSelected() const
1118 {
1119     return mMoveUnsureRules->isChecked();
1120 }
1121 
selectedSpamCollectionId() const1122 QString ASWizSpamRulesPage::selectedSpamCollectionId() const
1123 {
1124     return QString::number(selectedSpamCollection().id());
1125 }
1126 
selectedSpamCollectionName() const1127 QString ASWizSpamRulesPage::selectedSpamCollectionName() const
1128 {
1129     return selectedSpamCollection().name();
1130 }
1131 
selectedSpamCollection() const1132 Akonadi::Collection ASWizSpamRulesPage::selectedSpamCollection() const
1133 {
1134     if (mFolderReqForSpamFolder->hasCollection()) {
1135         return mFolderReqForSpamFolder->collection();
1136     } else {
1137         return CommonKernel->trashCollectionFolder();
1138     }
1139 }
1140 
selectedUnsureCollection() const1141 Akonadi::Collection ASWizSpamRulesPage::selectedUnsureCollection() const
1142 {
1143     if (mFolderReqForUnsureFolder->hasCollection()) {
1144         return mFolderReqForUnsureFolder->collection();
1145     } else {
1146         return CommonKernel->inboxCollectionFolder();
1147     }
1148 }
1149 
selectedUnsureCollectionName() const1150 QString ASWizSpamRulesPage::selectedUnsureCollectionName() const
1151 {
1152     return selectedUnsureCollection().name();
1153 }
1154 
selectedUnsureCollectionId() const1155 QString ASWizSpamRulesPage::selectedUnsureCollectionId() const
1156 {
1157     return QString::number(selectedUnsureCollection().id());
1158 }
1159 
processSelectionChange()1160 void ASWizSpamRulesPage::processSelectionChange()
1161 {
1162     mFolderReqForSpamFolder->setEnabled(mMoveSpamRules->isChecked());
1163     mFolderReqForUnsureFolder->setEnabled(mMoveUnsureRules->isChecked());
1164     Q_EMIT selectionChanged();
1165 }
1166 
allowUnsureFolderSelection(bool enabled)1167 void ASWizSpamRulesPage::allowUnsureFolderSelection(bool enabled)
1168 {
1169     mMoveUnsureRules->setEnabled(enabled);
1170     mMoveUnsureRules->setVisible(enabled);
1171     mFolderReqForUnsureFolder->setEnabled(enabled);
1172     mFolderReqForUnsureFolder->setVisible(enabled);
1173 }
1174 
allowMoveSpam(bool enabled)1175 void ASWizSpamRulesPage::allowMoveSpam(bool enabled)
1176 {
1177     mMarkRules->setEnabled(enabled);
1178     mMarkRules->setChecked(enabled);
1179     mMoveSpamRules->setEnabled(enabled);
1180     mMoveSpamRules->setChecked(enabled);
1181 }
1182 
1183 //---------------------------------------------------------------------------
ASWizVirusRulesPage(QWidget * parent,const QString & name)1184 ASWizVirusRulesPage::ASWizVirusRulesPage(QWidget *parent, const QString &name)
1185     : ASWizPage(parent, name)
1186 {
1187     auto grid = new QGridLayout();
1188     mLayout->addItem(grid);
1189 
1190     mPipeRules = new QCheckBox(i18n("Check messages using the anti-virus tools"), this);
1191     mPipeRules->setWhatsThis(
1192         i18n("Let the anti-virus tools check your messages. The wizard "
1193              "will create appropriate filters. The messages are usually "
1194              "marked by the tools so that following filters can react "
1195              "on this and, for example, move virus messages to a special folder."));
1196     grid->addWidget(mPipeRules, 0, 0);
1197 
1198     mMoveRules = new QCheckBox(i18n("Move detected viral messages to the selected folder"), this);
1199     mMoveRules->setWhatsThis(
1200         i18n("A filter to detect messages classified as virus-infected and to move "
1201              "those messages into a predefined folder is created. The "
1202              "default folder is the trash folder, but you may change that "
1203              "in the folder view."));
1204     grid->addWidget(mMoveRules, 1, 0);
1205 
1206     mMarkRules = new QCheckBox(i18n("Additionally, mark detected viral messages as read"), this);
1207     mMarkRules->setEnabled(false);
1208     mMarkRules->setWhatsThis(
1209         i18n("Mark messages which have been classified as "
1210              "virus-infected as read, as well as moving them "
1211              "to the selected folder."));
1212     grid->addWidget(mMarkRules, 2, 0);
1213     FolderTreeWidget::TreeViewOptions opt = FolderTreeWidget::None;
1214     opt |= FolderTreeWidget::UseDistinctSelectionModel;
1215 
1216     FolderTreeWidgetProxyModel::FolderTreeWidgetProxyModelOptions optReadableProxy = FolderTreeWidgetProxyModel::None;
1217     optReadableProxy |= FolderTreeWidgetProxyModel::HideVirtualFolder;
1218     optReadableProxy |= FolderTreeWidgetProxyModel::HideOutboxFolder;
1219 
1220     mFolderTree = new FolderTreeWidget(this, nullptr, opt, optReadableProxy);
1221     mFolderTree->readConfig();
1222     mFolderTree->folderTreeView()->expandAll();
1223     mFolderTree->folderTreeWidgetProxyModel()->setAccessRights(Akonadi::Collection::CanCreateCollection);
1224 
1225     mFolderTree->selectCollectionFolder(CommonKernel->trashCollectionFolder());
1226     mFolderTree->folderTreeView()->setDragDropMode(QAbstractItemView::NoDragDrop);
1227 
1228     mFolderTree->disableContextMenuAndExtraColumn();
1229     grid->addWidget(mFolderTree, 3, 0);
1230 
1231     connect(mPipeRules, &QCheckBox::clicked, this, &ASWizVirusRulesPage::processSelectionChange);
1232     connect(mMoveRules, &QCheckBox::clicked, this, &ASWizVirusRulesPage::processSelectionChange);
1233     connect(mMarkRules, &QCheckBox::clicked, this, &ASWizVirusRulesPage::processSelectionChange);
1234     connect(mMoveRules, &QCheckBox::toggled, mMarkRules, &QCheckBox::setEnabled);
1235 }
1236 
pipeRulesSelected() const1237 bool ASWizVirusRulesPage::pipeRulesSelected() const
1238 {
1239     return mPipeRules->isChecked();
1240 }
1241 
moveRulesSelected() const1242 bool ASWizVirusRulesPage::moveRulesSelected() const
1243 {
1244     return mMoveRules->isChecked();
1245 }
1246 
markReadRulesSelected() const1247 bool ASWizVirusRulesPage::markReadRulesSelected() const
1248 {
1249     return mMarkRules->isChecked();
1250 }
1251 
selectedFolderName() const1252 QString ASWizVirusRulesPage::selectedFolderName() const
1253 {
1254     if (mFolderTree->selectedCollection().isValid()) {
1255         return QString::number(mFolderTree->selectedCollection().id());
1256     } else {
1257         return QString::number(CommonKernel->trashCollectionFolder().id());
1258     }
1259 }
1260 
processSelectionChange()1261 void ASWizVirusRulesPage::processSelectionChange()
1262 {
1263     Q_EMIT selectionChanged();
1264 }
1265 
1266 //---------------------------------------------------------------------------
ASWizSummaryPage(QWidget * parent,const QString & name)1267 ASWizSummaryPage::ASWizSummaryPage(QWidget *parent, const QString &name)
1268     : ASWizPage(parent, name)
1269 {
1270     QBoxLayout *layout = new QVBoxLayout();
1271     mLayout->addItem(layout);
1272 
1273     mSummaryText = new QLabel(this);
1274     layout->addWidget(mSummaryText);
1275     layout->addStretch();
1276 }
1277 
setSummaryText(const QString & text)1278 void ASWizSummaryPage::setSummaryText(const QString &text)
1279 {
1280     mSummaryText->setText(text);
1281 }
1282