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.
22 #include "mailfilter.h"
24 // other kmail headers
25 #include "filteraction.h"
27 #include "filterlog.h"
28 #include "mailkernel.h"
29 using MailCommon::FilterLog
;
32 #include <Akonadi/AgentManager>
36 #include <kmessagebox.h>
39 #include <kconfiggroup.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;
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() );
87 FilterAction
*f
= desc
->create();
89 f
->argsFromString( action
->argsAsString() );
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
);
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
);
141 if ( status
== NoResult
) // No filters matched, keep copy of message
144 stopIt
= stopProcessingHere();
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() )
160 bool MailFilter::folderRemoved( const Akonadi::Collection
& aFolder
, const Akonadi::Collection
& aNewFolder
)
164 QListIterator
<FilterAction
*> it( mActions
);
165 while ( it
.hasNext() )
166 if ( it
.next()->folderRemoved( aFolder
, aNewFolder
) )
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
)
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
);
193 if ( applicability() == Checked
)
194 return mAccounts
.contains( id
);
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
;
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
);
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
;
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() ) );
253 //...create an instance...
254 FilterAction
*fa
= desc
->create();
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
);
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() ),
273 mAccounts
= config
.readEntry( "accounts-set",QStringList() );
277 void MailFilter::writeConfig(KConfigGroup
& config
) const
279 mPattern
.writeConfig(config
);
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
);
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
),
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()
320 QListIterator
<FilterAction
*> it( mActions
);
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
);
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() )
352 const QString
MailFilter::asString() const
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();
364 result
+= (*it
)->argsAsString();
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";
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() )
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();
395 if ( bStopProcessingHere
)
396 result
+= "If it matches, processing stops at this filter.\n";