summaryrefslogtreecommitdiffstats
path: root/kmail/compactionjob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmail/compactionjob.cpp')
-rw-r--r--kmail/compactionjob.cpp292
1 files changed, 292 insertions, 0 deletions
diff --git a/kmail/compactionjob.cpp b/kmail/compactionjob.cpp
new file mode 100644
index 00000000..1b9c143b
--- /dev/null
+++ b/kmail/compactionjob.cpp
@@ -0,0 +1,292 @@
+/**
+ * Copyright (c) 2004 David Faure <faure@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of this program with any edition of
+ * the Qt library by Trolltech AS, Norway (or with modified versions
+ * of Qt that use the same license as Qt), and distribute linked
+ * combinations including the two. You must obey the GNU General
+ * Public License in all respects for all of the code used other than
+ * Qt. If you modify this file, you may extend this exception to
+ * your version of the file, but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from
+ * your version.
+ */
+#include "compactionjob.h"
+#include "kmfolder.h"
+#include "broadcaststatus.h"
+using KPIM::BroadcastStatus;
+#include "kmfoldermbox.h"
+#include "kmfoldermaildir.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+using namespace KMail;
+
+// Look at this number of messages in each slotDoWork call
+#define COMPACTIONJOB_NRMESSAGES 100
+// And wait this number of milliseconds before calling it again
+#define COMPACTIONJOB_TIMERINTERVAL 100
+
+MboxCompactionJob::MboxCompactionJob( KMFolder* folder, bool immediate )
+ : ScheduledJob( folder, immediate ), mTimer( this, "mTimer" ), mTmpFile( 0 ),
+ mCurrentIndex( 0 ), mFolderOpen( false ), mSilent( false )
+{
+}
+
+MboxCompactionJob::~MboxCompactionJob()
+{
+}
+
+void MboxCompactionJob::kill()
+{
+ Q_ASSERT( mCancellable );
+ // We must close the folder if we opened it and got interrupted
+ if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() )
+ mSrcFolder->storage()->close("mboxcompact");
+
+ if ( mTmpFile )
+ fclose( mTmpFile );
+ mTmpFile = 0;
+ if ( !mTempName.isEmpty() )
+ QFile::remove( mTempName );
+ FolderJob::kill();
+}
+
+QString MboxCompactionJob::realLocation() const
+{
+ QString location = mSrcFolder->location();
+ QFileInfo inf( location );
+ if (inf.isSymLink()) {
+ KURL u; u.setPath( location );
+ // follow (and resolve) symlinks so that the final ::rename() always works
+ // KURL gives us support for absolute and relative links transparently.
+ return KURL( u, inf.readLink() ).path();
+ }
+ return location;
+}
+
+int MboxCompactionJob::executeNow( bool silent )
+{
+ mSilent = silent;
+ FolderStorage* storage = mSrcFolder->storage();
+ KMFolderMbox* mbox = static_cast<KMFolderMbox *>( storage );
+ if (!storage->compactable()) {
+ kdDebug(5006) << storage->location() << " compaction skipped." << endl;
+ if ( !mSilent ) {
+ QString str = i18n( "For safety reasons, compaction has been disabled for %1" ).arg( mbox->label() );
+ BroadcastStatus::instance()->setStatusMsg( str );
+ }
+ return 0;
+ }
+ kdDebug(5006) << "Compacting " << mSrcFolder->idString() << endl;
+
+ if (KMFolderIndex::IndexOk != mbox->indexStatus()) {
+ kdDebug(5006) << "Critical error: " << storage->location() <<
+ " has been modified by an external application while KMail was running." << endl;
+ // exit(1); backed out due to broken nfs
+ }
+
+ const QFileInfo pathInfo( realLocation() );
+ // Use /dir/.mailboxname.compacted so that it's hidden, and doesn't show up after restarting kmail
+ // (e.g. due to an unfortunate crash while compaction is happening)
+ mTempName = pathInfo.dirPath() + "/." + pathInfo.fileName() + ".compacted";
+
+ mode_t old_umask = umask(077);
+ mTmpFile = fopen(QFile::encodeName(mTempName), "w");
+ umask(old_umask);
+ if (!mTmpFile) {
+ kdWarning(5006) << "Couldn't start compacting " << mSrcFolder->label()
+ << " : " << strerror( errno )
+ << " while creating " << mTempName << endl;
+ return errno;
+ }
+ mOpeningFolder = true; // Ignore open-notifications while opening the folder
+ storage->open("mboxcompact");
+ mOpeningFolder = false;
+ mFolderOpen = true;
+ mOffset = 0;
+ mCurrentIndex = 0;
+
+ kdDebug(5006) << "MboxCompactionJob: starting to compact folder " << mSrcFolder->location() << " into " << mTempName << endl;
+ connect( &mTimer, SIGNAL( timeout() ), SLOT( slotDoWork() ) );
+ if ( !mImmediate )
+ mTimer.start( COMPACTIONJOB_TIMERINTERVAL );
+ slotDoWork();
+ return mErrorCode;
+}
+
+void MboxCompactionJob::slotDoWork()
+{
+ // No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
+ KMFolderMbox* mbox = static_cast<KMFolderMbox *>( mSrcFolder->storage() );
+ bool bDone = false;
+ int nbMessages = mImmediate ? -1 /*all*/ : COMPACTIONJOB_NRMESSAGES;
+ int rc = mbox->compact( mCurrentIndex, nbMessages,
+ mTmpFile, mOffset /*in-out*/, bDone /*out*/ );
+ if ( !mImmediate )
+ mCurrentIndex += COMPACTIONJOB_NRMESSAGES;
+ if ( rc || bDone ) // error, or finished
+ done( rc );
+}
+
+void MboxCompactionJob::done( int rc )
+{
+ mTimer.stop();
+ mCancellable = false;
+ KMFolderMbox* mbox = static_cast<KMFolderMbox *>( mSrcFolder->storage() );
+ if (!rc)
+ rc = fflush(mTmpFile);
+ if (!rc)
+ rc = fsync(fileno(mTmpFile));
+ rc |= fclose(mTmpFile);
+ QString str;
+ if (!rc) {
+ bool autoCreate = mbox->autoCreateIndex();
+ QString box( realLocation() );
+ ::rename(QFile::encodeName(mTempName), QFile::encodeName(box));
+ mbox->writeIndex();
+ mbox->writeConfig();
+ mbox->setAutoCreateIndex( false );
+ mbox->close("mboxcompact", true);
+ mbox->setAutoCreateIndex( autoCreate );
+ mbox->setNeedsCompacting( false ); // We are clean now
+ str = i18n( "Folder \"%1\" successfully compacted" ).arg( mSrcFolder->label() );
+ kdDebug(5006) << str << endl;
+ } else {
+ mbox->close("mboxcompact");
+ str = i18n( "Error occurred while compacting \"%1\". Compaction aborted." ).arg( mSrcFolder->label() );
+ kdDebug(5006) << "Error occurred while compacting " << mbox->location() << endl;
+ kdDebug(5006) << "Compaction aborted." << endl;
+ QFile::remove( mTempName );
+ }
+ mErrorCode = rc;
+
+ if ( !mSilent )
+ BroadcastStatus::instance()->setStatusMsg( str );
+
+ mFolderOpen = false;
+ deleteLater(); // later, because of the "return mErrorCode"
+}
+
+////
+
+MaildirCompactionJob::MaildirCompactionJob( KMFolder* folder, bool immediate )
+ : ScheduledJob( folder, immediate ), mTimer( this, "mTimer" ),
+ mCurrentIndex( 0 ), mFolderOpen( false ), mSilent( false )
+{
+}
+
+MaildirCompactionJob::~MaildirCompactionJob()
+{
+}
+
+void MaildirCompactionJob::kill()
+{
+ Q_ASSERT( mCancellable );
+ // We must close the folder if we opened it and got interrupted
+ if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() )
+ mSrcFolder->storage()->close("maildircompact");
+
+ FolderJob::kill();
+}
+
+int MaildirCompactionJob::executeNow( bool silent )
+{
+ mSilent = silent;
+ KMFolderMaildir* storage = static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
+ kdDebug(5006) << "Compacting " << mSrcFolder->idString() << endl;
+
+ mOpeningFolder = true; // Ignore open-notifications while opening the folder
+ storage->open("maildircompact");
+ mOpeningFolder = false;
+ mFolderOpen = true;
+ QString subdirNew(storage->location() + "/new/");
+ QDir d(subdirNew);
+ mEntryList = d.entryList();
+ mCurrentIndex = 0;
+
+ kdDebug(5006) << "MaildirCompactionJob: starting to compact in folder " << mSrcFolder->location() << endl;
+ connect( &mTimer, SIGNAL( timeout() ), SLOT( slotDoWork() ) );
+ if ( !mImmediate )
+ mTimer.start( COMPACTIONJOB_TIMERINTERVAL );
+ slotDoWork();
+ return mErrorCode;
+}
+
+void MaildirCompactionJob::slotDoWork()
+{
+ // No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
+ KMFolderMaildir* storage = static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
+ bool bDone = false;
+ int nbMessages = mImmediate ? -1 /*all*/ : COMPACTIONJOB_NRMESSAGES;
+ int rc = storage->compact( mCurrentIndex, nbMessages, mEntryList, bDone /*out*/ );
+ if ( !mImmediate )
+ mCurrentIndex += COMPACTIONJOB_NRMESSAGES;
+ if ( rc || bDone ) // error, or finished
+ done( rc );
+}
+
+void MaildirCompactionJob::done( int rc )
+{
+ KMFolderMaildir* storage = static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
+ mTimer.stop();
+ mCancellable = false;
+ QString str;
+ if ( !rc ) {
+ str = i18n( "Folder \"%1\" successfully compacted" ).arg( mSrcFolder->label() );
+ } else {
+ str = i18n( "Error occurred while compacting \"%1\". Compaction aborted." ).arg( mSrcFolder->label() );
+ }
+ mErrorCode = rc;
+ storage->setNeedsCompacting( false );
+ storage->close("maildircompact");
+ if ( storage->isOpened() )
+ storage->updateIndex();
+ if ( !mSilent )
+ BroadcastStatus::instance()->setStatusMsg( str );
+
+ mFolderOpen = false;
+ deleteLater(); // later, because of the "return mErrorCode"
+}
+
+////
+
+ScheduledJob* ScheduledCompactionTask::run()
+{
+ if ( !folder() || !folder()->needsCompacting() )
+ return 0;
+ switch( folder()->storage()->folderType() ) {
+ case KMFolderTypeMbox:
+ return new MboxCompactionJob( folder(), isImmediate() );
+ case KMFolderTypeCachedImap:
+ case KMFolderTypeMaildir:
+ return new MaildirCompactionJob( folder(), isImmediate() );
+ default: // imap, search, unknown...
+ return 0;
+ }
+}
+
+#include "compactionjob.moc"