kmail

expirejob.cpp
1 
29 #include "expirejob.h"
30 #include "kmfolder.h"
31 #include "globalsettings.h"
32 #include "folderstorage.h"
33 #include "broadcaststatus.h"
34 using KPIM::BroadcastStatus;
35 #include "kmcommands.h"
36 
37 #include <kdebug.h>
38 #include <klocale.h>
39 
40 using namespace KMail;
41 
42 // Look at this number of messages in each slotDoWork call
43 #define EXPIREJOB_NRMESSAGES 100
44 // And wait this number of milliseconds before calling it again
45 #define EXPIREJOB_TIMERINTERVAL 100
46 
47 /*
48  Testcases for folder expiry:
49  Automatic expiry:
50  - normal case (ensure folder has old mails and expiry settings, wait for auto-expiry)
51  - having the folder selected when the expiry job would run (gets delayed)
52  - selecting a folder while an expiry job is running for it (should interrupt)
53  - exiting kmail while an expiry job is running (should abort & delete things cleanly)
54  Manual expiry:
55  - RMB / expire (for one folder) [KMMainWidget::slotExpireFolder()]
56  - RMB on Local Folders / Expire All Folders [KMFolderMgr::expireAll()]
57  - Expire All Folders [KMMainWidget::slotExpireAll()]
58 */
59 
60 
61 ExpireJob::ExpireJob( KMFolder* folder, bool immediate )
62  : ScheduledJob( folder, immediate ), mTimer( this ), mCurrentIndex( 0 ),
63  mFolderOpen( false ), mMoveToFolder( 0 )
64 {
65 }
66 
67 ExpireJob::~ExpireJob()
68 {
69 }
70 
71 void ExpireJob::kill()
72 {
73  Q_ASSERT( mCancellable );
74  // We must close the folder if we opened it and got interrupted
75  if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() )
76  mSrcFolder->storage()->close( "expirejob" );
77  FolderJob::kill();
78 }
79 
80 void ExpireJob::execute()
81 {
82  mMaxUnreadTime = 0;
83  mMaxReadTime = 0;
84  mCurrentIndex = 0;
85 
86  int unreadDays, readDays;
87  mSrcFolder->daysToExpire( unreadDays, readDays );
88  if (unreadDays > 0) {
89  kdDebug(5006) << "ExpireJob: deleting unread older than "<< unreadDays << " days" << endl;
90  mMaxUnreadTime = time(0) - unreadDays * 3600 * 24;
91  }
92  if (readDays > 0) {
93  kdDebug(5006) << "ExpireJob: deleting read older than "<< readDays << " days" << endl;
94  mMaxReadTime = time(0) - readDays * 3600 * 24;
95  }
96 
97  if ((mMaxUnreadTime == 0) && (mMaxReadTime == 0)) {
98  kdDebug(5006) << "ExpireJob: nothing to do" << endl;
99  delete this;
100  return;
101  }
102 
103  FolderStorage* storage = mSrcFolder->storage();
104  mOpeningFolder = true; // Ignore open-notifications while opening the folder
105  storage->open( "expirejob" );
106  mOpeningFolder = false;
107  mFolderOpen = true;
108  mCurrentIndex = storage->count()-1;
109  kdDebug(5006) << "ExpireJob: starting to expire in folder " << mSrcFolder->location() << endl;
110  connect( &mTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotDoWork() ) );
111  mTimer.start( EXPIREJOB_TIMERINTERVAL );
112  slotDoWork();
113  // do nothing here, we might be deleted!
114 }
115 
116 void ExpireJob::slotDoWork()
117 {
118  // No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
119  FolderStorage* storage = mSrcFolder->storage();
120  int stopIndex = mImmediate ? 0 : TQMAX( 0, mCurrentIndex - EXPIREJOB_NRMESSAGES );
121 #ifdef DEBUG_SCHEDULER
122  kdDebug(5006) << "ExpireJob: checking messages " << mCurrentIndex << " to " << stopIndex << endl;
123 #endif
124  for( ; mCurrentIndex >= stopIndex; mCurrentIndex-- ) {
125  const KMMsgBase *mb = storage->getMsgBase( mCurrentIndex );
126  if (mb == 0)
127  continue;
128  if ( ( mb->isImportant() || mb->isTodo() || mb->isWatched() )
129  && GlobalSettings::self()->excludeImportantMailFromExpiry() )
130  continue;
131 
132  time_t maxTime = mb->isUnread() ? mMaxUnreadTime : mMaxReadTime;
133 
134  if (mb->date() < maxTime) {
135  mRemovedMsgs.append( storage->getMsgBase( mCurrentIndex ) );
136  }
137  }
138  if ( stopIndex == 0 )
139  done();
140 }
141 
142 void ExpireJob::done()
143 {
144  mTimer.stop();
145 
146  TQString str;
147  bool moving = false;
148 
149  if ( !mRemovedMsgs.isEmpty() ) {
150  int count = mRemovedMsgs.count();
151  // The command shouldn't kill us because it opens the folder
152  mCancellable = false;
153  if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
154  // Expire by deletion, i.e. move to null target folder
155  kdDebug(5006) << "ExpireJob: finished expiring in folder "
156  << mSrcFolder->location()
157  << " " << count << " messages to remove." << endl;
158  KMMoveCommand* cmd = new KMMoveCommand( 0, mRemovedMsgs );
159  connect( cmd, TQT_SIGNAL( completed( KMCommand * ) ),
160  this, TQT_SLOT( slotMessagesMoved( KMCommand * ) ) );
161  cmd->start();
162  moving = true;
163  str = i18n( "Removing 1 old message from folder %1...",
164  "Removing %n old messages from folder %1...", count )
165  .arg( mSrcFolder->label() );
166  } else {
167  // Expire by moving
168  mMoveToFolder =
169  kmkernel->findFolderById( mSrcFolder->expireToFolderId() );
170  if ( !mMoveToFolder ) {
171  str = i18n( "Cannot expire messages from folder %1: destination "
172  "folder %2 not found" )
173  .arg( mSrcFolder->label(), mSrcFolder->expireToFolderId() );
174  kdWarning(5006) << str << endl;
175  } else {
176  kdDebug(5006) << "ExpireJob: finished expiring in folder "
177  << mSrcFolder->location() << " "
178  << mRemovedMsgs.count() << " messages to move to "
179  << mMoveToFolder->label() << endl;
180  KMMoveCommand* cmd = new KMMoveCommand( mMoveToFolder, mRemovedMsgs );
181  connect( cmd, TQT_SIGNAL( completed( KMCommand * ) ),
182  this, TQT_SLOT( slotMessagesMoved( KMCommand * ) ) );
183  cmd->start();
184  moving = true;
185  str = i18n( "Moving 1 old message from folder %1 to folder %2...",
186  "Moving %n old messages from folder %1 to folder %2...",
187  count )
188  .arg( mSrcFolder->label(), mMoveToFolder->label() );
189  }
190  }
191  }
192  if ( !str.isEmpty() )
193  BroadcastStatus::instance()->setStatusMsg( str );
194 
195  KConfigGroup group( KMKernel::config(), "Folder-" + mSrcFolder->idString() );
196  group.writeEntry( "Current", -1 ); // i.e. make it invalid, the serial number will be used
197 
198  if ( !moving ) {
199  mSrcFolder->storage()->close( "expirejob" );
200  mFolderOpen = false;
201  delete this;
202  }
203 }
204 
205 void ExpireJob::slotMessagesMoved( KMCommand *command )
206 {
207  mSrcFolder->storage()->close( "expirejob" );
208  mFolderOpen = false;
209  TQString msg;
210  switch ( command->result() ) {
211  case KMCommand::OK:
212  if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
213  msg = i18n( "Removed 1 old message from folder %1.",
214  "Removed %n old messages from folder %1.",
215  mRemovedMsgs.count() )
216  .arg( mSrcFolder->label() );
217  }
218  else {
219  msg = i18n( "Moved 1 old message from folder %1 to folder %2.",
220  "Moved %n old messages from folder %1 to folder %2.",
221  mRemovedMsgs.count() )
222  .arg( mSrcFolder->label(), mMoveToFolder->label() );
223  }
224  break;
225  case KMCommand::Failed:
226  if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
227  msg = i18n( "Removing old messages from folder %1 failed." )
228  .arg( mSrcFolder->label() );
229  }
230  else {
231  msg = i18n( "Moving old messages from folder %1 to folder %2 failed." )
232  .arg( mSrcFolder->label(), mMoveToFolder->label() );
233  }
234  break;
235  case KMCommand::Canceled:
236  if ( mSrcFolder->expireAction() == KMFolder::ExpireDelete ) {
237  msg = i18n( "Removing old messages from folder %1 was canceled." )
238  .arg( mSrcFolder->label() );
239  }
240  else {
241  msg = i18n( "Moving old messages from folder %1 to folder %2 was "
242  "canceled." )
243  .arg( mSrcFolder->label(), mMoveToFolder->label() );
244  }
245  default: ;
246  }
247  BroadcastStatus::instance()->setStatusMsg( msg );
248 
249  deleteLater();
250 }
251 
252 #include "expirejob.moc"