You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

307 lines
9.8 KiB

/**
* 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 "config.h"
#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>
#include <QFileInfo>
#include <QDir>
#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 ), 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( "mboxcompactjob" );
}
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() ) {
kDebug(5006) << storage->location() << " compaction skipped." << endl;
if ( !mSilent ) {
QString str = i18n( "For safety reasons, compaction has been disabled for %1", mbox->label() );
BroadcastStatus::instance()->setStatusMsg( str );
}
return 0;
}
kDebug(5006) << "Compacting " << mSrcFolder->idString() << endl;
if ( KMFolderIndex::IndexOk != mbox->indexStatus() ) {
kDebug(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.path() + "/." + pathInfo.fileName() + ".compacted";
mode_t old_umask = umask( 077 );
mTmpFile = fopen( QFile::encodeName( mTempName ), "w" );
umask( old_umask );
if (!mTmpFile) {
kWarning(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( "mboxcompactjob" );
mOpeningFolder = false;
mFolderOpen = true;
mOffset = 0;
mCurrentIndex = 0;
kDebug(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", mSrcFolder->label() );
kDebug(5006) << str << endl;
} else {
mbox->close( "mboxcompact" );
str = i18n( "Error occurred while compacting \"%1\". Compaction aborted.", mSrcFolder->label() );
kDebug(5006) << "Error occurred while compacting " << mbox->location() << endl;
kDebug(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 ),
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() );
kDebug(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;
kDebug(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", mSrcFolder->label() );
} else {
str = i18n( "Error occurred while compacting \"%1\". Compaction aborted.", 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"