diff options
Diffstat (limited to 'kmail/actionscheduler.cpp')
-rw-r--r-- | kmail/actionscheduler.cpp | 836 |
1 files changed, 836 insertions, 0 deletions
diff --git a/kmail/actionscheduler.cpp b/kmail/actionscheduler.cpp new file mode 100644 index 000000000..3d92cd7a1 --- /dev/null +++ b/kmail/actionscheduler.cpp @@ -0,0 +1,836 @@ +/* Action Scheduler + + This file is part of KMail, the KDE mail client. + Copyright (c) Don Sanders <sanders@kde.org> + + KMail is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + KMail is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ +#include <kdebug.h> // FIXME + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "actionscheduler.h" + +#include "filterlog.h" +#include "messageproperty.h" +#include "kmfilter.h" +#include "kmfolderindex.h" +#include "kmfoldermgr.h" +#include "kmmsgdict.h" +#include "kmcommands.h" +#include "kmheaders.h" +#include "accountmanager.h" +using KMail::AccountManager; + +#include <qtimer.h> +#include <kconfig.h> +#include <kstandarddirs.h> + +using namespace KMail; +typedef QPtrList<KMMsgBase> KMMessageList; + + +KMFolderMgr* ActionScheduler::tempFolderMgr = 0; +int ActionScheduler::refCount = 0; +int ActionScheduler::count = 0; +QValueList<ActionScheduler*> *ActionScheduler::schedulerList = 0; +bool ActionScheduler::sEnabled = false; +bool ActionScheduler::sEnabledChecked = false; + +ActionScheduler::ActionScheduler(KMFilterMgr::FilterSet set, + QValueList<KMFilter*> filters, + KMHeaders *headers, + KMFolder *srcFolder) + :mSet( set ), mHeaders( headers ) +{ + ++count; + ++refCount; + mExecuting = false; + mExecutingLock = false; + mFetchExecuting = false; + mFiltersAreQueued = false; + mResult = ResultOk; + mIgnore = false; + mAutoDestruct = false; + mAlwaysMatch = false; + mAccountId = 0; + mAccount = false; + lastCommand = 0; + lastJob = 0; + finishTimer = new QTimer( this, "finishTimer" ); + connect( finishTimer, SIGNAL(timeout()), this, SLOT(finish())); + fetchMessageTimer = new QTimer( this, "fetchMessageTimer" ); + connect( fetchMessageTimer, SIGNAL(timeout()), this, SLOT(fetchMessage())); + tempCloseFoldersTimer = new QTimer( this, "tempCloseFoldersTimer" ); + connect( tempCloseFoldersTimer, SIGNAL(timeout()), this, SLOT(tempCloseFolders())); + processMessageTimer = new QTimer( this, "processMessageTimer" ); + connect( processMessageTimer, SIGNAL(timeout()), this, SLOT(processMessage())); + filterMessageTimer = new QTimer( this, "filterMessageTimer" ); + connect( filterMessageTimer, SIGNAL(timeout()), this, SLOT(filterMessage())); + timeOutTimer = new QTimer( this, "timeOutTimer" ); + connect( timeOutTimer, SIGNAL(timeout()), this, SLOT(timeOut())); + fetchTimeOutTimer = new QTimer( this, "fetchTimeOutTimer" ); + connect( fetchTimeOutTimer, SIGNAL(timeout()), this, SLOT(fetchTimeOut())); + + QValueList<KMFilter*>::Iterator it = filters.begin(); + for (; it != filters.end(); ++it) + mFilters.append( **it ); + mDestFolder = 0; + if (srcFolder) { + mDeleteSrcFolder = false; + setSourceFolder( srcFolder ); + } else { + QString tmpName; + tmpName.setNum( count ); + if (!tempFolderMgr) + tempFolderMgr = new KMFolderMgr(locateLocal("data","kmail/filter")); + KMFolder *tempFolder = tempFolderMgr->findOrCreate( tmpName ); + tempFolder->expunge(); + mDeleteSrcFolder = true; + setSourceFolder( tempFolder ); + } + if (!schedulerList) + schedulerList = new QValueList<ActionScheduler*>; + schedulerList->append( this ); +} + +ActionScheduler::~ActionScheduler() +{ + schedulerList->remove( this ); + tempCloseFolders(); + disconnect( mSrcFolder, SIGNAL(closed()), + this, SLOT(folderClosedOrExpunged()) ); + disconnect( mSrcFolder, SIGNAL(expunged(KMFolder*)), + this, SLOT(folderClosedOrExpunged()) ); + mSrcFolder->close("actionschedsrc"); + + if (mDeleteSrcFolder) + tempFolderMgr->remove(mSrcFolder); + + --refCount; + if (refCount == 0) { + delete tempFolderMgr; + tempFolderMgr = 0; + } +} + +void ActionScheduler::setAutoDestruct( bool autoDestruct ) +{ + mAutoDestruct = autoDestruct; +} + +void ActionScheduler::setAlwaysMatch( bool alwaysMatch ) +{ + mAlwaysMatch = alwaysMatch; +} + +void ActionScheduler::setDefaultDestinationFolder( KMFolder *destFolder ) +{ + mDestFolder = destFolder; +} + +void ActionScheduler::setSourceFolder( KMFolder *srcFolder ) +{ + srcFolder->open("actionschedsrc"); + if (mSrcFolder) { + disconnect( mSrcFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)), + this, SLOT(msgAdded(KMFolder*, Q_UINT32)) ); + disconnect( mSrcFolder, SIGNAL(closed()), + this, SLOT(folderClosedOrExpunged()) ); + disconnect( mSrcFolder, SIGNAL(expunged(KMFolder*)), + this, SLOT(folderClosedOrExpunged()) ); + mSrcFolder->close("actionschedsrc"); + } + mSrcFolder = srcFolder; + int i = 0; + for (i = 0; i < mSrcFolder->count(); ++i) + enqueue( mSrcFolder->getMsgBase( i )->getMsgSerNum() ); + if (mSrcFolder) { + connect( mSrcFolder, SIGNAL(msgAdded(KMFolder*, Q_UINT32)), + this, SLOT(msgAdded(KMFolder*, Q_UINT32)) ); + connect( mSrcFolder, SIGNAL(closed()), + this, SLOT(folderClosedOrExpunged()) ); + connect( mSrcFolder, SIGNAL(expunged(KMFolder*)), + this, SLOT(folderClosedOrExpunged()) ); + } +} + +void ActionScheduler::setFilterList( QValueList<KMFilter*> filters ) +{ + mFiltersAreQueued = true; + mQueuedFilters.clear(); + + QValueList<KMFilter*>::Iterator it = filters.begin(); + for (; it != filters.end(); ++it) + mQueuedFilters.append( **it ); + if (!mExecuting) { + mFilters = mQueuedFilters; + mFiltersAreQueued = false; + mQueuedFilters.clear(); + } +} + +void ActionScheduler::folderClosedOrExpunged() +{ + // mSrcFolder has been closed. reopen it. + if ( mSrcFolder ) + { + mSrcFolder->open( "actionsched" ); + } +} + +int ActionScheduler::tempOpenFolder( KMFolder* aFolder ) +{ + assert( aFolder ); + tempCloseFoldersTimer->stop(); + if ( aFolder == mSrcFolder.operator->() ) + return 0; + + int rc = aFolder->open("actionsched"); + if (rc) + return rc; + + mOpenFolders.append( aFolder ); + return 0; +} + +void ActionScheduler::tempCloseFolders() +{ + // close temp opened folders + QValueListConstIterator<QGuardedPtr<KMFolder> > it; + for (it = mOpenFolders.begin(); it != mOpenFolders.end(); ++it) { + KMFolder *folder = *it; + if (folder) + folder->close("actionsched"); + } + mOpenFolders.clear(); +} + +void ActionScheduler::execFilters(const QValueList<Q_UINT32> serNums) +{ + QValueListConstIterator<Q_UINT32> it; + for (it = serNums.begin(); it != serNums.end(); ++it) + execFilters( *it ); +} + +void ActionScheduler::execFilters(const QPtrList<KMMsgBase> msgList) +{ + KMMsgBase *msgBase; + QPtrList<KMMsgBase> list = msgList; + for (msgBase = list.first(); msgBase; msgBase = list.next()) + execFilters( msgBase->getMsgSerNum() ); +} + +void ActionScheduler::execFilters(KMMsgBase* msgBase) +{ + execFilters( msgBase->getMsgSerNum() ); +} + +void ActionScheduler::execFilters(Q_UINT32 serNum) +{ + if (mResult != ResultOk) { + if ((mResult != ResultCriticalError) && + !mExecuting && !mExecutingLock && !mFetchExecuting) { + mResult = ResultOk; // Recoverable error + if (!mFetchSerNums.isEmpty()) { + mFetchSerNums.push_back( mFetchSerNums.first() ); + mFetchSerNums.pop_front(); + } + } else + return; // An error has already occurred don't even try to process this msg + } + if (MessageProperty::filtering( serNum )) { + // Not good someone else is already filtering this msg + mResult = ResultError; + if (!mExecuting && !mFetchExecuting) + finishTimer->start( 0, true ); + } else { + // Everything is ok async fetch this message + mFetchSerNums.append( serNum ); + if (!mFetchExecuting) { + //Need to (re)start incomplete msg fetching chain + mFetchExecuting = true; + fetchMessageTimer->start( 0, true ); + } + } +} + +KMMsgBase *ActionScheduler::messageBase(Q_UINT32 serNum) +{ + int idx = -1; + KMFolder *folder = 0; + KMMsgBase *msg = 0; + KMMsgDict::instance()->getLocation( serNum, &folder, &idx ); + // It's possible that the message has been deleted or moved into a + // different folder + if (folder && (idx != -1)) { + // everything is ok + tempOpenFolder( folder ); // just in case msg has moved + msg = folder->getMsgBase( idx ); + } else { + // the message is gone! + mResult = ResultError; + finishTimer->start( 0, true ); + } + return msg; +} + +KMMessage *ActionScheduler::message(Q_UINT32 serNum) +{ + int idx = -1; + KMFolder *folder = 0; + KMMessage *msg = 0; + KMMsgDict::instance()->getLocation( serNum, &folder, &idx ); + // It's possible that the message has been deleted or moved into a + // different folder + if (folder && (idx != -1)) { + // everything is ok + msg = folder->getMsg( idx ); + tempOpenFolder( folder ); // just in case msg has moved + } else { + // the message is gone! + mResult = ResultError; + finishTimer->start( 0, true ); + } + return msg; +} + +void ActionScheduler::finish() +{ + if (mResult != ResultOk) { + // Must handle errors immediately + emit result( mResult ); + return; + } + + if (!mExecuting) { + + if (!mFetchSerNums.isEmpty()) { + // Possibly if (mResult == ResultOk) should cancel job and start again. + // Believe smarter logic to bail out if an error has occurred is required. + // Perhaps should be testing for mFetchExecuting or at least set it to true + fetchMessageTimer->start( 0, true ); // give it a bit of time at a test + return; + } else { + mFetchExecuting = false; + } + + if (mSerNums.begin() != mSerNums.end()) { + mExecuting = true; + processMessageTimer->start( 0, true ); + return; + } + + // If an error has occurred and a permanent source folder has + // been set then move all the messages left in the source folder + // to the inbox. If no permanent source folder has been set + // then abandon filtering of queued messages. + if (!mDeleteSrcFolder && !mDestFolder.isNull() ) { + while ( mSrcFolder->count() > 0 ) { + KMMessage *msg = mSrcFolder->getMsg( 0 ); + mDestFolder->moveMsg( msg ); + } + + // Wait a little while before closing temp folders, just in case + // new messages arrive for filtering. + tempCloseFoldersTimer->start( 60*1000, true ); + } + mSerNums.clear(); //abandon + mFetchSerNums.clear(); //abandon + + if (mFiltersAreQueued) + mFilters = mQueuedFilters; + mQueuedFilters.clear(); + mFiltersAreQueued = false; + ReturnCode aResult = mResult; + mResult = ResultOk; + mExecutingLock = false; + emit result( aResult ); + if (mAutoDestruct) + delete this; + } + // else a message may be in the process of being fetched or filtered + // wait until both of these commitments are finished then this + // method should be called again. +} + +void ActionScheduler::fetchMessage() +{ + QValueListIterator<Q_UINT32> mFetchMessageIt = mFetchSerNums.begin(); + while (mFetchMessageIt != mFetchSerNums.end()) { + if (!MessageProperty::transferInProgress(*mFetchMessageIt)) + break; + ++mFetchMessageIt; + } + + // Note: Perhaps this could be improved. We shouldn't give up straight away + // if !mFetchSerNums.isEmpty (becausing transferInProgress is true + // for some messages). Instead we should delay for a minute or so and + // again. + if (mFetchMessageIt == mFetchSerNums.end() && !mFetchSerNums.isEmpty()) { + mResult = ResultError; + } + if ((mFetchMessageIt == mFetchSerNums.end()) || (mResult != ResultOk)) { + mFetchExecuting = false; + if (!mSrcFolder->count()) + mSrcFolder->expunge(); + finishTimer->start( 0, true ); + return; + } + + //If we got this far then there's a valid message to work with + KMMsgBase *msgBase = messageBase( *mFetchMessageIt ); + + if ((mResult != ResultOk) || (!msgBase)) { + mFetchExecuting = false; + return; + } + mFetchUnget = msgBase->isMessage(); + KMMessage *msg = message( *mFetchMessageIt ); + if (mResult != ResultOk) { + mFetchExecuting = false; + return; + } + + if (msg && msg->isComplete()) { + messageFetched( msg ); + } else if (msg) { + fetchTimeOutTime = QTime::currentTime(); + fetchTimeOutTimer->start( 60 * 1000, true ); + FolderJob *job = msg->parent()->createJob( msg ); + connect( job, SIGNAL(messageRetrieved( KMMessage* )), + SLOT(messageFetched( KMMessage* )) ); + lastJob = job; + job->start(); + } else { + mFetchExecuting = false; + mResult = ResultError; + finishTimer->start( 0, true ); + return; + } +} + +void ActionScheduler::messageFetched( KMMessage *msg ) +{ + fetchTimeOutTimer->stop(); + if (!msg) { + // Should never happen, but sometimes does; + fetchMessageTimer->start( 0, true ); + return; + } + + mFetchSerNums.remove( msg->getMsgSerNum() ); + + // Note: This may not be necessary. What about when it's time to + // delete the original message? + // Is the new serial number being set correctly then? + if ((mSet & KMFilterMgr::Explicit) || + (msg->headerField( "X-KMail-Filtered" ).isEmpty())) { + QString serNumS; + serNumS.setNum( msg->getMsgSerNum() ); + KMMessage *newMsg = new KMMessage; + newMsg->fromString(msg->asString()); + newMsg->setStatus(msg->status()); + newMsg->setComplete(msg->isComplete()); + newMsg->setHeaderField( "X-KMail-Filtered", serNumS ); + mSrcFolder->addMsg( newMsg ); + } else { + fetchMessageTimer->start( 0, true ); + } + if (mFetchUnget && msg->parent()) + msg->parent()->unGetMsg( msg->parent()->find( msg )); + return; +} + +void ActionScheduler::msgAdded( KMFolder*, Q_UINT32 serNum ) +{ + if (!mIgnore) + enqueue( serNum ); +} + +void ActionScheduler::enqueue(Q_UINT32 serNum) +{ + if (mResult != ResultOk) + return; // An error has already occurred don't even try to process this msg + + if (MessageProperty::filtering( serNum )) { + // Not good someone else is already filtering this msg + mResult = ResultError; + if (!mExecuting && !mFetchExecuting) + finishTimer->start( 0, true ); + } else { + // Everything is ok async filter this message + mSerNums.append( serNum ); + + if (!mExecuting) { + // Note: Need to (re)start incomplete msg filtering chain + // The state of mFetchExecuting is of some concern. + mExecuting = true; + mMessageIt = mSerNums.begin(); + processMessageTimer->start( 0, true ); + } + } +} + +void ActionScheduler::processMessage() +{ + if (mExecutingLock) + return; + mExecutingLock = true; + mMessageIt = mSerNums.begin(); + while (mMessageIt != mSerNums.end()) { + if (!MessageProperty::transferInProgress(*mMessageIt)) + break; + ++mMessageIt; + } + + if (mMessageIt == mSerNums.end() && !mSerNums.isEmpty()) { + mExecuting = false; + processMessageTimer->start( 600, true ); + } + + if ((mMessageIt == mSerNums.end()) || (mResult != ResultOk)) { + mExecutingLock = false; + mExecuting = false; + finishTimer->start( 0, true ); + return; + } + + //If we got this far then there's a valid message to work with + KMMsgBase *msgBase = messageBase( *mMessageIt ); + if (!msgBase || mResult != ResultOk) { + mExecuting = false; + return; + } + + MessageProperty::setFiltering( *mMessageIt, true ); + MessageProperty::setFilterHandler( *mMessageIt, this ); + MessageProperty::setFilterFolder( *mMessageIt, mDestFolder ); + if ( FilterLog::instance()->isLogging() ) { + FilterLog::instance()->addSeparator(); + } + mFilterIt = mFilters.begin(); + + mUnget = msgBase->isMessage(); + KMMessage *msg = message( *mMessageIt ); + if (mResult != ResultOk) { + mExecuting = false; + return; + } + + bool mdnEnabled = true; + { + KConfigGroup mdnConfig( kmkernel->config(), "MDN" ); + int mode = mdnConfig.readNumEntry( "default-policy", 0 ); + if (!mode || mode < 0 || mode > 3) + mdnEnabled = false; + } + mdnEnabled = true; // For 3.2 force all mails to be complete + + if ((msg && msg->isComplete()) || + (msg && !(*mFilterIt).requiresBody(msg) && !mdnEnabled)) + { + // We have a complete message or + // we can work with an incomplete message + // Get a write lock on the message while it's being filtered + msg->setTransferInProgress( true ); + filterMessageTimer->start( 0, true ); + return; + } + if (msg) { + FolderJob *job = msg->parent()->createJob( msg ); + connect( job, SIGNAL(messageRetrieved( KMMessage* )), + SLOT(messageRetrieved( KMMessage* )) ); + job->start(); + } else { + mExecuting = false; + mResult = ResultError; + finishTimer->start( 0, true ); + return; + } +} + +void ActionScheduler::messageRetrieved(KMMessage* msg) +{ + // Get a write lock on the message while it's being filtered + msg->setTransferInProgress( true ); + filterMessageTimer->start( 0, true ); +} + +void ActionScheduler::filterMessage() +{ + if (mFilterIt == mFilters.end()) { + moveMessage(); + return; + } + if (((mSet & KMFilterMgr::Outbound) && (*mFilterIt).applyOnOutbound()) || + ((mSet & KMFilterMgr::Inbound) && (*mFilterIt).applyOnInbound() && + (!mAccount || + (mAccount && (*mFilterIt).applyOnAccount(mAccountId)))) || + ((mSet & KMFilterMgr::Explicit) && (*mFilterIt).applyOnExplicit())) { + + // filter is applicable + if ( FilterLog::instance()->isLogging() ) { + QString logText( i18n( "<b>Evaluating filter rules:</b> " ) ); + logText.append( (*mFilterIt).pattern()->asString() ); + FilterLog::instance()->add( logText, FilterLog::patternDesc ); + } + if (mAlwaysMatch || + (*mFilterIt).pattern()->matches( *mMessageIt )) { + if ( FilterLog::instance()->isLogging() ) { + FilterLog::instance()->add( i18n( "<b>Filter rules have matched.</b>" ), + FilterLog::patternResult ); + } + mFilterAction = (*mFilterIt).actions()->first(); + actionMessage(); + return; + } + } + ++mFilterIt; + filterMessageTimer->start( 0, true ); +} + +void ActionScheduler::actionMessage(KMFilterAction::ReturnCode res) +{ + if (res == KMFilterAction::CriticalError) { + mResult = ResultCriticalError; + finish(); //must handle critical errors immediately + } + if (mFilterAction) { + KMMessage *msg = message( *mMessageIt ); + if (msg) { + if ( FilterLog::instance()->isLogging() ) { + QString logText( i18n( "<b>Applying filter action:</b> %1" ) + .arg( mFilterAction->displayString() ) ); + FilterLog::instance()->add( logText, FilterLog::appliedAction ); + } + KMFilterAction *action = mFilterAction; + mFilterAction = (*mFilterIt).actions()->next(); + action->processAsync( msg ); + } + } else { + // there are no more actions + if ((*mFilterIt).stopProcessingHere()) + mFilterIt = mFilters.end(); + else + ++mFilterIt; + filterMessageTimer->start( 0, true ); + } +} + +void ActionScheduler::moveMessage() +{ + KMMsgBase *msgBase = messageBase( *mMessageIt ); + if (!msgBase) + return; + + MessageProperty::setTransferInProgress( *mMessageIt, false, true ); + KMMessage *msg = message( *mMessageIt ); + KMFolder *folder = MessageProperty::filterFolder( *mMessageIt ); + QString serNumS = msg->headerField( "X-KMail-Filtered" ); + if (!serNumS.isEmpty()) + mOriginalSerNum = serNumS.toUInt(); + else + mOriginalSerNum = 0; + MessageProperty::setFilterHandler( *mMessageIt, 0 ); + MessageProperty::setFiltering( *mMessageIt, false ); + mSerNums.remove( *mMessageIt ); + + KMMessage *orgMsg = 0; + ReturnCode mOldReturnCode = mResult; + if (mOriginalSerNum) + orgMsg = message( mOriginalSerNum ); + mResult = mOldReturnCode; // ignore errors in deleting original message + if (!orgMsg || !orgMsg->parent()) { + // Original message is gone, no point filtering it anymore + mSrcFolder->removeMsg( mSrcFolder->find( msg ) ); + kdDebug(5006) << "The original serial number is missing. " + << "Cannot complete the filtering." << endl; + mExecutingLock = false; + processMessageTimer->start( 0, true ); + return; + } else { + if (!folder) // no filter folder specified leave in current place + folder = orgMsg->parent(); + } + + mIgnore = true; + assert( msg->parent() == mSrcFolder.operator->() ); + mSrcFolder->take( mSrcFolder->find( msg ) ); + mSrcFolder->addMsg( msg ); + mIgnore = false; + + if (msg && folder && kmkernel->folderIsTrash( folder )) + KMFilterAction::sendMDN( msg, KMime::MDN::Deleted ); + + timeOutTime = QTime::currentTime(); + KMCommand *cmd = new KMMoveCommand( folder, msg ); + connect( cmd, SIGNAL( completed( KMCommand * ) ), + this, SLOT( moveMessageFinished( KMCommand * ) ) ); + cmd->start(); + // sometimes the move command doesn't complete so time out after a minute + // and move onto the next message + lastCommand = cmd; + timeOutTimer->start( 60 * 1000, true ); +} + +void ActionScheduler::moveMessageFinished( KMCommand *command ) +{ + timeOutTimer->stop(); + if ( command->result() != KMCommand::OK ) + mResult = ResultError; + + if (!mSrcFolder->count()) + mSrcFolder->expunge(); + + // in case the message stayed in the current folder TODO optimize + if ( mHeaders ) + mHeaders->clearSelectableAndAboutToBeDeleted( mOriginalSerNum ); + KMMessage *msg = 0; + ReturnCode mOldReturnCode = mResult; + if (mOriginalSerNum) { + msg = message( mOriginalSerNum ); + emit filtered( mOriginalSerNum ); + } + + mResult = mOldReturnCode; // ignore errors in deleting original message + KMCommand *cmd = 0; + if (msg && msg->parent()) { + cmd = new KMMoveCommand( 0, msg ); +// cmd->start(); // Note: sensitive logic here. + } + + if (mResult == ResultOk) { + mExecutingLock = false; + if (cmd) + connect( cmd, SIGNAL( completed( KMCommand * ) ), + this, SLOT( processMessage() ) ); + else + processMessageTimer->start( 0, true ); + } else { + // Note: An alternative to consider is just calling + // finishTimer->start and returning + if (cmd) + connect( cmd, SIGNAL( completed( KMCommand * ) ), + this, SLOT( finish() ) ); + else + finishTimer->start( 0, true ); + } + if (cmd) + cmd->start(); + // else moveMessageFinished should call finish +} + +void ActionScheduler::copyMessageFinished( KMCommand *command ) +{ + if ( command->result() != KMCommand::OK ) + actionMessage( KMFilterAction::ErrorButGoOn ); + else + actionMessage(); +} + +void ActionScheduler::timeOut() +{ + // Note: This is a good place for a debug statement + assert( lastCommand ); + // sometimes imap jobs seem to just stall so give up and move on + disconnect( lastCommand, SIGNAL( completed( KMCommand * ) ), + this, SLOT( moveMessageFinished( KMCommand * ) ) ); + lastCommand = 0; + mExecutingLock = false; + mExecuting = false; + finishTimer->start( 0, true ); + if (mOriginalSerNum) // Try again + execFilters( mOriginalSerNum ); +} + +void ActionScheduler::fetchTimeOut() +{ + // Note: This is a good place for a debug statement + assert( lastJob ); + // sometimes imap jobs seem to just stall so give up and move on + disconnect( lastJob, SIGNAL(messageRetrieved( KMMessage* )), + this, SLOT(messageFetched( KMMessage* )) ); + lastJob->kill(); + lastJob = 0; + fetchMessageTimer->start( 0, true ); +} + +QString ActionScheduler::debug() +{ + QString res; + QValueList<ActionScheduler*>::iterator it; + int i = 1; + for ( it = schedulerList->begin(); it != schedulerList->end(); ++it ) { + res.append( QString( "ActionScheduler #%1.\n" ).arg( i ) ); + if ((*it)->mAccount && kmkernel->find( (*it)->mAccountId )) { + res.append( QString( "Account %1, Name %2.\n" ) + .arg( (*it)->mAccountId ) + .arg( kmkernel->acctMgr()->find( (*it)->mAccountId )->name() ) ); + } + res.append( QString( "mExecuting %1, " ).arg( (*it)->mExecuting ? "true" : "false" ) ); + res.append( QString( "mExecutingLock %1, " ).arg( (*it)->mExecutingLock ? "true" : "false" ) ); + res.append( QString( "mFetchExecuting %1.\n" ).arg( (*it)->mFetchExecuting ? "true" : "false" ) ); + res.append( QString( "mOriginalSerNum %1.\n" ).arg( (*it)->mOriginalSerNum ) ); + res.append( QString( "mMessageIt %1.\n" ).arg( ((*it)->mMessageIt != 0) ? *(*it)->mMessageIt : 0 ) ); + res.append( QString( "mSerNums count %1, " ).arg( (*it)->mSerNums.count() ) ); + res.append( QString( "mFetchSerNums count %1.\n" ).arg( (*it)->mFetchSerNums.count() ) ); + res.append( QString( "mResult " ) ); + if ((*it)->mResult == ResultOk) + res.append( QString( "ResultOk.\n" ) ); + else if ((*it)->mResult == ResultError) + res.append( QString( "ResultError.\n" ) ); + else if ((*it)->mResult == ResultCriticalError) + res.append( QString( "ResultCriticalError.\n" ) ); + else + res.append( QString( "Unknown.\n" ) ); + + ++i; + } + return res; +} + +bool ActionScheduler::isEnabled() +{ + if (sEnabledChecked) + return sEnabled; + + sEnabledChecked = true; + KConfig* config = KMKernel::config(); + KConfigGroupSaver saver(config, "General"); + sEnabled = config->readBoolEntry("action-scheduler", false); + return sEnabled; +} + +bool ActionScheduler::ignoreChanges( bool ignore ) +{ + bool oldValue = mIgnore; + mIgnore = ignore; + return oldValue; +} + +#include "actionscheduler.moc" |