kmail

kmfilter.cpp
1 /*
2  * kmail: KDE mail client
3  * Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include "kmfilter.h"
25 #include "kmkernel.h"
26 #include "accountmanager.h"
28 #include "kmacctimap.h"
29 #include "kmfilteraction.h"
30 #include "kmglobal.h"
31 #include "filterlog.h"
32 using KMail::FilterLog;
33 
34 #include <tdelocale.h>
35 #include <tdemessagebox.h>
36 #include <kdebug.h>
37 #include <tdeconfig.h>
38 
39 #include <assert.h>
40 
41 
42 KMFilter::KMFilter( TDEConfig* aConfig, bool popFilter )
43  : bPopFilter(popFilter)
44 {
45  if (!bPopFilter)
46  mActions.setAutoDelete( true );
47 
48  if ( aConfig )
49  readConfig( aConfig );
50  else if ( bPopFilter )
51  mAction = Down;
52  else {
53  bApplyOnInbound = true;
54  bApplyOnOutbound = false;
55  bApplyOnExplicit = true;
56  bStopProcessingHere = true;
57  bConfigureShortcut = false;
58  bConfigureToolbar = false;
59  bAutoNaming = true;
60  mApplicability = All;
61  }
62 }
63 
64 
65 KMFilter::KMFilter( const KMFilter & aFilter )
66 {
67  bPopFilter = aFilter.isPopFilter();
68 
69  if ( !bPopFilter )
70  mActions.setAutoDelete( true );
71 
72  mPattern = aFilter.mPattern;
73 
74  if ( bPopFilter ){
75  mAction = aFilter.mAction;
76  } else {
77  bApplyOnInbound = aFilter.applyOnInbound();
78  bApplyOnOutbound = aFilter.applyOnOutbound();
79  bApplyOnExplicit = aFilter.applyOnExplicit();
80  bStopProcessingHere = aFilter.stopProcessingHere();
81  bConfigureShortcut = aFilter.configureShortcut();
82  bConfigureToolbar = aFilter.configureToolbar();
83  mApplicability = aFilter.applicability();
84  mIcon = aFilter.icon();
85  mShortcut = aFilter.shortcut();
86 
87  TQPtrListIterator<KMFilterAction> it( aFilter.mActions );
88  for ( it.toFirst() ; it.current() ; ++it ) {
89  KMFilterActionDesc *desc = (*kmkernel->filterActionDict())[ (*it)->name() ];
90  if ( desc ) {
91  KMFilterAction *f = desc->create();
92  if ( f ) {
93  f->argsFromString( (*it)->argsAsString() );
94  mActions.append( f );
95  }
96  }
97  }
98 
99  mAccounts.clear();
100  TQValueListConstIterator<int> it2;
101  for ( it2 = aFilter.mAccounts.begin() ; it2 != aFilter.mAccounts.end() ; ++it2 )
102  mAccounts.append( *it2 );
103  }
104 }
105 
106 // only for !bPopFilter
107 KMFilter::ReturnCode KMFilter::execActions( KMMessage* msg, bool& stopIt ) const
108 {
109  ReturnCode status = NoResult;
110 
111  TQPtrListIterator<KMFilterAction> it( mActions );
112  for ( it.toFirst() ; it.current() ; ++it ) {
113 
114  if ( FilterLog::instance()->isLogging() ) {
115  TQString logText( i18n( "<b>Applying filter action:</b> %1" )
116  .arg( (*it)->displayString() ) );
117  FilterLog::instance()->add( logText, FilterLog::appliedAction );
118  }
119 
120  KMFilterAction::ReturnCode result = (*it)->process( msg );
121 
122  switch ( result ) {
123  case KMFilterAction::CriticalError:
124  if ( FilterLog::instance()->isLogging() ) {
125  TQString logText = TQString( "<font color=#FF0000>%1</font>" )
126  .arg( i18n( "A critical error occurred. Processing stops here." ) );
127  FilterLog::instance()->add( logText, FilterLog::appliedAction );
128  }
129  // in case it's a critical error: return immediately!
130  return CriticalError;
131  case KMFilterAction::ErrorButGoOn:
132  if ( FilterLog::instance()->isLogging() ) {
133  TQString logText = TQString( "<font color=#FF0000>%1</font>" )
134  .arg( i18n( "A problem was found while applying this action." ) );
135  FilterLog::instance()->add( logText, FilterLog::appliedAction );
136  }
137  default:
138  break;
139  }
140  }
141 
142  if ( status == NoResult ) // No filters matched, keep copy of message
143  status = GoOn;
144 
145  stopIt = stopProcessingHere();
146 
147  return status;
148 }
149 
150 bool KMFilter::requiresBody( KMMsgBase* msg )
151 {
152  if (pattern() && pattern()->requiresBody())
153  return true; // no pattern means always matches?
154  TQPtrListIterator<KMFilterAction> it( *actions() );
155  for ( it.toFirst() ; it.current() ; ++it )
156  if ((*it)->requiresBody( msg ))
157  return true;
158  return false;
159 }
160 
162 // only for bPopFilter
163 void KMFilter::setAction(const KMPopFilterAction aAction)
164 {
165  mAction = aAction;
166 }
167 
168 // only for bPopFilter
169 KMPopFilterAction KMFilter::action()
170 {
171  return mAction;
172 }
173 
174 // only for !bPopFilter
175 bool KMFilter::folderRemoved( KMFolder* aFolder, KMFolder* aNewFolder )
176 {
177  bool rem = false;
178 
179  TQPtrListIterator<KMFilterAction> it( mActions );
180  for ( it.toFirst() ; it.current() ; ++it )
181  if ( (*it)->folderRemoved( aFolder, aNewFolder ) )
182  rem = true;
183 
184  return rem;
185 }
186 
187 void KMFilter::setApplyOnAccount( uint id, bool aApply )
188 {
189  if (aApply && !mAccounts.contains( id )) {
190  mAccounts.append( id );
191  } else if (!aApply && mAccounts.contains( id )) {
192  mAccounts.remove( id );
193  }
194 }
195 
196 bool KMFilter::applyOnAccount( uint id ) const
197 {
198  if ( applicability() == All )
199  return true;
200  if ( applicability() == ButImap ) {
201  KMAccount *account = kmkernel->acctMgr()->find( id );
202  bool result = account && !dynamic_cast<KMAcctImap*>(account);
203  return result;
204  }
205  if ( applicability() == Checked )
206  return mAccounts.contains( id );
207 
208  return false;
209 }
210 
211 
212 //-----------------------------------------------------------------------------
213 void KMFilter::readConfig(TDEConfig* config)
214 {
215  // MKSearchPattern::readConfig ensures
216  // that the pattern is purified.
217  mPattern.readConfig(config);
218 
219  if (bPopFilter) {
220  // get the action description...
221  TQString action = config->readEntry( "action" );
222  if ( action == "down" )
223  mAction = Down;
224  else if ( action == "later" )
225  mAction = Later;
226  else if ( action == "delete" )
227  mAction = Delete;
228  else
229  mAction = NoAction;
230  }
231  else {
232  TQStringList sets = config->readListEntry("apply-on");
233  if ( sets.isEmpty() && !config->hasKey("apply-on") ) {
234  bApplyOnOutbound = false;
235  bApplyOnInbound = true;
236  bApplyOnExplicit = true;
237  mApplicability = ButImap;
238  } else {
239  bApplyOnInbound = bool(sets.contains("check-mail"));
240  bApplyOnOutbound = bool(sets.contains("send-mail"));
241  bApplyOnExplicit = bool(sets.contains("manual-filtering"));
242  mApplicability = (AccountType)config->readNumEntry( "Applicability", ButImap );
243  }
244 
245  bStopProcessingHere = config->readBoolEntry("StopProcessingHere", true);
246  bConfigureShortcut = config->readBoolEntry("ConfigureShortcut", false);
247  TQString shortcut( config->readEntry( "Shortcut" ) );
248  if ( !shortcut.isEmpty() ) {
249  TDEShortcut sc( shortcut );
250  setShortcut( sc );
251  }
252  bConfigureToolbar = config->readBoolEntry("ConfigureToolbar", false);
253  bConfigureToolbar = bConfigureToolbar && bConfigureShortcut;
254  mIcon = config->readEntry( "Icon", "gear" );
255  bAutoNaming = config->readBoolEntry("AutomaticName", false);
256 
257  int i, numActions;
258  TQString actName, argsName;
259 
260  mActions.clear();
261 
262  numActions = config->readNumEntry("actions",0);
263  if (numActions > FILTER_MAX_ACTIONS) {
264  numActions = FILTER_MAX_ACTIONS ;
265  KMessageBox::information( 0, i18n("<qt>Too many filter actions in filter rule <b>%1</b>.</qt>").arg( mPattern.name() ) );
266  }
267 
268  for ( i=0 ; i < numActions ; i++ ) {
269  actName.sprintf("action-name-%d", i);
270  argsName.sprintf("action-args-%d", i);
271  // get the action description...
272  KMFilterActionDesc *desc = (*kmkernel->filterActionDict())[ config->readEntry( actName ) ];
273  if ( desc ) {
274  //...create an instance...
275  KMFilterAction *fa = desc->create();
276  if ( fa ) {
277  //...load it with it's parameter...
278  fa->argsFromString( config->readEntry( argsName ) );
279  //...check if it's emoty and...
280  if ( !fa->isEmpty() )
281  //...append it if it's not and...
282  mActions.append( fa );
283  else
284  //...delete is else.
285  delete fa;
286  }
287  } else
288  KMessageBox::information( 0 /* app-global modal dialog box */,
289  i18n("<qt>Unknown filter action <b>%1</b><br>in filter rule <b>%2</b>.<br>Ignoring it.</qt>")
290  .arg( config->readEntry( actName ) ).arg( mPattern.name() ) );
291  }
292 
293  mAccounts = config->readIntListEntry( "accounts-set" );
294  }
295 }
296 
297 
298 void KMFilter::writeConfig(TDEConfig* config) const
299 {
300  mPattern.writeConfig(config);
301 
302  if (bPopFilter) {
303  switch ( mAction ) {
304  case Down:
305  config->writeEntry( "action", "down" );
306  break;
307  case Later:
308  config->writeEntry( "action", "later" );
309  break;
310  case Delete:
311  config->writeEntry( "action", "delete" );
312  break;
313  default:
314  config->writeEntry( "action", "" );
315  }
316  } else {
317  TQStringList sets;
318  if ( bApplyOnInbound )
319  sets.append( "check-mail" );
320  if ( bApplyOnOutbound )
321  sets.append( "send-mail" );
322  if ( bApplyOnExplicit )
323  sets.append( "manual-filtering" );
324  config->writeEntry( "apply-on", sets );
325 
326  config->writeEntry( "StopProcessingHere", bStopProcessingHere );
327  config->writeEntry( "ConfigureShortcut", bConfigureShortcut );
328  if ( !mShortcut.isNull() )
329  config->writeEntry( "Shortcut", mShortcut.toString() );
330  config->writeEntry( "ConfigureToolbar", bConfigureToolbar );
331  config->writeEntry( "Icon", mIcon );
332  config->writeEntry( "AutomaticName", bAutoNaming );
333  config->writeEntry( "Applicability", mApplicability );
334 
335  TQString key;
336  int i;
337 
338  TQPtrListIterator<KMFilterAction> it( mActions );
339  for ( i=0, it.toFirst() ; it.current() ; ++it, ++i ) {
340  config->writeEntry( key.sprintf("action-name-%d", i),
341  (*it)->name() );
342  config->writeEntry( key.sprintf("action-args-%d", i),
343  (*it)->argsAsString() );
344  }
345  config->writeEntry( "actions", i );
346  config->writeEntry( "accounts-set", mAccounts );
347  }
348 }
349 
350 void KMFilter::purify()
351 {
352  mPattern.purify();
353 
354  if (!bPopFilter) {
355  TQPtrListIterator<KMFilterAction> it( mActions );
356  it.toLast();
357  while ( it.current() )
358  if ( (*it)->isEmpty() )
359  mActions.remove ( (*it) );
360  else
361  --it;
362 
363  // Remove invalid accounts from mAccounts - just to be tidy
364  TQValueListIterator<int> it2 = mAccounts.begin();
365  while ( it2 != mAccounts.end() ) {
366  if ( !kmkernel->acctMgr()->find( *it2 ) )
367  it2 = mAccounts.remove( it2 );
368  else
369  ++it2;
370  }
371  }
372 }
373 
374 bool KMFilter::isEmpty() const
375 {
376  if (bPopFilter)
377  return mPattern.isEmpty();
378  else
379  return mPattern.isEmpty() && mActions.isEmpty() && mAccounts.isEmpty();
380 }
381 
382 #ifndef NDEBUG
383 const TQString KMFilter::asString() const
384 {
385  TQString result;
386 
387  result += mPattern.asString();
388 
389  if (bPopFilter){
390  result += " action: ";
391  result += mAction;
392  result += "\n";
393  }
394  else {
395  TQPtrListIterator<KMFilterAction> it( mActions );
396  for ( it.toFirst() ; it.current() ; ++it ) {
397  result += " action: ";
398  result += (*it)->label();
399  result += " ";
400  result += (*it)->argsAsString();
401  result += "\n";
402  }
403  result += "This filter belongs to the following sets:";
404  if ( bApplyOnInbound )
405  result += " Inbound";
406  if ( bApplyOnOutbound )
407  result += " Outbound";
408  if ( bApplyOnExplicit )
409  result += " Explicit";
410  result += "\n";
411  if ( bApplyOnInbound && mApplicability == All ) {
412  result += "This filter applies to all accounts.\n";
413  } else if ( bApplyOnInbound && mApplicability == ButImap ) {
414  result += "This filter applies to all but online IMAP accounts.\n";
415  } else if ( bApplyOnInbound ) {
416  TQValueListConstIterator<int> it2;
417  result += "This filter applies to the following accounts:";
418  if ( mAccounts.isEmpty() )
419  result += " None";
420  else for ( it2 = mAccounts.begin() ; it2 != mAccounts.end() ; ++it2 )
421  if ( kmkernel->acctMgr()->find( *it2 ) )
422  result += " " + kmkernel->acctMgr()->find( *it2 )->name();
423  result += "\n";
424  }
425  if ( bStopProcessingHere )
426  result += "If it matches, processing stops at this filter.\n";
427  }
428  return result;
429 }
430 #endif
Abstract base class for KMail's filter actions.
ReturnCode
Possible return codes of process:
virtual void argsFromString(const TQString argsStr)=0
Read extra arguments from given string.
Mail folder.
Definition: kmfolder.h:68
Auxiliary struct to KMFilterActionDict.
virtual bool isEmpty() const
Determines whether this action is valid.
The account manager is responsible for creating accounts of various types via the factory method crea...
This is a Mime Message.
Definition: kmmessage.h:67
KMail Filter Log Collector.
Definition: filterlog.h:53