kmail

compactionjob.cpp

Go to the documentation of this file.
00001 
00028 #include "compactionjob.h"
00029 #include "kmfolder.h"
00030 #include "broadcaststatus.h"
00031 using KPIM::BroadcastStatus;
00032 #include "kmfoldermbox.h"
00033 #include "kmfoldermaildir.h"
00034 
00035 #include <kdebug.h>
00036 #include <klocale.h>
00037 
00038 #include <qfile.h>
00039 #include <qfileinfo.h>
00040 #include <qdir.h>
00041 
00042 #include <sys/types.h>
00043 #include <sys/stat.h>
00044 #include <errno.h>
00045 
00046 using namespace KMail;
00047 
00048 // Look at this number of messages in each slotDoWork call
00049 #define COMPACTIONJOB_NRMESSAGES 100
00050 // And wait this number of milliseconds before calling it again
00051 #define COMPACTIONJOB_TIMERINTERVAL 100
00052 
00053 MboxCompactionJob::MboxCompactionJob( KMFolder* folder, bool immediate )
00054  : ScheduledJob( folder, immediate ), mTimer( this, "mTimer" ), mTmpFile( 0 ),
00055    mCurrentIndex( 0 ), mFolderOpen( false ), mSilent( false )
00056 {
00057 }
00058 
00059 MboxCompactionJob::~MboxCompactionJob()
00060 {
00061 }
00062 
00063 void MboxCompactionJob::kill()
00064 {
00065   Q_ASSERT( mCancellable );
00066   // We must close the folder if we opened it and got interrupted
00067   if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() )
00068     mSrcFolder->storage()->close("mboxcompact");
00069 
00070   if ( mTmpFile )
00071     fclose( mTmpFile );
00072   mTmpFile = 0;
00073   if ( !mTempName.isEmpty() )
00074     QFile::remove( mTempName );
00075   FolderJob::kill();
00076 }
00077 
00078 QString MboxCompactionJob::realLocation() const
00079 {
00080   QString location = mSrcFolder->location();
00081   QFileInfo inf( location );
00082   if (inf.isSymLink()) {
00083     KURL u; u.setPath( location );
00084     // follow (and resolve) symlinks so that the final ::rename() always works
00085     // KURL gives us support for absolute and relative links transparently.
00086     return KURL( u, inf.readLink() ).path();
00087   }
00088   return location;
00089 }
00090 
00091 int MboxCompactionJob::executeNow( bool silent )
00092 {
00093   mSilent = silent;
00094   FolderStorage* storage = mSrcFolder->storage();
00095   KMFolderMbox* mbox = static_cast<KMFolderMbox *>( storage );
00096   if (!storage->compactable()) {
00097     kdDebug(5006) << storage->location() << " compaction skipped." << endl;
00098     if ( !mSilent ) {
00099       QString str = i18n( "For safety reasons, compaction has been disabled for %1" ).arg( mbox->label() );
00100       BroadcastStatus::instance()->setStatusMsg( str );
00101     }
00102     return 0;
00103   }
00104   kdDebug(5006) << "Compacting " << mSrcFolder->idString() << endl;
00105 
00106   if (KMFolderIndex::IndexOk != mbox->indexStatus()) {
00107       kdDebug(5006) << "Critical error: " << storage->location() <<
00108           " has been modified by an external application while KMail was running." << endl;
00109       //      exit(1); backed out due to broken nfs
00110   }
00111 
00112   const QFileInfo pathInfo( realLocation() );
00113   // Use /dir/.mailboxname.compacted so that it's hidden, and doesn't show up after restarting kmail
00114   // (e.g. due to an unfortunate crash while compaction is happening)
00115   mTempName = pathInfo.dirPath() + "/." + pathInfo.fileName() + ".compacted";
00116 
00117   mode_t old_umask = umask(077);
00118   mTmpFile = fopen(QFile::encodeName(mTempName), "w");
00119   umask(old_umask);
00120   if (!mTmpFile) {
00121     kdWarning(5006) << "Couldn't start compacting " << mSrcFolder->label()
00122                     << " : " << strerror( errno )
00123                     << " while creating " << mTempName << endl;
00124     return errno;
00125   }
00126   mOpeningFolder = true; // Ignore open-notifications while opening the folder
00127   storage->open("mboxcompact");
00128   mOpeningFolder = false;
00129   mFolderOpen = true;
00130   mOffset = 0;
00131   mCurrentIndex = 0;
00132 
00133   kdDebug(5006) << "MboxCompactionJob: starting to compact folder " << mSrcFolder->location() << " into " << mTempName << endl;
00134   connect( &mTimer, SIGNAL( timeout() ), SLOT( slotDoWork() ) );
00135   if ( !mImmediate )
00136     mTimer.start( COMPACTIONJOB_TIMERINTERVAL );
00137   slotDoWork();
00138   return mErrorCode;
00139 }
00140 
00141 void MboxCompactionJob::slotDoWork()
00142 {
00143   // No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
00144   KMFolderMbox* mbox = static_cast<KMFolderMbox *>( mSrcFolder->storage() );
00145   bool bDone = false;
00146   int nbMessages = mImmediate ? -1 /*all*/ : COMPACTIONJOB_NRMESSAGES;
00147   int rc = mbox->compact( mCurrentIndex, nbMessages,
00148                           mTmpFile, mOffset /*in-out*/, bDone /*out*/ );
00149   if ( !mImmediate )
00150     mCurrentIndex += COMPACTIONJOB_NRMESSAGES;
00151   if ( rc || bDone ) // error, or finished
00152     done( rc );
00153 }
00154 
00155 void MboxCompactionJob::done( int rc )
00156 {
00157   mTimer.stop();
00158   mCancellable = false;
00159   KMFolderMbox* mbox = static_cast<KMFolderMbox *>( mSrcFolder->storage() );
00160   if (!rc)
00161       rc = fflush(mTmpFile);
00162   if (!rc)
00163       rc = fsync(fileno(mTmpFile));
00164   rc |= fclose(mTmpFile);
00165   QString str;
00166   if (!rc) {
00167     bool autoCreate = mbox->autoCreateIndex();
00168     QString box( realLocation() );
00169     ::rename(QFile::encodeName(mTempName), QFile::encodeName(box));
00170     mbox->writeIndex();
00171     mbox->writeConfig();
00172     mbox->setAutoCreateIndex( false );
00173     mbox->close("mboxcompact", true);
00174     mbox->setAutoCreateIndex( autoCreate );
00175     mbox->setNeedsCompacting( false );            // We are clean now
00176     str = i18n( "Folder \"%1\" successfully compacted" ).arg( mSrcFolder->label() );
00177     kdDebug(5006) << str << endl;
00178   } else {
00179     mbox->close("mboxcompact");
00180     str = i18n( "Error occurred while compacting \"%1\". Compaction aborted." ).arg( mSrcFolder->label() );
00181     kdDebug(5006) << "Error occurred while compacting " << mbox->location() << endl;
00182     kdDebug(5006) << "Compaction aborted." << endl;
00183     QFile::remove( mTempName );
00184   }
00185   mErrorCode = rc;
00186 
00187   if ( !mSilent )
00188     BroadcastStatus::instance()->setStatusMsg( str );
00189 
00190   mFolderOpen = false;
00191   deleteLater(); // later, because of the "return mErrorCode"
00192 }
00193 
00195 
00196 MaildirCompactionJob::MaildirCompactionJob( KMFolder* folder, bool immediate )
00197  : ScheduledJob( folder, immediate ), mTimer( this, "mTimer" ),
00198    mCurrentIndex( 0 ), mFolderOpen( false ), mSilent( false )
00199 {
00200 }
00201 
00202 MaildirCompactionJob::~MaildirCompactionJob()
00203 {
00204 }
00205 
00206 void MaildirCompactionJob::kill()
00207 {
00208   Q_ASSERT( mCancellable );
00209   // We must close the folder if we opened it and got interrupted
00210   if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() )
00211     mSrcFolder->storage()->close("maildircompact");
00212 
00213   FolderJob::kill();
00214 }
00215 
00216 int MaildirCompactionJob::executeNow( bool silent )
00217 {
00218   mSilent = silent;
00219   KMFolderMaildir* storage = static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
00220   kdDebug(5006) << "Compacting " << mSrcFolder->idString() << endl;
00221 
00222   mOpeningFolder = true; // Ignore open-notifications while opening the folder
00223   storage->open("maildircompact");
00224   mOpeningFolder = false;
00225   mFolderOpen = true;
00226   QString subdirNew(storage->location() + "/new/");
00227   QDir d(subdirNew);
00228   mEntryList = d.entryList();
00229   mCurrentIndex = 0;
00230 
00231   kdDebug(5006) << "MaildirCompactionJob: starting to compact in folder " << mSrcFolder->location() << endl;
00232   connect( &mTimer, SIGNAL( timeout() ), SLOT( slotDoWork() ) );
00233   if ( !mImmediate )
00234     mTimer.start( COMPACTIONJOB_TIMERINTERVAL );
00235   slotDoWork();
00236   return mErrorCode;
00237 }
00238 
00239 void MaildirCompactionJob::slotDoWork()
00240 {
00241   // No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
00242   KMFolderMaildir* storage = static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
00243   bool bDone = false;
00244   int nbMessages = mImmediate ? -1 /*all*/ : COMPACTIONJOB_NRMESSAGES;
00245   int rc = storage->compact( mCurrentIndex, nbMessages, mEntryList, bDone /*out*/ );
00246   if ( !mImmediate )
00247     mCurrentIndex += COMPACTIONJOB_NRMESSAGES;
00248   if ( rc || bDone ) // error, or finished
00249     done( rc );
00250 }
00251 
00252 void MaildirCompactionJob::done( int rc )
00253 {
00254   KMFolderMaildir* storage = static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
00255   mTimer.stop();
00256   mCancellable = false;
00257   QString str;
00258   if ( !rc ) {
00259     str = i18n( "Folder \"%1\" successfully compacted" ).arg( mSrcFolder->label() );
00260   } else {
00261     str = i18n( "Error occurred while compacting \"%1\". Compaction aborted." ).arg( mSrcFolder->label() );
00262   }
00263   mErrorCode = rc;
00264   storage->setNeedsCompacting( false );
00265   storage->close("maildircompact");
00266   if ( storage->isOpened() )
00267     storage->updateIndex();
00268   if ( !mSilent )
00269     BroadcastStatus::instance()->setStatusMsg( str );
00270 
00271   mFolderOpen = false;
00272   deleteLater(); // later, because of the "return mErrorCode"
00273 }
00274 
00276 
00277 ScheduledJob* ScheduledCompactionTask::run()
00278 {
00279   if ( !folder() || !folder()->needsCompacting() )
00280     return 0;
00281   switch( folder()->storage()->folderType() ) {
00282   case KMFolderTypeMbox:
00283     return new MboxCompactionJob( folder(), isImmediate() );
00284   case KMFolderTypeCachedImap:
00285   case KMFolderTypeMaildir:
00286     return new MaildirCompactionJob( folder(), isImmediate() );
00287   default: // imap, search, unknown...
00288     return 0;
00289   }
00290 }
00291 
00292 #include "compactionjob.moc"