kmail Library API Documentation

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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Qt library by Trolltech AS, Norway (or with modified versions 00022 of Qt that use the same license as Qt), 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 Qt. 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 00031 #ifdef HAVE_CONFIG_H 00032 #include <config.h> 00033 #endif 00034 00035 #include "actionscheduler.h" 00036 00037 #include "messageproperty.h" 00038 #include "kmfilter.h" 00039 #include "kmfolderindex.h" 00040 #include "kmfoldermgr.h" 00041 #include "kmmsgdict.h" 00042 #include "kmcommands.h" 00043 #include "kmheaders.h" 00044 00045 #include <qtimer.h> 00046 #include <kconfig.h> 00047 #include <kstandarddirs.h> 00048 00049 using namespace KMail; 00050 typedef QPtrList<KMMsgBase> KMMessageList; 00051 00052 KMFolderMgr* ActionScheduler::tempFolderMgr = 0; 00053 int ActionScheduler::refCount = 0; 00054 int ActionScheduler::count = 0; 00055 00056 ActionScheduler::ActionScheduler(KMFilterMgr::FilterSet set, 00057 QPtrList<KMFilter> filters, 00058 KMHeaders *headers, 00059 KMFolder *srcFolder) 00060 :mSet( set ), mHeaders( headers ) 00061 { 00062 ++count; 00063 ++refCount; 00064 mExecuting = false; 00065 mExecutingLock = false; 00066 mFetchExecuting = false; 00067 mFiltersAreQueued = false; 00068 mResult = ResultOk; 00069 mIgnore = false; 00070 mAutoDestruct = false; 00071 mAlwaysMatch = false; 00072 KMFilter *filter; 00073 finishTimer = new QTimer( this ); 00074 connect( finishTimer, SIGNAL(timeout()), this, SLOT(finish())); 00075 fetchMessageTimer = new QTimer( this ); 00076 connect( fetchMessageTimer, SIGNAL(timeout()), this, SLOT(fetchMessage())); 00077 tempCloseFoldersTimer = new QTimer( this ); 00078 connect( tempCloseFoldersTimer, SIGNAL(timeout()), this, SLOT(tempCloseFolders())); 00079 processMessageTimer = new QTimer( this ); 00080 connect( processMessageTimer, SIGNAL(timeout()), this, SLOT(processMessage())); 00081 filterMessageTimer = new QTimer( this ); 00082 connect( filterMessageTimer, SIGNAL(timeout()), this, SLOT(filterMessage())); 00083 00084 for (filter = filters.first(); filter; filter = filters.next()) 00085 mFilters.append( *filter ); 00086 mDestFolder = 0; 00087 if (srcFolder) { 00088 mDeleteSrcFolder = false; 00089 setSourceFolder( srcFolder ); 00090 } else { 00091 QString tmpName; 00092 tmpName.setNum( count ); 00093 if (!tempFolderMgr) 00094 tempFolderMgr = new KMFolderMgr(locateLocal("data","kmail/filter")); 00095 KMFolder *tempFolder = tempFolderMgr->findOrCreate( tmpName ); 00096 tempFolder->expunge(); 00097 mDeleteSrcFolder = true; 00098 setSourceFolder( tempFolder ); 00099 } 00100 } 00101 00102 ActionScheduler::~ActionScheduler() 00103 { 00104 tempCloseFolders(); 00105 mSrcFolder->close(); 00106 00107 if (mDeleteSrcFolder) 00108 tempFolderMgr->remove(mSrcFolder); 00109 00110 --refCount; 00111 if (refCount == 0) { 00112 delete tempFolderMgr; 00113 tempFolderMgr = 0; 00114 } 00115 } 00116 00117 void ActionScheduler::setAutoDestruct( bool autoDestruct ) 00118 { 00119 mAutoDestruct = autoDestruct; 00120 } 00121 00122 void ActionScheduler::setAlwaysMatch( bool alwaysMatch ) 00123 { 00124 mAlwaysMatch = alwaysMatch; 00125 } 00126 00127 void ActionScheduler::setDefaultDestinationFolder( KMFolder *destFolder ) 00128 { 00129 mDestFolder = destFolder; 00130 } 00131 00132 void ActionScheduler::setSourceFolder( KMFolder *srcFolder ) 00133 { 00134 srcFolder->open(); 00135 if (mSrcFolder) { 00136 disconnect( mSrcFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)), 00137 this, SLOT(msgAdded(KMFolder*, Q_UINT32)) ); 00138 mSrcFolder->close(); 00139 } 00140 mSrcFolder = srcFolder; 00141 int i = 0; 00142 for (i = 0; i < mSrcFolder->count(); ++i) 00143 enqueue( mSrcFolder->getMsgBase( i )->getMsgSerNum() ); 00144 if (mSrcFolder) 00145 connect( mSrcFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)), 00146 this, SLOT(msgAdded(KMFolder*, Q_UINT32)) ); 00147 } 00148 00149 void ActionScheduler::setFilterList( QPtrList<KMFilter> filters ) 00150 { 00151 mFiltersAreQueued = true; 00152 mQueuedFilters.clear(); 00153 KMFilter *filter; 00154 for (filter = filters.first(); filter; filter = filters.next()) 00155 mQueuedFilters.append( *filter ); 00156 } 00157 00158 int ActionScheduler::tempOpenFolder( KMFolder* aFolder ) 00159 { 00160 assert( aFolder ); 00161 tempCloseFoldersTimer->stop(); 00162 if ( aFolder == mSrcFolder.operator->() ) 00163 return 0; 00164 00165 int rc = aFolder->open(); 00166 if (rc) 00167 return rc; 00168 00169 mOpenFolders.append( aFolder ); 00170 return 0; 00171 } 00172 00173 void ActionScheduler::tempCloseFolders() 00174 { 00175 // close temp opened folders 00176 QValueListConstIterator<QGuardedPtr<KMFolder> > it; 00177 for (it = mOpenFolders.begin(); it != mOpenFolders.end(); ++it) { 00178 KMFolder *folder = *it; 00179 if (folder) 00180 folder->close(); 00181 } 00182 mOpenFolders.clear(); 00183 } 00184 00185 void ActionScheduler::execFilters(const QValueList<Q_UINT32> serNums) 00186 { 00187 QValueListConstIterator<Q_UINT32> it; 00188 for (it = serNums.begin(); it != serNums.end(); ++it) 00189 execFilters( *it ); 00190 } 00191 00192 void ActionScheduler::execFilters(const QPtrList<KMMsgBase> msgList) 00193 { 00194 KMMsgBase *msgBase; 00195 QPtrList<KMMsgBase> list = msgList; 00196 for (msgBase = list.first(); msgBase; msgBase = list.next()) 00197 execFilters( msgBase->getMsgSerNum() ); 00198 } 00199 00200 void ActionScheduler::execFilters(KMMsgBase* msgBase) 00201 { 00202 execFilters( msgBase->getMsgSerNum() ); 00203 } 00204 00205 void ActionScheduler::execFilters(Q_UINT32 serNum) 00206 { 00207 if (mResult != ResultOk) 00208 return; // An error has already occurred don't even try to process this msg 00209 00210 if (MessageProperty::filtering( serNum )) { 00211 // Not good someone else is already filtering this msg 00212 mResult = ResultError; 00213 if (!mExecuting) 00214 finishTimer->start( 0, true ); 00215 } else { 00216 // Everything is ok async fetch this message 00217 mFetchSerNums.append( serNum ); 00218 if (!mFetchExecuting) { 00219 //Need to (re)start incomplete msg fetching chain 00220 mFetchExecuting = true; 00221 fetchMessageTimer->start( 0, true ); 00222 } 00223 } 00224 } 00225 00226 KMMsgBase *ActionScheduler::messageBase(Q_UINT32 serNum) 00227 { 00228 int idx = -1; 00229 KMFolder *folder = 0; 00230 KMMsgBase *msg = 0; 00231 kmkernel->msgDict()->getLocation( serNum, &folder, &idx ); 00232 // It's possible that the message has been deleted or moved into a 00233 // different folder 00234 if (folder && (idx != -1)) { 00235 // everything is ok 00236 msg = folder->getMsgBase( idx ); 00237 tempOpenFolder( folder ); // just in case msg has moved 00238 } else { 00239 // the message is gone! 00240 mResult = ResultError; 00241 finishTimer->start( 0, true ); 00242 } 00243 return msg; 00244 } 00245 00246 KMMessage *ActionScheduler::message(Q_UINT32 serNum) 00247 { 00248 int idx = -1; 00249 KMFolder *folder = 0; 00250 KMMessage *msg = 0; 00251 kmkernel->msgDict()->getLocation( serNum, &folder, &idx ); 00252 // It's possible that the message has been deleted or moved into a 00253 // different folder 00254 if (folder && (idx != -1)) { 00255 // everything is ok 00256 msg = folder->getMsg( idx ); 00257 tempOpenFolder( folder ); // just in case msg has moved 00258 } else { 00259 // the message is gone! 00260 mResult = ResultError; 00261 finishTimer->start( 0, true ); 00262 } 00263 return msg; 00264 } 00265 00266 void ActionScheduler::finish() 00267 { 00268 if (mResult == ResultCriticalError) { 00269 // Must handle critical errors immediately 00270 emit result( mResult ); 00271 return; 00272 } 00273 00274 if (!mFetchExecuting && !mExecuting) { 00275 // If an error has occurred and a permanent source folder has 00276 // been set then move all the messages left in the source folder 00277 // to the inbox. If no permanent source folder has been set 00278 // then abandon filtering of queued messages. 00279 if (!mDeleteSrcFolder && !mDestFolder.isNull() ) { 00280 while ( mSrcFolder->count() > 0 ) { 00281 KMMessage *msg = mSrcFolder->getMsg( 0 ); 00282 mDestFolder->moveMsg( msg ); 00283 } 00284 00285 // Wait a little while before closing temp folders, just in case 00286 // new messages arrive for filtering. 00287 tempCloseFoldersTimer->start( 60*1000, true ); 00288 } 00289 mSerNums.clear(); //abandon 00290 mFetchSerNums.clear(); //abandon 00291 00292 if (mFiltersAreQueued) 00293 mFilters = mQueuedFilters; 00294 mQueuedFilters.clear(); 00295 mFiltersAreQueued = false; 00296 ReturnCode aResult = mResult; 00297 mResult = ResultOk; 00298 mExecutingLock = false; 00299 emit result( aResult ); 00300 if (mAutoDestruct) 00301 delete this; 00302 } 00303 // else a message may be in the process of being fetched or filtered 00304 // wait until both of these commitments are finished then this 00305 // method should be called again. 00306 } 00307 00308 void ActionScheduler::fetchMessage() 00309 { 00310 QValueListIterator<Q_UINT32> mFetchMessageIt = mFetchSerNums.begin(); 00311 while (mFetchMessageIt != mFetchSerNums.end()) { 00312 if (!MessageProperty::transferInProgress(*mFetchMessageIt)) 00313 break; 00314 ++mFetchMessageIt; 00315 } 00316 if (mFetchMessageIt == mFetchSerNums.end() && !mFetchSerNums.isEmpty()) 00317 mResult = ResultError; 00318 if ((mFetchMessageIt == mFetchSerNums.end()) || (mResult != ResultOk)) { 00319 mFetchExecuting = false; 00320 if (!mSrcFolder->count()) 00321 mSrcFolder->expunge(); 00322 finishTimer->start( 0, true ); 00323 return; 00324 } 00325 00326 //If we got this far then there's a valid message to work with 00327 KMMsgBase *msgBase = messageBase( *mFetchMessageIt ); 00328 if (mResult != ResultOk) { 00329 mFetchExecuting = false; 00330 return; 00331 } 00332 mFetchUnget = msgBase->isMessage(); 00333 KMMessage *msg = message( *mFetchMessageIt ); 00334 if (mResult != ResultOk) { 00335 mFetchExecuting = false; 00336 return; 00337 } 00338 00339 if (msg && msg->isComplete()) { 00340 messageFetched( msg ); 00341 } else if (msg) { 00342 FolderJob *job = msg->parent()->createJob( msg ); 00343 connect( job, SIGNAL(messageRetrieved( KMMessage* )), 00344 SLOT(messageFetched( KMMessage* )) ); 00345 job->start(); 00346 } else { 00347 mFetchExecuting = false; 00348 mResult = ResultError; 00349 finishTimer->start( 0, true ); 00350 return; 00351 } 00352 } 00353 00354 void ActionScheduler::messageFetched( KMMessage *msg ) 00355 { 00356 mFetchSerNums.remove( mFetchSerNums.begin() ); 00357 00358 if ((mSet & KMFilterMgr::Explicit) || 00359 (msg->headerField( "X-KMail-Filtered" ).isEmpty())) { 00360 QString serNumS; 00361 serNumS.setNum( msg->getMsgSerNum() ); 00362 KMMessage *newMsg = new KMMessage; 00363 newMsg->fromString(msg->asString()); 00364 newMsg->setStatus(msg->status()); 00365 newMsg->setComplete(msg->isComplete()); 00366 newMsg->setHeaderField( "X-KMail-Filtered", serNumS ); 00367 mSrcFolder->addMsg( newMsg ); 00368 } 00369 if (mFetchUnget && msg->parent()) 00370 msg->parent()->unGetMsg( msg->parent()->find( msg )); 00371 fetchMessageTimer->start( 0, true ); 00372 return; 00373 } 00374 00375 void ActionScheduler::msgAdded( KMFolder*, Q_UINT32 serNum ) 00376 { 00377 if (!mIgnore) 00378 enqueue( serNum ); 00379 } 00380 00381 void ActionScheduler::enqueue(Q_UINT32 serNum) 00382 { 00383 if (mResult != ResultOk) 00384 return; // An error has already occurred don't even try to process this msg 00385 00386 if (MessageProperty::filtering( serNum )) { 00387 // Not good someone else is already filtering this msg 00388 mResult = ResultError; 00389 if (!mExecuting) 00390 finishTimer->start( 0, true ); 00391 } else { 00392 // Everything is ok async filter this message 00393 mSerNums.append( serNum ); 00394 00395 if (!mExecuting) { 00396 //Need to (re)start incomplete msg filtering chain 00397 mExecuting = true; 00398 mMessageIt = mSerNums.begin(); 00399 processMessageTimer->start( 0, true ); 00400 } 00401 } 00402 } 00403 00404 void ActionScheduler::processMessage() 00405 { 00406 if (mExecutingLock) 00407 return; 00408 mExecutingLock = true; 00409 mMessageIt = mSerNums.begin(); 00410 while (mMessageIt != mSerNums.end()) { 00411 if (!MessageProperty::transferInProgress(*mMessageIt)) 00412 break; 00413 ++mMessageIt; 00414 } 00415 if (mMessageIt == mSerNums.end() && !mSerNums.isEmpty()) 00416 mResult = ResultError; 00417 if ((mMessageIt == mSerNums.end()) || (mResult != ResultOk)) { 00418 mExecutingLock = false; 00419 mExecuting = false; 00420 finishTimer->start( 0, true ); 00421 return; 00422 } 00423 00424 //If we got this far then there's a valid message to work with 00425 KMMsgBase *msgBase = messageBase( *mMessageIt ); 00426 if (mResult != ResultOk) { 00427 mExecuting = false; 00428 return; 00429 } 00430 00431 MessageProperty::setFiltering( *mMessageIt, true ); 00432 MessageProperty::setFilterHandler( *mMessageIt, this ); 00433 MessageProperty::setFilterFolder( *mMessageIt, mDestFolder ); 00434 mFilterIt = mFilters.begin(); 00435 00436 mUnget = msgBase->isMessage(); 00437 KMMessage *msg = message( *mMessageIt ); 00438 if (mResult != ResultOk) { 00439 mExecuting = false; 00440 return; 00441 } 00442 00443 bool mdnEnabled = true; 00444 { 00445 KConfigGroup mdnConfig( kmkernel->config(), "MDN" ); 00446 int mode = mdnConfig.readNumEntry( "default-policy", 0 ); 00447 if (!mode || mode < 0 || mode > 3) 00448 mdnEnabled = false; 00449 } 00450 mdnEnabled = true; // For 3.2 force all mails to be complete 00451 00452 if ((msg && msg->isComplete()) || 00453 (msg && !(*mFilterIt).requiresBody(msg) && !mdnEnabled)) 00454 { 00455 // We have a complete message or 00456 // we can work with an incomplete message 00457 // Get a write lock on the message while it's being filtered 00458 msg->setTransferInProgress( true ); 00459 filterMessageTimer->start( 0, true ); 00460 return; 00461 } 00462 if (msg) { 00463 FolderJob *job = msg->parent()->createJob( msg ); 00464 connect( job, SIGNAL(messageRetrieved( KMMessage* )), 00465 SLOT(messageRetrieved( KMMessage* )) ); 00466 job->start(); 00467 } else { 00468 mExecuting = false; 00469 mResult = ResultError; 00470 finishTimer->start( 0, true ); 00471 return; 00472 } 00473 } 00474 00475 void ActionScheduler::messageRetrieved(KMMessage* msg) 00476 { 00477 // Get a write lock on the message while it's being filtered 00478 msg->setTransferInProgress( true ); 00479 filterMessageTimer->start( 0, true ); 00480 } 00481 00482 void ActionScheduler::filterMessage() 00483 { 00484 if (mFilterIt == mFilters.end()) { 00485 moveMessage(); 00486 return; 00487 } 00488 if (((mSet & KMFilterMgr::Outbound) && (*mFilterIt).applyOnOutbound()) || 00489 ((mSet & KMFilterMgr::Inbound) && (*mFilterIt).applyOnInbound()) || 00490 ((mSet & KMFilterMgr::Explicit) && (*mFilterIt).applyOnExplicit())) { 00491 // filter is applicable 00492 if (mAlwaysMatch || 00493 (*mFilterIt).pattern()->matches( *mMessageIt )) { 00494 mFilterAction = (*mFilterIt).actions()->first(); 00495 actionMessage(); 00496 return; 00497 } 00498 } 00499 ++mFilterIt; 00500 filterMessageTimer->start( 0, true ); 00501 } 00502 00503 void ActionScheduler::actionMessage(KMFilterAction::ReturnCode res) 00504 { 00505 if (res == KMFilterAction::CriticalError) { 00506 mResult = ResultCriticalError; 00507 finish(); //must handle critical errors immediately 00508 } 00509 if (mFilterAction) { 00510 KMMessage *msg = message( *mMessageIt ); 00511 if (msg) { 00512 KMFilterAction *action = mFilterAction; 00513 mFilterAction = (*mFilterIt).actions()->next(); 00514 action->processAsync( msg ); 00515 } 00516 } else { 00517 // there are no more actions 00518 if ((*mFilterIt).stopProcessingHere()) 00519 mFilterIt = mFilters.end(); 00520 else 00521 ++mFilterIt; 00522 filterMessageTimer->start( 0, true ); 00523 } 00524 } 00525 00526 void ActionScheduler::moveMessage() 00527 { 00528 KMMsgBase *msgBase = messageBase( *mMessageIt ); 00529 if (!msgBase) 00530 return; 00531 00532 MessageProperty::setTransferInProgress( *mMessageIt, false, true ); 00533 KMMessage *msg = message( *mMessageIt ); 00534 KMFolder *folder = MessageProperty::filterFolder( *mMessageIt ); 00535 QString serNumS = msg->headerField( "X-KMail-Filtered" ); 00536 if (!serNumS.isEmpty()) 00537 mOriginalSerNum = serNumS.toUInt(); 00538 else 00539 mOriginalSerNum = 0; 00540 MessageProperty::setFilterHandler( *mMessageIt, 0 ); 00541 MessageProperty::setFiltering( *mMessageIt, false ); 00542 mSerNums.remove( *mMessageIt ); 00543 00544 KMMessage *orgMsg = 0; 00545 ReturnCode mOldReturnCode = mResult; 00546 if (mOriginalSerNum) 00547 orgMsg = message( mOriginalSerNum ); 00548 mResult = mOldReturnCode; // ignore errors in deleting original message 00549 if (!orgMsg || !orgMsg->parent()) { 00550 // Original message is gone, no point filtering it anymore 00551 mSrcFolder->removeMsg( mSrcFolder->find( msg ) ); 00552 mExecutingLock = false; 00553 processMessageTimer->start( 0, true ); 00554 } else { 00555 if (!folder) // no filter folder specified leave in current place 00556 folder = orgMsg->parent(); 00557 } 00558 00559 mIgnore = true; 00560 assert( msg->parent() == mSrcFolder.operator->() ); 00561 mSrcFolder->take( mSrcFolder->find( msg ) ); 00562 mSrcFolder->addMsg( msg ); 00563 mIgnore = false; 00564 00565 if (msg && kmkernel->folderIsTrash( folder )) 00566 KMFilterAction::sendMDN( msg, KMime::MDN::Deleted ); 00567 00568 KMCommand *cmd = new KMMoveCommand( folder, msg ); 00569 connect ( cmd, SIGNAL( completed(bool) ), 00570 this, SLOT( moveMessageFinished(bool) ) ); 00571 cmd->start(); 00572 } 00573 00574 void ActionScheduler::moveMessageFinished(bool success) 00575 { 00576 if ( !success ) 00577 mResult = ResultError; 00578 00579 if (!mSrcFolder->count()) 00580 mSrcFolder->expunge(); 00581 00582 // in case the message stayed in the current folder TODO optimize 00583 if ( mHeaders ) 00584 mHeaders->clearSelectableAndAboutToBeDeleted( mOriginalSerNum ); 00585 KMMessage *msg = 0; 00586 ReturnCode mOldReturnCode = mResult; 00587 if (mOriginalSerNum) 00588 msg = message( mOriginalSerNum ); 00589 mResult = mOldReturnCode; // ignore errors in deleting original message 00590 if (msg && msg->parent()) { 00591 KMCommand *cmd = new KMMoveCommand( 0, msg ); 00592 cmd->start(); 00593 } 00594 00595 if (mResult == ResultOk) { 00596 mExecutingLock = false; 00597 processMessageTimer->start( 0, true ); 00598 } else { 00599 finishTimer->start( 0, true ); 00600 } 00601 // else moveMessageFinished should call finish 00602 } 00603 00604 #include "actionscheduler.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jul 28 23:57:56 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003