36 #include "actionscheduler.h"
38 #include "filterlog.h"
39 #include "messageproperty.h"
41 #include "kmfolderindex.h"
42 #include "kmfoldermgr.h"
43 #include "kmmsgdict.h"
44 #include "kmcommands.h"
45 #include "kmheaders.h"
46 #include "accountmanager.h"
50 #include <tdeconfig.h>
51 #include <kstandarddirs.h>
53 using namespace KMail;
54 typedef TQPtrList<KMMsgBase> KMMessageList;
57 KMFolderMgr* ActionScheduler::tempFolderMgr = 0;
58 int ActionScheduler::refCount = 0;
59 int ActionScheduler::count = 0;
60 TQValueList<ActionScheduler*> *ActionScheduler::schedulerList = 0;
61 bool ActionScheduler::sEnabled =
false;
62 bool ActionScheduler::sEnabledChecked =
false;
64 ActionScheduler::ActionScheduler(KMFilterMgr::FilterSet set,
65 TQValueList<KMFilter*> filters,
68 :mSet( set ), mHeaders( headers )
73 mExecutingLock =
false;
74 mFetchExecuting =
false;
75 mFiltersAreQueued =
false;
78 mAutoDestruct =
false;
84 finishTimer =
new TQTimer(
this,
"finishTimer" );
85 connect( finishTimer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(finish()));
86 fetchMessageTimer =
new TQTimer(
this,
"fetchMessageTimer" );
87 connect( fetchMessageTimer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(fetchMessage()));
88 tempCloseFoldersTimer =
new TQTimer(
this,
"tempCloseFoldersTimer" );
89 connect( tempCloseFoldersTimer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(tempCloseFolders()));
90 processMessageTimer =
new TQTimer(
this,
"processMessageTimer" );
91 connect( processMessageTimer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(processMessage()));
92 filterMessageTimer =
new TQTimer(
this,
"filterMessageTimer" );
93 connect( filterMessageTimer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(filterMessage()));
94 timeOutTimer =
new TQTimer(
this,
"timeOutTimer" );
95 connect( timeOutTimer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(timeOut()));
96 fetchTimeOutTimer =
new TQTimer(
this,
"fetchTimeOutTimer" );
97 connect( fetchTimeOutTimer, TQ_SIGNAL(timeout()),
this, TQ_SLOT(fetchTimeOut()));
99 TQValueList<KMFilter*>::Iterator it = filters.begin();
100 for (; it != filters.end(); ++it)
101 mFilters.append( **it );
104 mDeleteSrcFolder =
false;
105 setSourceFolder( srcFolder );
108 tmpName.setNum( count );
110 tempFolderMgr =
new KMFolderMgr(locateLocal(
"data",
"kmail/filter"));
111 KMFolder *tempFolder = tempFolderMgr->findOrCreate( tmpName );
113 mDeleteSrcFolder =
true;
114 setSourceFolder( tempFolder );
117 schedulerList =
new TQValueList<ActionScheduler*>;
118 schedulerList->append(
this );
121 ActionScheduler::~ActionScheduler()
123 schedulerList->remove(
this );
125 disconnect( mSrcFolder, TQ_SIGNAL(closed()),
126 this, TQ_SLOT(folderClosedOrExpunged()) );
127 disconnect( mSrcFolder, TQ_SIGNAL(expunged(
KMFolder*)),
128 this, TQ_SLOT(folderClosedOrExpunged()) );
129 mSrcFolder->close(
"actionschedsrc");
131 if (mDeleteSrcFolder)
132 tempFolderMgr->remove(mSrcFolder);
136 delete tempFolderMgr;
141 void ActionScheduler::setAutoDestruct(
bool autoDestruct )
143 mAutoDestruct = autoDestruct;
146 void ActionScheduler::setAlwaysMatch(
bool alwaysMatch )
148 mAlwaysMatch = alwaysMatch;
151 void ActionScheduler::setDefaultDestinationFolder(
KMFolder *destFolder )
153 mDestFolder = destFolder;
156 void ActionScheduler::setSourceFolder(
KMFolder *srcFolder )
158 srcFolder->
open(
"actionschedsrc");
160 disconnect( mSrcFolder, TQ_SIGNAL(msgAdded(
KMFolder*, TQ_UINT32)),
161 this, TQ_SLOT(msgAdded(
KMFolder*, TQ_UINT32)) );
162 disconnect( mSrcFolder, TQ_SIGNAL(closed()),
163 this, TQ_SLOT(folderClosedOrExpunged()) );
164 disconnect( mSrcFolder, TQ_SIGNAL(expunged(
KMFolder*)),
165 this, TQ_SLOT(folderClosedOrExpunged()) );
166 mSrcFolder->close(
"actionschedsrc");
168 mSrcFolder = srcFolder;
170 for (i = 0; i < mSrcFolder->count(); ++i)
171 enqueue( mSrcFolder->getMsgBase( i )->getMsgSerNum() );
173 connect( mSrcFolder, TQ_SIGNAL(msgAdded(
KMFolder*, TQ_UINT32)),
174 this, TQ_SLOT(msgAdded(
KMFolder*, TQ_UINT32)) );
175 connect( mSrcFolder, TQ_SIGNAL(closed()),
176 this, TQ_SLOT(folderClosedOrExpunged()) );
177 connect( mSrcFolder, TQ_SIGNAL(expunged(
KMFolder*)),
178 this, TQ_SLOT(folderClosedOrExpunged()) );
182 void ActionScheduler::setFilterList( TQValueList<KMFilter*> filters )
184 mFiltersAreQueued =
true;
185 mQueuedFilters.clear();
187 TQValueList<KMFilter*>::Iterator it = filters.begin();
188 for (; it != filters.end(); ++it)
189 mQueuedFilters.append( **it );
191 mFilters = mQueuedFilters;
192 mFiltersAreQueued =
false;
193 mQueuedFilters.clear();
197 void ActionScheduler::folderClosedOrExpunged()
202 mSrcFolder->open(
"actionsched" );
206 int ActionScheduler::tempOpenFolder(
KMFolder* aFolder )
209 tempCloseFoldersTimer->stop();
210 if ( aFolder == mSrcFolder.operator->() )
213 int rc = aFolder->
open(
"actionsched");
217 mOpenFolders.append( aFolder );
221 void ActionScheduler::tempCloseFolders()
224 TQValueListConstIterator<TQGuardedPtr<KMFolder> > it;
225 for (it = mOpenFolders.begin(); it != mOpenFolders.end(); ++it) {
228 folder->
close(
"actionsched");
230 mOpenFolders.clear();
233 void ActionScheduler::execFilters(
const TQValueList<TQ_UINT32> serNums)
235 TQValueListConstIterator<TQ_UINT32> it;
236 for (it = serNums.begin(); it != serNums.end(); ++it)
240 void ActionScheduler::execFilters(
const TQPtrList<KMMsgBase> msgList)
243 TQPtrList<KMMsgBase> list = msgList;
244 for (msgBase = list.first(); msgBase; msgBase = list.next())
245 execFilters( msgBase->getMsgSerNum() );
248 void ActionScheduler::execFilters(KMMsgBase* msgBase)
250 execFilters( msgBase->getMsgSerNum() );
253 void ActionScheduler::execFilters(TQ_UINT32 serNum)
255 if (mResult != ResultOk) {
256 if ((mResult != ResultCriticalError) &&
257 !mExecuting && !mExecutingLock && !mFetchExecuting) {
259 if (!mFetchSerNums.isEmpty()) {
260 mFetchSerNums.push_back( mFetchSerNums.first() );
261 mFetchSerNums.pop_front();
266 if (MessageProperty::filtering( serNum )) {
268 mResult = ResultError;
269 if (!mExecuting && !mFetchExecuting)
270 finishTimer->start( 0,
true );
273 mFetchSerNums.append( serNum );
274 if (!mFetchExecuting) {
276 mFetchExecuting =
true;
277 fetchMessageTimer->start( 0,
true );
282 KMMsgBase *ActionScheduler::messageBase(TQ_UINT32 serNum)
290 if (folder && (idx != -1)) {
292 tempOpenFolder( folder );
296 mResult = ResultError;
297 finishTimer->start( 0,
true );
302 KMMessage *ActionScheduler::message(TQ_UINT32 serNum)
310 if (folder && (idx != -1)) {
312 msg = folder->
getMsg( idx );
313 tempOpenFolder( folder );
316 mResult = ResultError;
317 finishTimer->start( 0,
true );
322 void ActionScheduler::finish()
324 if (mResult != ResultOk) {
326 emit result( mResult );
332 if (!mFetchSerNums.isEmpty()) {
336 fetchMessageTimer->start( 0,
true );
339 mFetchExecuting =
false;
342 if (mSerNums.begin() != mSerNums.end()) {
344 processMessageTimer->start( 0,
true );
352 if (!mDeleteSrcFolder && !mDestFolder.isNull() ) {
353 while ( mSrcFolder->count() > 0 ) {
354 KMMessage *msg = mSrcFolder->getMsg( 0 );
355 mDestFolder->moveMsg( msg );
360 tempCloseFoldersTimer->start( 60*1000,
true );
363 mFetchSerNums.clear();
365 if (mFiltersAreQueued)
366 mFilters = mQueuedFilters;
367 mQueuedFilters.clear();
368 mFiltersAreQueued =
false;
369 ReturnCode aResult = mResult;
371 mExecutingLock =
false;
372 emit result( aResult );
381 void ActionScheduler::fetchMessage()
383 TQValueListIterator<TQ_UINT32> mFetchMessageIt = mFetchSerNums.begin();
384 while (mFetchMessageIt != mFetchSerNums.end()) {
385 if (!MessageProperty::transferInProgress(*mFetchMessageIt))
394 if (mFetchMessageIt == mFetchSerNums.end() && !mFetchSerNums.isEmpty()) {
395 mResult = ResultError;
397 if ((mFetchMessageIt == mFetchSerNums.end()) || (mResult != ResultOk)) {
398 mFetchExecuting =
false;
399 if (!mSrcFolder->count())
400 mSrcFolder->expunge();
401 finishTimer->start( 0,
true );
406 KMMsgBase *msgBase = messageBase( *mFetchMessageIt );
408 if ((mResult != ResultOk) || (!msgBase)) {
409 mFetchExecuting =
false;
412 mFetchUnget = msgBase->isMessage();
413 KMMessage *msg = message( *mFetchMessageIt );
414 if (mResult != ResultOk) {
415 mFetchExecuting =
false;
420 messageFetched( msg );
422 fetchTimeOutTime = TQTime::currentTime();
423 fetchTimeOutTimer->start( 60 * 1000,
true );
424 FolderJob *job = msg->parent()->createJob( msg );
425 connect( job, TQ_SIGNAL(messageRetrieved(
KMMessage* )),
430 mFetchExecuting =
false;
431 mResult = ResultError;
432 finishTimer->start( 0,
true );
437 void ActionScheduler::messageFetched(
KMMessage *msg )
439 fetchTimeOutTimer->stop();
442 fetchMessageTimer->start( 0,
true );
446 mFetchSerNums.remove( msg->getMsgSerNum() );
451 if ((mSet & KMFilterMgr::Explicit) ||
452 (msg->
headerField(
"X-KMail-Filtered" ).isEmpty())) {
454 serNumS.setNum( msg->getMsgSerNum() );
456 newMsg->fromString(msg->
asString());
460 mSrcFolder->addMsg( newMsg );
462 fetchMessageTimer->start( 0,
true );
464 if (mFetchUnget && msg->parent())
465 msg->parent()->unGetMsg( msg->parent()->find( msg ));
469 void ActionScheduler::msgAdded(
KMFolder*, TQ_UINT32 serNum )
475 void ActionScheduler::enqueue(TQ_UINT32 serNum)
477 if (mResult != ResultOk)
480 if (MessageProperty::filtering( serNum )) {
482 mResult = ResultError;
483 if (!mExecuting && !mFetchExecuting)
484 finishTimer->start( 0,
true );
487 mSerNums.append( serNum );
493 mMessageIt = mSerNums.begin();
494 processMessageTimer->start( 0,
true );
499 void ActionScheduler::processMessage()
503 mExecutingLock =
true;
504 mMessageIt = mSerNums.begin();
505 while (mMessageIt != mSerNums.end()) {
506 if (!MessageProperty::transferInProgress(*mMessageIt))
511 if (mMessageIt == mSerNums.end() && !mSerNums.isEmpty()) {
513 processMessageTimer->start( 600,
true );
516 if ((mMessageIt == mSerNums.end()) || (mResult != ResultOk)) {
517 mExecutingLock =
false;
519 finishTimer->start( 0,
true );
524 KMMsgBase *msgBase = messageBase( *mMessageIt );
525 if (!msgBase || mResult != ResultOk) {
530 MessageProperty::setFiltering( *mMessageIt,
true );
531 MessageProperty::setFilterHandler( *mMessageIt,
this );
532 MessageProperty::setFilterFolder( *mMessageIt, mDestFolder );
533 if ( FilterLog::instance()->isLogging() ) {
534 FilterLog::instance()->addSeparator();
536 mFilterIt = mFilters.begin();
538 mUnget = msgBase->isMessage();
540 if (mResult != ResultOk) {
545 bool mdnEnabled =
true;
547 TDEConfigGroup mdnConfig( kmkernel->config(),
"MDN" );
548 int mode = mdnConfig.readNumEntry(
"default-policy", 0 );
549 if (!mode || mode < 0 || mode > 3)
555 (msg && !(*mFilterIt).requiresBody(msg) && !mdnEnabled))
561 filterMessageTimer->start( 0,
true );
565 FolderJob *job = msg->parent()->createJob( msg );
566 connect( job, TQ_SIGNAL(messageRetrieved(
KMMessage* )),
567 TQ_SLOT(messageRetrieved(
KMMessage* )) );
571 mResult = ResultError;
572 finishTimer->start( 0,
true );
577 void ActionScheduler::messageRetrieved(
KMMessage* msg)
581 filterMessageTimer->start( 0,
true );
584 void ActionScheduler::filterMessage()
586 if (mFilterIt == mFilters.end()) {
590 if (((mSet & KMFilterMgr::Outbound) && (*mFilterIt).applyOnOutbound()) ||
591 ((mSet & KMFilterMgr::Inbound) && (*mFilterIt).applyOnInbound() &&
593 (mAccount && (*mFilterIt).applyOnAccount(mAccountId)))) ||
594 ((mSet & KMFilterMgr::Explicit) && (*mFilterIt).applyOnExplicit())) {
597 if ( FilterLog::instance()->isLogging() ) {
598 TQString logText( i18n(
"<b>Evaluating filter rules:</b> " ) );
599 logText.append( (*mFilterIt).pattern()->asString() );
600 FilterLog::instance()->add( logText, FilterLog::patternDesc );
603 (*mFilterIt).pattern()->matches( *mMessageIt )) {
604 if ( FilterLog::instance()->isLogging() ) {
605 FilterLog::instance()->add( i18n(
"<b>Filter rules have matched.</b>" ),
606 FilterLog::patternResult );
608 mFilterAction = (*mFilterIt).actions()->first();
614 filterMessageTimer->start( 0,
true );
619 if (res == KMFilterAction::CriticalError) {
620 mResult = ResultCriticalError;
626 if ( FilterLog::instance()->isLogging() ) {
627 TQString logText( i18n(
"<b>Applying filter action:</b> %1" )
628 .arg( mFilterAction->displayString() ) );
629 FilterLog::instance()->add( logText, FilterLog::appliedAction );
632 mFilterAction = (*mFilterIt).actions()->next();
637 if ((*mFilterIt).stopProcessingHere())
638 mFilterIt = mFilters.end();
641 filterMessageTimer->start( 0,
true );
645 void ActionScheduler::moveMessage()
647 KMMsgBase *msgBase = messageBase( *mMessageIt );
651 MessageProperty::setTransferInProgress( *mMessageIt,
false,
true );
653 KMFolder *folder = MessageProperty::filterFolder( *mMessageIt );
654 TQString serNumS = msg->
headerField(
"X-KMail-Filtered" );
655 if (!serNumS.isEmpty())
656 mOriginalSerNum = serNumS.toUInt();
659 MessageProperty::setFilterHandler( *mMessageIt, 0 );
660 MessageProperty::setFiltering( *mMessageIt,
false );
661 mSerNums.remove( *mMessageIt );
664 ReturnCode mOldReturnCode = mResult;
666 orgMsg = message( mOriginalSerNum );
667 mResult = mOldReturnCode;
668 if (!orgMsg || !orgMsg->parent()) {
670 mSrcFolder->removeMsg( mSrcFolder->find( msg ) );
671 kdDebug(5006) <<
"The original serial number is missing. "
672 <<
"Cannot complete the filtering." << endl;
673 mExecutingLock =
false;
674 processMessageTimer->start( 0,
true );
678 folder = orgMsg->parent();
682 assert( msg->parent() == mSrcFolder.operator->() );
683 mSrcFolder->take( mSrcFolder->find( msg ) );
684 mSrcFolder->addMsg( msg );
687 if (msg && folder && kmkernel->folderIsTrash( folder ))
690 timeOutTime = TQTime::currentTime();
691 KMCommand *cmd =
new KMMoveCommand( folder, msg );
692 connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
693 this, TQ_SLOT( moveMessageFinished( KMCommand * ) ) );
698 timeOutTimer->start( 60 * 1000,
true );
701 void ActionScheduler::moveMessageFinished( KMCommand *command )
703 timeOutTimer->stop();
704 if ( command->result() != KMCommand::OK )
705 mResult = ResultError;
707 if (!mSrcFolder->count())
708 mSrcFolder->expunge();
712 mHeaders->clearSelectableAndAboutToBeDeleted( mOriginalSerNum );
714 ReturnCode mOldReturnCode = mResult;
715 if (mOriginalSerNum) {
716 msg = message( mOriginalSerNum );
717 emit filtered( mOriginalSerNum );
720 mResult = mOldReturnCode;
722 if (msg && msg->parent()) {
723 cmd =
new KMMoveCommand( 0, msg );
727 if (mResult == ResultOk) {
728 mExecutingLock =
false;
730 connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
731 this, TQ_SLOT( processMessage() ) );
733 processMessageTimer->start( 0,
true );
738 connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
739 this, TQ_SLOT( finish() ) );
741 finishTimer->start( 0,
true );
748 void ActionScheduler::copyMessageFinished( KMCommand *command )
750 if ( command->result() != KMCommand::OK )
751 actionMessage( KMFilterAction::ErrorButGoOn );
756 void ActionScheduler::timeOut()
759 assert( lastCommand );
761 disconnect( lastCommand, TQ_SIGNAL( completed( KMCommand * ) ),
762 this, TQ_SLOT( moveMessageFinished( KMCommand * ) ) );
764 mExecutingLock =
false;
766 finishTimer->start( 0,
true );
768 execFilters( mOriginalSerNum );
771 void ActionScheduler::fetchTimeOut()
776 disconnect( lastJob, TQ_SIGNAL(messageRetrieved(
KMMessage* )),
777 this, TQ_SLOT(messageFetched(
KMMessage* )) );
780 fetchMessageTimer->start( 0,
true );
783 TQString ActionScheduler::debug()
786 TQValueList<ActionScheduler*>::iterator it;
788 for ( it = schedulerList->begin(); it != schedulerList->end(); ++it ) {
789 res.append( TQString(
"ActionScheduler #%1.\n" ).arg( i ) );
790 if ((*it)->mAccount && kmkernel->find( (*it)->mAccountId )) {
791 res.append( TQString(
"Account %1, Name %2.\n" )
792 .arg( (*it)->mAccountId )
793 .arg( kmkernel->acctMgr()->find( (*it)->mAccountId )->name() ) );
795 res.append( TQString(
"mExecuting %1, " ).arg( (*it)->mExecuting ?
"true" :
"false" ) );
796 res.append( TQString(
"mExecutingLock %1, " ).arg( (*it)->mExecutingLock ?
"true" :
"false" ) );
797 res.append( TQString(
"mFetchExecuting %1.\n" ).arg( (*it)->mFetchExecuting ?
"true" :
"false" ) );
798 res.append( TQString(
"mOriginalSerNum %1.\n" ).arg( (*it)->mOriginalSerNum ) );
799 res.append( TQString(
"mMessageIt %1.\n" ).arg( ((*it)->mMessageIt != 0) ? *(*it)->mMessageIt : 0 ) );
800 res.append( TQString(
"mSerNums count %1, " ).arg( (*it)->mSerNums.count() ) );
801 res.append( TQString(
"mFetchSerNums count %1.\n" ).arg( (*it)->mFetchSerNums.count() ) );
802 res.append( TQString(
"mResult " ) );
803 if ((*it)->mResult == ResultOk)
804 res.append( TQString(
"ResultOk.\n" ) );
805 else if ((*it)->mResult == ResultError)
806 res.append( TQString(
"ResultError.\n" ) );
807 else if ((*it)->mResult == ResultCriticalError)
808 res.append( TQString(
"ResultCriticalError.\n" ) );
810 res.append( TQString(
"Unknown.\n" ) );
817 bool ActionScheduler::isEnabled()
822 sEnabledChecked =
true;
823 TDEConfig* config = KMKernel::config();
824 TDEConfigGroupSaver saver(config,
"General");
825 sEnabled = config->readBoolEntry(
"action-scheduler",
false);
829 bool ActionScheduler::ignoreChanges(
bool ignore )
831 bool oldValue = mIgnore;
836 #include "actionscheduler.moc"
Abstract base class for KMail's filter actions.
ReturnCode
Possible return codes of process:
static void sendMDN(KMMessage *msg, KMime::MDN::DispositionType d, const TQValueList< KMime::MDN::DispositionModifier > &m=TQValueList< KMime::MDN::DispositionModifier >())
Automates the sending of MDNs from filter actions.
virtual void processAsync(KMMessage *msg) const
Execute an action on given message asynchronously.
int expunge()
Delete entire folder.
void close(const char *owner, bool force=false)
Close folder.
KMMessage * getMsg(int idx)
Read message at given index.
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
int open(const char *owner)
Open folder for access.
void setTransferInProgress(bool value, bool force=false)
Set that the message shall not be deleted because it is still required.
void setStatus(const KMMsgStatus status, int idx=-1)
Set status and mark dirty.
TQCString asString() const
Return the entire message contents as a string.
void setComplete(bool v)
Set if the message is a complete message.
KMMsgStatus status() const
Status of the message.
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
bool isComplete() const
Return true if the complete message is available without referring to the backing store.
void setHeaderField(const TQCString &name, const TQString &value, HeaderFieldType type=Unstructured, bool prepend=false)
Set the header field with the given name to the given value.
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
static const KMMsgDict * instance()
Access the globally unique MessageDict.
The account manager is responsible for creating accounts of various types via the factory method crea...