1 /*  -*- mode: C++ -*-
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 #pragma once
9 
10 #include <KAssistantDialog>
11 #include <KSharedConfig>
12 
13 #include <Akonadi/Collection>
14 #include <QList>
15 
16 class QLabel;
17 class QCheckBox;
18 class QBoxLayout;
19 class QListWidget;
20 
21 namespace MailCommon
22 {
23 class FolderTreeWidget;
24 class FolderRequester;
25 }
26 
27 namespace KMail
28 {
29 class ASWizInfoPage;
30 class ASWizSpamRulesPage;
31 class ASWizVirusRulesPage;
32 class ASWizSummaryPage;
33 
34 //---------------------------------------------------------------------------
35 /**
36     @short KMail anti-spam wizard.
37     @author Andreas Gungl <a.gungl@gmx.de>
38 
39     The wizard helps to create filter rules to let KMail operate
40     with external anti-spam tools. The wizard tries to detect the
41     tools, but the user can override the preselections.
42     Then the user can decide what functionality shall be supported
43     by the created filter rules.
44     The wizard will append the created filter rules after the
45     last existing rule to keep possible conflicts with existing
46     filter configurations minimal.
47 
48     Anti-virus support was added by Fred Emmott <fred87@users.sf.net>
49 
50     The configuration for the tools to get checked and set up
51     is read from a config file. The structure of the file is as
52     following:
53     <pre>
54     [General]
55     tools=1
56 
57     [Spamtool #1]
58     Ident=spamassassin
59     Version=0
60     Priority=1
61     VisibleName=&Spamassassin
62     Executable=spamassassin -V
63     URL=http://spamassassin.org
64     PipeFilterName=SpamAssassin Check
65     PipeCmdDetect=spamassassin -L
66     ExecCmdSpam=sa-learn --spam --no-sync --single
67     ExecCmdHam=sa-learn --ham --no-sync --single
68     PipeCmdNoSpam=spamassassin -d
69     DetectionHeader=X-Spam-Status
70     DetectionPattern=yes
71     DetectionPattern2=
72     DetectionOnly=0
73     UseRegExp=0
74     SupportsBayes=1
75     SupportsUnsure=0
76     ServerSided=0
77     type=spam
78     </pre>
79     The name of the config file is kmail.antispamrc
80     and it's expected in the config dir of KDE.
81 
82   */
83 class AntiSpamWizard : public KAssistantDialog
84 {
85     Q_OBJECT
86 
87 public:
88     /** The wizard can be used for setting up anti-spam tools and for
89           setting up anti-virus tools.
90       */
91     enum WizardMode {
92         AntiSpam,
93         AntiVirus,
94     };
95 
96     /** Constructor that needs to initialize from the main folder tree
97         of KMail.
98         @param mode The mode the wizard should run in.
99         @param parent The parent widget for the wizard.
100         @param mainFolderTree The main folder tree from which the folders
101           are copied to allow the selection of a spam folder in a tree
102           within one of the wizard pages.
103       */
104     AntiSpamWizard(WizardMode mode, QWidget *parent);
105 
106 protected:
107     /**
108         Instances of this class store the settings for one tool as read from
109         the config file. Visible name and What's this text cannot get
110         translated!
111       */
112     class SpamToolConfig
113     {
114     public:
SpamToolConfig()115         SpamToolConfig()
116         {
117         }
118 
119         SpamToolConfig(const QString &toolId,
120                        int configVersion,
121                        int prio,
122                        const QString &name,
123                        const QString &exec,
124                        const QString &url,
125                        const QString &filter,
126                        const QString &detection,
127                        const QString &spam,
128                        const QString &ham,
129                        const QString &noSpam,
130                        const QString &header,
131                        const QString &pattern,
132                        const QString &pattern2,
133                        const QString &serverPattern,
134                        bool detectionOnly,
135                        bool regExp,
136                        bool bayesFilter,
137                        bool tristateDetection,
138                        WizardMode type);
139 
140         Q_REQUIRED_RESULT int getVersion() const;
141         Q_REQUIRED_RESULT int getPrio() const;
142         Q_REQUIRED_RESULT QString getId() const;
143         Q_REQUIRED_RESULT QString getVisibleName() const;
144         Q_REQUIRED_RESULT QString getExecutable() const;
145         Q_REQUIRED_RESULT QString getWhatsThisText() const;
146         Q_REQUIRED_RESULT QString getFilterName() const;
147         Q_REQUIRED_RESULT QString getDetectCmd() const;
148         Q_REQUIRED_RESULT QString getSpamCmd() const;
149         Q_REQUIRED_RESULT QString getHamCmd() const;
150         Q_REQUIRED_RESULT QString getNoSpamCmd() const;
151         Q_REQUIRED_RESULT QString getDetectionHeader() const;
152         Q_REQUIRED_RESULT QString getDetectionPattern() const;
153         Q_REQUIRED_RESULT QString getDetectionPattern2() const;
154         Q_REQUIRED_RESULT QString getServerPattern() const;
155         Q_REQUIRED_RESULT bool isServerBased() const;
156         Q_REQUIRED_RESULT bool isDetectionOnly() const;
157         Q_REQUIRED_RESULT bool isUseRegExp() const;
158         Q_REQUIRED_RESULT bool useBayesFilter() const;
159         Q_REQUIRED_RESULT bool hasTristateDetection() const;
160         Q_REQUIRED_RESULT WizardMode getType() const;
161         // convenience methods for types
162         Q_REQUIRED_RESULT bool isSpamTool() const;
163         Q_REQUIRED_RESULT bool isVirusTool() const;
164 
165     private:
166         // used to identify configs for the same tool
167         QString mId;
168         // The version of the config data, used for merging and
169         // detecting newer configs
170         int mVersion;
171         // the priority of the tool in the list presented to the user
172         int mPrio;
173         // the name as shown by the checkbox in the dialog page
174         QString mVisibleName;
175         // the command to check the existence of the tool
176         QString mExecutable;
177         // the What's This help text (e.g. url for the tool)
178         QString mWhatsThisText;
179         // name for the created filter in the filter list
180         QString mFilterName;
181         // pipe through cmd used to detect spam messages
182         QString mDetectCmd;
183         // pipe through cmd to let the tool learn a spam message
184         QString mSpamCmd;
185         // pipe through cmd to let the tool learn a ham message
186         QString mHamCmd;
187         // pipe through cmd to let the tool delete the spam markup
188         QString mNoSpamCmd;
189         // by which header are messages marked as spam
190         QString mDetectionHeader;
191         // what header pattern is used to mark spam messages
192         QString mDetectionPattern;
193         // what header pattern is used to mark unsure messages
194         QString mDetectionPattern2;
195         // what header pattern is used in the account to check for a certain server
196         QString mServerPattern;
197         // filter cannot search actively but relies on pattern by regExp or contain rule
198         bool mDetectionOnly;
199         // filter searches for the pattern by regExp or contain rule
200         bool mUseRegExp;
201         // can the tool learn spam and ham, has it a bayesian algorithm
202         bool mSupportsBayesFilter;
203         // differentiate between ham, spam and a third "unsure" state
204         bool mSupportsUnsure;
205         // Is the tool AntiSpam or AntiVirus
206         WizardMode mType;
207     };
208 
209     /**
210         Instances of this class control reading the configuration of the
211         anti-spam tools from global and user config files as well as the
212         merging of different config versions.
213       */
214     class ConfigReader
215     {
216     public:
217         ConfigReader(WizardMode mode, QVector<SpamToolConfig> &configList);
218         ~ConfigReader();
219 
getToolList()220         QVector<SpamToolConfig> &getToolList()
221         {
222             return mToolList;
223         }
224 
225         void readAndMergeConfig();
226 
227     private:
228         QVector<SpamToolConfig> &mToolList;
229         KSharedConfig::Ptr mConfig;
230         WizardMode mMode;
231 
232         SpamToolConfig readToolConfig(KConfigGroup &configGroup);
233         SpamToolConfig createDummyConfig();
234 
235         void mergeToolConfig(const SpamToolConfig &config);
236         void sortToolList();
237     };
238 
239     /** Evaluate the settings made and create the appropriate filter rules. */
240     void accept() override;
241 
242 protected Q_SLOTS:
243     /** Modify the status of the wizard to reflect the selection of spam tools. */
244     void checkProgramsSelections();
245     /** Modify the status of the wizard to reflect the selected functionality. */
246     void checkVirusRulesSelections();
247     /** Check if the spam tools are available via the PATH */
248     void checkToolAvailability();
249     /** Show a help topic */
250     void slotHelpClicked();
251     /** Create the summary text based on the current settings */
252     void slotBuildSummary();
253 
254 private:
255     /* Check for the availability of an executible along the PATH */
256     Q_REQUIRED_RESULT int checkForProgram(const QString &executable) const;
257     /* generic checks if any option in a page is checked */
258     Q_REQUIRED_RESULT bool anyVirusOptionChecked() const;
259     /* convenience method calling the appropriate filter manager method */
260     const QString uniqueNameFor(const QString &name);
261     /* convenience method to sort out new and existing filters */
262     void sortFilterOnExistance(const QString &intendedFilterName, QString &newFilters, QString &replaceFilters);
263 
264     /* The pages in the wizard */
265     ASWizInfoPage *mInfoPage = nullptr;
266     ASWizSpamRulesPage *mSpamRulesPage = nullptr;
267     ASWizVirusRulesPage *mVirusRulesPage = nullptr;
268     ASWizSummaryPage *mSummaryPage = nullptr;
269 
270     KPageWidgetItem *mInfoPageItem = nullptr;
271     KPageWidgetItem *mSpamRulesPageItem = nullptr;
272     KPageWidgetItem *mVirusRulesPageItem = nullptr;
273     KPageWidgetItem *mSummaryPageItem = nullptr;
274 
275     /* The configured tools and it's settings to be used in the wizard. */
276     QVector<SpamToolConfig> mToolList;
277 
278     /* Are any spam tools selected? */
279     bool mSpamToolsUsed;
280     /* Are any virus tools selected? */
281     bool mVirusToolsUsed;
282 
283     WizardMode mMode;
284 };
285 
286 //---------------------------------------------------------------------------
287 class ASWizPage : public QWidget
288 {
289     Q_OBJECT
290 public:
291     ASWizPage(QWidget *parent, const QString &name);
292 
293 protected:
294     QBoxLayout *mLayout = nullptr;
295 };
296 
297 //---------------------------------------------------------------------------
298 class ASWizInfoPage : public ASWizPage
299 {
300     Q_OBJECT
301 
302 public:
303     ASWizInfoPage(AntiSpamWizard::WizardMode mode, QWidget *parent, const QString &name);
304 
305     void setScanProgressText(const QString &toolName);
306     void addAvailableTool(const QString &visibleName);
307     Q_REQUIRED_RESULT bool isProgramSelected(const QString &visibleName) const;
308 
309 Q_SIGNALS:
310     void selectionChanged();
311 
312 private:
313     void processSelectionChange();
314     QLabel *mScanProgressText = nullptr;
315     QLabel *mSelectionHint = nullptr;
316     QListWidget *mToolsList = nullptr;
317 };
318 
319 //---------------------------------------------------------------------------
320 class ASWizSpamRulesPage : public ASWizPage
321 {
322     Q_OBJECT
323 
324 public:
325     ASWizSpamRulesPage(QWidget *parent, const QString &name);
326 
327     Q_REQUIRED_RESULT bool markAsReadSelected() const;
328     Q_REQUIRED_RESULT bool moveSpamSelected() const;
329     Q_REQUIRED_RESULT bool moveUnsureSelected() const;
330 
331     Q_REQUIRED_RESULT QString selectedUnsureCollectionName() const;
332     Q_REQUIRED_RESULT QString selectedUnsureCollectionId() const;
333 
334     void allowUnsureFolderSelection(bool enabled);
335     void allowMoveSpam(bool enabled);
336 
337     Q_REQUIRED_RESULT QString selectedSpamCollectionId() const;
338     Q_REQUIRED_RESULT QString selectedSpamCollectionName() const;
339 
340 protected:
341     Akonadi::Collection selectedSpamCollection() const;
342     Akonadi::Collection selectedUnsureCollection() const;
343 
344 Q_SIGNALS:
345     void selectionChanged();
346 
347 private:
348     void processSelectionChange();
349     QCheckBox *mMarkRules = nullptr;
350     QCheckBox *mMoveSpamRules = nullptr;
351     QCheckBox *mMoveUnsureRules = nullptr;
352     MailCommon::FolderRequester *mFolderReqForSpamFolder = nullptr;
353     MailCommon::FolderRequester *mFolderReqForUnsureFolder = nullptr;
354 };
355 
356 //-------------------------------------------------------------------------
357 class ASWizVirusRulesPage : public ASWizPage
358 {
359     Q_OBJECT
360 
361 public:
362     ASWizVirusRulesPage(QWidget *parent, const QString &name);
363 
364     Q_REQUIRED_RESULT bool pipeRulesSelected() const;
365     Q_REQUIRED_RESULT bool moveRulesSelected() const;
366     Q_REQUIRED_RESULT bool markReadRulesSelected() const;
367 
368     Q_REQUIRED_RESULT QString selectedFolderName() const;
369 
370 Q_SIGNALS:
371     void selectionChanged();
372 
373 private:
374     void processSelectionChange();
375     QCheckBox *mPipeRules = nullptr;
376     QCheckBox *mMoveRules = nullptr;
377     MailCommon::FolderTreeWidget *mFolderTree = nullptr;
378     QCheckBox *mMarkRules = nullptr;
379 };
380 
381 //---------------------------------------------------------------------------
382 class ASWizSummaryPage : public ASWizPage
383 {
384     Q_OBJECT
385 
386 public:
387     ASWizSummaryPage(QWidget *parent, const QString &name);
388 
389     void setSummaryText(const QString &text);
390 
391 private:
392     QLabel *mSummaryText = nullptr;
393 };
394 } // namespace KMail
395