kmail

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