compactionjob.cpp
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 <tqfile.h> 00039 #include <tqfileinfo.h> 00040 #include <tqdir.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 TQFile::remove( mTempName ); 00075 FolderJob::kill(); 00076 } 00077 00078 TQString MboxCompactionJob::realLocation() const 00079 { 00080 TQString location = mSrcFolder->location(); 00081 TQFileInfo 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 TQString 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 TQFileInfo 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(TQFile::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, TQT_SIGNAL( timeout() ), TQT_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 TQString str; 00166 if (!rc) { 00167 bool autoCreate = mbox->autoCreateIndex(); 00168 TQString box( realLocation() ); 00169 ::rename(TQFile::encodeName(mTempName), TQFile::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 TQFile::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 TQString subdirNew(storage->location() + "/new/"); 00227 TQDir 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, TQT_SIGNAL( timeout() ), TQT_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 TQString 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"