Build with clang.
[kdepim.git] / mailcommon / mailfilter.cpp
blob7aad7ecd37cabc57483e328de2d9b578fa62449e
1 /* -*- mode: C++; c-file-style: "gnu" -*-
2 * kmail: KDE mail client
3 * Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
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.
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.
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.
21 // my header
22 #include "mailfilter.h"
24 // other kmail headers
25 #include "filteraction.h"
26 #include "mailutil.h"
27 #include "filterlog.h"
28 #include "mailkernel.h"
29 using MailCommon::FilterLog;
31 // KDEPIMLIBS headers
32 #include <Akonadi/AgentManager>
34 // other KDE headers
35 #include <klocale.h>
36 #include <kmessagebox.h>
37 #include <kdebug.h>
38 #include <kconfig.h>
39 #include <kconfiggroup.h>
41 // other headers
42 #include <assert.h>
44 using namespace MailCommon;
46 MailFilter::MailFilter()
48 bApplyOnInbound = true;
49 bApplyBeforeOutbound = false;
50 bApplyOnOutbound = false;
51 bApplyOnExplicit = true;
52 bStopProcessingHere = true;
53 bConfigureShortcut = false;
54 bConfigureToolbar = false;
55 bAutoNaming = true;
56 mApplicability = All;
60 MailFilter::MailFilter( const KConfigGroup & aConfig )
62 readConfig( aConfig );
66 MailFilter::MailFilter( const MailFilter & aFilter )
68 mPattern = aFilter.mPattern;
70 bApplyOnInbound = aFilter.applyOnInbound();
71 bApplyBeforeOutbound = aFilter.applyBeforeOutbound();
72 bApplyOnOutbound = aFilter.applyOnOutbound();
73 bApplyOnExplicit = aFilter.applyOnExplicit();
74 bStopProcessingHere = aFilter.stopProcessingHere();
75 bConfigureShortcut = aFilter.configureShortcut();
76 bConfigureToolbar = aFilter.configureToolbar();
77 mToolbarName = aFilter.toolbarName();
78 mApplicability = aFilter.applicability();
79 mIcon = aFilter.icon();
80 mShortcut = aFilter.shortcut();
82 QListIterator<FilterAction*> it( aFilter.mActions );
83 while ( it.hasNext() ) {
84 FilterAction *action = it.next();
85 FilterActionDesc *desc = FilterIf->filterActionDict()->value( action->name() );
86 if ( desc ) {
87 FilterAction *f = desc->create();
88 if ( f ) {
89 f->argsFromString( action->argsAsString() );
90 mActions.append( f );
95 mAccounts.clear();
96 QStringList::ConstIterator it2;
97 for ( it2 = aFilter.mAccounts.constBegin() ; it2 != aFilter.mAccounts.constEnd() ; ++it2 )
98 mAccounts.append( *it2 );
101 MailFilter::~MailFilter()
103 qDeleteAll( mActions );
106 MailFilter::ReturnCode MailFilter::execActions( const Akonadi::Item &item, bool& stopIt ) const
108 ReturnCode status = NoResult;
110 QList<FilterAction*>::const_iterator it( mActions.begin() );
111 for ( ; it != mActions.constEnd() ; ++it ) {
113 if ( FilterLog::instance()->isLogging() ) {
114 const QString logText( i18n( "<b>Applying filter action:</b> %1",
115 (*it)->displayString() ) );
116 FilterLog::instance()->add( logText, FilterLog::AppliedAction );
119 FilterAction::ReturnCode result = (*it)->process( item );
121 switch ( result ) {
122 case FilterAction::CriticalError:
123 if ( FilterLog::instance()->isLogging() ) {
124 const QString logText = QString( "<font color=#FF0000>%1</font>" )
125 .arg( i18n( "A critical error occurred. Processing stops here." ) );
126 FilterLog::instance()->add( logText, FilterLog::AppliedAction );
128 // in case it's a critical error: return immediately!
129 return CriticalError;
130 case FilterAction::ErrorButGoOn:
131 if ( FilterLog::instance()->isLogging() ) {
132 const QString logText = QString( "<font color=#FF0000>%1</font>" )
133 .arg( i18n( "A problem was found while applying this action." ) );
134 FilterLog::instance()->add( logText, FilterLog::AppliedAction );
136 default:
137 break;
141 if ( status == NoResult ) // No filters matched, keep copy of message
142 status = GoOn;
144 stopIt = stopProcessingHere();
146 return status;
149 bool MailFilter::requiresBody()
151 if (pattern() && pattern()->requiresBody())
152 return true; // no pattern means always matches?
153 QListIterator<FilterAction*> it( *actions() );
154 while ( it.hasNext() )
155 if ( it.next()->requiresBody() )
156 return true;
157 return false;
160 bool MailFilter::folderRemoved( const Akonadi::Collection & aFolder, const Akonadi::Collection& aNewFolder )
162 bool rem = false;
164 QListIterator<FilterAction*> it( mActions );
165 while ( it.hasNext() )
166 if ( it.next()->folderRemoved( aFolder, aNewFolder ) )
167 rem = true;
169 return rem;
172 void MailFilter::setApplyOnAccount( const QString& id, bool aApply )
174 if (aApply && !mAccounts.contains( id )) {
175 mAccounts.append( id );
176 } else if (!aApply && mAccounts.contains( id )) {
177 mAccounts.removeAll( id );
181 bool MailFilter::applyOnAccount( const QString& id ) const
183 if ( applicability() == All )
184 return true;
185 if ( applicability() == ButImap ) {
186 Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance( id );
187 if ( instance.isValid() ) {
188 return ( instance.type().identifier() != IMAP_RESOURCE_IDENTIFIER );
189 } else {
190 return false;
193 if ( applicability() == Checked )
194 return mAccounts.contains( id );
196 return false;
200 //-----------------------------------------------------------------------------
201 void MailFilter::readConfig(const KConfigGroup & config)
203 // MKSearchPattern::readConfig ensures
204 // that the pattern is purified.
205 mPattern.readConfig(config);
207 const QStringList sets = config.readEntry("apply-on", QStringList() );
208 if ( sets.isEmpty() && !config.hasKey("apply-on") ) {
209 bApplyBeforeOutbound = false;
210 bApplyOnOutbound = false;
211 bApplyOnInbound = true;
212 bApplyOnExplicit = true;
213 mApplicability = ButImap;
214 } else {
215 bApplyBeforeOutbound = bool(sets.contains("before-send-mail"));
216 bApplyOnInbound = bool(sets.contains("check-mail"));
217 bApplyOnOutbound = bool(sets.contains("send-mail"));
218 bApplyOnExplicit = bool(sets.contains("manual-filtering"));
219 mApplicability = (AccountType) config.readEntry(
220 "Applicability", (int)ButImap );
223 bStopProcessingHere = config.readEntry( "StopProcessingHere", true );
224 bConfigureShortcut = config.readEntry( "ConfigureShortcut", false );
225 QString shortcut( config.readEntry( "Shortcut", QString() ) );
226 if ( !shortcut.isEmpty() ) {
227 KShortcut sc( shortcut );
228 setShortcut( sc );
230 bConfigureToolbar = config.readEntry( "ConfigureToolbar", false );
231 bConfigureToolbar = bConfigureToolbar && bConfigureShortcut;
232 mToolbarName = config.readEntry( "ToolbarName", name() );
233 mIcon = config.readEntry( "Icon", "system-run" );
234 bAutoNaming = config.readEntry( "AutomaticName", false );
236 QString actName, argsName;
238 mActions.clear();
240 int numActions = config.readEntry( "actions", 0 );
241 if (numActions > FILTER_MAX_ACTIONS) {
242 numActions = FILTER_MAX_ACTIONS ;
243 KMessageBox::information( 0, i18n("<qt>Too many filter actions in filter rule <b>%1</b>.</qt>", mPattern.name() ) );
246 for ( int i=0 ; i < numActions ; ++i ) {
247 actName.sprintf("action-name-%d", i);
248 argsName.sprintf("action-args-%d", i);
249 // get the action description...
250 FilterActionDesc *desc = FilterIf->filterActionDict()->value(
251 config.readEntry( actName, QString() ) );
252 if ( desc ) {
253 //...create an instance...
254 FilterAction *fa = desc->create();
255 if ( fa ) {
256 //...load it with it's parameter...
257 fa->argsFromString( config.readEntry( argsName, QString() ) );
258 //...check if it's emoty and...
259 if ( !fa->isEmpty() )
260 //...append it if it's not and...
261 mActions.append( fa );
262 else
263 //...delete is else.
264 delete fa;
266 } else
267 KMessageBox::information( 0 /* app-global modal dialog box */,
268 i18n("<qt>Unknown filter action <b>%1</b><br />in filter rule <b>%2</b>.<br />Ignoring it.</qt>",
269 config.readEntry( actName, QString() ),
270 mPattern.name() ) );
273 mAccounts = config.readEntry( "accounts-set",QStringList() );
277 void MailFilter::writeConfig(KConfigGroup & config) const
279 mPattern.writeConfig(config);
281 QStringList sets;
282 if ( bApplyOnInbound )
283 sets.append( "check-mail" );
284 if ( bApplyBeforeOutbound )
285 sets.append( "before-send-mail" );
286 if ( bApplyOnOutbound )
287 sets.append( "send-mail" );
288 if ( bApplyOnExplicit )
289 sets.append( "manual-filtering" );
290 config.writeEntry( "apply-on", sets );
292 config.writeEntry( "StopProcessingHere", bStopProcessingHere );
293 config.writeEntry( "ConfigureShortcut", bConfigureShortcut );
294 if ( !mShortcut.isEmpty() )
295 config.writeEntry( "Shortcut", mShortcut.toString() );
296 config.writeEntry( "ConfigureToolbar", bConfigureToolbar );
297 config.writeEntry( "ToolbarName", mToolbarName );
298 config.writeEntry( "Icon", mIcon );
299 config.writeEntry( "AutomaticName", bAutoNaming );
300 config.writeEntry( "Applicability", (int)mApplicability );
302 QString key;
303 int i;
305 QList<FilterAction*>::const_iterator it;
306 for ( i=0, it = mActions.constBegin() ; it != mActions.constEnd() ; ++it, ++i ) {
307 config.writeEntry( key.sprintf("action-name-%d", i),
308 (*it)->name() );
309 config.writeEntry( key.sprintf("action-args-%d", i),
310 (*it)->argsAsString() );
312 config.writeEntry( "actions", i );
313 config.writeEntry( "accounts-set", mAccounts );
316 void MailFilter::purify()
318 mPattern.purify();
320 QListIterator<FilterAction*> it( mActions );
321 it.toBack();
322 while ( it.hasPrevious() ) {
323 FilterAction *action = it.previous();
324 if ( action->isEmpty() )
325 mActions.removeAll ( action );
327 // Remove invalid accounts from mAccounts - just to be tidy
328 QStringList::Iterator it2 = mAccounts.begin();
329 while ( it2 != mAccounts.end() ) {
330 if ( !Akonadi::AgentManager::self()->instance( *it2 ).isValid() )
331 it2 = mAccounts.erase( it2 );
332 else
333 ++it2;
337 bool MailFilter::isEmpty() const
339 return ( mPattern.isEmpty() && mActions.isEmpty() ) ||
340 ( ( applicability() == Checked ) && mAccounts.isEmpty() );
343 QString MailFilter::toolbarName() const
345 if ( mToolbarName.isEmpty() )
346 return name();
347 else
348 return mToolbarName;
351 #ifndef NDEBUG
352 const QString MailFilter::asString() const
354 QString result;
356 result += "Filter name: " + name() + '\n';
357 result += mPattern.asString() + '\n';
359 QList<FilterAction*>::const_iterator it( mActions.begin() );
360 for ( ; it != mActions.end() ; ++it ) {
361 result += " action: ";
362 result += (*it)->label();
363 result += ' ';
364 result += (*it)->argsAsString();
365 result += '\n';
367 result += "This filter belongs to the following sets:";
368 if ( bApplyOnInbound )
369 result += " Inbound";
370 if ( bApplyBeforeOutbound )
371 result += " before-Outbound";
372 if ( bApplyOnOutbound )
373 result += " Outbound";
374 if ( bApplyOnExplicit )
375 result += " Explicit";
376 result += '\n';
377 if ( bApplyOnInbound && mApplicability == All ) {
378 result += "This filter applies to all accounts.\n";
379 } else if ( bApplyOnInbound && mApplicability == ButImap ) {
380 result += "This filter applies to all but IMAP accounts.\n";
381 } else if ( bApplyOnInbound ) {
382 QStringList::ConstIterator it2;
383 result += "This filter applies to the following accounts:";
384 if ( mAccounts.isEmpty() )
385 result += " None";
386 else {
387 for ( it2 = mAccounts.begin() ; it2 != mAccounts.end() ; ++it2 ) {
388 if ( Akonadi::AgentManager::self()->instance( *it2 ).isValid() ) {
389 result += ' ' + Akonadi::AgentManager::self()->instance( *it2 ).name();
393 result += '\n';
395 if ( bStopProcessingHere )
396 result += "If it matches, processing stops at this filter.\n";
398 return result;
400 #endif