kmail

expirejob.cpp

Go to the documentation of this file.
00001 
00029 #include "expirejob.h"
00030 #include "kmfolder.h"
00031 #include "globalsettings.h"
00032 #include "folderstorage.h"
00033 #include "broadcaststatus.h"
00034 using KPIM::BroadcastStatus;
00035 #include "kmcommands.h"
00036 
00037 #include <kdebug.h>
00038 #include <klocale.h>
00039 
00040 using namespace KMail;
00041 
00042 // Look at this number of messages in each slotDoWork call
00043 #define EXPIREJOB_NRMESSAGES 100
00044 // And wait this number of milliseconds before calling it again
00045 #define EXPIREJOB_TIMERINTERVAL 100
00046 
00047 /*
00048  Testcases for folder expiry:
00049   Automatic expiry:
00050   - normal case (ensure folder has old mails and expiry settings, wait for auto-expiry)
00051   - having the folder selected when the expiry job would run (gets delayed)
00052   - selecting a folder while an expiry job is running for it (should interrupt)
00053   - exiting kmail while an expiry job is running (should abort & delete things cleanly)
00054   Manual expiry:
00055   - RMB / expire (for one folder)                   [KMMainWidget::slotExpireFolder()]
00056   - RMB on Local Folders / Expire All Folders       [KMFolderMgr::expireAll()]
00057   - Expire All Folders                              [KMMainWidget::slotExpireAll()]
00058 */
00059 
00060 
00061 ExpireJob::ExpireJob( KMFolder* folder, bool immediate )
00062  : ScheduledJob( folder, immediate ), mTimer( this ), mCurrentIndex( 0 ),
00063    mFolderOpen( false ), mMoveToFolder( 0 )
00064 {
00065 }
00066 
00067 ExpireJob::~ExpireJob()
00068 {
00069 }
00070 
00071 void ExpireJob::kill()
00072 {
00073   Q_ASSERT( mCancellable );
00074   // We must close the folder if we opened it and got interrupted
00075   if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() )
00076     mSrcFolder->storage()->close( "expirejob" );
00077   FolderJob::kill();
00078 }
00079 
00080 void ExpireJob::execute()
00081 {
00082   mMaxUnreadTime = 0;
00083   mMaxReadTime = 0;
00084   mCurrentIndex = 0;
00085 
00086   int unreadDays, readDays;
00087   mSrcFolder->daysToExpire( unreadDays, readDays );
00088   if (unreadDays > 0) {
00089     kdDebug(5006) << "ExpireJob: deleting unread older than "<< unreadDays << " days" << endl;
00090     mMaxUnreadTime = time(0) - unreadDays * 3600 * 24;
00091   }
00092   if (readDays > 0) {
00093     kdDebug(5006) << "ExpireJob: deleting read older than "<< readDays << " days" << endl;
00094     mMaxReadTime = time(0) - readDays * 3600 * 24;
00095   }
00096 
00097   if ((mMaxUnreadTime == 0) && (mMaxReadTime == 0)) {
00098     kdDebug(5006) << "ExpireJob: nothing to do" << endl;
00099     delete this;
00100     return;
00101   }
00102 
00103   FolderStorage* storage = mSrcFolder->storage();
00104   mOpeningFolder = true; // Ignore open-notifications while opening the folder
00105   storage->open( "expirejob" );
00106   mOpeningFolder = false;
00107   mFolderOpen = true;
00108   mCurrentIndex = storage->count()-1;
00109   kdDebug(5006) << "ExpireJob: starting to expire in folder " << mSrcFolder->location() << endl;
00110   connect( &mTimer, SIGNAL( timeout() ), SLOT( slotDoWork() ) );
00111   mTimer.start( EXPIREJOB_TIMERINTERVAL );
00112   slotDoWork();
00113   // do nothing here, we might be deleted!
00114 }
00115 
00116 void ExpireJob::slotDoWork()
00117 {
00118   // No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
00119   FolderStorage* storage = mSrcFolder->storage();
00120   int stopIndex = mImmediate ? 0 : QMAX( 0, mCurrentIndex - EXPIREJOB_NRMESSAGES );
00121 #ifdef DEBUG_SCHEDULER
00122   kdDebug(5006) << "ExpireJob: checking messages " << mCurrentIndex << " to " << stopIndex << endl;
00123 #endif
00124   for( ; mCurrentIndex >= stopIndex; mCurrentIndex-- ) {
00125     const KMMsgBase *mb = storage->getMsgBase( mCurrentIndex );
00126     if (mb == 0)
00127       continue;
00128     if ( ( mb->isImportant() || mb->isTodo() || mb->isWatched() )
00129       && GlobalSettings::self()->excludeImportantMailFromExpiry() )
00130        continue;
00131 
00132     time_t maxTime = mb->isUnread() ? mMaxUnreadTime : mMaxReadTime;
00133 
00134     if (mb->date() < maxTime) {
00135       mRemovedMsgs.append( storage->getMsgBase( mCurrentIndex ) );
00136     }
00137   }
00138   if ( stopIndex == 0 )
00139     done();
00140 }
00141 
00142 void ExpireJob::done()
00143 {
00144   mTimer.stop();
00145 
00146   QString str;
00147   bool moving = false;
00148 
00149   if ( !mRemovedMsgs.isEmpty() ) {
00150     int count = mRemovedMsgs.count();
00151     // The command shouldn't kill us because it opens the folder
00152     mCancellable = false;
00153     if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
00154       // Expire by deletion, i.e. move to null target folder
00155       kdDebug(5006) << "ExpireJob: finished expiring in folder "
00156                     << mSrcFolder->location()
00157                     << " " << count << " messages to remove." << endl;
00158       KMMoveCommand* cmd = new KMMoveCommand( 0, mRemovedMsgs );
00159       connect( cmd, SIGNAL( completed( KMCommand * ) ),
00160                this, SLOT( slotMessagesMoved( KMCommand * ) ) );
00161       cmd->start();
00162       moving = true;
00163       str = i18n( "Removing 1 old message from folder %1...",
00164                   "Removing %n old messages from folder %1...", count )
00165             .arg( mSrcFolder->label() );
00166     } else {
00167       // Expire by moving
00168       mMoveToFolder =
00169         kmkernel->findFolderById( mSrcFolder->expireToFolderId() );
00170       if ( !mMoveToFolder ) {
00171         str = i18n( "Cannot expire messages from folder %1: destination "
00172                     "folder %2 not found" )
00173               .arg( mSrcFolder->label(), mSrcFolder->expireToFolderId() );
00174         kdWarning(5006) << str << endl;
00175       } else {
00176         kdDebug(5006) << "ExpireJob: finished expiring in folder "
00177                       << mSrcFolder->location() << " "
00178                       << mRemovedMsgs.count() << " messages to move to "
00179                       << mMoveToFolder->label() << endl;
00180         KMMoveCommand* cmd = new KMMoveCommand( mMoveToFolder, mRemovedMsgs );
00181         connect( cmd, SIGNAL( completed( KMCommand * ) ),
00182                  this, SLOT( slotMessagesMoved( KMCommand * ) ) );
00183         cmd->start();
00184         moving = true;
00185         str = i18n( "Moving 1 old message from folder %1 to folder %2...",
00186                     "Moving %n old messages from folder %1 to folder %2...",
00187                     count )
00188               .arg( mSrcFolder->label(), mMoveToFolder->label() );
00189       }
00190     }
00191   }
00192   if ( !str.isEmpty() )
00193     BroadcastStatus::instance()->setStatusMsg( str );
00194 
00195   KConfigGroup group( KMKernel::config(), "Folder-" + mSrcFolder->idString() );
00196   group.writeEntry( "Current", -1 ); // i.e. make it invalid, the serial number will be used
00197 
00198   if ( !moving ) {
00199     mSrcFolder->storage()->close( "expirejob" );
00200     mFolderOpen = false;
00201     delete this;
00202   }
00203 }
00204 
00205 void ExpireJob::slotMessagesMoved( KMCommand *command )
00206 {
00207   mSrcFolder->storage()->close( "expirejob" );
00208   mFolderOpen = false;
00209   QString msg;
00210   switch ( command->result() ) {
00211   case KMCommand::OK:
00212     if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
00213       msg = i18n( "Removed 1 old message from folder %1.",
00214                   "Removed %n old messages from folder %1.",
00215                   mRemovedMsgs.count() )
00216             .arg( mSrcFolder->label() );
00217     }
00218     else {
00219       msg = i18n( "Moved 1 old message from folder %1 to folder %2.",
00220                   "Moved %n old messages from folder %1 to folder %2.",
00221                   mRemovedMsgs.count() )
00222             .arg( mSrcFolder->label(), mMoveToFolder->label() );
00223     }
00224     break;
00225   case KMCommand::Failed:
00226     if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
00227       msg = i18n( "Removing old messages from folder %1 failed." )
00228             .arg( mSrcFolder->label() );
00229     }
00230     else {
00231       msg = i18n( "Moving old messages from folder %1 to folder %2 failed." )
00232             .arg( mSrcFolder->label(), mMoveToFolder->label() );
00233     }
00234     break;
00235   case KMCommand::Canceled:
00236     if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
00237       msg = i18n( "Removing old messages from folder %1 was canceled." )
00238             .arg( mSrcFolder->label() );
00239     }
00240     else {
00241       msg = i18n( "Moving old messages from folder %1 to folder %2 was "
00242                   "canceled." )
00243             .arg( mSrcFolder->label(), mMoveToFolder->label() );
00244     }
00245   default: ;
00246   }
00247   BroadcastStatus::instance()->setStatusMsg( msg );
00248 
00249   deleteLater();
00250 }
00251 
00252 #include "expirejob.moc"