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"
33
34#include <tdelocale.h>
35#include <tdemessagebox.h>
36#include <kdebug.h>
37#include <tdeconfig.h>
38
39#include <assert.h>
40
41
42KMFilter::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
65KMFilter::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
107KMFilter::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
150bool 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
163void KMFilter::setAction(const KMPopFilterAction aAction)
164{
165 mAction = aAction;
166}
167
168// only for bPopFilter
169KMPopFilterAction KMFilter::action()
170{
171 return mAction;
172}
173
174// only for !bPopFilter
175bool 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
187void 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
196bool 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//-----------------------------------------------------------------------------
213void 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
298void 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
350void 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
374bool 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
383const 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.