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 bool isEmpty() const
Determines whether this action is valid.
virtual void argsFromString(const TQString argsStr)=0
Read extra arguments from given string.
Mail folder.
Definition: kmfolder.h:69
This is a Mime Message.
Definition: kmmessage.h:68
The account manager is responsible for creating accounts of various types via the factory method crea...
KMail Filter Log Collector.
Definition: filterlog.h:54
Auxiliary struct to KMFilterActionDict.