Sophie

Sophie

distrib > Mandriva > 2006.0 > i586 > by-pkgid > 5d8446d2074649ee8939b13a97d00f34 > files > 8

kdepim-3.4.2-13mdk.src.rpm

--- kdepim-3.1.95/kmail/kmmainwin.rc--	2004-01-20 08:13:24.417860544 -0500
+++ kdepim-3.1.95/kmail/kmmainwin.rc	2004-01-20 08:13:51.755704560 -0500
@@ -1,5 +1,5 @@
 <!DOCTYPE kpartgui>
-<kpartgui version="69" name="kmmainwin" >
+<kpartgui version="70" name="kmmainwin" >
  <MenuBar>
   <Menu noMerge="1" name="file" >
    <text>&amp;File</text>
@@ -123,6 +123,8 @@
    <Action name="filter" append="save_merge"/>
    <Action name="popFilter" append="save_merge"/>
    <Separator/>
+   <Action name="antiSpamWizard" append="save_merge"/>
+   <Separator/>
    <Action name="options_configure_keybinding" group="settings_configure"/>
    <Action name="kmail_configure_notifications" group="settings_configure"/>
    <Action name="options_configure_toolbars" group="settings_configure" />
--- kdepim-3.1.95/kmail/Makefile.am--	2004-01-20 08:10:51.803061520 -0500
+++ kdepim-3.1.95/kmail/Makefile.am	2004-01-20 08:11:48.755403448 -0500
@@ -83,7 +83,7 @@ libkmailprivate_la_SOURCES = kmmessage.c
 		folderIface.cpp folderIface.skel mailserviceimpl.cpp \
 		attachmentlistview.cpp ssllabel.cpp \
 		iobserver.cpp isubject.cpp bodyvisitor.cpp \
-		urlhandlermanager.cpp dictionarycombobox.cpp startupwizard.cpp
+		urlhandlermanager.cpp dictionarycombobox.cpp startupwizard.cpp antispamwizard.cpp
 
 kmail_SOURCES = main.cpp
 
@@ -117,6 +117,9 @@ update_DATA = kmail.upd upgrade-transpor
 		kmail-3.2-update-loop-on-goto-unread-settings.sh \
 		kmail-3.2-misc.sh
 
+confdir = $(kde_confdir)
+conf_DATA = kmail.antispamrc
+
 tipdir = $(kde_datadir)/kmail
 tip_DATA = tips
 
--- kdepim-3.1.95/kmail/kmmainwidget.cpp--	2004-01-20 08:10:26.723874136 -0500
+++ kdepim-3.1.95/kmail/kmmainwidget.cpp	2004-01-20 08:12:53.663535912 -0500
@@ -69,6 +69,9 @@ using KMail::HeaderStyle;
 #include "folderjob.h"
 using KMail::FolderJob;
 #include "mailinglist-magic.h"
+#include "antispamwizard.h"
+using KMail::AntiSpamWizard;
+#include <kstandarddirs.h>
 #include "kmgroupware.h"
 
 #include <assert.h>
@@ -2649,6 +2652,10 @@ void KMMainWidget::setupActions()
   (void) new KAction( i18n("Configure &POP Filters..."), 0, this,
  		      SLOT(slotPopFilter()), actionCollection(), "popFilter" );
 
+  if(KStandardDirs::mandrake_distro_version()!=KStandardDirs::DOWNLOAD){
+  (void) new KAction( i18n("&Anti Spam Wizard..."), 0, this,
+  SLOT(slotAntiSpamWizard()), actionCollection(), "antiSpamWizard" );
+  }
   (void) new KAction( KGuiItem( i18n("KMail &Introduction"), 0,
 				i18n("Display KMail's Welcome Page") ),
 		      0, this, SLOT(slotIntro()),
@@ -3250,3 +3257,10 @@ void KMMainWidget::toggleSystray(bool en
     mSystemTray->setMode(mode);
   }
 }
+
+//-----------------------------------------------------------------------------
+void KMMainWidget::slotAntiSpamWizard()
+{
+  AntiSpamWizard wiz( this, folderTree(), actionCollection() );
+  wiz.exec();
+}
--- kdepim-3.1.95/kmail/kmmainwidget.h.kmail_anti_spam	2004-01-20 08:07:04.178665688 -0500
+++ kdepim-3.1.95/kmail/kmmainwidget.h	2004-01-20 08:07:04.230657784 -0500
@@ -347,7 +347,7 @@ protected slots:
   void slotPrintMsg();
 
   void slotConfigChanged();
-
+  void slotAntiSpamWizard();
 private:
   // Message actions
   KAction *mTrashAction, *mDeleteAction, *mSaveAsAction, *mEditAction,
--- kdepim-3.1.95/kmail/kmfiltermgr.h.kmail_anti_spam	2004-01-20 03:32:04.000000000 -0500
+++ kdepim-3.1.95/kmail/kmfiltermgr.h	2004-01-20 08:07:04.230657784 -0500
@@ -44,6 +44,11 @@ public:
   int moveMessage(KMMessage *msg) const;
   void endFiltering(KMMsgBase *msgBase) const;
 
+  /** Append the new filter to the current list of filters and
+      write everything back into the configuration.*/
+  void appendFilter( KMFilter* filter );
+
+
   /** Process given message by applying the filter rules one by
       one. You can select which set of filters (incoming or outgoing)
       should be used.
--- kdepim-3.1.95/kmail/kmfiltermgr.cpp.kmail_anti_spam	2004-01-20 03:32:04.000000000 -0500
+++ kdepim-3.1.95/kmail/kmfiltermgr.cpp	2004-01-20 08:07:04.229657936 -0500
@@ -281,6 +281,15 @@ void KMFilterMgr::createFilter( const QC
   mEditDialog->createFilter( field, value );
 }
 
+//-----------------------------------------------------------------------------
+void KMFilterMgr::appendFilter( KMFilter* filter )
+{
+  beginUpdate();
+  append( filter );
+  writeConfig( TRUE );
+  endUpdate();
+}
+
 
 //-----------------------------------------------------------------------------
 bool KMFilterMgr::folderRemoved(KMFolder* aFolder, KMFolder* aNewFolder)
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ kdepim/kmail/antispamwizard.h	2004-05-26 23:17:48.000000000 +0200
@@ -0,0 +1,339 @@
+/*
+    This file is part of KMail.
+    Copyright (c) 2003 Andreas Gungl <a.gungl@gmx.de>
+
+    KMail is free software; you can redistribute it and/or modify it
+    under the terms of the GNU General Public License, version 2, as
+    published by the Free Software Foundation.
+
+    KMail is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+#ifndef KMAIL_ANTISPAMWIZARD_H
+#define KMAIL_ANTISPAMWIZARD_H
+
+#include <kconfig.h>
+#include <kwizard.h>
+
+#include <qcheckbox.h>
+#include <qdict.h>
+
+class KActionCollection;
+class KMFolderTree;
+class QLabel;
+
+namespace KMail {
+
+  class SimpleFolderTree;
+
+  class ASWizInfoPage;
+  class ASWizProgramsPage;
+  class ASWizSpamRulesPage;
+  class ASWizVirusRulesPage;
+
+  //---------------------------------------------------------------------------
+  /**
+    @short KMail anti-spam wizard.
+    @author Andreas Gungl <a.gungl@gmx.de>
+
+    The wizard helps to create filter rules to let KMail operate
+    with external anti-spam tools. The wizard tries to detect the
+    tools, but the user can overide the preselections.
+    Then the user can decide what funtionality shall be supported
+    by the created filter rules.
+    The wizard will append the created filter rules after the
+    last existing rule to keep possible conflicts with existing
+    filter configurations minimal.
+    
+    Anti-virus support was added by Fred Emmott <fred87@users.sf.net>
+
+    The configuration for the tools to get checked and set up
+    is read fro a config file. The structure of the file is as
+    following:
+    <pre>
+    [General]
+    tools=1
+
+    [Spamtool #1]
+    Ident=spamassassin
+    Version=0
+    VisibleName=&Spamassassin
+    Executable=spamassassin -V
+    URL=http://spamassassin.org
+    PipeFilterName=SpamAssassin Check
+    PipeCmdDetect=spamassassin -L
+    ExecCmdSpam=sa-learn --spam --no-rebuild --single
+    ExecCmdHam=sa-learn --ham --no-rebuild --single
+    DetectionHeader=X-Spam-Flag
+    DetectionPattern=yes
+    UseRegExp=0
+    SupportsBayes=1
+    type=spam
+    </pre>
+    The name of the config file is kmail.antispamrc
+    and it's expected in the config dir of KDE.
+    
+  */
+  class AntiSpamWizard : public KWizard
+  {
+    Q_OBJECT
+
+    public:
+      /** Constructor that needs to initialize from the main folder tree
+        of KMail.
+        @param parent The parent widget for the wizard.
+        @param mainFolderTree The main folder tree from which the folders
+          are copied to allow the selection of a spam folder in a tree
+          within one of the wizard pages.
+        @param collection In this collection there the wizard will search
+          for the filter menu actions which get created for classification
+          rules (to add them later to the main toolbar).
+      */
+      AntiSpamWizard( QWidget * parent, KMFolderTree * mainFolderTree,
+                      KActionCollection * collection );
+
+    protected:
+      /** Evaluate the settings made and create the appropriate filter rules. */
+      void accept();
+      /** Check for the availability of an executible along the PATH */
+      int checkForProgram( QString executable );
+      /**
+        Instances of this class store the settings for one tool as read from
+        the config file. Visible name and What's this text can not get
+        translated!
+      */
+      class SpamToolConfig
+      {
+        public:
+          SpamToolConfig() {};
+          SpamToolConfig( QString toolId, int configVersion,
+                        QString name, QString exec, QString url, QString filter,
+                        QString detection, QString spam, QString ham,
+                        QString header, QString pattern, bool regExp,
+                        bool bayesFilter, QString type );
+    
+          int getVersion() const { return mVersion; };
+          QString getId()  const { return mId; };
+          QString getVisibleName()  const { return mVisibleName; };
+          QString getExecutable() const { return mExecutable; };
+          QString getWhatsThisText() const { return mWhatsThisText; };
+          QString getFilterName() const { return mFilterName; };
+          QString getDetectCmd() const { return mDetectCmd; };
+          QString getSpamCmd() const { return mSpamCmd; };
+          QString getHamCmd() const { return mHamCmd; };
+          QString getDetectionHeader() const { return mDetectionHeader; };
+          QString getDetectionPattern() const { return mDetectionPattern; };
+          bool isUseRegExp() const { return mUseRegExp; };
+          bool useBayesFilter() const { return mSupportsBayesFilter; };
+          QString getType() const { return mType; };
+          // convinience methods for types
+          bool isSpamTool() const { return ( mType == "spam" ); };
+          bool isVirusTool() const { return ( mType == "av" ); };
+    
+        private:
+          // used to identifiy configs for the same tool
+          QString mId;
+          // The version of the config data, used for merging and 
+          // detecting newer configs
+          int mVersion;
+          // the name as shown by the checkbox in the dialog page
+          QString mVisibleName;
+          // the command to check the existance of the tool
+          QString mExecutable;
+          // the What's This help text (e.g. url for the tool)
+          QString mWhatsThisText;
+          // name for the created filter in the filter list
+          QString mFilterName;
+          // pipe through cmd used to detect spam messages
+          QString mDetectCmd;
+          // pipe through cmd to let the tool learn a spam message
+          QString mSpamCmd;
+          // pipe through cmd to let the tool learn a ham message
+          QString mHamCmd;
+          // by which header are messages marked as spam
+          QString mDetectionHeader;
+          // what header pattern is used to mark spam messages
+          QString mDetectionPattern;
+          // filter searches for the pattern by regExp or contain rule
+          bool mUseRegExp;
+          // can the tool learn spam and ham, has it a bayesian algorithm
+          bool mSupportsBayesFilter;
+          // Is the tool anti-spam ("spam") or anti-virus ("av")
+          QString mType;
+      };
+      /**
+        Instances of this class control reading the configuration of the 
+        anti-spam tools from global and user config files as well as the 
+        merging of different config versions.
+      */
+      class ConfigReader
+      {
+        public:
+          ConfigReader( QValueList<SpamToolConfig> & configList );
+          
+          QValueList<SpamToolConfig> & getToolList() { return mToolList; };
+          
+          void readAndMergeConfig();
+          
+        private:
+          QValueList<SpamToolConfig> & mToolList;
+          KConfig mConfig;
+          
+          SpamToolConfig readToolConfig( KConfigGroup & configGroup );
+          SpamToolConfig createDummyConfig();
+          
+          void mergeToolConfig( SpamToolConfig config );
+      };
+      
+      
+    protected slots:
+      /** Modify the status of the wizard to reflect the selection of spam tools. */
+      void checkProgramsSelections();
+      /** Modify the status of the wizard to reflect the selected functionality. */
+      void checkSpamRulesSelections();
+      /** Modify the status of the wizard to reflect the selected functionality. */
+      void checkVirusRulesSelections();
+      /** Check if the spam tools are available via the PATH */
+      void checkToolAvailability();
+      /** Show a help topic */
+      void slotHelpClicked();
+
+    private:
+      /* generic checks if any option in a page is checked */
+      bool anySpamOptionChecked();
+      bool anyVirusOptionChecked();
+    
+      /* The pages in the wizard */
+      ASWizInfoPage * mInfoPage;
+      ASWizProgramsPage * mProgramsPage;
+      ASWizSpamRulesPage * mSpamRulesPage;
+      ASWizVirusRulesPage * mVirusRulesPage;
+
+      /* The configured tools and it's settings to be used in the wizard. */
+      QValueList<SpamToolConfig> mToolList;
+
+      /* The action collection where the filter menu action is searched in */
+      KActionCollection * mActionCollection;
+      
+      /* Are any spam tools selected? */
+      bool mSpamToolsUsed;
+      /* Are any virus tools selected? */
+      bool mVirusToolsUsed;
+  };
+
+
+  //---------------------------------------------------------------------------
+
+  //---------------------------------------------------------------------------
+  class ASWizInfoPage : public QWidget
+  {
+    public:
+      ASWizInfoPage( QWidget *parent, const char *name );
+      
+      void setScanProgressText( const QString &toolName );
+
+    private:
+      QLabel *mIntroText;
+      QLabel *mScanProgressText;
+  };
+
+  //---------------------------------------------------------------------------
+  class ASWizProgramsPage : public QWidget
+  {
+    Q_OBJECT
+
+    public:
+      ASWizProgramsPage( QWidget *parent, const char *name,
+                         QStringList &checkBoxTextList,
+                         QStringList &checkBoxWhatsThisList );
+
+      bool isProgramSelected( const QString &visibleName );
+      void setProgramAsFound( const QString &visibleName, bool found );
+
+    private slots:
+      void processSelectionChange();
+
+    signals:
+      void selectionChanged();
+
+    private:
+      QDict<QCheckBox> mProgramDict;
+  };
+
+  //---------------------------------------------------------------------------
+  class ASWizSpamRulesPage : public QWidget
+  {
+    Q_OBJECT
+
+    public:
+      ASWizSpamRulesPage( QWidget * parent, const char * name, KMFolderTree * mainFolderTree );
+
+      bool pipeRulesSelected() const;
+      bool classifyRulesSelected() const;
+      bool moveRulesSelected() const;
+      bool markReadRulesSelected() const;
+      
+      QString selectedFolderName() const;
+      void allowClassification( bool enabled );
+
+    private slots:
+      void processSelectionChange();
+
+    signals:
+      void selectionChanged();
+
+    private:
+      QCheckBox * mPipeRules;
+      QCheckBox * mClassifyRules;
+      QCheckBox * mMoveRules;
+      SimpleFolderTree *mFolderTree;
+      QCheckBox * mMarkRules;
+  };
+  
+  //-------------------------------------------------------------------------
+  class ASWizVirusRulesPage : public QWidget
+  {
+    Q_OBJECT
+    
+    public:
+      ASWizVirusRulesPage( QWidget * parent, const char * name, KMFolderTree * mainFolderTree );
+      
+      bool pipeRulesSelected() const;
+      bool moveRulesSelected() const;
+      bool markReadRulesSelected() const;
+      
+      QString selectedFolderName() const;
+      
+    private slots:
+      void processSelectionChange();
+    signals:
+      void selectionChanged();
+      
+    private:
+      QCheckBox * mPipeRules;
+      QCheckBox * mMoveRules;
+      SimpleFolderTree *mFolderTree;
+      QCheckBox * mMarkRules;
+  };
+
+
+} // namespace KMail
+
+#endif // KMAIL_ANTISPAMWIZARD_H
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ kdepim/kmail/antispamwizard.cpp	2004-06-16 14:19:41.000000000 +0200
@@ -0,0 +1,977 @@
+/*
+    This file is part of KMail.
+    Copyright (c) 2003 Andreas Gungl <a.gungl@gmx.de>
+
+    KMail is free software; you can redistribute it and/or modify it
+    under the terms of the GNU General Public License, version 2, as
+    published by the Free Software Foundation.
+
+    KMail is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    In addition, as a special exception, the copyright holders give
+    permission to link the code of this program with any edition of
+    the Qt library by Trolltech AS, Norway (or with modified versions
+    of Qt that use the same license as Qt), and distribute linked
+    combinations including the two.  You must obey the GNU General
+    Public License in all respects for all of the code used other than
+    Qt.  If you modify this file, you may extend this exception to
+    your version of the file, but you are not obligated to do so.  If
+    you do not wish to do so, delete this exception statement from
+    your version.
+*/
+
+#include "antispamwizard.h"
+#include "kcursorsaver.h"
+#include "kmfilter.h"
+#include "kmfilteraction.h"
+#include "kmfiltermgr.h"
+#include "kmkernel.h"
+#include "kmfolderseldlg.h"
+#include "kmfoldertree.h"
+#include "kmmainwin.h"
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+
+#include <qdom.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+using namespace KMail;
+
+AntiSpamWizard::AntiSpamWizard( QWidget* parent, KMFolderTree * mainFolderTree,
+                                KActionCollection * collection )
+  : KWizard( parent )
+{
+  // read the configuration for the anti-spam tools
+  ConfigReader reader( mToolList );
+  reader.readAndMergeConfig();
+  mToolList = reader.getToolList();
+
+#ifndef NDEBUG
+    kdDebug(5006) << endl << "Considered anti-spam/virus tools: " << endl;
+#endif
+  QStringList descriptionList;
+  QStringList whatsThisList;
+  QValueListIterator<SpamToolConfig> it = mToolList.begin();
+  while ( it != mToolList.end() )
+  {
+    descriptionList.append( (*it).getVisibleName() );
+    whatsThisList.append( (*it).getWhatsThisText() );
+#ifndef NDEBUG
+    kdDebug(5006) << "Predefined tool: " << (*it).getId() << endl;
+    kdDebug(5006) << "Config version: " << (*it).getVersion() << endl;
+    kdDebug(5006) << "Displayed name: " << (*it).getVisibleName() << endl;
+    kdDebug(5006) << "Executable: " << (*it).getExecutable() << endl;
+    kdDebug(5006) << "WhatsThis URL: " << (*it).getWhatsThisText() << endl;
+    kdDebug(5006) << "Filter name: " << (*it).getFilterName() << endl;
+    kdDebug(5006) << "Detection command: " << (*it).getDetectCmd() << endl;
+    kdDebug(5006) << "Learn spam command: " << (*it).getSpamCmd() << endl;
+    kdDebug(5006) << "Learn ham command: " << (*it).getHamCmd() << endl;
+    kdDebug(5006) << "Detection header: " << (*it).getDetectionHeader() << endl;
+    kdDebug(5006) << "Detection pattern: " << (*it).getDetectionPattern() << endl;
+    kdDebug(5006) << "Use as RegExp: " << (*it).isUseRegExp() << endl;
+    kdDebug(5006) << "Supports Bayes Filter: " << (*it).useBayesFilter() << endl;
+    kdDebug(5006) << "Type: " << (*it).getType() << endl << endl;
+#endif
+    it++;
+  }
+
+  mActionCollection = collection;
+
+  setCaption( i18n( "Anti-Spam/Virus Wizard" ));
+  mInfoPage = new ASWizInfoPage( 0, "" );
+  addPage( mInfoPage, i18n( "Welcome to the KMail Anti-Spam/Virus Wizard." ));
+  mProgramsPage = new ASWizProgramsPage( 0, "", descriptionList, whatsThisList );
+  addPage( mProgramsPage, i18n( "Please select the tools to be used by KMail." ));
+  mSpamRulesPage = new ASWizSpamRulesPage( 0, "", mainFolderTree );
+  mVirusRulesPage = new ASWizVirusRulesPage( 0, "", mainFolderTree );
+
+  connect( mProgramsPage, SIGNAL( selectionChanged( void ) ),
+            this, SLOT( checkProgramsSelections( void ) ) );
+  connect( mSpamRulesPage, SIGNAL( selectionChanged( void ) ),
+            this, SLOT( checkSpamRulesSelections( void ) ) );
+  connect( mVirusRulesPage, SIGNAL( selectionChanged( void ) ),
+            this, SLOT( checkVirusRulesSelections( void ) ) );
+
+  connect( this, SIGNAL( helpClicked( void) ),
+            this, SLOT( slotHelpClicked( void ) ) );
+
+  setNextEnabled( mInfoPage, false );
+  setNextEnabled( mProgramsPage, false );
+
+  QTimer::singleShot( 500, this, SLOT( checkToolAvailability( void ) ) );
+}
+
+
+void AntiSpamWizard::accept()
+{
+  kdDebug( 5006 ) << "Folder name for spam is "
+                  << mSpamRulesPage->selectedFolderName() << endl;
+  kdDebug( 5006 ) << "Folder name for viruses is "
+                  << mVirusRulesPage->selectedFolderName() << endl;
+
+  KMFilterActionDict dict;
+
+  // Let's start with virus detection and handling,
+  // so we can avoid spam checks for viral messages
+  QValueListIterator<SpamToolConfig> it = mToolList.begin();
+  while ( it != mToolList.end() )
+  {
+    if ( mProgramsPage->isProgramSelected( (*it).getVisibleName() ) &&
+       ( mVirusRulesPage->pipeRulesSelected() && (*it).isVirusTool() ) )
+    {
+      // pipe messages through the anti-virus tools,
+      // one single filter for each tool
+      // (could get combined but so it's easier to understand for the user)
+      KMFilter* pipeFilter = new KMFilter();
+      QPtrList<KMFilterAction>* pipeFilterActions = pipeFilter->actions();
+      KMFilterAction* pipeFilterAction = dict["filter app"]->create();
+      pipeFilterAction->argsFromString( (*it).getDetectCmd() );
+      pipeFilterActions->append( pipeFilterAction );
+      KMSearchPattern* pipeFilterPattern = pipeFilter->pattern();
+      pipeFilterPattern->setName( (*it).getFilterName() );
+      pipeFilterPattern->append( KMSearchRule::createInstance( "<size>",
+                                 KMSearchRule::FuncIsGreaterOrEqual, "0" ) );
+      pipeFilter->setApplyOnOutbound( FALSE);
+      pipeFilter->setApplyOnInbound();
+      pipeFilter->setApplyOnExplicit();
+      pipeFilter->setStopProcessingHere( FALSE );
+      pipeFilter->setConfigureShortcut( FALSE );
+
+      KMKernel::self()->filterMgr()->appendFilter( pipeFilter );
+    }
+    it++;
+  }
+
+  if ( mVirusRulesPage->moveRulesSelected() )
+  {
+    // Sort out viruses depending on header fields set by the tools
+    KMFilter* virusFilter = new KMFilter();
+    QPtrList<KMFilterAction>* virusFilterActions = virusFilter->actions();
+    KMFilterAction* virusFilterAction1 = dict["transfer"]->create();
+    virusFilterAction1->argsFromString( mVirusRulesPage->selectedFolderName() );
+    virusFilterActions->append( virusFilterAction1 );
+    if ( mVirusRulesPage->markReadRulesSelected() ) {
+      KMFilterAction* virusFilterAction2 = dict["set status"]->create();
+      virusFilterAction2->argsFromString( "R" ); // Read
+      virusFilterActions->append( virusFilterAction2 );
+    }
+    KMSearchPattern* virusFilterPattern = virusFilter->pattern();
+    virusFilterPattern->setName( i18n( "Virus handling" ) );
+    virusFilterPattern->setOp( KMSearchPattern::OpOr );
+    it = mToolList.begin();
+    while ( it != mToolList.end() )
+    {
+      if ( mProgramsPage->isProgramSelected( (*it).getVisibleName() ))
+      {
+        if ( (*it).isVirusTool() )
+        {
+            const QCString header = (*it).getDetectionHeader().ascii();
+            const QString & pattern = (*it).getDetectionPattern();
+            if ( (*it).isUseRegExp() )
+              virusFilterPattern->append(
+                KMSearchRule::createInstance( header,
+                KMSearchRule::FuncRegExp, pattern ) );
+            else
+              virusFilterPattern->append(
+                KMSearchRule::createInstance( header,
+                KMSearchRule::FuncContains, pattern ) );
+        }
+      }
+      it++;
+    }
+    virusFilter->setApplyOnOutbound( FALSE);
+    virusFilter->setApplyOnInbound();
+    virusFilter->setApplyOnExplicit();
+    virusFilter->setStopProcessingHere( TRUE );
+    virusFilter->setConfigureShortcut( FALSE );
+
+    KMKernel::self()->filterMgr()->appendFilter( virusFilter );
+  }
+
+  it = mToolList.begin();
+  while ( it != mToolList.end() )
+  {
+    if ( mProgramsPage->isProgramSelected( (*it).getVisibleName() ) &&
+       ( mSpamRulesPage->pipeRulesSelected() && (*it).isSpamTool() ) )
+    {
+      // pipe messages through the anti-spam tools,
+      // one single filter for each tool
+      // (could get combined but so it's easier to understand for the user)
+      KMFilter* pipeFilter = new KMFilter();
+      QPtrList<KMFilterAction>* pipeFilterActions = pipeFilter->actions();
+      KMFilterAction* pipeFilterAction = dict["filter app"]->create();
+      pipeFilterAction->argsFromString( (*it).getDetectCmd() );
+      pipeFilterActions->append( pipeFilterAction );
+      KMSearchPattern* pipeFilterPattern = pipeFilter->pattern();
+      pipeFilterPattern->setName( (*it).getFilterName() );
+      pipeFilterPattern->append( KMSearchRule::createInstance( "<size>",
+                                 KMSearchRule::FuncIsGreaterOrEqual, "0" ) );
+      pipeFilter->setApplyOnOutbound( FALSE);
+      pipeFilter->setApplyOnInbound();
+      pipeFilter->setApplyOnExplicit();
+      pipeFilter->setStopProcessingHere( FALSE );
+      pipeFilter->setConfigureShortcut( FALSE );
+
+      KMKernel::self()->filterMgr()->appendFilter( pipeFilter );
+    }
+    it++;
+  }
+
+  if ( mSpamRulesPage->moveRulesSelected() )
+  {
+    // Sort out spam depending on header fields set by the tools
+    KMFilter* spamFilter = new KMFilter();
+    QPtrList<KMFilterAction>* spamFilterActions = spamFilter->actions();
+    KMFilterAction* spamFilterAction1 = dict["transfer"]->create();
+    spamFilterAction1->argsFromString( mSpamRulesPage->selectedFolderName() );
+    spamFilterActions->append( spamFilterAction1 );
+    KMFilterAction* spamFilterAction2 = dict["set status"]->create();
+    spamFilterAction2->argsFromString( "P" ); // Spam
+    spamFilterActions->append( spamFilterAction2 );
+    if ( mSpamRulesPage->markReadRulesSelected() ) {
+      KMFilterAction* spamFilterAction3 = dict["set status"]->create();
+      spamFilterAction3->argsFromString( "R" ); // Read
+      spamFilterActions->append( spamFilterAction3 );
+    }
+    KMSearchPattern* spamFilterPattern = spamFilter->pattern();
+    spamFilterPattern->setName( i18n( "Spam handling" ) );
+    spamFilterPattern->setOp( KMSearchPattern::OpOr );
+    it = mToolList.begin();
+    while ( it != mToolList.end() )
+    {
+      if ( mProgramsPage->isProgramSelected( (*it).getVisibleName() ) )
+      {
+          if ( (*it).isSpamTool() )
+          {
+            const QCString header = (*it).getDetectionHeader().ascii();
+            const QString & pattern = (*it).getDetectionPattern();
+            if ( (*it).isUseRegExp() )
+              spamFilterPattern->append(
+                KMSearchRule::createInstance( header,
+                KMSearchRule::FuncRegExp, pattern ) );
+            else
+              spamFilterPattern->append(
+                KMSearchRule::createInstance( header,
+                KMSearchRule::FuncContains, pattern ) );
+          }
+      }
+      it++;
+    }
+    spamFilter->setApplyOnOutbound( FALSE);
+    spamFilter->setApplyOnInbound();
+    spamFilter->setApplyOnExplicit();
+    spamFilter->setStopProcessingHere( TRUE );
+    spamFilter->setConfigureShortcut( FALSE );
+
+    KMKernel::self()->filterMgr()->appendFilter( spamFilter );
+  }
+
+  if ( mSpamRulesPage->classifyRulesSelected() )
+  {
+    // Classify messages manually as Spam
+    KMFilter* classSpamFilter = new KMFilter();
+    classSpamFilter->setIcon( "mark_as_spam" );
+    QPtrList<KMFilterAction>* classSpamFilterActions = classSpamFilter->actions();
+    KMFilterAction* classSpamFilterActionFirst = dict["set status"]->create();
+    classSpamFilterActionFirst->argsFromString( "P" );
+    classSpamFilterActions->append( classSpamFilterActionFirst );
+    it = mToolList.begin();
+    while ( it != mToolList.end() )
+    {
+      if ( mProgramsPage->isProgramSelected( (*it).getVisibleName() )
+          && (*it).useBayesFilter() )
+      {
+        KMFilterAction* classSpamFilterAction = dict["execute"]->create();
+        classSpamFilterAction->argsFromString( (*it).getSpamCmd() );
+        classSpamFilterActions->append( classSpamFilterAction );
+      }
+      it++;
+    }
+    KMFilterAction* classSpamFilterActionLast = dict["transfer"]->create();
+    classSpamFilterActionLast->argsFromString( mSpamRulesPage->selectedFolderName() );
+    classSpamFilterActions->append( classSpamFilterActionLast );
+
+    KMSearchPattern* classSpamFilterPattern = classSpamFilter->pattern();
+    classSpamFilterPattern->setName( i18n( "Classify as spam" ) );
+    classSpamFilterPattern->append( KMSearchRule::createInstance( "<size>",
+                                    KMSearchRule::FuncIsGreaterOrEqual, "0" ) );
+    classSpamFilter->setApplyOnOutbound( FALSE);
+    classSpamFilter->setApplyOnInbound( FALSE );
+    classSpamFilter->setApplyOnExplicit( FALSE );
+    classSpamFilter->setStopProcessingHere( TRUE );
+    classSpamFilter->setConfigureShortcut( TRUE );
+    KMKernel::self()->filterMgr()->appendFilter( classSpamFilter );
+
+    // Classify messages manually as not Spam / as Ham
+    KMFilter* classHamFilter = new KMFilter();
+    QPtrList<KMFilterAction>* classHamFilterActions = classHamFilter->actions();
+    KMFilterAction* classHamFilterActionFirst = dict["set status"]->create();
+    classHamFilterActionFirst->argsFromString( "H" );
+    classHamFilterActions->append( classHamFilterActionFirst );
+    it = mToolList.begin();
+    while ( it != mToolList.end() )
+    {
+      if ( mProgramsPage->isProgramSelected( (*it).getVisibleName() )
+          && (*it).useBayesFilter() )
+      {
+        KMFilterAction* classHamFilterAction = dict["execute"]->create();
+        classHamFilterAction->argsFromString( (*it).getHamCmd() );
+        classHamFilterActions->append( classHamFilterAction );
+      }
+      it++;
+    }
+    KMSearchPattern* classHamFilterPattern = classHamFilter->pattern();
+    classHamFilterPattern->setName( i18n( "Classify as NOT spam" ) );
+    classHamFilterPattern->append( KMSearchRule::createInstance( "<size>",
+                                   KMSearchRule::FuncIsGreaterOrEqual, "0" ) );
+    classHamFilter->setApplyOnOutbound( FALSE);
+    classHamFilter->setApplyOnInbound( FALSE );
+    classHamFilter->setApplyOnExplicit( FALSE );
+    classHamFilter->setStopProcessingHere( TRUE );
+    classHamFilter->setConfigureShortcut( TRUE );
+    KMKernel::self()->filterMgr()->appendFilter( classHamFilter );
+
+    // add the classification filter actions to the toolbar
+    QString filterNameSpam =
+        QString( "Filter %1" ).arg( classSpamFilterPattern->name() );
+    filterNameSpam = filterNameSpam.replace( " ", "_" );
+    QString filterNameHam =
+        QString( "Filter %1" ).arg( classHamFilterPattern->name() );
+    filterNameHam = filterNameHam.replace( " ", "_" );
+
+    // FIXME post KDE 3.2
+    // The following code manipulates the kmmainwin.rc file directly. Usuallay
+    // one would expect to let the toolbar write back it's change from above
+    // i.e. the new structure including the two added actions.
+    // In KDE 3.2 there is no API for that so I only fund the way to read in
+    // the XML file myself, to change it and write it out then.
+    // As soon as an API is available, the following part can certainly get
+    // replaced by one or two statements.
+    // (a.gungl@gmx.de)
+
+    // make the toolbar changes persistent - let's be very conservative here
+    QString config =
+        KXMLGUIFactory::readConfigFile( "kmmainwin.rc", KMKernel::self()->xmlGuiInstance() );
+#ifndef NDEBUG
+    kdDebug(5006) << "Read kmmainwin.rc contents (last 1000 chars printed):" << endl;
+    kdDebug(5006) << config.right( 1000 ) << endl;
+    kdDebug(5006) << "#####################################################" << endl;
+#endif
+    QDomDocument domDoc;
+    domDoc.setContent( config );
+    QDomNodeList domNodeList = domDoc.elementsByTagName( "ToolBar" );
+    if ( domNodeList.count() > 0 )
+      kdDebug(5006) << "ToolBar section found." << endl;
+    else
+      kdDebug(5006) << "No ToolBar section found." << endl;
+    for ( unsigned int i = 0; i < domNodeList.count(); i++ )
+    {
+      QDomNode domNode = domNodeList.item( i );
+      QDomNamedNodeMap nodeMap = domNode.attributes();
+      kdDebug(5006) << "name=" << nodeMap.namedItem( "name" ).nodeValue() << endl;
+      if ( nodeMap.namedItem( "name" ).nodeValue() == "mainToolBar" )
+      {
+        kdDebug(5006) << "mainToolBar section found." << endl;
+        bool spamActionFound = false;
+        bool hamActionFound = false;
+        QDomNodeList domNodeChildList = domNode.childNodes();
+        for ( unsigned int j = 0; j < domNodeChildList.count(); j++ )
+        {
+          QDomNode innerDomNode = domNodeChildList.item( j );
+          QDomNamedNodeMap innerNodeMap = innerDomNode.attributes();
+          if ( innerNodeMap.namedItem( "name" ).nodeValue() == filterNameSpam )
+            spamActionFound = true;
+          if ( innerNodeMap.namedItem( "name" ).nodeValue() == filterNameHam )
+            hamActionFound = true;
+        }
+
+        // append the new actions if not yet existing
+        if ( !spamActionFound )
+        {
+          QDomElement domElemSpam = domDoc.createElement( "Action" );
+          domElemSpam.setAttribute( "name", filterNameSpam );
+          domNode.appendChild( domElemSpam );
+          kdDebug(5006) << "Spam action added to toolbar." << endl;
+        }
+        if ( !hamActionFound )
+        {
+          QDomElement domElemHam = domDoc.createElement( "Action" );
+          domElemHam.setAttribute( "name", filterNameHam );
+          domNode.appendChild( domElemHam );
+          kdDebug(5006) << "Ham action added to toolbar." << endl;
+        }
+        if ( !spamActionFound || !hamActionFound )
+        {
+#ifndef NDEBUG
+          kdDebug(5006) << "New kmmainwin.rc structur (last 1000 chars printed):" << endl;
+          kdDebug(5006) << domDoc.toString().right( 1000 ) << endl;
+          kdDebug(5006) << "####################################################" << endl;
+#endif
+          // write back the modified resource file
+          KXMLGUIFactory::saveConfigFile( domDoc, "kmmainwin.rc",
+              KMKernel::self()->xmlGuiInstance() );
+        }
+      }
+      else
+        kdDebug(5006) << "No mainToolBar section found." << endl;
+    }
+  }
+
+  QDialog::accept();
+}
+
+
+void AntiSpamWizard::checkProgramsSelections()
+{
+  bool status = false;
+  bool canClassify = false;
+  QValueListIterator<SpamToolConfig> it = mToolList.begin();
+  mSpamToolsUsed = false;
+  mVirusToolsUsed = false;
+  while ( it != mToolList.end() )
+  {
+    if ( mProgramsPage->isProgramSelected( (*it).getVisibleName() ) )
+    {
+      status = true;
+      if ( (*it).isSpamTool() )
+        mSpamToolsUsed = true;
+      if ( (*it).isVirusTool() )
+        mVirusToolsUsed = true;
+    }
+    if ( (*it).useBayesFilter() )
+      canClassify = true;
+    it++;
+  }
+
+  mSpamRulesPage->allowClassification( canClassify );
+
+  removePage( mSpamRulesPage );
+  removePage( mVirusRulesPage );
+  if ( mSpamToolsUsed )
+  {
+    addPage( mSpamRulesPage, i18n( "Please select the spam filters to be created inside KMail." ));
+    checkSpamRulesSelections();
+  }
+  if ( mVirusToolsUsed )
+  {
+    addPage( mVirusRulesPage, i18n( "Please select the virus filters to be created inside KMail." ));
+    checkVirusRulesSelections();
+  }
+
+  setNextEnabled( mProgramsPage, status );
+}
+
+
+void AntiSpamWizard::checkSpamRulesSelections()
+{
+  if ( anySpamOptionChecked() )
+  {
+    if ( mVirusToolsUsed )
+      setNextEnabled( mSpamRulesPage, true );
+    else
+      setFinishEnabled( mSpamRulesPage, true );
+  }
+  else
+  {
+    setNextEnabled( mSpamRulesPage, mVirusToolsUsed );
+    setFinishEnabled( mSpamRulesPage, false );
+  }
+  if ( mVirusToolsUsed )
+    checkVirusRulesSelections();
+}
+
+void AntiSpamWizard::checkVirusRulesSelections()
+{
+  if ( anyVirusOptionChecked() )
+    setFinishEnabled( mVirusRulesPage, true );
+  else
+    setFinishEnabled( mVirusRulesPage,
+                      anySpamOptionChecked() && mSpamToolsUsed );
+}
+
+
+void AntiSpamWizard::checkToolAvailability()
+{
+  KCursorSaver busy(KBusyPtr::busy()); // this can take some time to find the tools
+
+  // checkboxes for the tools
+  QValueListIterator<SpamToolConfig> it = mToolList.begin();
+  while ( it != mToolList.end() )
+  {
+    QString text( i18n("Scanning for %1...").arg( (*it).getId() ) );
+    mInfoPage->setScanProgressText( text );
+    KApplication::kApplication()->processEvents( 200 );
+    int rc = checkForProgram( (*it).getExecutable() );
+    mProgramsPage->setProgramAsFound( (*it).getVisibleName(), !rc );
+    it++;
+  }
+  mInfoPage->setScanProgressText( i18n("Scanning for anti-spam/virus tools finished.") );
+  setNextEnabled( mInfoPage, true );
+}
+
+
+int AntiSpamWizard::checkForProgram( QString executable )
+{
+  kdDebug(5006) << "Testing for executable:" << executable << endl;
+  KProcess process;
+  process << executable;
+  process.setUseShell( true );
+  process.start( KProcess::Block );
+  return process.exitStatus();
+}
+
+
+void AntiSpamWizard::slotHelpClicked()
+{
+  KApplication::kApplication()->invokeHelp( "the-anti-spam-wizard", "kmail" );
+}
+
+
+bool AntiSpamWizard::anySpamOptionChecked()
+{
+  return ( mSpamRulesPage->moveRulesSelected()
+        || mSpamRulesPage->pipeRulesSelected()
+        || mSpamRulesPage->classifyRulesSelected() );
+}
+
+bool AntiSpamWizard::anyVirusOptionChecked()
+{
+  return ( mVirusRulesPage->moveRulesSelected() || mVirusRulesPage->pipeRulesSelected() );
+}
+
+
+//---------------------------------------------------------------------------
+AntiSpamWizard::SpamToolConfig::SpamToolConfig(QString toolId,
+      int configVersion,QString name, QString exec,
+      QString url, QString filter, QString detection, QString spam, QString ham,
+      QString header, QString pattern, bool regExp, bool bayesFilter, QString type)
+  : mId( toolId ), mVersion( configVersion ),
+    mVisibleName( name ), mExecutable( exec ), mWhatsThisText( url ),
+    mFilterName( filter ), mDetectCmd( detection ), mSpamCmd( spam ),
+    mHamCmd( ham ), mDetectionHeader( header ), mDetectionPattern( pattern ),
+    mUseRegExp( regExp ), mSupportsBayesFilter( bayesFilter ), mType( type )
+{
+}
+
+
+//---------------------------------------------------------------------------
+AntiSpamWizard::ConfigReader::ConfigReader( QValueList<SpamToolConfig> & configList )
+  : mToolList( configList ),
+    mConfig( "kmail.antispamrc", true )
+{}
+
+
+void AntiSpamWizard::ConfigReader::readAndMergeConfig()
+{
+  // read the configuration from the global config file
+  mConfig.setReadDefaults( true );
+  KConfigGroup general( &mConfig, "General" );
+  int registeredTools = general.readNumEntry( "tools", 0 );
+  for (int i = 1; i <= registeredTools; i++)
+  {
+    KConfigGroup toolConfig( &mConfig,
+      QCString("Spamtool #") + QCString().setNum(i) );
+    mToolList.append( readToolConfig( toolConfig ) );
+  }
+
+  // read the configuration from the user config file
+  // and merge newer config data
+  mConfig.setReadDefaults( false );
+  KConfigGroup user_general( &mConfig, "General" );
+  int user_registeredTools = user_general.readNumEntry( "tools", 0 );
+  for (int i = 1; i <= user_registeredTools; i++)
+  {
+    KConfigGroup toolConfig( &mConfig,
+      QCString("Spamtool #") + QCString().setNum(i) );
+    mergeToolConfig( readToolConfig( toolConfig ) );
+  }
+  // Make sure to have add least one tool listed even when the
+  // config file was not found or whatever went wrong
+  if ( registeredTools < 1 && user_registeredTools < 1)
+    mToolList.append( createDummyConfig() );
+}
+
+
+AntiSpamWizard::SpamToolConfig
+    AntiSpamWizard::ConfigReader::readToolConfig( KConfigGroup & configGroup )
+{
+  QString id = configGroup.readEntry( "Ident" );
+  int version = configGroup.readNumEntry( "Version" );
+#ifndef NDEBUG
+  kdDebug(5006) << "Found predefined tool: " << id << endl;
+  kdDebug(5006) << "With config version  : " << version << endl;
+#endif
+  QString name = configGroup.readEntry( "VisibleName" );
+  QString executable = configGroup.readEntry( "Executable" );
+  QString url = configGroup.readEntry( "URL" );
+  QString filterName = configGroup.readEntry( "PipeFilterName" );
+  QString detectCmd = configGroup.readEntry( "PipeCmdDetect" );
+  QString spamCmd = configGroup.readEntry( "ExecCmdSpam" );
+  QString hamCmd = configGroup.readEntry( "ExecCmdHam" );
+  QString header = configGroup.readEntry( "DetectionHeader" );
+  QString pattern = configGroup.readEntry( "DetectionPattern" );
+  bool useRegExp  = configGroup.readBoolEntry( "UseRegExp" );
+  bool supportsBayes = configGroup.readBoolEntry( "SupportsBayes" );
+  QString type = configGroup.readEntry( "Type" );
+  return SpamToolConfig( id, version, name, executable, url,
+                         filterName, detectCmd, spamCmd, hamCmd,
+                         header, pattern, useRegExp, supportsBayes, type );
+}
+
+
+AntiSpamWizard::SpamToolConfig AntiSpamWizard::ConfigReader::createDummyConfig()
+{
+  return SpamToolConfig( "spamassassin", 0,
+                        "&SpamAssassin", "spamassassin -V",
+                        "http://spamassassin.org", "SpamAssassin Check",
+                        "spamassassin -L",
+                        "sa-learn -L --spam --no-rebuild --single",
+                        "sa-learn -L --ham --no-rebuild --single",
+                        "X-Spam-Flag", "yes",
+                        false, true, "spam" );
+}
+
+
+void AntiSpamWizard::ConfigReader::mergeToolConfig( AntiSpamWizard::SpamToolConfig config )
+{
+  bool found = false;
+  QValueListIterator<SpamToolConfig> it = mToolList.begin();
+  while ( it != mToolList.end() && !found)
+  {
+#ifndef NDEBUG
+    kdDebug(5006) << "Check against tool: " << (*it).getId() << endl;
+    kdDebug(5006) << "Against version   : " << (*it).getVersion() << endl;
+#endif
+    if ( (*it).getId() == config.getId() )
+    {
+      found = true;
+      if ( (*it).getVersion() < config.getVersion() )
+      {
+#ifndef NDEBUG
+        kdDebug(5006) << "Replacing config ..." << endl;
+#endif
+        mToolList.remove( it );
+        mToolList.append( config );
+      }
+    }
+    else it++;
+  }
+  if ( !found )
+    mToolList.append( config );
+}
+
+
+//---------------------------------------------------------------------------
+ASWizInfoPage::ASWizInfoPage( QWidget * parent, const char * name )
+  : QWidget( parent, name )
+{
+  QGridLayout *grid = new QGridLayout( this, 1, 1, KDialog::marginHint(),
+                                        KDialog::spacingHint() );
+  grid->setColStretch( 1, 10 );
+
+  mIntroText = new QLabel( this );
+  mIntroText->setText( i18n(
+    "<p>Here you get some assistance in setting up KMail's filter "
+    "rules to use some commonly-known anti-spam/virus tools.</p>"
+    "<p>The wizard can detect those tools on your computer as "
+    "well as create filter rules to classify messages using these "
+    "tools and to separate messages classified as spam, or which "
+    "contain viruses. The wizard will not take any existing filter "
+    "rules into consideration but will append new rules in any"
+    "case.</p>"
+    "<p><b>WARNING:</b> As KMail is blocked during the scan of the "
+    "messages for spam or viruses, you may encounter problems with "
+    "the responsivness of KMail because anti-spam/virus tool "
+    "operations are usually time consuming. Please consider "
+    "deleting the filter rules created by the wizard to get "
+    "back to the former behavior."
+    ) );
+  grid->addWidget( mIntroText, 0, 0 );
+
+  mScanProgressText = new QLabel( this );
+  mScanProgressText->setText( "" ) ;
+  grid->addWidget( mScanProgressText, 1, 0 );
+}
+
+void ASWizInfoPage::setScanProgressText( const QString &toolName )
+{
+  mScanProgressText->setText( toolName );
+}
+
+//---------------------------------------------------------------------------
+ASWizProgramsPage::ASWizProgramsPage( QWidget * parent, const char * name,
+                                      QStringList &checkBoxTextList,
+                                      QStringList &checkBoxWhatsThisList )
+  : QWidget( parent, name )
+{
+  QGridLayout *grid = new QGridLayout( this, 3, 1, KDialog::marginHint(),
+                                        KDialog::spacingHint() );
+  // checkboxes for the tools
+  int row = 0;
+  QStringList::Iterator it1 = checkBoxTextList.begin();
+  QStringList::Iterator it2 = checkBoxWhatsThisList.begin();
+  while ( it1 != checkBoxTextList.end() )
+  {
+    QCheckBox *box = new QCheckBox( *it1, this );
+    if ( it2 != checkBoxWhatsThisList.end() )
+    {
+      QWhatsThis::add( box, *it2 );
+      QToolTip::add( box, *it2 );
+      ++it2;
+    }
+    grid->addWidget( box, row++, 0 );
+    connect( box, SIGNAL(clicked()),
+             this, SLOT(processSelectionChange(void)) );
+    mProgramDict.insert( *it1, box );
+    ++it1;
+  }
+
+  // hint text
+  QLabel *introText = new QLabel( this );
+  introText->setText( i18n(
+    "<p>For these tools it is possible to let the "
+    "wizard create filter rules. KMail tried to find the tools "
+    "in the PATH of your system; the wizard does not allow you "
+    "to create rules for tools which were not found: "
+    "this is to keep your configuration consistent and "
+    "to minimize the risk of unpredicted behavior.</p>"
+    ) );
+  grid->addWidget( introText, row++, 0 );
+}
+
+
+bool ASWizProgramsPage::isProgramSelected( const QString &visibleName )
+{
+  if ( mProgramDict[visibleName] )
+    return mProgramDict[visibleName]->isChecked();
+  else
+    return false;
+}
+
+
+void ASWizProgramsPage::setProgramAsFound( const QString &visibleName, bool found )
+{
+  QCheckBox * box = mProgramDict[visibleName];
+  if ( box )
+  {
+    QString foundText( i18n("(found on this system)") );
+    QString notFoundText( i18n("(not found on this system)") );
+    QString labelText = visibleName;
+    labelText += " ";
+    if ( found )
+      labelText += foundText;
+    else
+    {
+      labelText += notFoundText;
+      box->setEnabled( false );
+    }
+    box->setText( labelText );
+  }
+}
+
+
+void ASWizProgramsPage::processSelectionChange()
+{
+  emit selectionChanged();
+}
+
+//---------------------------------------------------------------------------
+ASWizSpamRulesPage::ASWizSpamRulesPage( QWidget * parent, const char * name,
+                                        KMFolderTree * mainFolderTree )
+  : QWidget( parent, name )
+{
+  QGridLayout *grid = new QGridLayout( this, 5, 1, KDialog::marginHint(),
+                                       KDialog::spacingHint() );
+
+  mClassifyRules = new QCheckBox( i18n("Classify messages manually as spam"), this );
+  QWhatsThis::add( mClassifyRules,
+      i18n( "Sometimes messages are classified wrongly or even not at all; "
+            "the latter might be by intention, because you perhaps filter "
+            "out messages from mailing lists before you let the anti-spam "
+            "tools classify the rest of the messages. You can correct these "
+            "wrong or missing classifications manually by using the "
+            "appropriate toolbar buttons which trigger special filters "
+            "created by this wizard." ) );
+  grid->addWidget( mClassifyRules, 0, 0 );
+
+  mPipeRules = new QCheckBox( i18n("Classify messages using the anti-spam tools"), this );
+  QWhatsThis::add( mPipeRules,
+      i18n( "Let the anti-spam tools classify your messages. The wizard "
+            "will create appropriate filters. The messages are usually "
+            "marked by the tools so that following filters can react "
+            "on this and, for example, move spam messages to a special folder.") );
+  grid->addWidget( mPipeRules, 1, 0 );
+
+  mMoveRules = new QCheckBox( i18n("Move detected spam messages to the selected folder"), this );
+  QWhatsThis::add( mMoveRules,
+      i18n( "A filter to detect messages classified as spam and to move "
+            "those messages into a predefined folder is created. The "
+            "default folder is the trash folder, but you may change that "
+            "in the folder view.") );
+  grid->addWidget( mMoveRules, 2, 0 );
+
+  mMarkRules = new QCheckBox( i18n("Additionally, mark detected spam messages as read"), this );
+  mMarkRules->setEnabled( false );
+  QWhatsThis::add( mMarkRules,
+      i18n( "Mark messages which have been classified as "
+            "spam as read, as well as moving them to the selected "
+            "folder.") );
+  grid->addWidget( mMarkRules, 3, 0 );
+
+  QString s = "trash";
+  mFolderTree = new SimpleFolderTree( this, mainFolderTree, s, true );
+  grid->addWidget( mFolderTree, 4, 0 );
+
+  connect( mPipeRules, SIGNAL(clicked()),
+            this, SLOT(processSelectionChange(void)) );
+  connect( mClassifyRules, SIGNAL(clicked()),
+            this, SLOT(processSelectionChange(void)) );
+  connect( mMoveRules, SIGNAL(clicked()),
+            this, SLOT(processSelectionChange(void)) );
+  connect( mMarkRules, SIGNAL(clicked()),
+            this, SLOT(processSelectionChange(void)) );
+  connect( mMoveRules, SIGNAL( toggled( bool ) ),
+           mMarkRules, SLOT( setEnabled( bool ) ) );
+}
+
+bool ASWizSpamRulesPage::pipeRulesSelected() const
+{
+  return mPipeRules->isChecked();
+}
+
+
+bool ASWizSpamRulesPage::classifyRulesSelected() const
+{
+  return mClassifyRules->isChecked();
+}
+
+
+bool ASWizSpamRulesPage::moveRulesSelected() const
+{
+  return mMoveRules->isChecked();
+}
+
+bool ASWizSpamRulesPage::markReadRulesSelected() const
+{
+  return mMarkRules->isChecked();
+}
+
+
+QString ASWizSpamRulesPage::selectedFolderName() const
+{
+  QString name = "trash";
+  if ( mFolderTree->folder() )
+    name = mFolderTree->folder()->idString();
+  return name;
+}
+
+void ASWizSpamRulesPage::processSelectionChange()
+{
+  emit selectionChanged();
+}
+
+
+void ASWizSpamRulesPage::allowClassification( bool enabled )
+{
+  if ( enabled )
+    mClassifyRules->setEnabled( true );
+  else
+  {
+    mClassifyRules->setChecked( false );
+    mClassifyRules->setEnabled( false );
+  }
+}
+
+//---------------------------------------------------------------------------
+ASWizVirusRulesPage::ASWizVirusRulesPage( QWidget * parent, const char * name,
+                                  KMFolderTree * mainFolderTree )
+  : QWidget( parent, name )
+{
+  QGridLayout *grid = new QGridLayout( this, 5, 1, KDialog::marginHint(),
+                                       KDialog::spacingHint() );
+
+  mPipeRules = new QCheckBox( i18n("Classify messages using the anti-virus tools"), this );
+  QWhatsThis::add( mPipeRules,
+      i18n( "Let the anti-virus tools classify your messages. The wizard "
+            "will create appropriate filters. The messages are usually "
+            "marked by the tools so that following filters can react "
+            "on this and, for example, move virus messages to a special folder.") );
+  grid->addWidget( mPipeRules, 0, 0 );
+
+  mMoveRules = new QCheckBox( i18n("Move detected viral messages to the selected folder"), this );
+  QWhatsThis::add( mMoveRules,
+      i18n( "A filter to detect messages classified as virus-infected and to move "
+            "those messages into a predefined folder is created. The "
+            "default folder is the trash folder, but you may change that "
+            "in the folder view.") );
+  grid->addWidget( mMoveRules, 1, 0 );
+
+  mMarkRules = new QCheckBox( i18n("Additionally, mark detected viral messages as read"), this );
+  mMarkRules->setEnabled( false );
+  QWhatsThis::add( mMarkRules,
+      i18n( "Mark messages which have been classified as "
+            "virus-infected as read, as well as moving them "
+            "to the selected folder.") );
+  grid->addWidget( mMarkRules, 2, 0 );
+
+  QString s = "trash";
+  mFolderTree = new SimpleFolderTree( this, mainFolderTree, s, true );
+  grid->addWidget( mFolderTree, 3, 0 );
+
+  connect( mPipeRules, SIGNAL(clicked()),
+            this, SLOT(processSelectionChange(void)) );
+  connect( mMoveRules, SIGNAL(clicked()),
+            this, SLOT(processSelectionChange(void)) );
+  connect( mMarkRules, SIGNAL(clicked()),
+            this, SLOT(processSelectionChange(void)) );
+  connect( mMoveRules, SIGNAL( toggled( bool ) ),
+           mMarkRules, SLOT( setEnabled( bool ) ) );
+}
+
+bool ASWizVirusRulesPage::pipeRulesSelected() const
+{
+  return mPipeRules->isChecked();
+}
+
+
+bool ASWizVirusRulesPage::moveRulesSelected() const
+{
+  return mMoveRules->isChecked();
+}
+
+bool ASWizVirusRulesPage::markReadRulesSelected() const
+{
+  return mMarkRules->isChecked();
+}
+
+
+QString ASWizVirusRulesPage::selectedFolderName() const
+{
+  QString name = "trash";
+  if ( mFolderTree->folder() )
+    name = mFolderTree->folder()->idString();
+  return name;
+}
+
+void ASWizVirusRulesPage::processSelectionChange()
+{
+  emit selectionChanged();
+}
+
+#include "antispamwizard.moc"
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ kdepim/kmail/kmail.antispamrc	2004-06-08 13:57:46.000000000 +0200
@@ -0,0 +1,114 @@
+[General]
+tools=7
+
+[Spamtool #1]
+Ident=spamassassin
+Version=1
+VisibleName=&Spamassassin
+Executable=spamassassin -V
+URL=http://spamassassin.org
+PipeFilterName=SpamAssassin Check
+PipeCmdDetect=spamassassin -L
+ExecCmdSpam=sa-learn -L --spam --no-rebuild --single
+ExecCmdHam=sa-learn -L --ham --no-rebuild --single
+DetectionHeader=X-Spam-Flag
+DetectionPattern=yes
+UseRegExp=0
+SupportsBayes=1
+Type=spam
+
+[Spamtool #2]
+Ident=bogofilter
+Version=1
+VisibleName=&Bogofilter
+Executable=bogofilter -V
+URL=http://bogofilter.sourceforge.net
+PipeFilterName=Bogofilter Check
+PipeCmdDetect=bogofilter -p -e -u
+ExecCmdSpam=bogofilter -N -s
+ExecCmdHam=bogofilter -S -n
+DetectionHeader=X-Bogosity
+DetectionPattern=yes
+UseRegExp=0
+SupportsBayes=1
+Type=spam
+
+[Spamtool #3]
+Ident=annoyance-filter
+Version=1
+VisibleName=&Annoyance-Filter
+Executable=$HOME/.annoyance-filter/annoyance-filter --version
+URL=http://www.fourmilab.ch/annoyance-filter
+PipeFilterName=Annoyance-Filter Check
+PipeCmdDetect=$HOME/.annoyance-filter/annoyance-filter --fread $HOME/.annoyance-filter/FastDict.bin --phrasemin 1 --phrasemax 2 --transcript - --test -
+ExecCmdSpam=$HOME/.annoyance-filter/annoyance-filter --read $HOME/.annoyance-filter/Dict.bin --phrasemin 1 --phrasemax 2 --junk - --prune --write $HOME/.annoyance-filter/Dict.bin --fwrite $HOME/.annoyance-filter/FastDict.bin
+ExecCmdHam=$HOME/.annoyance-filter/annoyance-filter --read $HOME/.annoyance-filter/Dict.bin --phrasemin 1 --phrasemax 2 --mail - --prune --write $HOME/.annoyance-filter/Dict.bin --fwrite $HOME/.annoyance-filter/FastDict.bin
+DetectionHeader=X-Annoyance-Filter-Classification
+DetectionPattern=Junk
+UseRegExp=0
+SupportsBayes=1
+Type=spam
+
+[Spamtool #4]
+Ident=ClamAV
+Version=1
+VisibleName=&Clam Anti-Virus
+Executable=clamscan -V
+URL=http://clamav.sf.net
+PipeFilterName=Clam Anti-Virus Check
+PipeCmdDetect=kmail_clamav.sh
+ExecCmdSpam=
+ExecCmdHam=
+DetectionHeader=X-Virus-Flag
+DetectionPattern=yes
+UseRegExp=0
+SupportsBayes=0
+Type=av
+
+[Spamtool #5]
+Ident=SophosAV
+Version=1
+VisibleName=Sophos Anti&Virus
+Executable=sweep -v
+URL=http://www.sophos.com
+PipeFilterName=Sophos Anti-Virus Check
+PipeCmdDetect=kmail_sav.sh
+ExecCmdSpam=
+ExecCmdHam=
+DetectionHeader=X-Virus-Flag
+DetectionPattern=yes
+UseRegExp=0
+SupportsBayes=0
+Type=av
+
+[Spamtool #6]
+Ident=FProt
+Version=1
+VisibleName=&F-Prot AntiVirus
+Executable=f-prot -verno
+URL=http://www.f-prot.com
+PipeFilterName=F-Prot Anti-Virus Check
+PipeCmdDetect=kmail_fprot.sh
+ExecCmdSpam=
+ExecCmdHam=
+DetectionHeader=X-Virus-Flag
+DetectionPattern=yes
+UseRegExp=0
+SupportsBayes=0
+Type=av
+
+[Spamtool #7]
+Ident=AntiVir
+Version=1
+VisibleName=H+BEDV AntiVir
+Executable= antivir --version
+URL=http://www.antivir.de
+PipeFilterName=AntiVir Anti-Virus Check
+PipeCmdDetect=kmail_antivir.sh
+ExecCmdSpam=
+ExecCmdHam=
+DetectionHeader=X-Virus-Flag
+DetectionPattern=yes
+UseRegExp=0
+SupportsBayes=0
+Type=av
--- kdepim-3.2.3/kmail/kmfolderseldlg.cpp	2004-06-17 10:40:43.329748572 +0200
+++ kdepim-3.2.3/kmail/kmfolderseldlg.cpp--	2004-06-17 10:37:51.917954856 +0200
@@ -54,7 +54,7 @@ FolderItem::FolderItem( QListViewItem * 
 
 //-----------------------------------------------------------------------------
 SimpleFolderTree::SimpleFolderTree( QWidget * parent, 
+				    KMFolderTree * folderTree, QString & preSelection , bool mustBeReadWrite)
-                  KMFolderTree * folderTree, QString & preSelection )
   : KListView( parent )
 {
   assert( folderTree );
@@ -119,7 +119,7 @@ SimpleFolderTree::SimpleFolderTree( QWid
     item->setText( columnIdx, fti->text( 0 ) );
     // Make items without folders and top level items unselectable
     // (i.e. root item Local Folders and IMAP accounts)
+    if ( !fti->folder() || depth == 0 || ( mustBeReadWrite && fti->folder()->isReadOnly() ))
-    if ( !fti->folder() || depth == 0 )
       item->setSelectable( false );
     else {
       item->setFolder( fti->folder() );
@@ -154,14 +154,14 @@ const KMFolder * SimpleFolderTree::folde
 QString KMFolderSelDlg::oldSelection;
 
 //-----------------------------------------------------------------------------
+KMFolderSelDlg::KMFolderSelDlg( KMMainWidget * parent, const QString& caption, bool mustBeReadWrite )
-KMFolderSelDlg::KMFolderSelDlg( KMMainWidget * parent, const QString& caption )
   : KDialogBase( parent, "folder dialog", true, caption,
                  Ok|Cancel, Ok, true ) // mainwin as parent, modal
 {
   KMFolderTree * ft = parent->folderTree();
   assert( ft );
 
+  mTreeView = new KMail::SimpleFolderTree( makeVBoxMainWidget(), ft, oldSelection,mustBeReadWrite );
-  mTreeView = new KMail::SimpleFolderTree( makeVBoxMainWidget(), ft, oldSelection );
   mTreeView->setFocus();
   connect( mTreeView, SIGNAL( doubleClicked( QListViewItem*, const QPoint&, int ) ), 
            this, SLOT( slotSelect() ) );
--- kdepim-3.2.3/kmail/kmfolderseldlg.h	2004-06-17 10:41:21.465770507 +0200
+++ kdepim-3.2.3/kmail/kmfolderseldlg.h--	2004-06-17 10:37:56.718196334 +0200
@@ -19,7 +19,7 @@ namespace KMail {
   class SimpleFolderTree : public KListView
   {
     public:
+      SimpleFolderTree( QWidget * parent, KMFolderTree * folderTree, QString & preSelection, bool mustBeReadWrite );
-      SimpleFolderTree( QWidget * parent, KMFolderTree * folderTree, QString & preSelection );
       
       const KMFolder * folder() const;
   };
@@ -34,7 +34,7 @@ class KMFolderSelDlg: public KDialogBase
 public:
   /** Constructor. @p parent @em must be a @ref KMMainWin, because we
       need it's foldertree. */
+  KMFolderSelDlg( KMMainWidget * parent, const QString& caption, bool mustBeReadWrite );
-  KMFolderSelDlg( KMMainWidget * parent, const QString& caption );
   virtual ~KMFolderSelDlg();
 
   /** Returns selected folder */
--- kdepim-3.2.3/kmail/kmmainwidget.cpp--	2004-06-17 10:42:09.953333793 +0200
+++ kdepim-3.2.3/kmail/kmmainwidget.cpp	2004-06-17 10:43:11.875595424 +0200
@@ -1329,7 +1329,7 @@ void KMMainWidget::slotToggleTotalColumn
 //-----------------------------------------------------------------------------
 void KMMainWidget::slotMoveMsg()
 {
-  KMFolderSelDlg dlg(this,i18n("Move Message to Folder"));
+  KMFolderSelDlg dlg(this,i18n("Move Message to Folder"),true);
   KMFolder* dest;
 
   if (!dlg.exec()) return;
@@ -1380,7 +1380,7 @@ void KMMainWidget::slotEditVacation()
 //-----------------------------------------------------------------------------
 void KMMainWidget::slotCopyMsg()
 {
-  KMFolderSelDlg dlg(this,i18n("Copy Message to Folder"));
+  KMFolderSelDlg dlg(this,i18n("Copy Message to Folder"),true);
   KMFolder* dest;
 
   if (!dlg.exec()) return;
--- kdepim/kmail/kmail_part.cpp--	2004-06-17 11:47:33.861908672 +0200
+++ kdepim/kmail/kmail_part.cpp	2004-06-17 11:56:53.340629869 +0200
@@ -43,6 +43,7 @@
 
 #include <kapplication.h>
 #include <kparts/genericfactory.h>
+#include <kparts/mainwindow.h>
 #include <knotifyclient.h>
 #include <dcopclient.h>
 #include <kiconloader.h>
@@ -144,6 +145,8 @@ KMailPart::KMailPart(QWidget *parentWidg
            this, SLOT(slotIconChanged(KMFolderTreeItem*)) );
   connect( mainWidget->folderTree(), SIGNAL(nameChanged(KMFolderTreeItem*)),
            this, SLOT(slotNameChanged(KMFolderTreeItem*)) );
+  connect( mainWidget, SIGNAL(modifiedToolBarConfig()),
+           this, SLOT(slotToolbarChanged()) );
   connect( this, SIGNAL(textChanged(const QString&)), ie, SIGNAL(textChanged(const QString&)) );
   connect( this, SIGNAL(iconChanged(const QPixmap&)), ie, SIGNAL(iconChanged(const QPixmap&)) );
 
@@ -204,6 +207,36 @@ void KMailPart::slotNameChanged( KMFolde
   emit textChanged( fti->folder()->label() );
 }
 
+// The sole purpose of the following class is to publicize the protected
+// method KParts::MainWindow::createGUI() since we need to call it so that
+// the toolbar is redrawn when necessary.
+// It can be removed once createGUI() has been made public _and_ we don't
+// longer rely on kdelibs 3.2.
+class KPartsMainWindowWithPublicizedCreateGUI : public KParts::MainWindow
+{
+public:
+  void createGUIPublic( KParts::Part *part ) {
+    createGUI( part );
+  }
+};
+
+void KMailPart::slotToolbarChanged()
+{
+  kdDebug(5006) << "KMailPart - need to reload the toolbar" << endl;
+  reloadXML();
+  KParts::MainWindow *win =
+    dynamic_cast<KParts::MainWindow*>( mainWidget->topLevelWidget() );
+  if ( win ) {
+    ( static_cast<KPartsMainWindowWithPublicizedCreateGUI*>( win ) )
+      ->createGUIPublic( this );
+  }
+  else {
+    kdDebug(5006) << "KMailPart::slotToolbarChanged() - "
+                  << "dynamic_cast<KPart::MainWindow*>( toplevelWidget() ) "
+                  << "failed" << endl;
+  }
+}
+
 
 void KMailPart::guiActivateEvent(KParts::GUIActivateEvent *e)
 {
--- kdepim/kmail/kmail_part.h--	2004-06-17 11:47:37.646667879 +0200
+++ kdepim/kmail/kmail_part.h	2004-06-17 11:53:53.105141207 +0200
@@ -67,7 +67,7 @@ class KMailPart: public KParts::ReadOnly
     void exportFolder( KMFolder* folder );
     void slotIconChanged( KMFolderTreeItem *fti );
     void slotNameChanged( KMFolderTreeItem *fti );
-
+    void slotToolbarChanged();
   signals:
     void textChanged( const QString& );
     void iconChanged( const QPixmap& );
--- kdepim/kmail/kmmainwidget.cpp--	2004-06-17 11:48:35.272837799 +0200
+++ kdepim/kmail/kmmainwidget.cpp	2004-06-17 11:51:55.001465499 +0200
@@ -3271,6 +3271,7 @@ void KMMainWidget::slotAntiSpamWizard()
 {
   AntiSpamWizard wiz( this, folderTree(), actionCollection() );
   wiz.exec();
+  emit modifiedToolBarConfig();
 }
 
 //-----------------------------------------------------------------------------
--- kdepim/kmail/kmmainwidget.h--	2004-06-17 11:49:15.853175195 +0200
+++ kdepim/kmail/kmmainwidget.h	2004-06-17 11:52:16.533944974 +0200
@@ -176,6 +176,7 @@ public slots:
 signals:
   void messagesTransfered(bool);
   void captionChangeRequest( const QString & caption );
+  void modifiedToolBarConfig( void );
 
 protected:
   void setupActions();