actionscheduler.cpp
00001 /* Action Scheduler 00002 00003 This file is part of KMail, the KDE mail client. 00004 Copyright (c) Don Sanders <sanders@kde.org> 00005 00006 KMail is free software; you can redistribute it and/or modify it 00007 under the terms of the GNU General Public License, version 2, as 00008 published by the Free Software Foundation. 00009 00010 KMail is distributed in the hope that it will be useful, but 00011 WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with this program; if not, write to the Free Software 00017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00018 00019 In addition, as a special exception, the copyright holders give 00020 permission to link the code of this program with any edition of 00021 the TQt library by Trolltech AS, Norway (or with modified versions 00022 of TQt that use the same license as TQt), and distribute linked 00023 combinations including the two. You must obey the GNU General 00024 Public License in all respects for all of the code used other than 00025 TQt. If you modify this file, you may extend this exception to 00026 your version of the file, but you are not obligated to do so. If 00027 you do not wish to do so, delete this exception statement from 00028 your version. 00029 */ 00030 #include <kdebug.h> // FIXME 00031 00032 #ifdef HAVE_CONFIG_H 00033 #include <config.h> 00034 #endif 00035 00036 #include "actionscheduler.h" 00037 00038 #include "filterlog.h" 00039 #include "messageproperty.h" 00040 #include "kmfilter.h" 00041 #include "kmfolderindex.h" 00042 #include "kmfoldermgr.h" 00043 #include "kmmsgdict.h" 00044 #include "kmcommands.h" 00045 #include "kmheaders.h" 00046 #include "accountmanager.h" 00047 using KMail::AccountManager; 00048 00049 #include <tqtimer.h> 00050 #include <tdeconfig.h> 00051 #include <kstandarddirs.h> 00052 00053 using namespace KMail; 00054 typedef TQPtrList<KMMsgBase> KMMessageList; 00055 00056 00057 KMFolderMgr* ActionScheduler::tempFolderMgr = 0; 00058 int ActionScheduler::refCount = 0; 00059 int ActionScheduler::count = 0; 00060 TQValueList<ActionScheduler*> *ActionScheduler::schedulerList = 0; 00061 bool ActionScheduler::sEnabled = false; 00062 bool ActionScheduler::sEnabledChecked = false; 00063 00064 ActionScheduler::ActionScheduler(KMFilterMgr::FilterSet set, 00065 TQValueList<KMFilter*> filters, 00066 KMHeaders *headers, 00067 KMFolder *srcFolder) 00068 :mSet( set ), mHeaders( headers ) 00069 { 00070 ++count; 00071 ++refCount; 00072 mExecuting = false; 00073 mExecutingLock = false; 00074 mFetchExecuting = false; 00075 mFiltersAreQueued = false; 00076 mResult = ResultOk; 00077 mIgnore = false; 00078 mAutoDestruct = false; 00079 mAlwaysMatch = false; 00080 mAccountId = 0; 00081 mAccount = false; 00082 lastCommand = 0; 00083 lastJob = 0; 00084 finishTimer = new TQTimer( this, "finishTimer" ); 00085 connect( finishTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(finish())); 00086 fetchMessageTimer = new TQTimer( this, "fetchMessageTimer" ); 00087 connect( fetchMessageTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(fetchMessage())); 00088 tempCloseFoldersTimer = new TQTimer( this, "tempCloseFoldersTimer" ); 00089 connect( tempCloseFoldersTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(tempCloseFolders())); 00090 processMessageTimer = new TQTimer( this, "processMessageTimer" ); 00091 connect( processMessageTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(processMessage())); 00092 filterMessageTimer = new TQTimer( this, "filterMessageTimer" ); 00093 connect( filterMessageTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(filterMessage())); 00094 timeOutTimer = new TQTimer( this, "timeOutTimer" ); 00095 connect( timeOutTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(timeOut())); 00096 fetchTimeOutTimer = new TQTimer( this, "fetchTimeOutTimer" ); 00097 connect( fetchTimeOutTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(fetchTimeOut())); 00098 00099 TQValueList<KMFilter*>::Iterator it = filters.begin(); 00100 for (; it != filters.end(); ++it) 00101 mFilters.append( **it ); 00102 mDestFolder = 0; 00103 if (srcFolder) { 00104 mDeleteSrcFolder = false; 00105 setSourceFolder( srcFolder ); 00106 } else { 00107 TQString tmpName; 00108 tmpName.setNum( count ); 00109 if (!tempFolderMgr) 00110 tempFolderMgr = new KMFolderMgr(locateLocal("data","kmail/filter")); 00111 KMFolder *tempFolder = tempFolderMgr->findOrCreate( tmpName ); 00112 tempFolder->expunge(); 00113 mDeleteSrcFolder = true; 00114 setSourceFolder( tempFolder ); 00115 } 00116 if (!schedulerList) 00117 schedulerList = new TQValueList<ActionScheduler*>; 00118 schedulerList->append( this ); 00119 } 00120 00121 ActionScheduler::~ActionScheduler() 00122 { 00123 schedulerList->remove( this ); 00124 tempCloseFolders(); 00125 disconnect( mSrcFolder, TQT_SIGNAL(closed()), 00126 this, TQT_SLOT(folderClosedOrExpunged()) ); 00127 disconnect( mSrcFolder, TQT_SIGNAL(expunged(KMFolder*)), 00128 this, TQT_SLOT(folderClosedOrExpunged()) ); 00129 mSrcFolder->close("actionschedsrc"); 00130 00131 if (mDeleteSrcFolder) 00132 tempFolderMgr->remove(mSrcFolder); 00133 00134 --refCount; 00135 if (refCount == 0) { 00136 delete tempFolderMgr; 00137 tempFolderMgr = 0; 00138 } 00139 } 00140 00141 void ActionScheduler::setAutoDestruct( bool autoDestruct ) 00142 { 00143 mAutoDestruct = autoDestruct; 00144 } 00145 00146 void ActionScheduler::setAlwaysMatch( bool alwaysMatch ) 00147 { 00148 mAlwaysMatch = alwaysMatch; 00149 } 00150 00151 void ActionScheduler::setDefaultDestinationFolder( KMFolder *destFolder ) 00152 { 00153 mDestFolder = destFolder; 00154 } 00155 00156 void ActionScheduler::setSourceFolder( KMFolder *srcFolder ) 00157 { 00158 srcFolder->open("actionschedsrc"); 00159 if (mSrcFolder) { 00160 disconnect( mSrcFolder, TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)), 00161 this, TQT_SLOT(msgAdded(KMFolder*, TQ_UINT32)) ); 00162 disconnect( mSrcFolder, TQT_SIGNAL(closed()), 00163 this, TQT_SLOT(folderClosedOrExpunged()) ); 00164 disconnect( mSrcFolder, TQT_SIGNAL(expunged(KMFolder*)), 00165 this, TQT_SLOT(folderClosedOrExpunged()) ); 00166 mSrcFolder->close("actionschedsrc"); 00167 } 00168 mSrcFolder = srcFolder; 00169 int i = 0; 00170 for (i = 0; i < mSrcFolder->count(); ++i) 00171 enqueue( mSrcFolder->getMsgBase( i )->getMsgSerNum() ); 00172 if (mSrcFolder) { 00173 connect( mSrcFolder, TQT_SIGNAL(msgAdded(KMFolder*, TQ_UINT32)), 00174 this, TQT_SLOT(msgAdded(KMFolder*, TQ_UINT32)) ); 00175 connect( mSrcFolder, TQT_SIGNAL(closed()), 00176 this, TQT_SLOT(folderClosedOrExpunged()) ); 00177 connect( mSrcFolder, TQT_SIGNAL(expunged(KMFolder*)), 00178 this, TQT_SLOT(folderClosedOrExpunged()) ); 00179 } 00180 } 00181 00182 void ActionScheduler::setFilterList( TQValueList<KMFilter*> filters ) 00183 { 00184 mFiltersAreQueued = true; 00185 mQueuedFilters.clear(); 00186 00187 TQValueList<KMFilter*>::Iterator it = filters.begin(); 00188 for (; it != filters.end(); ++it) 00189 mQueuedFilters.append( **it ); 00190 if (!mExecuting) { 00191 mFilters = mQueuedFilters; 00192 mFiltersAreQueued = false; 00193 mQueuedFilters.clear(); 00194 } 00195 } 00196 00197 void ActionScheduler::folderClosedOrExpunged() 00198 { 00199 // mSrcFolder has been closed. reopen it. 00200 if ( mSrcFolder ) 00201 { 00202 mSrcFolder->open( "actionsched" ); 00203 } 00204 } 00205 00206 int ActionScheduler::tempOpenFolder( KMFolder* aFolder ) 00207 { 00208 assert( aFolder ); 00209 tempCloseFoldersTimer->stop(); 00210 if ( aFolder == mSrcFolder.operator->() ) 00211 return 0; 00212 00213 int rc = aFolder->open("actionsched"); 00214 if (rc) 00215 return rc; 00216 00217 mOpenFolders.append( aFolder ); 00218 return 0; 00219 } 00220 00221 void ActionScheduler::tempCloseFolders() 00222 { 00223 // close temp opened folders 00224 TQValueListConstIterator<TQGuardedPtr<KMFolder> > it; 00225 for (it = mOpenFolders.begin(); it != mOpenFolders.end(); ++it) { 00226 KMFolder *folder = *it; 00227 if (folder) 00228 folder->close("actionsched"); 00229 } 00230 mOpenFolders.clear(); 00231 } 00232 00233 void ActionScheduler::execFilters(const TQValueList<TQ_UINT32> serNums) 00234 { 00235 TQValueListConstIterator<TQ_UINT32> it; 00236 for (it = serNums.begin(); it != serNums.end(); ++it) 00237 execFilters( *it ); 00238 } 00239 00240 void ActionScheduler::execFilters(const TQPtrList<KMMsgBase> msgList) 00241 { 00242 KMMsgBase *msgBase; 00243 TQPtrList<KMMsgBase> list = msgList; 00244 for (msgBase = list.first(); msgBase; msgBase = list.next()) 00245 execFilters( msgBase->getMsgSerNum() ); 00246 } 00247 00248 void ActionScheduler::execFilters(KMMsgBase* msgBase) 00249 { 00250 execFilters( msgBase->getMsgSerNum() ); 00251 } 00252 00253 void ActionScheduler::execFilters(TQ_UINT32 serNum) 00254 { 00255 if (mResult != ResultOk) { 00256 if ((mResult != ResultCriticalError) && 00257 !mExecuting && !mExecutingLock && !mFetchExecuting) { 00258 mResult = ResultOk; // Recoverable error 00259 if (!mFetchSerNums.isEmpty()) { 00260 mFetchSerNums.push_back( mFetchSerNums.first() ); 00261 mFetchSerNums.pop_front(); 00262 } 00263 } else 00264 return; // An error has already occurred don't even try to process this msg 00265 } 00266 if (MessageProperty::filtering( serNum )) { 00267 // Not good someone else is already filtering this msg 00268 mResult = ResultError; 00269 if (!mExecuting && !mFetchExecuting) 00270 finishTimer->start( 0, true ); 00271 } else { 00272 // Everything is ok async fetch this message 00273 mFetchSerNums.append( serNum ); 00274 if (!mFetchExecuting) { 00275 //Need to (re)start incomplete msg fetching chain 00276 mFetchExecuting = true; 00277 fetchMessageTimer->start( 0, true ); 00278 } 00279 } 00280 } 00281 00282 KMMsgBase *ActionScheduler::messageBase(TQ_UINT32 serNum) 00283 { 00284 int idx = -1; 00285 KMFolder *folder = 0; 00286 KMMsgBase *msg = 0; 00287 KMMsgDict::instance()->getLocation( serNum, &folder, &idx ); 00288 // It's possible that the message has been deleted or moved into a 00289 // different folder 00290 if (folder && (idx != -1)) { 00291 // everything is ok 00292 tempOpenFolder( folder ); // just in case msg has moved 00293 msg = folder->getMsgBase( idx ); 00294 } else { 00295 // the message is gone! 00296 mResult = ResultError; 00297 finishTimer->start( 0, true ); 00298 } 00299 return msg; 00300 } 00301 00302 KMMessage *ActionScheduler::message(TQ_UINT32 serNum) 00303 { 00304 int idx = -1; 00305 KMFolder *folder = 0; 00306 KMMessage *msg = 0; 00307 KMMsgDict::instance()->getLocation( serNum, &folder, &idx ); 00308 // It's possible that the message has been deleted or moved into a 00309 // different folder 00310 if (folder && (idx != -1)) { 00311 // everything is ok 00312 msg = folder->getMsg( idx ); 00313 tempOpenFolder( folder ); // just in case msg has moved 00314 } else { 00315 // the message is gone! 00316 mResult = ResultError; 00317 finishTimer->start( 0, true ); 00318 } 00319 return msg; 00320 } 00321 00322 void ActionScheduler::finish() 00323 { 00324 if (mResult != ResultOk) { 00325 // Must handle errors immediately 00326 emit result( mResult ); 00327 return; 00328 } 00329 00330 if (!mExecuting) { 00331 00332 if (!mFetchSerNums.isEmpty()) { 00333 // Possibly if (mResult == ResultOk) should cancel job and start again. 00334 // Believe smarter logic to bail out if an error has occurred is required. 00335 // Perhaps should be testing for mFetchExecuting or at least set it to true 00336 fetchMessageTimer->start( 0, true ); // give it a bit of time at a test 00337 return; 00338 } else { 00339 mFetchExecuting = false; 00340 } 00341 00342 if (mSerNums.begin() != mSerNums.end()) { 00343 mExecuting = true; 00344 processMessageTimer->start( 0, true ); 00345 return; 00346 } 00347 00348 // If an error has occurred and a permanent source folder has 00349 // been set then move all the messages left in the source folder 00350 // to the inbox. If no permanent source folder has been set 00351 // then abandon filtering of queued messages. 00352 if (!mDeleteSrcFolder && !mDestFolder.isNull() ) { 00353 while ( mSrcFolder->count() > 0 ) { 00354 KMMessage *msg = mSrcFolder->getMsg( 0 ); 00355 mDestFolder->moveMsg( msg ); 00356 } 00357 00358 // Wait a little while before closing temp folders, just in case 00359 // new messages arrive for filtering. 00360 tempCloseFoldersTimer->start( 60*1000, true ); 00361 } 00362 mSerNums.clear(); //abandon 00363 mFetchSerNums.clear(); //abandon 00364 00365 if (mFiltersAreQueued) 00366 mFilters = mQueuedFilters; 00367 mQueuedFilters.clear(); 00368 mFiltersAreQueued = false; 00369 ReturnCode aResult = mResult; 00370 mResult = ResultOk; 00371 mExecutingLock = false; 00372 emit result( aResult ); 00373 if (mAutoDestruct) 00374 delete this; 00375 } 00376 // else a message may be in the process of being fetched or filtered 00377 // wait until both of these commitments are finished then this 00378 // method should be called again. 00379 } 00380 00381 void ActionScheduler::fetchMessage() 00382 { 00383 TQValueListIterator<TQ_UINT32> mFetchMessageIt = mFetchSerNums.begin(); 00384 while (mFetchMessageIt != mFetchSerNums.end()) { 00385 if (!MessageProperty::transferInProgress(*mFetchMessageIt)) 00386 break; 00387 ++mFetchMessageIt; 00388 } 00389 00390 // Note: Perhaps this could be improved. We shouldn't give up straight away 00391 // if !mFetchSerNums.isEmpty (becausing transferInProgress is true 00392 // for some messages). Instead we should delay for a minute or so and 00393 // again. 00394 if (mFetchMessageIt == mFetchSerNums.end() && !mFetchSerNums.isEmpty()) { 00395 mResult = ResultError; 00396 } 00397 if ((mFetchMessageIt == mFetchSerNums.end()) || (mResult != ResultOk)) { 00398 mFetchExecuting = false; 00399 if (!mSrcFolder->count()) 00400 mSrcFolder->expunge(); 00401 finishTimer->start( 0, true ); 00402 return; 00403 } 00404 00405 //If we got this far then there's a valid message to work with 00406 KMMsgBase *msgBase = messageBase( *mFetchMessageIt ); 00407 00408 if ((mResult != ResultOk) || (!msgBase)) { 00409 mFetchExecuting = false; 00410 return; 00411 } 00412 mFetchUnget = msgBase->isMessage(); 00413 KMMessage *msg = message( *mFetchMessageIt ); 00414 if (mResult != ResultOk) { 00415 mFetchExecuting = false; 00416 return; 00417 } 00418 00419 if (msg && msg->isComplete()) { 00420 messageFetched( msg ); 00421 } else if (msg) { 00422 fetchTimeOutTime = TQTime::currentTime(); 00423 fetchTimeOutTimer->start( 60 * 1000, true ); 00424 FolderJob *job = msg->parent()->createJob( msg ); 00425 connect( job, TQT_SIGNAL(messageRetrieved( KMMessage* )), 00426 TQT_SLOT(messageFetched( KMMessage* )) ); 00427 lastJob = job; 00428 job->start(); 00429 } else { 00430 mFetchExecuting = false; 00431 mResult = ResultError; 00432 finishTimer->start( 0, true ); 00433 return; 00434 } 00435 } 00436 00437 void ActionScheduler::messageFetched( KMMessage *msg ) 00438 { 00439 fetchTimeOutTimer->stop(); 00440 if (!msg) { 00441 // Should never happen, but sometimes does; 00442 fetchMessageTimer->start( 0, true ); 00443 return; 00444 } 00445 00446 mFetchSerNums.remove( msg->getMsgSerNum() ); 00447 00448 // Note: This may not be necessary. What about when it's time to 00449 // delete the original message? 00450 // Is the new serial number being set correctly then? 00451 if ((mSet & KMFilterMgr::Explicit) || 00452 (msg->headerField( "X-KMail-Filtered" ).isEmpty())) { 00453 TQString serNumS; 00454 serNumS.setNum( msg->getMsgSerNum() ); 00455 KMMessage *newMsg = new KMMessage; 00456 newMsg->fromString(msg->asString()); 00457 newMsg->setStatus(msg->status()); 00458 newMsg->setComplete(msg->isComplete()); 00459 newMsg->setHeaderField( "X-KMail-Filtered", serNumS ); 00460 mSrcFolder->addMsg( newMsg ); 00461 } else { 00462 fetchMessageTimer->start( 0, true ); 00463 } 00464 if (mFetchUnget && msg->parent()) 00465 msg->parent()->unGetMsg( msg->parent()->find( msg )); 00466 return; 00467 } 00468 00469 void ActionScheduler::msgAdded( KMFolder*, TQ_UINT32 serNum ) 00470 { 00471 if (!mIgnore) 00472 enqueue( serNum ); 00473 } 00474 00475 void ActionScheduler::enqueue(TQ_UINT32 serNum) 00476 { 00477 if (mResult != ResultOk) 00478 return; // An error has already occurred don't even try to process this msg 00479 00480 if (MessageProperty::filtering( serNum )) { 00481 // Not good someone else is already filtering this msg 00482 mResult = ResultError; 00483 if (!mExecuting && !mFetchExecuting) 00484 finishTimer->start( 0, true ); 00485 } else { 00486 // Everything is ok async filter this message 00487 mSerNums.append( serNum ); 00488 00489 if (!mExecuting) { 00490 // Note: Need to (re)start incomplete msg filtering chain 00491 // The state of mFetchExecuting is of some concern. 00492 mExecuting = true; 00493 mMessageIt = mSerNums.begin(); 00494 processMessageTimer->start( 0, true ); 00495 } 00496 } 00497 } 00498 00499 void ActionScheduler::processMessage() 00500 { 00501 if (mExecutingLock) 00502 return; 00503 mExecutingLock = true; 00504 mMessageIt = mSerNums.begin(); 00505 while (mMessageIt != mSerNums.end()) { 00506 if (!MessageProperty::transferInProgress(*mMessageIt)) 00507 break; 00508 ++mMessageIt; 00509 } 00510 00511 if (mMessageIt == mSerNums.end() && !mSerNums.isEmpty()) { 00512 mExecuting = false; 00513 processMessageTimer->start( 600, true ); 00514 } 00515 00516 if ((mMessageIt == mSerNums.end()) || (mResult != ResultOk)) { 00517 mExecutingLock = false; 00518 mExecuting = false; 00519 finishTimer->start( 0, true ); 00520 return; 00521 } 00522 00523 //If we got this far then there's a valid message to work with 00524 KMMsgBase *msgBase = messageBase( *mMessageIt ); 00525 if (!msgBase || mResult != ResultOk) { 00526 mExecuting = false; 00527 return; 00528 } 00529 00530 MessageProperty::setFiltering( *mMessageIt, true ); 00531 MessageProperty::setFilterHandler( *mMessageIt, this ); 00532 MessageProperty::setFilterFolder( *mMessageIt, mDestFolder ); 00533 if ( FilterLog::instance()->isLogging() ) { 00534 FilterLog::instance()->addSeparator(); 00535 } 00536 mFilterIt = mFilters.begin(); 00537 00538 mUnget = msgBase->isMessage(); 00539 KMMessage *msg = message( *mMessageIt ); 00540 if (mResult != ResultOk) { 00541 mExecuting = false; 00542 return; 00543 } 00544 00545 bool mdnEnabled = true; 00546 { 00547 TDEConfigGroup mdnConfig( kmkernel->config(), "MDN" ); 00548 int mode = mdnConfig.readNumEntry( "default-policy", 0 ); 00549 if (!mode || mode < 0 || mode > 3) 00550 mdnEnabled = false; 00551 } 00552 mdnEnabled = true; // For 3.2 force all mails to be complete 00553 00554 if ((msg && msg->isComplete()) || 00555 (msg && !(*mFilterIt).requiresBody(msg) && !mdnEnabled)) 00556 { 00557 // We have a complete message or 00558 // we can work with an incomplete message 00559 // Get a write lock on the message while it's being filtered 00560 msg->setTransferInProgress( true ); 00561 filterMessageTimer->start( 0, true ); 00562 return; 00563 } 00564 if (msg) { 00565 FolderJob *job = msg->parent()->createJob( msg ); 00566 connect( job, TQT_SIGNAL(messageRetrieved( KMMessage* )), 00567 TQT_SLOT(messageRetrieved( KMMessage* )) ); 00568 job->start(); 00569 } else { 00570 mExecuting = false; 00571 mResult = ResultError; 00572 finishTimer->start( 0, true ); 00573 return; 00574 } 00575 } 00576 00577 void ActionScheduler::messageRetrieved(KMMessage* msg) 00578 { 00579 // Get a write lock on the message while it's being filtered 00580 msg->setTransferInProgress( true ); 00581 filterMessageTimer->start( 0, true ); 00582 } 00583 00584 void ActionScheduler::filterMessage() 00585 { 00586 if (mFilterIt == mFilters.end()) { 00587 moveMessage(); 00588 return; 00589 } 00590 if (((mSet & KMFilterMgr::Outbound) && (*mFilterIt).applyOnOutbound()) || 00591 ((mSet & KMFilterMgr::Inbound) && (*mFilterIt).applyOnInbound() && 00592 (!mAccount || 00593 (mAccount && (*mFilterIt).applyOnAccount(mAccountId)))) || 00594 ((mSet & KMFilterMgr::Explicit) && (*mFilterIt).applyOnExplicit())) { 00595 00596 // filter is applicable 00597 if ( FilterLog::instance()->isLogging() ) { 00598 TQString logText( i18n( "<b>Evaluating filter rules:</b> " ) ); 00599 logText.append( (*mFilterIt).pattern()->asString() ); 00600 FilterLog::instance()->add( logText, FilterLog::patternDesc ); 00601 } 00602 if (mAlwaysMatch || 00603 (*mFilterIt).pattern()->matches( *mMessageIt )) { 00604 if ( FilterLog::instance()->isLogging() ) { 00605 FilterLog::instance()->add( i18n( "<b>Filter rules have matched.</b>" ), 00606 FilterLog::patternResult ); 00607 } 00608 mFilterAction = (*mFilterIt).actions()->first(); 00609 actionMessage(); 00610 return; 00611 } 00612 } 00613 ++mFilterIt; 00614 filterMessageTimer->start( 0, true ); 00615 } 00616 00617 void ActionScheduler::actionMessage(KMFilterAction::ReturnCode res) 00618 { 00619 if (res == KMFilterAction::CriticalError) { 00620 mResult = ResultCriticalError; 00621 finish(); //must handle critical errors immediately 00622 } 00623 if (mFilterAction) { 00624 KMMessage *msg = message( *mMessageIt ); 00625 if (msg) { 00626 if ( FilterLog::instance()->isLogging() ) { 00627 TQString logText( i18n( "<b>Applying filter action:</b> %1" ) 00628 .arg( mFilterAction->displayString() ) ); 00629 FilterLog::instance()->add( logText, FilterLog::appliedAction ); 00630 } 00631 KMFilterAction *action = mFilterAction; 00632 mFilterAction = (*mFilterIt).actions()->next(); 00633 action->processAsync( msg ); 00634 } 00635 } else { 00636 // there are no more actions 00637 if ((*mFilterIt).stopProcessingHere()) 00638 mFilterIt = mFilters.end(); 00639 else 00640 ++mFilterIt; 00641 filterMessageTimer->start( 0, true ); 00642 } 00643 } 00644 00645 void ActionScheduler::moveMessage() 00646 { 00647 KMMsgBase *msgBase = messageBase( *mMessageIt ); 00648 if (!msgBase) 00649 return; 00650 00651 MessageProperty::setTransferInProgress( *mMessageIt, false, true ); 00652 KMMessage *msg = message( *mMessageIt ); 00653 KMFolder *folder = MessageProperty::filterFolder( *mMessageIt ); 00654 TQString serNumS = msg->headerField( "X-KMail-Filtered" ); 00655 if (!serNumS.isEmpty()) 00656 mOriginalSerNum = serNumS.toUInt(); 00657 else 00658 mOriginalSerNum = 0; 00659 MessageProperty::setFilterHandler( *mMessageIt, 0 ); 00660 MessageProperty::setFiltering( *mMessageIt, false ); 00661 mSerNums.remove( *mMessageIt ); 00662 00663 KMMessage *orgMsg = 0; 00664 ReturnCode mOldReturnCode = mResult; 00665 if (mOriginalSerNum) 00666 orgMsg = message( mOriginalSerNum ); 00667 mResult = mOldReturnCode; // ignore errors in deleting original message 00668 if (!orgMsg || !orgMsg->parent()) { 00669 // Original message is gone, no point filtering it anymore 00670 mSrcFolder->removeMsg( mSrcFolder->find( msg ) ); 00671 kdDebug(5006) << "The original serial number is missing. " 00672 << "Cannot complete the filtering." << endl; 00673 mExecutingLock = false; 00674 processMessageTimer->start( 0, true ); 00675 return; 00676 } else { 00677 if (!folder) // no filter folder specified leave in current place 00678 folder = orgMsg->parent(); 00679 } 00680 00681 mIgnore = true; 00682 assert( msg->parent() == mSrcFolder.operator->() ); 00683 mSrcFolder->take( mSrcFolder->find( msg ) ); 00684 mSrcFolder->addMsg( msg ); 00685 mIgnore = false; 00686 00687 if (msg && folder && kmkernel->folderIsTrash( folder )) 00688 KMFilterAction::sendMDN( msg, KMime::MDN::Deleted ); 00689 00690 timeOutTime = TQTime::currentTime(); 00691 KMCommand *cmd = new KMMoveCommand( folder, msg ); 00692 connect( cmd, TQT_SIGNAL( completed( KMCommand * ) ), 00693 this, TQT_SLOT( moveMessageFinished( KMCommand * ) ) ); 00694 cmd->start(); 00695 // sometimes the move command doesn't complete so time out after a minute 00696 // and move onto the next message 00697 lastCommand = cmd; 00698 timeOutTimer->start( 60 * 1000, true ); 00699 } 00700 00701 void ActionScheduler::moveMessageFinished( KMCommand *command ) 00702 { 00703 timeOutTimer->stop(); 00704 if ( command->result() != KMCommand::OK ) 00705 mResult = ResultError; 00706 00707 if (!mSrcFolder->count()) 00708 mSrcFolder->expunge(); 00709 00710 // in case the message stayed in the current folder TODO optimize 00711 if ( mHeaders ) 00712 mHeaders->clearSelectableAndAboutToBeDeleted( mOriginalSerNum ); 00713 KMMessage *msg = 0; 00714 ReturnCode mOldReturnCode = mResult; 00715 if (mOriginalSerNum) { 00716 msg = message( mOriginalSerNum ); 00717 emit filtered( mOriginalSerNum ); 00718 } 00719 00720 mResult = mOldReturnCode; // ignore errors in deleting original message 00721 KMCommand *cmd = 0; 00722 if (msg && msg->parent()) { 00723 cmd = new KMMoveCommand( 0, msg ); 00724 // cmd->start(); // Note: sensitive logic here. 00725 } 00726 00727 if (mResult == ResultOk) { 00728 mExecutingLock = false; 00729 if (cmd) 00730 connect( cmd, TQT_SIGNAL( completed( KMCommand * ) ), 00731 this, TQT_SLOT( processMessage() ) ); 00732 else 00733 processMessageTimer->start( 0, true ); 00734 } else { 00735 // Note: An alternative to consider is just calling 00736 // finishTimer->start and returning 00737 if (cmd) 00738 connect( cmd, TQT_SIGNAL( completed( KMCommand * ) ), 00739 this, TQT_SLOT( finish() ) ); 00740 else 00741 finishTimer->start( 0, true ); 00742 } 00743 if (cmd) 00744 cmd->start(); 00745 // else moveMessageFinished should call finish 00746 } 00747 00748 void ActionScheduler::copyMessageFinished( KMCommand *command ) 00749 { 00750 if ( command->result() != KMCommand::OK ) 00751 actionMessage( KMFilterAction::ErrorButGoOn ); 00752 else 00753 actionMessage(); 00754 } 00755 00756 void ActionScheduler::timeOut() 00757 { 00758 // Note: This is a good place for a debug statement 00759 assert( lastCommand ); 00760 // sometimes imap jobs seem to just stall so give up and move on 00761 disconnect( lastCommand, TQT_SIGNAL( completed( KMCommand * ) ), 00762 this, TQT_SLOT( moveMessageFinished( KMCommand * ) ) ); 00763 lastCommand = 0; 00764 mExecutingLock = false; 00765 mExecuting = false; 00766 finishTimer->start( 0, true ); 00767 if (mOriginalSerNum) // Try again 00768 execFilters( mOriginalSerNum ); 00769 } 00770 00771 void ActionScheduler::fetchTimeOut() 00772 { 00773 // Note: This is a good place for a debug statement 00774 assert( lastJob ); 00775 // sometimes imap jobs seem to just stall so give up and move on 00776 disconnect( lastJob, TQT_SIGNAL(messageRetrieved( KMMessage* )), 00777 this, TQT_SLOT(messageFetched( KMMessage* )) ); 00778 lastJob->kill(); 00779 lastJob = 0; 00780 fetchMessageTimer->start( 0, true ); 00781 } 00782 00783 TQString ActionScheduler::debug() 00784 { 00785 TQString res; 00786 TQValueList<ActionScheduler*>::iterator it; 00787 int i = 1; 00788 for ( it = schedulerList->begin(); it != schedulerList->end(); ++it ) { 00789 res.append( TQString( "ActionScheduler #%1.\n" ).arg( i ) ); 00790 if ((*it)->mAccount && kmkernel->find( (*it)->mAccountId )) { 00791 res.append( TQString( "Account %1, Name %2.\n" ) 00792 .arg( (*it)->mAccountId ) 00793 .arg( kmkernel->acctMgr()->find( (*it)->mAccountId )->name() ) ); 00794 } 00795 res.append( TQString( "mExecuting %1, " ).arg( (*it)->mExecuting ? "true" : "false" ) ); 00796 res.append( TQString( "mExecutingLock %1, " ).arg( (*it)->mExecutingLock ? "true" : "false" ) ); 00797 res.append( TQString( "mFetchExecuting %1.\n" ).arg( (*it)->mFetchExecuting ? "true" : "false" ) ); 00798 res.append( TQString( "mOriginalSerNum %1.\n" ).arg( (*it)->mOriginalSerNum ) ); 00799 res.append( TQString( "mMessageIt %1.\n" ).arg( ((*it)->mMessageIt != 0) ? *(*it)->mMessageIt : 0 ) ); 00800 res.append( TQString( "mSerNums count %1, " ).arg( (*it)->mSerNums.count() ) ); 00801 res.append( TQString( "mFetchSerNums count %1.\n" ).arg( (*it)->mFetchSerNums.count() ) ); 00802 res.append( TQString( "mResult " ) ); 00803 if ((*it)->mResult == ResultOk) 00804 res.append( TQString( "ResultOk.\n" ) ); 00805 else if ((*it)->mResult == ResultError) 00806 res.append( TQString( "ResultError.\n" ) ); 00807 else if ((*it)->mResult == ResultCriticalError) 00808 res.append( TQString( "ResultCriticalError.\n" ) ); 00809 else 00810 res.append( TQString( "Unknown.\n" ) ); 00811 00812 ++i; 00813 } 00814 return res; 00815 } 00816 00817 bool ActionScheduler::isEnabled() 00818 { 00819 if (sEnabledChecked) 00820 return sEnabled; 00821 00822 sEnabledChecked = true; 00823 TDEConfig* config = KMKernel::config(); 00824 TDEConfigGroupSaver saver(config, "General"); 00825 sEnabled = config->readBoolEntry("action-scheduler", false); 00826 return sEnabled; 00827 } 00828 00829 bool ActionScheduler::ignoreChanges( bool ignore ) 00830 { 00831 bool oldValue = mIgnore; 00832 mIgnore = ignore; 00833 return oldValue; 00834 } 00835 00836 #include "actionscheduler.moc"