kmail

actionscheduler.cpp
1 /* Action Scheduler
2 
3  This file is part of KMail, the KDE mail client.
4  Copyright (c) Don Sanders <sanders@kde.org>
5 
6  KMail is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License, version 2, as
8  published by the Free Software Foundation.
9 
10  KMail is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  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  In addition, as a special exception, the copyright holders give
20  permission to link the code of this program with any edition of
21  the TQt library by Trolltech AS, Norway (or with modified versions
22  of TQt that use the same license as TQt), and distribute linked
23  combinations including the two. You must obey the GNU General
24  Public License in all respects for all of the code used other than
25  TQt. If you modify this file, you may extend this exception to
26  your version of the file, but you are not obligated to do so. If
27  you do not wish to do so, delete this exception statement from
28  your version.
29 */
30 #include <kdebug.h> // FIXME
31 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include "actionscheduler.h"
37 
38 #include "filterlog.h"
39 #include "messageproperty.h"
40 #include "kmfilter.h"
41 #include "kmfolderindex.h"
42 #include "kmfoldermgr.h"
43 #include "kmmsgdict.h"
44 #include "kmcommands.h"
45 #include "kmheaders.h"
46 #include "accountmanager.h"
48 
49 #include <tqtimer.h>
50 #include <tdeconfig.h>
51 #include <kstandarddirs.h>
52 
53 using namespace KMail;
54 typedef TQPtrList<KMMsgBase> KMMessageList;
55 
56 
57 KMFolderMgr* ActionScheduler::tempFolderMgr = 0;
58 int ActionScheduler::refCount = 0;
59 int ActionScheduler::count = 0;
60 TQValueList<ActionScheduler*> *ActionScheduler::schedulerList = 0;
61 bool ActionScheduler::sEnabled = false;
62 bool ActionScheduler::sEnabledChecked = false;
63 
64 ActionScheduler::ActionScheduler(KMFilterMgr::FilterSet set,
65  TQValueList<KMFilter*> filters,
66  KMHeaders *headers,
67  KMFolder *srcFolder)
68  :mSet( set ), mHeaders( headers )
69 {
70  ++count;
71  ++refCount;
72  mExecuting = false;
73  mExecutingLock = false;
74  mFetchExecuting = false;
75  mFiltersAreQueued = false;
76  mResult = ResultOk;
77  mIgnore = false;
78  mAutoDestruct = false;
79  mAlwaysMatch = false;
80  mAccountId = 0;
81  mAccount = false;
82  lastCommand = 0;
83  lastJob = 0;
84  finishTimer = new TQTimer( this, "finishTimer" );
85  connect( finishTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(finish()));
86  fetchMessageTimer = new TQTimer( this, "fetchMessageTimer" );
87  connect( fetchMessageTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(fetchMessage()));
88  tempCloseFoldersTimer = new TQTimer( this, "tempCloseFoldersTimer" );
89  connect( tempCloseFoldersTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(tempCloseFolders()));
90  processMessageTimer = new TQTimer( this, "processMessageTimer" );
91  connect( processMessageTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(processMessage()));
92  filterMessageTimer = new TQTimer( this, "filterMessageTimer" );
93  connect( filterMessageTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(filterMessage()));
94  timeOutTimer = new TQTimer( this, "timeOutTimer" );
95  connect( timeOutTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(timeOut()));
96  fetchTimeOutTimer = new TQTimer( this, "fetchTimeOutTimer" );
97  connect( fetchTimeOutTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(fetchTimeOut()));
98 
99  TQValueList<KMFilter*>::Iterator it = filters.begin();
100  for (; it != filters.end(); ++it)
101  mFilters.append( **it );
102  mDestFolder = 0;
103  if (srcFolder) {
104  mDeleteSrcFolder = false;
105  setSourceFolder( srcFolder );
106  } else {
107  TQString tmpName;
108  tmpName.setNum( count );
109  if (!tempFolderMgr)
110  tempFolderMgr = new KMFolderMgr(locateLocal("data","kmail/filter"));
111  KMFolder *tempFolder = tempFolderMgr->findOrCreate( tmpName );
112  tempFolder->expunge();
113  mDeleteSrcFolder = true;
114  setSourceFolder( tempFolder );
115  }
116  if (!schedulerList)
117  schedulerList = new TQValueList<ActionScheduler*>;
118  schedulerList->append( this );
119 }
120 
121 ActionScheduler::~ActionScheduler()
122 {
123  schedulerList->remove( this );
124  tempCloseFolders();
125  disconnect( mSrcFolder, TQ_SIGNAL(closed()),
126  this, TQ_SLOT(folderClosedOrExpunged()) );
127  disconnect( mSrcFolder, TQ_SIGNAL(expunged(KMFolder*)),
128  this, TQ_SLOT(folderClosedOrExpunged()) );
129  mSrcFolder->close("actionschedsrc");
130 
131  if (mDeleteSrcFolder)
132  tempFolderMgr->remove(mSrcFolder);
133 
134  --refCount;
135  if (refCount == 0) {
136  delete tempFolderMgr;
137  tempFolderMgr = 0;
138  }
139 }
140 
141 void ActionScheduler::setAutoDestruct( bool autoDestruct )
142 {
143  mAutoDestruct = autoDestruct;
144 }
145 
146 void ActionScheduler::setAlwaysMatch( bool alwaysMatch )
147 {
148  mAlwaysMatch = alwaysMatch;
149 }
150 
151 void ActionScheduler::setDefaultDestinationFolder( KMFolder *destFolder )
152 {
153  mDestFolder = destFolder;
154 }
155 
156 void ActionScheduler::setSourceFolder( KMFolder *srcFolder )
157 {
158  srcFolder->open("actionschedsrc");
159  if (mSrcFolder) {
160  disconnect( mSrcFolder, TQ_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
161  this, TQ_SLOT(msgAdded(KMFolder*, TQ_UINT32)) );
162  disconnect( mSrcFolder, TQ_SIGNAL(closed()),
163  this, TQ_SLOT(folderClosedOrExpunged()) );
164  disconnect( mSrcFolder, TQ_SIGNAL(expunged(KMFolder*)),
165  this, TQ_SLOT(folderClosedOrExpunged()) );
166  mSrcFolder->close("actionschedsrc");
167  }
168  mSrcFolder = srcFolder;
169  int i = 0;
170  for (i = 0; i < mSrcFolder->count(); ++i)
171  enqueue( mSrcFolder->getMsgBase( i )->getMsgSerNum() );
172  if (mSrcFolder) {
173  connect( mSrcFolder, TQ_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)),
174  this, TQ_SLOT(msgAdded(KMFolder*, TQ_UINT32)) );
175  connect( mSrcFolder, TQ_SIGNAL(closed()),
176  this, TQ_SLOT(folderClosedOrExpunged()) );
177  connect( mSrcFolder, TQ_SIGNAL(expunged(KMFolder*)),
178  this, TQ_SLOT(folderClosedOrExpunged()) );
179  }
180 }
181 
182 void ActionScheduler::setFilterList( TQValueList<KMFilter*> filters )
183 {
184  mFiltersAreQueued = true;
185  mQueuedFilters.clear();
186 
187  TQValueList<KMFilter*>::Iterator it = filters.begin();
188  for (; it != filters.end(); ++it)
189  mQueuedFilters.append( **it );
190  if (!mExecuting) {
191  mFilters = mQueuedFilters;
192  mFiltersAreQueued = false;
193  mQueuedFilters.clear();
194  }
195 }
196 
197 void ActionScheduler::folderClosedOrExpunged()
198 {
199  // mSrcFolder has been closed. reopen it.
200  if ( mSrcFolder )
201  {
202  mSrcFolder->open( "actionsched" );
203  }
204 }
205 
206 int ActionScheduler::tempOpenFolder( KMFolder* aFolder )
207 {
208  assert( aFolder );
209  tempCloseFoldersTimer->stop();
210  if ( aFolder == mSrcFolder.operator->() )
211  return 0;
212 
213  int rc = aFolder->open("actionsched");
214  if (rc)
215  return rc;
216 
217  mOpenFolders.append( aFolder );
218  return 0;
219 }
220 
221 void ActionScheduler::tempCloseFolders()
222 {
223  // close temp opened folders
224  TQValueListConstIterator<TQGuardedPtr<KMFolder> > it;
225  for (it = mOpenFolders.begin(); it != mOpenFolders.end(); ++it) {
226  KMFolder *folder = *it;
227  if (folder)
228  folder->close("actionsched");
229  }
230  mOpenFolders.clear();
231 }
232 
233 void ActionScheduler::execFilters(const TQValueList<TQ_UINT32> serNums)
234 {
235  TQValueListConstIterator<TQ_UINT32> it;
236  for (it = serNums.begin(); it != serNums.end(); ++it)
237  execFilters( *it );
238 }
239 
240 void ActionScheduler::execFilters(const TQPtrList<KMMsgBase> msgList)
241 {
242  KMMsgBase *msgBase;
243  TQPtrList<KMMsgBase> list = msgList;
244  for (msgBase = list.first(); msgBase; msgBase = list.next())
245  execFilters( msgBase->getMsgSerNum() );
246 }
247 
248 void ActionScheduler::execFilters(KMMsgBase* msgBase)
249 {
250  execFilters( msgBase->getMsgSerNum() );
251 }
252 
253 void ActionScheduler::execFilters(TQ_UINT32 serNum)
254 {
255  if (mResult != ResultOk) {
256  if ((mResult != ResultCriticalError) &&
257  !mExecuting && !mExecutingLock && !mFetchExecuting) {
258  mResult = ResultOk; // Recoverable error
259  if (!mFetchSerNums.isEmpty()) {
260  mFetchSerNums.push_back( mFetchSerNums.first() );
261  mFetchSerNums.pop_front();
262  }
263  } else
264  return; // An error has already occurred don't even try to process this msg
265  }
266  if (MessageProperty::filtering( serNum )) {
267  // Not good someone else is already filtering this msg
268  mResult = ResultError;
269  if (!mExecuting && !mFetchExecuting)
270  finishTimer->start( 0, true );
271  } else {
272  // Everything is ok async fetch this message
273  mFetchSerNums.append( serNum );
274  if (!mFetchExecuting) {
275  //Need to (re)start incomplete msg fetching chain
276  mFetchExecuting = true;
277  fetchMessageTimer->start( 0, true );
278  }
279  }
280 }
281 
282 KMMsgBase *ActionScheduler::messageBase(TQ_UINT32 serNum)
283 {
284  int idx = -1;
285  KMFolder *folder = 0;
286  KMMsgBase *msg = 0;
287  KMMsgDict::instance()->getLocation( serNum, &folder, &idx );
288  // It's possible that the message has been deleted or moved into a
289  // different folder
290  if (folder && (idx != -1)) {
291  // everything is ok
292  tempOpenFolder( folder ); // just in case msg has moved
293  msg = folder->getMsgBase( idx );
294  } else {
295  // the message is gone!
296  mResult = ResultError;
297  finishTimer->start( 0, true );
298  }
299  return msg;
300 }
301 
302 KMMessage *ActionScheduler::message(TQ_UINT32 serNum)
303 {
304  int idx = -1;
305  KMFolder *folder = 0;
306  KMMessage *msg = 0;
307  KMMsgDict::instance()->getLocation( serNum, &folder, &idx );
308  // It's possible that the message has been deleted or moved into a
309  // different folder
310  if (folder && (idx != -1)) {
311  // everything is ok
312  msg = folder->getMsg( idx );
313  tempOpenFolder( folder ); // just in case msg has moved
314  } else {
315  // the message is gone!
316  mResult = ResultError;
317  finishTimer->start( 0, true );
318  }
319  return msg;
320 }
321 
322 void ActionScheduler::finish()
323 {
324  if (mResult != ResultOk) {
325  // Must handle errors immediately
326  emit result( mResult );
327  return;
328  }
329 
330  if (!mExecuting) {
331 
332  if (!mFetchSerNums.isEmpty()) {
333  // Possibly if (mResult == ResultOk) should cancel job and start again.
334  // Believe smarter logic to bail out if an error has occurred is required.
335  // Perhaps should be testing for mFetchExecuting or at least set it to true
336  fetchMessageTimer->start( 0, true ); // give it a bit of time at a test
337  return;
338  } else {
339  mFetchExecuting = false;
340  }
341 
342  if (mSerNums.begin() != mSerNums.end()) {
343  mExecuting = true;
344  processMessageTimer->start( 0, true );
345  return;
346  }
347 
348  // If an error has occurred and a permanent source folder has
349  // been set then move all the messages left in the source folder
350  // to the inbox. If no permanent source folder has been set
351  // then abandon filtering of queued messages.
352  if (!mDeleteSrcFolder && !mDestFolder.isNull() ) {
353  while ( mSrcFolder->count() > 0 ) {
354  KMMessage *msg = mSrcFolder->getMsg( 0 );
355  mDestFolder->moveMsg( msg );
356  }
357 
358  // Wait a little while before closing temp folders, just in case
359  // new messages arrive for filtering.
360  tempCloseFoldersTimer->start( 60*1000, true );
361  }
362  mSerNums.clear(); //abandon
363  mFetchSerNums.clear(); //abandon
364 
365  if (mFiltersAreQueued)
366  mFilters = mQueuedFilters;
367  mQueuedFilters.clear();
368  mFiltersAreQueued = false;
369  ReturnCode aResult = mResult;
370  mResult = ResultOk;
371  mExecutingLock = false;
372  emit result( aResult );
373  if (mAutoDestruct)
374  delete this;
375  }
376  // else a message may be in the process of being fetched or filtered
377  // wait until both of these commitments are finished then this
378  // method should be called again.
379 }
380 
381 void ActionScheduler::fetchMessage()
382 {
383  TQValueListIterator<TQ_UINT32> mFetchMessageIt = mFetchSerNums.begin();
384  while (mFetchMessageIt != mFetchSerNums.end()) {
385  if (!MessageProperty::transferInProgress(*mFetchMessageIt))
386  break;
387  ++mFetchMessageIt;
388  }
389 
390  // Note: Perhaps this could be improved. We shouldn't give up straight away
391  // if !mFetchSerNums.isEmpty (becausing transferInProgress is true
392  // for some messages). Instead we should delay for a minute or so and
393  // again.
394  if (mFetchMessageIt == mFetchSerNums.end() && !mFetchSerNums.isEmpty()) {
395  mResult = ResultError;
396  }
397  if ((mFetchMessageIt == mFetchSerNums.end()) || (mResult != ResultOk)) {
398  mFetchExecuting = false;
399  if (!mSrcFolder->count())
400  mSrcFolder->expunge();
401  finishTimer->start( 0, true );
402  return;
403  }
404 
405  //If we got this far then there's a valid message to work with
406  KMMsgBase *msgBase = messageBase( *mFetchMessageIt );
407 
408  if ((mResult != ResultOk) || (!msgBase)) {
409  mFetchExecuting = false;
410  return;
411  }
412  mFetchUnget = msgBase->isMessage();
413  KMMessage *msg = message( *mFetchMessageIt );
414  if (mResult != ResultOk) {
415  mFetchExecuting = false;
416  return;
417  }
418 
419  if (msg && msg->isComplete()) {
420  messageFetched( msg );
421  } else if (msg) {
422  fetchTimeOutTime = TQTime::currentTime();
423  fetchTimeOutTimer->start( 60 * 1000, true );
424  FolderJob *job = msg->parent()->createJob( msg );
425  connect( job, TQ_SIGNAL(messageRetrieved( KMMessage* )),
426  TQ_SLOT(messageFetched( KMMessage* )) );
427  lastJob = job;
428  job->start();
429  } else {
430  mFetchExecuting = false;
431  mResult = ResultError;
432  finishTimer->start( 0, true );
433  return;
434  }
435 }
436 
437 void ActionScheduler::messageFetched( KMMessage *msg )
438 {
439  fetchTimeOutTimer->stop();
440  if (!msg) {
441  // Should never happen, but sometimes does;
442  fetchMessageTimer->start( 0, true );
443  return;
444  }
445 
446  mFetchSerNums.remove( msg->getMsgSerNum() );
447 
448  // Note: This may not be necessary. What about when it's time to
449  // delete the original message?
450  // Is the new serial number being set correctly then?
451  if ((mSet & KMFilterMgr::Explicit) ||
452  (msg->headerField( "X-KMail-Filtered" ).isEmpty())) {
453  TQString serNumS;
454  serNumS.setNum( msg->getMsgSerNum() );
455  KMMessage *newMsg = new KMMessage;
456  newMsg->fromString(msg->asString());
457  newMsg->setStatus(msg->status());
458  newMsg->setComplete(msg->isComplete());
459  newMsg->setHeaderField( "X-KMail-Filtered", serNumS );
460  mSrcFolder->addMsg( newMsg );
461  } else {
462  fetchMessageTimer->start( 0, true );
463  }
464  if (mFetchUnget && msg->parent())
465  msg->parent()->unGetMsg( msg->parent()->find( msg ));
466  return;
467 }
468 
469 void ActionScheduler::msgAdded( KMFolder*, TQ_UINT32 serNum )
470 {
471  if (!mIgnore)
472  enqueue( serNum );
473 }
474 
475 void ActionScheduler::enqueue(TQ_UINT32 serNum)
476 {
477  if (mResult != ResultOk)
478  return; // An error has already occurred don't even try to process this msg
479 
480  if (MessageProperty::filtering( serNum )) {
481  // Not good someone else is already filtering this msg
482  mResult = ResultError;
483  if (!mExecuting && !mFetchExecuting)
484  finishTimer->start( 0, true );
485  } else {
486  // Everything is ok async filter this message
487  mSerNums.append( serNum );
488 
489  if (!mExecuting) {
490  // Note: Need to (re)start incomplete msg filtering chain
491  // The state of mFetchExecuting is of some concern.
492  mExecuting = true;
493  mMessageIt = mSerNums.begin();
494  processMessageTimer->start( 0, true );
495  }
496  }
497 }
498 
499 void ActionScheduler::processMessage()
500 {
501  if (mExecutingLock)
502  return;
503  mExecutingLock = true;
504  mMessageIt = mSerNums.begin();
505  while (mMessageIt != mSerNums.end()) {
506  if (!MessageProperty::transferInProgress(*mMessageIt))
507  break;
508  ++mMessageIt;
509  }
510 
511  if (mMessageIt == mSerNums.end() && !mSerNums.isEmpty()) {
512  mExecuting = false;
513  processMessageTimer->start( 600, true );
514  }
515 
516  if ((mMessageIt == mSerNums.end()) || (mResult != ResultOk)) {
517  mExecutingLock = false;
518  mExecuting = false;
519  finishTimer->start( 0, true );
520  return;
521  }
522 
523  //If we got this far then there's a valid message to work with
524  KMMsgBase *msgBase = messageBase( *mMessageIt );
525  if (!msgBase || mResult != ResultOk) {
526  mExecuting = false;
527  return;
528  }
529 
530  MessageProperty::setFiltering( *mMessageIt, true );
531  MessageProperty::setFilterHandler( *mMessageIt, this );
532  MessageProperty::setFilterFolder( *mMessageIt, mDestFolder );
533  if ( FilterLog::instance()->isLogging() ) {
534  FilterLog::instance()->addSeparator();
535  }
536  mFilterIt = mFilters.begin();
537 
538  mUnget = msgBase->isMessage();
539  KMMessage *msg = message( *mMessageIt );
540  if (mResult != ResultOk) {
541  mExecuting = false;
542  return;
543  }
544 
545  bool mdnEnabled = true;
546  {
547  TDEConfigGroup mdnConfig( kmkernel->config(), "MDN" );
548  int mode = mdnConfig.readNumEntry( "default-policy", 0 );
549  if (!mode || mode < 0 || mode > 3)
550  mdnEnabled = false;
551  }
552  mdnEnabled = true; // For 3.2 force all mails to be complete
553 
554  if ((msg && msg->isComplete()) ||
555  (msg && !(*mFilterIt).requiresBody(msg) && !mdnEnabled))
556  {
557  // We have a complete message or
558  // we can work with an incomplete message
559  // Get a write lock on the message while it's being filtered
560  msg->setTransferInProgress( true );
561  filterMessageTimer->start( 0, true );
562  return;
563  }
564  if (msg) {
565  FolderJob *job = msg->parent()->createJob( msg );
566  connect( job, TQ_SIGNAL(messageRetrieved( KMMessage* )),
567  TQ_SLOT(messageRetrieved( KMMessage* )) );
568  job->start();
569  } else {
570  mExecuting = false;
571  mResult = ResultError;
572  finishTimer->start( 0, true );
573  return;
574  }
575 }
576 
577 void ActionScheduler::messageRetrieved(KMMessage* msg)
578 {
579  // Get a write lock on the message while it's being filtered
580  msg->setTransferInProgress( true );
581  filterMessageTimer->start( 0, true );
582 }
583 
584 void ActionScheduler::filterMessage()
585 {
586  if (mFilterIt == mFilters.end()) {
587  moveMessage();
588  return;
589  }
590  if (((mSet & KMFilterMgr::Outbound) && (*mFilterIt).applyOnOutbound()) ||
591  ((mSet & KMFilterMgr::Inbound) && (*mFilterIt).applyOnInbound() &&
592  (!mAccount ||
593  (mAccount && (*mFilterIt).applyOnAccount(mAccountId)))) ||
594  ((mSet & KMFilterMgr::Explicit) && (*mFilterIt).applyOnExplicit())) {
595 
596  // filter is applicable
597  if ( FilterLog::instance()->isLogging() ) {
598  TQString logText( i18n( "<b>Evaluating filter rules:</b> " ) );
599  logText.append( (*mFilterIt).pattern()->asString() );
600  FilterLog::instance()->add( logText, FilterLog::patternDesc );
601  }
602  if (mAlwaysMatch ||
603  (*mFilterIt).pattern()->matches( *mMessageIt )) {
604  if ( FilterLog::instance()->isLogging() ) {
605  FilterLog::instance()->add( i18n( "<b>Filter rules have matched.</b>" ),
606  FilterLog::patternResult );
607  }
608  mFilterAction = (*mFilterIt).actions()->first();
609  actionMessage();
610  return;
611  }
612  }
613  ++mFilterIt;
614  filterMessageTimer->start( 0, true );
615 }
616 
617 void ActionScheduler::actionMessage(KMFilterAction::ReturnCode res)
618 {
619  if (res == KMFilterAction::CriticalError) {
620  mResult = ResultCriticalError;
621  finish(); //must handle critical errors immediately
622  }
623  if (mFilterAction) {
624  KMMessage *msg = message( *mMessageIt );
625  if (msg) {
626  if ( FilterLog::instance()->isLogging() ) {
627  TQString logText( i18n( "<b>Applying filter action:</b> %1" )
628  .arg( mFilterAction->displayString() ) );
629  FilterLog::instance()->add( logText, FilterLog::appliedAction );
630  }
631  KMFilterAction *action = mFilterAction;
632  mFilterAction = (*mFilterIt).actions()->next();
633  action->processAsync( msg );
634  }
635  } else {
636  // there are no more actions
637  if ((*mFilterIt).stopProcessingHere())
638  mFilterIt = mFilters.end();
639  else
640  ++mFilterIt;
641  filterMessageTimer->start( 0, true );
642  }
643 }
644 
645 void ActionScheduler::moveMessage()
646 {
647  KMMsgBase *msgBase = messageBase( *mMessageIt );
648  if (!msgBase)
649  return;
650 
651  MessageProperty::setTransferInProgress( *mMessageIt, false, true );
652  KMMessage *msg = message( *mMessageIt );
653  KMFolder *folder = MessageProperty::filterFolder( *mMessageIt );
654  TQString serNumS = msg->headerField( "X-KMail-Filtered" );
655  if (!serNumS.isEmpty())
656  mOriginalSerNum = serNumS.toUInt();
657  else
658  mOriginalSerNum = 0;
659  MessageProperty::setFilterHandler( *mMessageIt, 0 );
660  MessageProperty::setFiltering( *mMessageIt, false );
661  mSerNums.remove( *mMessageIt );
662 
663  KMMessage *orgMsg = 0;
664  ReturnCode mOldReturnCode = mResult;
665  if (mOriginalSerNum)
666  orgMsg = message( mOriginalSerNum );
667  mResult = mOldReturnCode; // ignore errors in deleting original message
668  if (!orgMsg || !orgMsg->parent()) {
669  // Original message is gone, no point filtering it anymore
670  mSrcFolder->removeMsg( mSrcFolder->find( msg ) );
671  kdDebug(5006) << "The original serial number is missing. "
672  << "Cannot complete the filtering." << endl;
673  mExecutingLock = false;
674  processMessageTimer->start( 0, true );
675  return;
676  } else {
677  if (!folder) // no filter folder specified leave in current place
678  folder = orgMsg->parent();
679  }
680 
681  mIgnore = true;
682  assert( msg->parent() == mSrcFolder.operator->() );
683  mSrcFolder->take( mSrcFolder->find( msg ) );
684  mSrcFolder->addMsg( msg );
685  mIgnore = false;
686 
687  if (msg && folder && kmkernel->folderIsTrash( folder ))
688  KMFilterAction::sendMDN( msg, KMime::MDN::Deleted );
689 
690  timeOutTime = TQTime::currentTime();
691  KMCommand *cmd = new KMMoveCommand( folder, msg );
692  connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
693  this, TQ_SLOT( moveMessageFinished( KMCommand * ) ) );
694  cmd->start();
695  // sometimes the move command doesn't complete so time out after a minute
696  // and move onto the next message
697  lastCommand = cmd;
698  timeOutTimer->start( 60 * 1000, true );
699 }
700 
701 void ActionScheduler::moveMessageFinished( KMCommand *command )
702 {
703  timeOutTimer->stop();
704  if ( command->result() != KMCommand::OK )
705  mResult = ResultError;
706 
707  if (!mSrcFolder->count())
708  mSrcFolder->expunge();
709 
710  // in case the message stayed in the current folder TODO optimize
711  if ( mHeaders )
712  mHeaders->clearSelectableAndAboutToBeDeleted( mOriginalSerNum );
713  KMMessage *msg = 0;
714  ReturnCode mOldReturnCode = mResult;
715  if (mOriginalSerNum) {
716  msg = message( mOriginalSerNum );
717  emit filtered( mOriginalSerNum );
718  }
719 
720  mResult = mOldReturnCode; // ignore errors in deleting original message
721  KMCommand *cmd = 0;
722  if (msg && msg->parent()) {
723  cmd = new KMMoveCommand( 0, msg );
724 // cmd->start(); // Note: sensitive logic here.
725  }
726 
727  if (mResult == ResultOk) {
728  mExecutingLock = false;
729  if (cmd)
730  connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
731  this, TQ_SLOT( processMessage() ) );
732  else
733  processMessageTimer->start( 0, true );
734  } else {
735  // Note: An alternative to consider is just calling
736  // finishTimer->start and returning
737  if (cmd)
738  connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
739  this, TQ_SLOT( finish() ) );
740  else
741  finishTimer->start( 0, true );
742  }
743  if (cmd)
744  cmd->start();
745  // else moveMessageFinished should call finish
746 }
747 
748 void ActionScheduler::copyMessageFinished( KMCommand *command )
749 {
750  if ( command->result() != KMCommand::OK )
751  actionMessage( KMFilterAction::ErrorButGoOn );
752  else
753  actionMessage();
754 }
755 
756 void ActionScheduler::timeOut()
757 {
758  // Note: This is a good place for a debug statement
759  assert( lastCommand );
760  // sometimes imap jobs seem to just stall so give up and move on
761  disconnect( lastCommand, TQ_SIGNAL( completed( KMCommand * ) ),
762  this, TQ_SLOT( moveMessageFinished( KMCommand * ) ) );
763  lastCommand = 0;
764  mExecutingLock = false;
765  mExecuting = false;
766  finishTimer->start( 0, true );
767  if (mOriginalSerNum) // Try again
768  execFilters( mOriginalSerNum );
769 }
770 
771 void ActionScheduler::fetchTimeOut()
772 {
773  // Note: This is a good place for a debug statement
774  assert( lastJob );
775  // sometimes imap jobs seem to just stall so give up and move on
776  disconnect( lastJob, TQ_SIGNAL(messageRetrieved( KMMessage* )),
777  this, TQ_SLOT(messageFetched( KMMessage* )) );
778  lastJob->kill();
779  lastJob = 0;
780  fetchMessageTimer->start( 0, true );
781 }
782 
783 TQString ActionScheduler::debug()
784 {
785  TQString res;
786  TQValueList<ActionScheduler*>::iterator it;
787  int i = 1;
788  for ( it = schedulerList->begin(); it != schedulerList->end(); ++it ) {
789  res.append( TQString( "ActionScheduler #%1.\n" ).arg( i ) );
790  if ((*it)->mAccount && kmkernel->find( (*it)->mAccountId )) {
791  res.append( TQString( "Account %1, Name %2.\n" )
792  .arg( (*it)->mAccountId )
793  .arg( kmkernel->acctMgr()->find( (*it)->mAccountId )->name() ) );
794  }
795  res.append( TQString( "mExecuting %1, " ).arg( (*it)->mExecuting ? "true" : "false" ) );
796  res.append( TQString( "mExecutingLock %1, " ).arg( (*it)->mExecutingLock ? "true" : "false" ) );
797  res.append( TQString( "mFetchExecuting %1.\n" ).arg( (*it)->mFetchExecuting ? "true" : "false" ) );
798  res.append( TQString( "mOriginalSerNum %1.\n" ).arg( (*it)->mOriginalSerNum ) );
799  res.append( TQString( "mMessageIt %1.\n" ).arg( ((*it)->mMessageIt != 0) ? *(*it)->mMessageIt : 0 ) );
800  res.append( TQString( "mSerNums count %1, " ).arg( (*it)->mSerNums.count() ) );
801  res.append( TQString( "mFetchSerNums count %1.\n" ).arg( (*it)->mFetchSerNums.count() ) );
802  res.append( TQString( "mResult " ) );
803  if ((*it)->mResult == ResultOk)
804  res.append( TQString( "ResultOk.\n" ) );
805  else if ((*it)->mResult == ResultError)
806  res.append( TQString( "ResultError.\n" ) );
807  else if ((*it)->mResult == ResultCriticalError)
808  res.append( TQString( "ResultCriticalError.\n" ) );
809  else
810  res.append( TQString( "Unknown.\n" ) );
811 
812  ++i;
813  }
814  return res;
815 }
816 
817 bool ActionScheduler::isEnabled()
818 {
819  if (sEnabledChecked)
820  return sEnabled;
821 
822  sEnabledChecked = true;
823  TDEConfig* config = KMKernel::config();
824  TDEConfigGroupSaver saver(config, "General");
825  sEnabled = config->readBoolEntry("action-scheduler", false);
826  return sEnabled;
827 }
828 
829 bool ActionScheduler::ignoreChanges( bool ignore )
830 {
831  bool oldValue = mIgnore;
832  mIgnore = ignore;
833  return oldValue;
834 }
835 
836 #include "actionscheduler.moc"
Abstract base class for KMail's filter actions.
ReturnCode
Possible return codes of process:
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.
virtual void processAsync(KMMessage *msg) const
Execute an action on given message asynchronously.
Mail folder.
Definition: kmfolder.h:69
int expunge()
Delete entire folder.
Definition: kmfolder.cpp:526
void close(const char *owner, bool force=false)
Close folder.
Definition: kmfolder.cpp:489
KMMessage * getMsg(int idx)
Read message at given index.
Definition: kmfolder.cpp:321
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
Definition: kmfolder.cpp:360
int open(const char *owner)
Open folder for access.
Definition: kmfolder.cpp:479
The widget that shows the contents of folders.
Definition: kmheaders.h:47
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:243
void setStatus(const KMMsgStatus status, int idx=-1)
Set status and mark dirty.
Definition: kmmessage.cpp:4153
TQCString asString() const
Return the entire message contents as a string.
Definition: kmmessage.cpp:314
void setComplete(bool v)
Set if the message is a complete message.
Definition: kmmessage.h:869
KMMsgStatus status() const
Status of the message.
Definition: kmmessage.h:830
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
Definition: kmmessage.cpp:2289
bool isComplete() const
Return true if the complete message is available without referring to the backing store.
Definition: kmmessage.h:867
void setHeaderField(const TQCString &name, const TQString &value, HeaderFieldType type=Unstructured, bool prepend=false)
Set the header field with the given name to the given value.
Definition: kmmessage.cpp:2339
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
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167
The account manager is responsible for creating accounts of various types via the factory method crea...
folderdiaquotatab.h
Definition: aboutdata.cpp:40