|
|
|
/**
|
|
|
|
* 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 TQt library by Trolltech AS, Norway (or with modified versions
|
|
|
|
* of TQt that use the same license as TQt), 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
|
|
|
|
* TQt. 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 <tqfile.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
#include <tqdir.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() )
|
|
|
|
TQFile::remove( mTempName );
|
|
|
|
FolderJob::kill();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MboxCompactionJob::realLocation() const
|
|
|
|
{
|
|
|
|
TQString location = mSrcFolder->location();
|
|
|
|
TQFileInfo 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 ) {
|
|
|
|
TQString 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 TQFileInfo 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(TQFile::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, TQT_SIGNAL( timeout() ), TQT_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);
|
|
|
|
TQString str;
|
|
|
|
if (!rc) {
|
|
|
|
bool autoCreate = mbox->autoCreateIndex();
|
|
|
|
TQString box( realLocation() );
|
|
|
|
::rename(TQFile::encodeName(mTempName), TQFile::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;
|
|
|
|
TQFile::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;
|
|
|
|
TQString subdirNew(storage->location() + "/new/");
|
|
|
|
TQDir d(subdirNew);
|
|
|
|
mEntryList = d.entryList();
|
|
|
|
mCurrentIndex = 0;
|
|
|
|
|
|
|
|
kdDebug(5006) << "MaildirCompactionJob: starting to compact in folder " << mSrcFolder->location() << endl;
|
|
|
|
connect( &mTimer, TQT_SIGNAL( timeout() ), TQT_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;
|
|
|
|
TQString 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"
|