kmail

actionscheduler.cpp
1/* Action Scheduler
2
3 This file is part of KMail, the KDE mail client.
4 Copyright (c) Don Sanders <sanders@kde.org>
5
6 KMail is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License, version 2, as
8 published by the Free Software Foundation.
9
10 KMail is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of this program with any edition of
21 the TQt library by Trolltech AS, Norway (or with modified versions
22 of TQt that use the same license as TQt), and distribute linked
23 combinations including the two. You must obey the GNU General
24 Public License in all respects for all of the code used other than
25 TQt. If you modify this file, you may extend this exception to
26 your version of the file, but you are not obligated to do so. If
27 you do not wish to do so, delete this exception statement from
28 your version.
29*/
30#include <kdebug.h> // FIXME
31
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36#include "actionscheduler.h"
37
38#include "filterlog.h"
39#include "messageproperty.h"
40#include "kmfilter.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"
48
49#include <tqtimer.h>
50#include <tdeconfig.h>
51#include <tdestandarddirs.h>
52
53using namespace KMail;
54typedef TQPtrList<KMMsgBase> KMMessageList;
55
56
57KMFolderMgr* ActionScheduler::tempFolderMgr = 0;
58int ActionScheduler::refCount = 0;
59int ActionScheduler::count = 0;
60TQValueList<ActionScheduler*> *ActionScheduler::schedulerList = 0;
61bool ActionScheduler::sEnabled = false;
62bool ActionScheduler::sEnabledChecked = false;
63
64ActionScheduler::ActionScheduler(KMFilterMgr::FilterSet set,
65 TQValueList<KMFilter*> filters,
66 KMHeaders *headers,
67 KMFolder *srcFolder)
68 :mSet( set ), mHeaders( headers )
69{
70 ++count;
71 ++refCount;
72 mExecuting = false;
73 mExecutingLock = false;
74 mFetchExecuting = false;
75 mFiltersAreQueued = false;
76 mResult = ResultOk;
77 mIgnore = false;
78 mAutoDestruct = false;
79 mAlwaysMatch = false;
80 mAccountId = 0;
81 mAccount = false;
82 lastCommand = 0;
83 lastJob = 0;
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()));
98
99 TQValueList<KMFilter*>::Iterator it = filters.begin();
100 for (; it != filters.end(); ++it)
101 mFilters.append( **it );
102 mDestFolder = 0;
103 if (srcFolder) {
104 mDeleteSrcFolder = false;
105 setSourceFolder( srcFolder );
106 } else {
107 TQString tmpName;
108 tmpName.setNum( count );
109 if (!tempFolderMgr)
110 tempFolderMgr = new KMFolderMgr(locateLocal("data","kmail/filter"));
111 KMFolder *tempFolder = tempFolderMgr->findOrCreate( tmpName );
112 tempFolder->expunge();
113 mDeleteSrcFolder = true;
114 setSourceFolder( tempFolder );
115 }
116 if (!schedulerList)
117 schedulerList = new TQValueList<ActionScheduler*>;
118 schedulerList->append( this );
119}
120
121ActionScheduler::~ActionScheduler()
122{
123 schedulerList->remove( this );
124 tempCloseFolders();
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");
130
131 if (mDeleteSrcFolder)
132 tempFolderMgr->remove(mSrcFolder);
133
134 --refCount;
135 if (refCount == 0) {
136 delete tempFolderMgr;
137 tempFolderMgr = 0;
138 }
139}
140
141void ActionScheduler::setAutoDestruct( bool autoDestruct )
142{
143 mAutoDestruct = autoDestruct;
144}
145
146void ActionScheduler::setAlwaysMatch( bool alwaysMatch )
147{
148 mAlwaysMatch = alwaysMatch;
149}
150
151void ActionScheduler::setDefaultDestinationFolder( KMFolder *destFolder )
152{
153 mDestFolder = destFolder;
154}
155
156void ActionScheduler::setSourceFolder( KMFolder *srcFolder )
157{
158 srcFolder->open("actionschedsrc");
159 if (mSrcFolder) {
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");
167 }
168 mSrcFolder = srcFolder;
169 int i = 0;
170 for (i = 0; i < mSrcFolder->count(); ++i)
171 enqueue( mSrcFolder->getMsgBase( i )->getMsgSerNum() );
172 if (mSrcFolder) {
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()) );
179 }
180}
181
182void ActionScheduler::setFilterList( TQValueList<KMFilter*> filters )
183{
184 mFiltersAreQueued = true;
185 mQueuedFilters.clear();
186
187 TQValueList<KMFilter*>::Iterator it = filters.begin();
188 for (; it != filters.end(); ++it)
189 mQueuedFilters.append( **it );
190 if (!mExecuting) {
191 mFilters = mQueuedFilters;
192 mFiltersAreQueued = false;
193 mQueuedFilters.clear();
194 }
195}
196
197void ActionScheduler::folderClosedOrExpunged()
198{
199 // mSrcFolder has been closed. reopen it.
200 if ( mSrcFolder )
201 {
202 mSrcFolder->open( "actionsched" );
203 }
204}
205
206int ActionScheduler::tempOpenFolder( KMFolder* aFolder )
207{
208 assert( aFolder );
209 tempCloseFoldersTimer->stop();
210 if ( aFolder == mSrcFolder.operator->() )
211 return 0;
212
213 int rc = aFolder->open("actionsched");
214 if (rc)
215 return rc;
216
217 mOpenFolders.append( aFolder );
218 return 0;
219}
220
221void ActionScheduler::tempCloseFolders()
222{
223 // close temp opened folders
224 TQValueListConstIterator<TQGuardedPtr<KMFolder> > it;
225 for (it = mOpenFolders.begin(); it != mOpenFolders.end(); ++it) {
226 KMFolder *folder = *it;
227 if (folder)
228 folder->close("actionsched");
229 }
230 mOpenFolders.clear();
231}
232
233void ActionScheduler::execFilters(const TQValueList<TQ_UINT32> serNums)
234{
235 TQValueListConstIterator<TQ_UINT32> it;
236 for (it = serNums.begin(); it != serNums.end(); ++it)
237 execFilters( *it );
238}
239
240void ActionScheduler::execFilters(const TQPtrList<KMMsgBase> msgList)
241{
242 KMMsgBase *msgBase;
243 TQPtrList<KMMsgBase> list = msgList;
244 for (msgBase = list.first(); msgBase; msgBase = list.next())
245 execFilters( msgBase->getMsgSerNum() );
246}
247
248void ActionScheduler::execFilters(KMMsgBase* msgBase)
249{
250 execFilters( msgBase->getMsgSerNum() );
251}
252
253void ActionScheduler::execFilters(TQ_UINT32 serNum)
254{
255 if (mResult != ResultOk) {
256 if ((mResult != ResultCriticalError) &&
257 !mExecuting && !mExecutingLock && !mFetchExecuting) {
258 mResult = ResultOk; // Recoverable error
259 if (!mFetchSerNums.isEmpty()) {
260 mFetchSerNums.push_back( mFetchSerNums.first() );
261 mFetchSerNums.pop_front();
262 }
263 } else
264 return; // An error has already occurred don't even try to process this msg
265 }
266 if (MessageProperty::filtering( serNum )) {
267 // Not good someone else is already filtering this msg
268 mResult = ResultError;
269 if (!mExecuting && !mFetchExecuting)
270 finishTimer->start( 0, true );
271 } else {
272 // Everything is ok async fetch this message
273 mFetchSerNums.append( serNum );
274 if (!mFetchExecuting) {
275 //Need to (re)start incomplete msg fetching chain
276 mFetchExecuting = true;
277 fetchMessageTimer->start( 0, true );
278 }
279 }
280}
281
282KMMsgBase *ActionScheduler::messageBase(TQ_UINT32 serNum)
283{
284 int idx = -1;
285 KMFolder *folder = 0;
286 KMMsgBase *msg = 0;
287 KMMsgDict::instance()->getLocation( serNum, &folder, &idx );
288 // It's possible that the message has been deleted or moved into a
289 // different folder
290 if (folder && (idx != -1)) {
291 // everything is ok
292 tempOpenFolder( folder ); // just in case msg has moved
293 msg = folder->getMsgBase( idx );
294 } else {
295 // the message is gone!
296 mResult = ResultError;
297 finishTimer->start( 0, true );
298 }
299 return msg;
300}
301
302KMMessage *ActionScheduler::message(TQ_UINT32 serNum)
303{
304 int idx = -1;
305 KMFolder *folder = 0;
306 KMMessage *msg = 0;
307 KMMsgDict::instance()->getLocation( serNum, &folder, &idx );
308 // It's possible that the message has been deleted or moved into a
309 // different folder
310 if (folder && (idx != -1)) {
311 // everything is ok
312 msg = folder->getMsg( idx );
313 tempOpenFolder( folder ); // just in case msg has moved
314 } else {
315 // the message is gone!
316 mResult = ResultError;
317 finishTimer->start( 0, true );
318 }
319 return msg;
320}
321
322void ActionScheduler::finish()
323{
324 if (mResult != ResultOk) {
325 // Must handle errors immediately
326 emit result( mResult );
327 return;
328 }
329
330 if (!mExecuting) {
331
332 if (!mFetchSerNums.isEmpty()) {
333 // Possibly if (mResult == ResultOk) should cancel job and start again.
334 // Believe smarter logic to bail out if an error has occurred is required.
335 // Perhaps should be testing for mFetchExecuting or at least set it to true
336 fetchMessageTimer->start( 0, true ); // give it a bit of time at a test
337 return;
338 } else {
339 mFetchExecuting = false;
340 }
341
342 if (mSerNums.begin() != mSerNums.end()) {
343 mExecuting = true;
344 processMessageTimer->start( 0, true );
345 return;
346 }
347
348 // If an error has occurred and a permanent source folder has
349 // been set then move all the messages left in the source folder
350 // to the inbox. If no permanent source folder has been set
351 // then abandon filtering of queued messages.
352 if (!mDeleteSrcFolder && !mDestFolder.isNull() ) {
353 while ( mSrcFolder->count() > 0 ) {
354 KMMessage *msg = mSrcFolder->getMsg( 0 );
355 mDestFolder->moveMsg( msg );
356 }
357
358 // Wait a little while before closing temp folders, just in case
359 // new messages arrive for filtering.
360 tempCloseFoldersTimer->start( 60*1000, true );
361 }
362 mSerNums.clear(); //abandon
363 mFetchSerNums.clear(); //abandon
364
365 if (mFiltersAreQueued)
366 mFilters = mQueuedFilters;
367 mQueuedFilters.clear();
368 mFiltersAreQueued = false;
369 ReturnCode aResult = mResult;
370 mResult = ResultOk;
371 mExecutingLock = false;
372 emit result( aResult );
373 if (mAutoDestruct)
374 delete this;
375 }
376 // else a message may be in the process of being fetched or filtered
377 // wait until both of these commitments are finished then this
378 // method should be called again.
379}
380
381void ActionScheduler::fetchMessage()
382{
383 TQValueListIterator<TQ_UINT32> mFetchMessageIt = mFetchSerNums.begin();
384 while (mFetchMessageIt != mFetchSerNums.end()) {
385 if (!MessageProperty::transferInProgress(*mFetchMessageIt))
386 break;
387 ++mFetchMessageIt;
388 }
389
390 // Note: Perhaps this could be improved. We shouldn't give up straight away
391 // if !mFetchSerNums.isEmpty (becausing transferInProgress is true
392 // for some messages). Instead we should delay for a minute or so and
393 // again.
394 if (mFetchMessageIt == mFetchSerNums.end() && !mFetchSerNums.isEmpty()) {
395 mResult = ResultError;
396 }
397 if ((mFetchMessageIt == mFetchSerNums.end()) || (mResult != ResultOk)) {
398 mFetchExecuting = false;
399 if (!mSrcFolder->count())
400 mSrcFolder->expunge();
401 finishTimer->start( 0, true );
402 return;
403 }
404
405 //If we got this far then there's a valid message to work with
406 KMMsgBase *msgBase = messageBase( *mFetchMessageIt );
407
408 if ((mResult != ResultOk) || (!msgBase)) {
409 mFetchExecuting = false;
410 return;
411 }
412 mFetchUnget = msgBase->isMessage();
413 KMMessage *msg = message( *mFetchMessageIt );
414 if (mResult != ResultOk) {
415 mFetchExecuting = false;
416 return;
417 }
418
419 if (msg && msg->isComplete()) {
420 messageFetched( msg );
421 } else if (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* )),
426 TQ_SLOT(messageFetched( KMMessage* )) );
427 lastJob = job;
428 job->start();
429 } else {
430 mFetchExecuting = false;
431 mResult = ResultError;
432 finishTimer->start( 0, true );
433 return;
434 }
435}
436
437void ActionScheduler::messageFetched( KMMessage *msg )
438{
439 fetchTimeOutTimer->stop();
440 if (!msg) {
441 // Should never happen, but sometimes does;
442 fetchMessageTimer->start( 0, true );
443 return;
444 }
445
446 mFetchSerNums.remove( msg->getMsgSerNum() );
447
448 // Note: This may not be necessary. What about when it's time to
449 // delete the original message?
450 // Is the new serial number being set correctly then?
451 if ((mSet & KMFilterMgr::Explicit) ||
452 (msg->headerField( "X-KMail-Filtered" ).isEmpty())) {
453 TQString serNumS;
454 serNumS.setNum( msg->getMsgSerNum() );
455 KMMessage *newMsg = new KMMessage;
456 newMsg->fromString(msg->asString());
457 newMsg->setStatus(msg->status());
458 newMsg->setComplete(msg->isComplete());
459 newMsg->setHeaderField( "X-KMail-Filtered", serNumS );
460 mSrcFolder->addMsg( newMsg );
461 } else {
462 fetchMessageTimer->start( 0, true );
463 }
464 if (mFetchUnget && msg->parent())
465 msg->parent()->unGetMsg( msg->parent()->find( msg ));
466 return;
467}
468
469void ActionScheduler::msgAdded( KMFolder*, TQ_UINT32 serNum )
470{
471 if (!mIgnore)
472 enqueue( serNum );
473}
474
475void ActionScheduler::enqueue(TQ_UINT32 serNum)
476{
477 if (mResult != ResultOk)
478 return; // An error has already occurred don't even try to process this msg
479
480 if (MessageProperty::filtering( serNum )) {
481 // Not good someone else is already filtering this msg
482 mResult = ResultError;
483 if (!mExecuting && !mFetchExecuting)
484 finishTimer->start( 0, true );
485 } else {
486 // Everything is ok async filter this message
487 mSerNums.append( serNum );
488
489 if (!mExecuting) {
490 // Note: Need to (re)start incomplete msg filtering chain
491 // The state of mFetchExecuting is of some concern.
492 mExecuting = true;
493 mMessageIt = mSerNums.begin();
494 processMessageTimer->start( 0, true );
495 }
496 }
497}
498
499void ActionScheduler::processMessage()
500{
501 if (mExecutingLock)
502 return;
503 mExecutingLock = true;
504 mMessageIt = mSerNums.begin();
505 while (mMessageIt != mSerNums.end()) {
506 if (!MessageProperty::transferInProgress(*mMessageIt))
507 break;
508 ++mMessageIt;
509 }
510
511 if (mMessageIt == mSerNums.end() && !mSerNums.isEmpty()) {
512 mExecuting = false;
513 processMessageTimer->start( 600, true );
514 }
515
516 if ((mMessageIt == mSerNums.end()) || (mResult != ResultOk)) {
517 mExecutingLock = false;
518 mExecuting = false;
519 finishTimer->start( 0, true );
520 return;
521 }
522
523 //If we got this far then there's a valid message to work with
524 KMMsgBase *msgBase = messageBase( *mMessageIt );
525 if (!msgBase || mResult != ResultOk) {
526 mExecuting = false;
527 return;
528 }
529
530 MessageProperty::setFiltering( *mMessageIt, true );
531 MessageProperty::setFilterHandler( *mMessageIt, this );
532 MessageProperty::setFilterFolder( *mMessageIt, mDestFolder );
533 if ( FilterLog::instance()->isLogging() ) {
534 FilterLog::instance()->addSeparator();
535 }
536 mFilterIt = mFilters.begin();
537
538 mUnget = msgBase->isMessage();
539 KMMessage *msg = message( *mMessageIt );
540 if (mResult != ResultOk) {
541 mExecuting = false;
542 return;
543 }
544
545 bool mdnEnabled = true;
546 {
547 TDEConfigGroup mdnConfig( kmkernel->config(), "MDN" );
548 int mode = mdnConfig.readNumEntry( "default-policy", 0 );
549 if (!mode || mode < 0 || mode > 3)
550 mdnEnabled = false;
551 }
552 mdnEnabled = true; // For 3.2 force all mails to be complete
553
554 if ((msg && msg->isComplete()) ||
555 (msg && !(*mFilterIt).requiresBody(msg) && !mdnEnabled))
556 {
557 // We have a complete message or
558 // we can work with an incomplete message
559 // Get a write lock on the message while it's being filtered
560 msg->setTransferInProgress( true );
561 filterMessageTimer->start( 0, true );
562 return;
563 }
564 if (msg) {
565 FolderJob *job = msg->parent()->createJob( msg );
566 connect( job, TQ_SIGNAL(messageRetrieved( KMMessage* )),
567 TQ_SLOT(messageRetrieved( KMMessage* )) );
568 job->start();
569 } else {
570 mExecuting = false;
571 mResult = ResultError;
572 finishTimer->start( 0, true );
573 return;
574 }
575}
576
577void ActionScheduler::messageRetrieved(KMMessage* msg)
578{
579 // Get a write lock on the message while it's being filtered
580 msg->setTransferInProgress( true );
581 filterMessageTimer->start( 0, true );
582}
583
584void ActionScheduler::filterMessage()
585{
586 if (mFilterIt == mFilters.end()) {
587 moveMessage();
588 return;
589 }
590 if (((mSet & KMFilterMgr::Outbound) && (*mFilterIt).applyOnOutbound()) ||
591 ((mSet & KMFilterMgr::Inbound) && (*mFilterIt).applyOnInbound() &&
592 (!mAccount ||
593 (mAccount && (*mFilterIt).applyOnAccount(mAccountId)))) ||
594 ((mSet & KMFilterMgr::Explicit) && (*mFilterIt).applyOnExplicit())) {
595
596 // filter is applicable
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 );
601 }
602 if (mAlwaysMatch ||
603 (*mFilterIt).pattern()->matches( *mMessageIt )) {
604 if ( FilterLog::instance()->isLogging() ) {
605 FilterLog::instance()->add( i18n( "<b>Filter rules have matched.</b>" ),
606 FilterLog::patternResult );
607 }
608 mFilterAction = (*mFilterIt).actions()->first();
609 actionMessage();
610 return;
611 }
612 }
613 ++mFilterIt;
614 filterMessageTimer->start( 0, true );
615}
616
617void ActionScheduler::actionMessage(KMFilterAction::ReturnCode res)
618{
619 if (res == KMFilterAction::CriticalError) {
620 mResult = ResultCriticalError;
621 finish(); //must handle critical errors immediately
622 }
623 if (mFilterAction) {
624 KMMessage *msg = message( *mMessageIt );
625 if (msg) {
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 );
630 }
631 KMFilterAction *action = mFilterAction;
632 mFilterAction = (*mFilterIt).actions()->next();
633 action->processAsync( msg );
634 }
635 } else {
636 // there are no more actions
637 if ((*mFilterIt).stopProcessingHere())
638 mFilterIt = mFilters.end();
639 else
640 ++mFilterIt;
641 filterMessageTimer->start( 0, true );
642 }
643}
644
645void ActionScheduler::moveMessage()
646{
647 KMMsgBase *msgBase = messageBase( *mMessageIt );
648 if (!msgBase)
649 return;
650
651 MessageProperty::setTransferInProgress( *mMessageIt, false, true );
652 KMMessage *msg = message( *mMessageIt );
653 KMFolder *folder = MessageProperty::filterFolder( *mMessageIt );
654 TQString serNumS = msg->headerField( "X-KMail-Filtered" );
655 if (!serNumS.isEmpty())
656 mOriginalSerNum = serNumS.toUInt();
657 else
658 mOriginalSerNum = 0;
659 MessageProperty::setFilterHandler( *mMessageIt, 0 );
660 MessageProperty::setFiltering( *mMessageIt, false );
661 mSerNums.remove( *mMessageIt );
662
663 KMMessage *orgMsg = 0;
664 ReturnCode mOldReturnCode = mResult;
665 if (mOriginalSerNum)
666 orgMsg = message( mOriginalSerNum );
667 mResult = mOldReturnCode; // ignore errors in deleting original message
668 if (!orgMsg || !orgMsg->parent()) {
669 // Original message is gone, no point filtering it anymore
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 );
675 return;
676 } else {
677 if (!folder) // no filter folder specified leave in current place
678 folder = orgMsg->parent();
679 }
680
681 mIgnore = true;
682 assert( msg->parent() == mSrcFolder.operator->() );
683 mSrcFolder->take( mSrcFolder->find( msg ) );
684 mSrcFolder->addMsg( msg );
685 mIgnore = false;
686
687 if (msg && folder && kmkernel->folderIsTrash( folder ))
688 KMFilterAction::sendMDN( msg, KMime::MDN::Deleted );
689
690 timeOutTime = TQTime::currentTime();
691 KMCommand *cmd = new KMMoveCommand( folder, msg );
692 connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
693 this, TQ_SLOT( moveMessageFinished( KMCommand * ) ) );
694 cmd->start();
695 // sometimes the move command doesn't complete so time out after a minute
696 // and move onto the next message
697 lastCommand = cmd;
698 timeOutTimer->start( 60 * 1000, true );
699}
700
701void ActionScheduler::moveMessageFinished( KMCommand *command )
702{
703 timeOutTimer->stop();
704 if ( command->result() != KMCommand::OK )
705 mResult = ResultError;
706
707 if (!mSrcFolder->count())
708 mSrcFolder->expunge();
709
710 // in case the message stayed in the current folder TODO optimize
711 if ( mHeaders )
712 mHeaders->clearSelectableAndAboutToBeDeleted( mOriginalSerNum );
713 KMMessage *msg = 0;
714 ReturnCode mOldReturnCode = mResult;
715 if (mOriginalSerNum) {
716 msg = message( mOriginalSerNum );
717 emit filtered( mOriginalSerNum );
718 }
719
720 mResult = mOldReturnCode; // ignore errors in deleting original message
721 KMCommand *cmd = 0;
722 if (msg && msg->parent()) {
723 cmd = new KMMoveCommand( 0, msg );
724// cmd->start(); // Note: sensitive logic here.
725 }
726
727 if (mResult == ResultOk) {
728 mExecutingLock = false;
729 if (cmd)
730 connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
731 this, TQ_SLOT( processMessage() ) );
732 else
733 processMessageTimer->start( 0, true );
734 } else {
735 // Note: An alternative to consider is just calling
736 // finishTimer->start and returning
737 if (cmd)
738 connect( cmd, TQ_SIGNAL( completed( KMCommand * ) ),
739 this, TQ_SLOT( finish() ) );
740 else
741 finishTimer->start( 0, true );
742 }
743 if (cmd)
744 cmd->start();
745 // else moveMessageFinished should call finish
746}
747
748void ActionScheduler::copyMessageFinished( KMCommand *command )
749{
750 if ( command->result() != KMCommand::OK )
751 actionMessage( KMFilterAction::ErrorButGoOn );
752 else
753 actionMessage();
754}
755
756void ActionScheduler::timeOut()
757{
758 // Note: This is a good place for a debug statement
759 assert( lastCommand );
760 // sometimes imap jobs seem to just stall so give up and move on
761 disconnect( lastCommand, TQ_SIGNAL( completed( KMCommand * ) ),
762 this, TQ_SLOT( moveMessageFinished( KMCommand * ) ) );
763 lastCommand = 0;
764 mExecutingLock = false;
765 mExecuting = false;
766 finishTimer->start( 0, true );
767 if (mOriginalSerNum) // Try again
768 execFilters( mOriginalSerNum );
769}
770
771void ActionScheduler::fetchTimeOut()
772{
773 // Note: This is a good place for a debug statement
774 assert( lastJob );
775 // sometimes imap jobs seem to just stall so give up and move on
776 disconnect( lastJob, TQ_SIGNAL(messageRetrieved( KMMessage* )),
777 this, TQ_SLOT(messageFetched( KMMessage* )) );
778 lastJob->kill();
779 lastJob = 0;
780 fetchMessageTimer->start( 0, true );
781}
782
783TQString ActionScheduler::debug()
784{
785 TQString res;
786 TQValueList<ActionScheduler*>::iterator it;
787 int i = 1;
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() ) );
794 }
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" ) );
809 else
810 res.append( TQString( "Unknown.\n" ) );
811
812 ++i;
813 }
814 return res;
815}
816
817bool ActionScheduler::isEnabled()
818{
819 if (sEnabledChecked)
820 return sEnabled;
821
822 sEnabledChecked = true;
823 TDEConfig* config = KMKernel::config();
824 TDEConfigGroupSaver saver(config, "General");
825 sEnabled = config->readBoolEntry("action-scheduler", false);
826 return sEnabled;
827}
828
829bool ActionScheduler::ignoreChanges( bool ignore )
830{
831 bool oldValue = mIgnore;
832 mIgnore = ignore;
833 return oldValue;
834}
835
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.
Mail folder.
Definition: kmfolder.h:69
int expunge()
Delete entire folder.
Definition: kmfolder.cpp:526
void close(const char *owner, bool force=false)
Close folder.
Definition: kmfolder.cpp:489
KMMessage * getMsg(int idx)
Read message at given index.
Definition: kmfolder.cpp:321
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
Definition: kmfolder.cpp:360
int open(const char *owner)
Open folder for access.
Definition: kmfolder.cpp:479
The widget that shows the contents of folders.
Definition: kmheaders.h:47
This is a Mime Message.
Definition: kmmessage.h:68
void setTransferInProgress(bool value, bool force=false)
Set that the message shall not be deleted because it is still required.
Definition: kmmessage.cpp:243
void setStatus(const KMMsgStatus status, int idx=-1)
Set status and mark dirty.
Definition: kmmessage.cpp:4153
TQCString asString() const
Return the entire message contents as a string.
Definition: kmmessage.cpp:314
void setComplete(bool v)
Set if the message is a complete message.
Definition: kmmessage.h:869
KMMsgStatus status() const
Status of the message.
Definition: kmmessage.h:830
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
Definition: kmmessage.cpp:2289
bool isComplete() const
Return true if the complete message is available without referring to the backing store.
Definition: kmmessage.h:867
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.
Definition: kmmessage.cpp:2339
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...
Definition: kmmsgdict.cpp:319
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167
The account manager is responsible for creating accounts of various types via the factory method crea...
folderdiaquotatab.h
Definition: aboutdata.cpp:40