kmail

kmfiltermgr.cpp
1 // -*- mode: C++; c-file-style: "gnu" -*-
2 // kmfiltermgr.cpp
3 
4 // my header
5 #ifdef HAVE_CONFIG_H
6 #include <config.h>
7 #endif
8 
9 #include "kmfiltermgr.h"
10 
11 // other kmail headers
12 #include "filterlog.h"
13 using KMail::FilterLog;
14 #include "kmfilterdlg.h"
15 #include "kmfolderindex.h"
16 #include "filterimporterexporter.h"
18 #include "kmfoldermgr.h"
19 #include "kmmsgdict.h"
20 #include "messageproperty.h"
21 using KMail::MessageProperty;
22 
23 // other KDE headers
24 #include <kdebug.h>
25 #include <tdelocale.h>
26 #include <tdeconfig.h>
27 
28 // other TQt headers
29 #include <tqregexp.h>
30 #include <tqvaluevector.h>
31 
32 // other headers
33 #include <assert.h>
34 
35 
36 //-----------------------------------------------------------------------------
37 KMFilterMgr::KMFilterMgr( bool popFilter )
38  : mEditDialog( 0 ),
39  bPopFilter( popFilter ),
40  mShowLater( false ),
41  mDirtyBufferedFolderTarget( true ),
42  mBufferedFolderTarget( true ),
43  mRefCount( 0 )
44 {
45  connect( kmkernel, TQT_SIGNAL( folderRemoved( KMFolder* ) ),
46  this, TQT_SLOT( slotFolderRemoved( KMFolder* ) ) );
47 }
48 
49 
50 //-----------------------------------------------------------------------------
51 KMFilterMgr::~KMFilterMgr()
52 {
53  deref( true );
54  writeConfig( false );
55  clear();
56 }
57 
58 void KMFilterMgr::clear()
59 {
60  mDirtyBufferedFolderTarget = true;
61  for ( TQValueListIterator<KMFilter*> it = mFilters.begin() ;
62  it != mFilters.end() ; ++it ) {
63  delete *it;
64  }
65 }
66 
67 //-----------------------------------------------------------------------------
68 void KMFilterMgr::readConfig(void)
69 {
70  TDEConfig* config = KMKernel::config();
71  clear();
72 
73  if (bPopFilter) {
74  TDEConfigGroupSaver saver(config, "General");
75  mShowLater = config->readNumEntry("popshowDLmsgs",0);
76  }
77  mFilters = FilterImporterExporter::readFiltersFromConfig( config, bPopFilter );
78 }
79 
80 //-----------------------------------------------------------------------------
81 void KMFilterMgr::writeConfig(bool withSync)
82 {
83  TDEConfig* config = KMKernel::config();
84 
85  // Now, write out the new stuff:
86  FilterImporterExporter::writeFiltersToConfig( mFilters, config, bPopFilter );
87  TDEConfigGroupSaver saver(config, "General");
88  if (bPopFilter)
89  config->writeEntry("popshowDLmsgs", mShowLater);
90 
91  if (withSync) config->sync();
92 }
93 
94 int KMFilterMgr::processPop( KMMessage * msg ) const {
95  for ( TQValueListConstIterator<KMFilter*> it = mFilters.constBegin();
96  it != mFilters.constEnd() ; ++it )
97  if ( (*it)->pattern()->matches( msg ) )
98  return (*it)->action();
99  return NoAction;
100 }
101 
102 bool KMFilterMgr::beginFiltering(KMMsgBase *msgBase) const
103 {
104  if (MessageProperty::filtering( msgBase ))
105  return false;
106  MessageProperty::setFiltering( msgBase, true );
107  MessageProperty::setFilterFolder( msgBase, 0 );
108  if ( FilterLog::instance()->isLogging() ) {
109  FilterLog::instance()->addSeparator();
110  }
111  return true;
112 }
113 
114 int KMFilterMgr::moveMessage(KMMessage *msg) const
115 {
116  if (MessageProperty::filterFolder(msg)->moveMsg( msg ) == 0) {
117  if ( kmkernel->folderIsTrash( MessageProperty::filterFolder( msg )))
118  KMFilterAction::sendMDN( msg, KMime::MDN::Deleted );
119  } else {
120  kdDebug(5006) << "KMfilterAction - couldn't move msg" << endl;
121  return 2;
122  }
123  return 0;
124 }
125 
126 void KMFilterMgr::endFiltering(KMMsgBase *msgBase) const
127 {
128  KMFolder *parent = msgBase->parent();
129  if ( parent ) {
130  if ( parent == MessageProperty::filterFolder( msgBase ) ) {
131  parent->take( parent->find( msgBase ) );
132  }
133  else if ( ! MessageProperty::filterFolder( msgBase ) ) {
134  int index = parent->find( msgBase );
135  KMMessage *msg = parent->getMsg( index );
136  parent->take( index );
137  parent->addMsgKeepUID( msg );
138  }
139  }
140  MessageProperty::setFiltering( msgBase, false );
141 }
142 
143 int KMFilterMgr::process( KMMessage * msg, const KMFilter * filter ) {
144  if ( !msg || !filter || !beginFiltering( msg ))
145  return 1;
146  bool stopIt = false;
147  int result = 1;
148 
149  if ( FilterLog::instance()->isLogging() ) {
150  TQString logText( i18n( "<b>Evaluating filter rules:</b> " ) );
151  logText.append( filter->pattern()->asString() );
152  FilterLog::instance()->add( logText, FilterLog::patternDesc );
153  }
154 
155  if (filter->pattern()->matches( msg )) {
156  if ( FilterLog::instance()->isLogging() ) {
157  FilterLog::instance()->add( i18n( "<b>Filter rules have matched.</b>" ),
158  FilterLog::patternResult );
159  }
160  if (filter->execActions( msg, stopIt ) == KMFilter::CriticalError)
161  return 2;
162 
163  KMFolder *folder = MessageProperty::filterFolder( msg );
164 
165  endFiltering( msg );
166  if (folder) {
167  tempOpenFolder( folder );
168  result = folder->moveMsg( msg );
169  }
170  } else {
171  endFiltering( msg );
172  result = 1;
173  }
174  return result;
175 }
176 
177 int KMFilterMgr::process( TQ_UINT32 serNum, const KMFilter *filter )
178 {
179  bool stopIt = false;
180  int result = 1;
181 
182  if ( !filter )
183  return 1;
184 
185  if ( isMatching( serNum, filter ) ) {
186  KMFolder *folder = 0;
187  int idx = -1;
188  // get the message with the serNum
189  KMMsgDict::instance()->getLocation( serNum, &folder, &idx );
190  if ( !folder || ( idx == -1 ) || ( idx >= folder->count() ) ) {
191  return 1;
192  }
193  KMFolderOpener openFolder(folder, "filtermgr");
194  KMMsgBase *msgBase = folder->getMsgBase( idx );
195  bool unGet = !msgBase->isMessage();
196  KMMessage *msg = folder->getMsg( idx );
197  // do the actual filtering stuff
198  if ( !msg || !beginFiltering( msg ) ) {
199  if ( unGet )
200  folder->unGetMsg( idx );
201  return 1;
202  }
203  if ( filter->execActions( msg, stopIt ) == KMFilter::CriticalError ) {
204  if ( unGet )
205  folder->unGetMsg( idx );
206  return 2;
207  }
208 
209  KMFolder *targetFolder = MessageProperty::filterFolder( msg );
210 
211  endFiltering( msg );
212  if ( targetFolder ) {
213  tempOpenFolder( targetFolder );
214  msg->setTransferInProgress( false );
215  result = targetFolder->moveMsg( msg );
216  msg->setTransferInProgress( true );
217  }
218  if ( unGet )
219  folder->unGetMsg( idx );
220  } else {
221  result = 1;
222  }
223  return result;
224 }
225 
226 int KMFilterMgr::process( KMMessage * msg, FilterSet set,
227  bool account, uint accountId ) {
228  if ( bPopFilter )
229  return processPop( msg );
230 
231  if ( set == NoSet ) {
232  kdDebug(5006) << "KMFilterMgr: process() called with not filter set selected"
233  << endl;
234  return 1;
235  }
236 
237  bool stopIt = false;
238  bool atLeastOneRuleMatched = false;
239 
240  if (!beginFiltering( msg ))
241  return 1;
242  for ( TQValueListConstIterator<KMFilter*> it = mFilters.constBegin();
243  !stopIt && it != mFilters.constEnd() ; ++it ) {
244 
245  if ( ( ( (set&Inbound) && (*it)->applyOnInbound() ) &&
246  ( !account ||
247  ( account && (*it)->applyOnAccount( accountId ) ) ) ) ||
248  ( (set&Outbound) && (*it)->applyOnOutbound() ) ||
249  ( (set&Explicit) && (*it)->applyOnExplicit() ) ) {
250  // filter is applicable
251 
252  if ( FilterLog::instance()->isLogging() ) {
253  TQString logText( i18n( "<b>Evaluating filter rules:</b> " ) );
254  logText.append( (*it)->pattern()->asString() );
255  FilterLog::instance()->add( logText, FilterLog::patternDesc );
256  }
257  if ( (*it)->pattern()->matches( msg ) ) {
258  // filter matches
259  if ( FilterLog::instance()->isLogging() ) {
260  FilterLog::instance()->add( i18n( "<b>Filter rules have matched.</b>" ),
261  FilterLog::patternResult );
262  }
263  atLeastOneRuleMatched = true;
264  // execute actions:
265  if ( (*it)->execActions(msg, stopIt) == KMFilter::CriticalError )
266  return 2;
267  }
268  }
269  }
270 
271  KMFolder *folder = MessageProperty::filterFolder( msg );
272  /* endFilter does a take() and addButKeepUID() to ensure the changed
273  * message is on disk. This is unnessecary if nothing matched, so just
274  * reset state and don't update the listview at all. */
275  if ( atLeastOneRuleMatched )
276  endFiltering( msg );
277  else
278  MessageProperty::setFiltering( msg, false );
279  if (folder) {
280  tempOpenFolder( folder );
281  folder->moveMsg(msg);
282  return 0;
283  }
284  return 1;
285 }
286 
287 bool KMFilterMgr::isMatching( TQ_UINT32 serNum, const KMFilter *filter )
288 {
289  bool result = false;
290  if ( FilterLog::instance()->isLogging() ) {
291  TQString logText( i18n( "<b>Evaluating filter rules:</b> " ) );
292  logText.append( filter->pattern()->asString() );
293  FilterLog::instance()->add( logText, FilterLog::patternDesc );
294  }
295  if ( filter->pattern()->matches( serNum ) ) {
296  if ( FilterLog::instance()->isLogging() ) {
297  FilterLog::instance()->add( i18n( "<b>Filter rules have matched.</b>" ),
298  FilterLog::patternResult );
299  }
300  result = true;
301  }
302  return result;
303 }
304 
305 bool KMFilterMgr::atLeastOneFilterAppliesTo( unsigned int accountID ) const
306 {
307  TQValueListConstIterator<KMFilter*> it = mFilters.constBegin();
308  for ( ; it != mFilters.constEnd() ; ++it ) {
309  if ( (*it)->applyOnAccount( accountID ) ) {
310  return true;
311  }
312  }
313  return false;
314 }
315 
316 bool KMFilterMgr::atLeastOneIncomingFilterAppliesTo( unsigned int accountID ) const
317 {
318  TQValueListConstIterator<KMFilter*> it = mFilters.constBegin();
319  for ( ; it != mFilters.constEnd() ; ++it ) {
320  if ( (*it)->applyOnInbound() && (*it)->applyOnAccount( accountID ) ) {
321  return true;
322  }
323  }
324  return false;
325 }
326 
327 bool KMFilterMgr::atLeastOneOnlineImapFolderTarget()
328 {
329  if (!mDirtyBufferedFolderTarget)
330  return mBufferedFolderTarget;
331 
332  mDirtyBufferedFolderTarget = false;
333 
334  TQValueListConstIterator<KMFilter*> it = mFilters.constBegin();
335  for ( ; it != mFilters.constEnd() ; ++it ) {
336  KMFilter *filter = *it;
337  TQPtrListIterator<KMFilterAction> jt( *filter->actions() );
338  for ( jt.toFirst() ; jt.current() ; ++jt ) {
339  KMFilterActionWithFolder *f = dynamic_cast<KMFilterActionWithFolder*>(*jt);
340  if (!f)
341  continue;
342  TQString name = f->argsAsString();
343  KMFolder *folder = kmkernel->imapFolderMgr()->findIdString( name );
344  if (folder) {
345  mBufferedFolderTarget = true;
346  return true;
347  }
348  }
349  }
350  mBufferedFolderTarget = false;
351  return false;
352 }
353 
354 //-----------------------------------------------------------------------------
355 void KMFilterMgr::ref(void)
356 {
357  mRefCount++;
358 }
359 
360 //-----------------------------------------------------------------------------
361 void KMFilterMgr::deref(bool force)
362 {
363  if (!force)
364  mRefCount--;
365  if (mRefCount < 0)
366  mRefCount = 0;
367  if (mRefCount && !force)
368  return;
369  TQValueVector< KMFolder *>::const_iterator it;
370  for ( it = mOpenFolders.constBegin(); it != mOpenFolders.constEnd(); ++it )
371  (*it)->close("filtermgr");
372  mOpenFolders.clear();
373 }
374 
375 
376 //-----------------------------------------------------------------------------
377 int KMFilterMgr::tempOpenFolder(KMFolder* aFolder)
378 {
379  assert( aFolder );
380 
381  int rc = aFolder->open("filermgr");
382  if (rc) return rc;
383 
384  mOpenFolders.append( aFolder );
385  return 0;
386 }
387 
388 
389 //-----------------------------------------------------------------------------
390 void KMFilterMgr::openDialog( TQWidget *, bool checkForEmptyFilterList )
391 {
392  if( !mEditDialog )
393  {
394  //
395  // We can't use the parent as long as the dialog is modeless
396  // and there is one shared dialog for all top level windows.
397  //
398  mEditDialog = new KMFilterDlg( 0, "filterdialog", bPopFilter,
399  checkForEmptyFilterList );
400  }
401  mEditDialog->show();
402 }
403 
404 
405 //-----------------------------------------------------------------------------
406 void KMFilterMgr::createFilter( const TQCString & field, const TQString & value )
407 {
408  openDialog( 0, false );
409  mEditDialog->createFilter( field, value );
410 }
411 
412 
413 //-----------------------------------------------------------------------------
414 const TQString KMFilterMgr::createUniqueName( const TQString & name )
415 {
416  TQString uniqueName = name;
417  int counter = 0;
418  bool found = true;
419 
420  while ( found ) {
421  found = false;
422  for ( TQValueListConstIterator<KMFilter*> it = mFilters.constBegin();
423  it != mFilters.constEnd(); ++it ) {
424  if ( !( (*it)->name().compare( uniqueName ) ) ) {
425  found = true;
426  ++counter;
427  uniqueName = name;
428  uniqueName += TQString( " (" ) + TQString::number( counter )
429  + TQString( ")" );
430  break;
431  }
432  }
433  }
434  return uniqueName;
435 }
436 
437 
438 //-----------------------------------------------------------------------------
439 void KMFilterMgr::appendFilters( const TQValueList<KMFilter*> &filters,
440  bool replaceIfNameExists )
441 {
442  mDirtyBufferedFolderTarget = true;
443  beginUpdate();
444  if ( replaceIfNameExists ) {
445  TQValueListConstIterator<KMFilter*> it1 = filters.constBegin();
446  for ( ; it1 != filters.constEnd() ; ++it1 ) {
447  TQValueListConstIterator<KMFilter*> it2 = mFilters.constBegin();
448  for ( ; it2 != mFilters.constEnd() ; ++it2 ) {
449  if ( (*it1)->name() == (*it2)->name() ) {
450  mFilters.remove( (*it2) );
451  it2 = mFilters.constBegin();
452  }
453  }
454  }
455  }
456  mFilters += filters;
457  writeConfig( true );
458  endUpdate();
459 }
460 
461 void KMFilterMgr::setFilters( const TQValueList<KMFilter*> &filters )
462 {
463  beginUpdate();
464  clear();
465  mFilters = filters;
466  writeConfig( true );
467  endUpdate();
468 }
469 
470 void KMFilterMgr::slotFolderRemoved( KMFolder * aFolder )
471 {
472  folderRemoved( aFolder, 0 );
473 }
474 
475 //-----------------------------------------------------------------------------
476 bool KMFilterMgr::folderRemoved(KMFolder* aFolder, KMFolder* aNewFolder)
477 {
478  mDirtyBufferedFolderTarget = true;
479  bool rem = false;
480  TQValueListConstIterator<KMFilter*> it = mFilters.constBegin();
481  for ( ; it != mFilters.constEnd() ; ++it )
482  if ( (*it)->folderRemoved(aFolder, aNewFolder) )
483  rem = true;
484 
485  return rem;
486 }
487 
488 
489 //-----------------------------------------------------------------------------
490 #ifndef NDEBUG
491 void KMFilterMgr::dump(void) const
492 {
493 
494  TQValueListConstIterator<KMFilter*> it = mFilters.constBegin();
495  for ( ; it != mFilters.constEnd() ; ++it ) {
496  kdDebug(5006) << (*it)->asString() << endl;
497  }
498 }
499 #endif
500 
501 //-----------------------------------------------------------------------------
502 void KMFilterMgr::endUpdate(void)
503 {
504  emit filterListUpdated();
505 }
506 
507 #include "kmfiltermgr.moc"
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
Definition: kmfolder.cpp:360
static void sendMDN(KMMessage *msg, KMime::MDN::DispositionType d, const TQValueList< KMime::MDN::DispositionModifier > &m=TQValueList< KMime::MDN::DispositionModifier >())
Automates the sending of MDNs from filter actions.
Utility class that provides persisting of filters to/from TDEConfig.
KMMessage * take(int idx)
Detach message from this folder.
Definition: kmfolder.cpp:380
RAII for KMFolder::open() / close().
Definition: kmfolder.h:688
int find(const KMMsgBase *msg) const
Returns the index of the given message or -1 if not found.
Definition: kmfolder.cpp:435
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
Definition: kmmsgdict.cpp:319
KMFilterDlg(TQWidget *parent=0, const char *name=0, bool popFilter=false, bool createDummyFilter=true)
Create the filter dialog.
Mail folder.
Definition: kmfolder.h:68
int count(bool cache=false) const
Number of messages in this folder.
Definition: kmfolder.cpp:445
KMMessage * getMsg(int idx)
Read message at given index.
Definition: kmfolder.cpp:321
This is a Mime Message.
Definition: kmmessage.h:68
void setTransferInProgress(bool value, bool force=false)
Set that the message shall not be deleted because it is still required.
Definition: kmmessage.cpp:246
int open(const char *owner)
Open folder for access.
Definition: kmfolder.cpp:479
int moveMsg(KMMessage *msg, int *index_return=0)
Detaches the given message from it&#39;s current folder and adds it to this folder.
Definition: kmfolder.cpp:425
KMMsgInfo * unGetMsg(int idx)
Replace KMMessage with KMMsgInfo and delete KMMessage.
Definition: kmfolder.cpp:326
KMail Filter Log Collector.
Definition: filterlog.h:53
int addMsgKeepUID(KMMessage *msg, int *index_return=0)
(Note(bo): This needs to be fixed better at a later point.) This is overridden by dIMAP because addMs...
Definition: kmfolder.cpp:395
virtual const TQString argsAsString() const
Return extra arguments as string.
Abstract base class for KMail&#39;s filter actions that need a mail folder as parameter, e.g.