kmail

kmfilter.cpp

Go to the documentation of this file.
00001 /* -*- mode: C++; c-file-style: "gnu" -*-
00002  * kmail: KDE mail client
00003  * Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018  *
00019  */
00020 #ifdef HAVE_CONFIG_H
00021 #include <config.h>
00022 #endif
00023 
00024 #include "kmfilter.h"
00025 #include "kmkernel.h"
00026 #include "accountmanager.h"
00027 using KMail::AccountManager;
00028 #include "kmacctimap.h"
00029 #include "kmfilteraction.h"
00030 #include "kmglobal.h"
00031 #include "filterlog.h"
00032 using KMail::FilterLog;
00033 
00034 #include <klocale.h>
00035 #include <kmessagebox.h>
00036 #include <kdebug.h>
00037 #include <kconfig.h>
00038 
00039 #include <assert.h>
00040 
00041 
00042 KMFilter::KMFilter( KConfig* aConfig, bool popFilter )
00043   : bPopFilter(popFilter)
00044 {
00045   if (!bPopFilter)
00046     mActions.setAutoDelete( true );
00047 
00048   if ( aConfig )
00049     readConfig( aConfig );
00050   else if ( bPopFilter )
00051     mAction = Down;
00052   else {
00053     bApplyOnInbound = true;
00054     bApplyOnOutbound = false;
00055     bApplyOnExplicit = true;
00056     bStopProcessingHere = true;
00057     bConfigureShortcut = false;
00058     bConfigureToolbar = false;
00059     bAutoNaming = true;
00060     mApplicability = All;
00061   }
00062 }
00063 
00064 
00065 KMFilter::KMFilter( const KMFilter & aFilter )
00066 {
00067   bPopFilter = aFilter.isPopFilter();
00068 
00069   if ( !bPopFilter )
00070     mActions.setAutoDelete( true );
00071 
00072   mPattern = aFilter.mPattern;
00073 
00074   if ( bPopFilter ){
00075     mAction = aFilter.mAction;
00076   } else {
00077     bApplyOnInbound = aFilter.applyOnInbound();
00078     bApplyOnOutbound = aFilter.applyOnOutbound();
00079     bApplyOnExplicit = aFilter.applyOnExplicit();
00080     bStopProcessingHere = aFilter.stopProcessingHere();
00081     bConfigureShortcut = aFilter.configureShortcut();
00082     bConfigureToolbar = aFilter.configureToolbar();
00083     mApplicability = aFilter.applicability();
00084     mIcon = aFilter.icon();
00085     mShortcut = aFilter.shortcut();
00086 
00087     QPtrListIterator<KMFilterAction> it( aFilter.mActions );
00088     for ( it.toFirst() ; it.current() ; ++it ) {
00089       KMFilterActionDesc *desc = (*kmkernel->filterActionDict())[ (*it)->name() ];
00090       if ( desc ) {
00091         KMFilterAction *f = desc->create();
00092         if ( f ) {
00093           f->argsFromString( (*it)->argsAsString() );
00094           mActions.append( f );
00095         }
00096       }
00097     }
00098 
00099     mAccounts.clear();
00100     QValueListConstIterator<int> it2;
00101     for ( it2 = aFilter.mAccounts.begin() ; it2 != aFilter.mAccounts.end() ; ++it2 )
00102       mAccounts.append( *it2 );
00103   }
00104 }
00105 
00106 // only for !bPopFilter
00107 KMFilter::ReturnCode KMFilter::execActions( KMMessage* msg, bool& stopIt ) const
00108 {
00109   ReturnCode status = NoResult;
00110 
00111   QPtrListIterator<KMFilterAction> it( mActions );
00112   for ( it.toFirst() ; it.current() ; ++it ) {
00113 
00114     if ( FilterLog::instance()->isLogging() ) {
00115       QString logText( i18n( "<b>Applying filter action:</b> %1" )
00116                        .arg( (*it)->displayString() ) );
00117       FilterLog::instance()->add( logText, FilterLog::appliedAction );
00118     }
00119 
00120     KMFilterAction::ReturnCode result = (*it)->process( msg );
00121 
00122     switch ( result ) {
00123     case KMFilterAction::CriticalError:
00124       if ( FilterLog::instance()->isLogging() ) {
00125         QString logText = QString( "<font color=#FF0000>%1</font>" )
00126           .arg( i18n( "A critical error occurred. Processing stops here." ) );
00127         FilterLog::instance()->add( logText, FilterLog::appliedAction );
00128       }
00129       // in case it's a critical error: return immediately!
00130       return CriticalError;
00131     case KMFilterAction::ErrorButGoOn:
00132       if ( FilterLog::instance()->isLogging() ) {
00133         QString logText = QString( "<font color=#FF0000>%1</font>" )
00134           .arg( i18n( "A problem was found while applying this action." ) );
00135         FilterLog::instance()->add( logText, FilterLog::appliedAction );
00136       }
00137     default:
00138       break;
00139     }
00140   }
00141 
00142   if ( status == NoResult ) // No filters matched, keep copy of message
00143     status = GoOn;
00144 
00145   stopIt = stopProcessingHere();
00146 
00147   return status;
00148 }
00149 
00150 bool KMFilter::requiresBody( KMMsgBase* msg )
00151 {
00152   if (pattern() && pattern()->requiresBody())
00153     return true; // no pattern means always matches?
00154   QPtrListIterator<KMFilterAction> it( *actions() );
00155   for ( it.toFirst() ; it.current() ; ++it )
00156     if ((*it)->requiresBody( msg ))
00157       return true;
00158   return false;
00159 }
00160 
00162 // only for bPopFilter
00163 void KMFilter::setAction(const KMPopFilterAction aAction)
00164 {
00165   mAction = aAction;
00166 }
00167 
00168 // only for bPopFilter
00169 KMPopFilterAction KMFilter::action()
00170 {
00171   return mAction;
00172 }
00173 
00174 // only for !bPopFilter
00175 bool KMFilter::folderRemoved( KMFolder* aFolder, KMFolder* aNewFolder )
00176 {
00177   bool rem = false;
00178 
00179   QPtrListIterator<KMFilterAction> it( mActions );
00180   for ( it.toFirst() ; it.current() ; ++it )
00181     if ( (*it)->folderRemoved( aFolder, aNewFolder ) )
00182       rem = true;
00183 
00184   return rem;
00185 }
00186 
00187 void KMFilter::setApplyOnAccount( uint id, bool aApply )
00188 {
00189   if (aApply && !mAccounts.contains( id )) {
00190     mAccounts.append( id );
00191   } else if (!aApply && mAccounts.contains( id )) {
00192     mAccounts.remove( id );
00193   }
00194 }
00195 
00196 bool KMFilter::applyOnAccount( uint id ) const
00197 {
00198   if ( applicability() == All )
00199     return true;
00200   if ( applicability() == ButImap ) {
00201     KMAccount *account = kmkernel->acctMgr()->find( id );
00202     bool result =  account && !dynamic_cast<KMAcctImap*>(account);
00203     return result;
00204   }
00205   if ( applicability() == Checked )
00206     return mAccounts.contains( id );
00207 
00208   return false;
00209 }
00210 
00211 
00212 //-----------------------------------------------------------------------------
00213 void KMFilter::readConfig(KConfig* config)
00214 {
00215   // MKSearchPattern::readConfig ensures
00216   // that the pattern is purified.
00217   mPattern.readConfig(config);
00218 
00219   if (bPopFilter) {
00220     // get the action description...
00221     QString action = config->readEntry( "action" );
00222     if ( action == "down" )
00223       mAction = Down;
00224     else if ( action == "later" )
00225       mAction = Later;
00226     else if ( action == "delete" )
00227       mAction = Delete;
00228     else
00229       mAction = NoAction;
00230   }
00231   else {
00232     QStringList sets = config->readListEntry("apply-on");
00233     if ( sets.isEmpty() && !config->hasKey("apply-on") ) {
00234       bApplyOnOutbound = false;
00235       bApplyOnInbound = true;
00236       bApplyOnExplicit = true;
00237       mApplicability = ButImap;
00238     } else {
00239       bApplyOnInbound = bool(sets.contains("check-mail"));
00240       bApplyOnOutbound = bool(sets.contains("send-mail"));
00241       bApplyOnExplicit = bool(sets.contains("manual-filtering"));
00242       mApplicability = (AccountType)config->readNumEntry( "Applicability", ButImap );
00243     }
00244 
00245     bStopProcessingHere = config->readBoolEntry("StopProcessingHere", true);
00246     bConfigureShortcut = config->readBoolEntry("ConfigureShortcut", false);
00247     QString shortcut( config->readEntry( "Shortcut" ) );
00248     if ( !shortcut.isEmpty() ) {
00249       KShortcut sc( shortcut );
00250       setShortcut( sc );
00251     }
00252     bConfigureToolbar = config->readBoolEntry("ConfigureToolbar", false);
00253     bConfigureToolbar = bConfigureToolbar && bConfigureShortcut;
00254     mIcon = config->readEntry( "Icon", "gear" );
00255     bAutoNaming = config->readBoolEntry("AutomaticName", false);
00256 
00257     int i, numActions;
00258     QString actName, argsName;
00259 
00260     mActions.clear();
00261 
00262     numActions = config->readNumEntry("actions",0);
00263     if (numActions > FILTER_MAX_ACTIONS) {
00264       numActions = FILTER_MAX_ACTIONS ;
00265       KMessageBox::information( 0, i18n("<qt>Too many filter actions in filter rule <b>%1</b>.</qt>").arg( mPattern.name() ) );
00266     }
00267 
00268     for ( i=0 ; i < numActions ; i++ ) {
00269       actName.sprintf("action-name-%d", i);
00270       argsName.sprintf("action-args-%d", i);
00271       // get the action description...
00272       KMFilterActionDesc *desc = (*kmkernel->filterActionDict())[ config->readEntry( actName ) ];
00273       if ( desc ) {
00274         //...create an instance...
00275         KMFilterAction *fa = desc->create();
00276         if ( fa ) {
00277           //...load it with it's parameter...
00278           fa->argsFromString( config->readEntry( argsName ) );
00279           //...check if it's emoty and...
00280           if ( !fa->isEmpty() )
00281             //...append it if it's not and...
00282             mActions.append( fa );
00283           else
00284             //...delete is else.
00285             delete fa;
00286         }
00287       } else
00288         KMessageBox::information( 0 /* app-global modal dialog box */,
00289                                   i18n("<qt>Unknown filter action <b>%1</b><br>in filter rule <b>%2</b>.<br>Ignoring it.</qt>")
00290                                        .arg( config->readEntry( actName ) ).arg( mPattern.name() ) );
00291     }
00292 
00293     mAccounts = config->readIntListEntry( "accounts-set" );
00294   }
00295 }
00296 
00297 
00298 void KMFilter::writeConfig(KConfig* config) const
00299 {
00300   mPattern.writeConfig(config);
00301 
00302   if (bPopFilter) {
00303     switch ( mAction ) {
00304     case Down:
00305       config->writeEntry( "action", "down" );
00306       break;
00307     case Later:
00308       config->writeEntry( "action", "later" );
00309       break;
00310     case Delete:
00311       config->writeEntry( "action", "delete" );
00312       break;
00313     default:
00314       config->writeEntry( "action", "" );
00315     }
00316   } else {
00317     QStringList sets;
00318     if ( bApplyOnInbound )
00319       sets.append( "check-mail" );
00320     if ( bApplyOnOutbound )
00321       sets.append( "send-mail" );
00322     if ( bApplyOnExplicit )
00323       sets.append( "manual-filtering" );
00324     config->writeEntry( "apply-on", sets );
00325 
00326     config->writeEntry( "StopProcessingHere", bStopProcessingHere );
00327     config->writeEntry( "ConfigureShortcut", bConfigureShortcut );
00328     if ( !mShortcut.isNull() )
00329       config->writeEntry( "Shortcut", mShortcut.toString() );
00330     config->writeEntry( "ConfigureToolbar", bConfigureToolbar );
00331     config->writeEntry( "Icon", mIcon );
00332     config->writeEntry( "AutomaticName", bAutoNaming );
00333     config->writeEntry( "Applicability", mApplicability );
00334 
00335     QString key;
00336     int i;
00337 
00338     QPtrListIterator<KMFilterAction> it( mActions );
00339     for ( i=0, it.toFirst() ; it.current() ; ++it, ++i ) {
00340       config->writeEntry( key.sprintf("action-name-%d", i),
00341                           (*it)->name() );
00342       config->writeEntry( key.sprintf("action-args-%d", i),
00343                           (*it)->argsAsString() );
00344     }
00345     config->writeEntry( "actions", i );
00346     config->writeEntry( "accounts-set", mAccounts );
00347   }
00348 }
00349 
00350 void KMFilter::purify()
00351 {
00352   mPattern.purify();
00353 
00354   if (!bPopFilter) {
00355     QPtrListIterator<KMFilterAction> it( mActions );
00356     it.toLast();
00357     while ( it.current() )
00358       if ( (*it)->isEmpty() )
00359         mActions.remove ( (*it) );
00360       else
00361         --it;
00362 
00363     // Remove invalid accounts from mAccounts - just to be tidy
00364     QValueListIterator<int> it2 = mAccounts.begin();
00365     while ( it2 != mAccounts.end() ) {
00366       if ( !kmkernel->acctMgr()->find( *it2 ) )
00367          it2 = mAccounts.remove( it2 );
00368       else
00369          ++it2;
00370     }
00371   }
00372 }
00373 
00374 bool KMFilter::isEmpty() const
00375 {
00376   if (bPopFilter)
00377     return mPattern.isEmpty();
00378   else
00379     return mPattern.isEmpty() && mActions.isEmpty() && mAccounts.isEmpty();
00380 }
00381 
00382 #ifndef NDEBUG
00383 const QString KMFilter::asString() const
00384 {
00385   QString result;
00386 
00387   result += mPattern.asString();
00388 
00389   if (bPopFilter){
00390     result += "    action: ";
00391     result += mAction;
00392     result += "\n";
00393   }
00394   else {
00395     QPtrListIterator<KMFilterAction> it( mActions );
00396     for ( it.toFirst() ; it.current() ; ++it ) {
00397       result += "    action: ";
00398       result += (*it)->label();
00399       result += " ";
00400       result += (*it)->argsAsString();
00401       result += "\n";
00402     }
00403     result += "This filter belongs to the following sets:";
00404     if ( bApplyOnInbound )
00405       result += " Inbound";
00406     if ( bApplyOnOutbound )
00407       result += " Outbound";
00408     if ( bApplyOnExplicit )
00409       result += " Explicit";
00410     result += "\n";
00411     if ( bApplyOnInbound && mApplicability == All ) {
00412       result += "This filter applies to all accounts.\n";
00413     } else if ( bApplyOnInbound && mApplicability == ButImap ) {
00414       result += "This filter applies to all but online IMAP accounts.\n";
00415     } else if ( bApplyOnInbound ) {
00416       QValueListConstIterator<int> it2;
00417       result += "This filter applies to the following accounts:";
00418       if ( mAccounts.isEmpty() )
00419         result += " None";
00420       else for ( it2 = mAccounts.begin() ; it2 != mAccounts.end() ; ++it2 )
00421         if ( kmkernel->acctMgr()->find( *it2 ) )
00422           result += " " + kmkernel->acctMgr()->find( *it2 )->name();
00423       result += "\n";
00424     }
00425     if ( bStopProcessingHere )
00426       result += "If it matches, processing stops at this filter.\n";
00427   }
00428   return result;
00429 }
00430 #endif