2
* Copyright (c) 2004 David Faure <faure@kde.org>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; version 2 of the License
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program; if not, write to the Free Software
15
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
* In addition, as a special exception, the copyright holders give
18
* permission to link the code of this program with any edition of
19
* the Qt library by Trolltech AS, Norway (or with modified versions
20
* of Qt that use the same license as Qt), and distribute linked
21
* combinations including the two. You must obey the GNU General
22
* Public License in all respects for all of the code used other than
23
* Qt. If you modify this file, you may extend this exception to
24
* your version of the file, but you are not obligated to do so. If
25
* you do not wish to do so, delete this exception statement from
29
#include "compactionjob.h"
31
#include "broadcaststatus.h"
32
using KPIM::BroadcastStatus;
33
#include "kmfoldermbox.h"
34
#include "kmfoldermaildir.h"
44
#include <sys/types.h>
49
using namespace KMail;
51
// Look at this number of messages in each slotDoWork call
52
#define COMPACTIONJOB_NRMESSAGES 100
53
// And wait this number of milliseconds before calling it again
54
#define COMPACTIONJOB_TIMERINTERVAL 100
56
MboxCompactionJob::MboxCompactionJob( KMFolder* folder, bool immediate )
57
: ScheduledJob( folder, immediate ), mTimer( this ), mTmpFile( 0 ),
58
mCurrentIndex( 0 ), mFolderOpen( false ), mSilent( false )
62
MboxCompactionJob::~MboxCompactionJob()
66
void MboxCompactionJob::kill()
68
Q_ASSERT( mCancellable );
69
// We must close the folder if we opened it and got interrupted
70
if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() ) {
71
mSrcFolder->storage()->close( "mboxcompact" );
78
if ( !mTempName.isEmpty() ) {
79
QFile::remove( mTempName );
84
QString MboxCompactionJob::realLocation() const
86
QString location = mSrcFolder->location();
87
QFileInfo inf( location );
88
if (inf.isSymLink()) {
89
KUrl u; u.setPath( location );
90
// follow (and resolve) symlinks so that the final KDE_rename() always works
91
// KUrl gives us support for absolute and relative links transparently.
92
return KUrl( u, inf.readLink() ).toLocalFile();
97
int MboxCompactionJob::executeNow( bool silent )
100
FolderStorage *storage = mSrcFolder->storage();
101
KMFolderMbox *mbox = static_cast<KMFolderMbox *>( storage );
102
if ( !storage->compactable() ) {
103
kDebug() << storage->location() << "compaction skipped.";
105
QString str = i18n( "For safety reasons, compaction has been disabled for %1", mbox->label() );
106
BroadcastStatus::instance()->setStatusMsg( str );
110
kDebug() << "Compacting" << mSrcFolder->idString();
112
if ( KMFolderIndex::IndexOk != mbox->indexStatus() ) {
113
kDebug() << "Critical error:" << storage->location()
114
<< "has been modified by an external application while KMail was running.";
115
// exit(1); backed out due to broken nfs
118
const QFileInfo pathInfo( realLocation() );
119
// Use /dir/.mailboxname.compacted so that it's hidden, and doesn't show up after restarting kmail
120
// (e.g. due to an unfortunate crash while compaction is happening)
121
mTempName = pathInfo.path() + "/." + pathInfo.fileName() + ".compacted";
123
mode_t old_umask = umask( 077 );
124
mTmpFile = KDE_fopen( QFile::encodeName( mTempName ), "w" );
127
kWarning() <<"Couldn't start compacting" << mSrcFolder->label()
128
<< ":" << strerror( errno )
129
<< "while creating" << mTempName;
132
mOpeningFolder = true; // Ignore open-notifications while opening the folder
133
storage->open( "mboxcompact" );
134
mOpeningFolder = false;
139
kDebug() << "MboxCompactionJob: starting to compact folder"
140
<< mSrcFolder->location() << "into" << mTempName;
141
connect( &mTimer, SIGNAL( timeout() ), SLOT( slotDoWork() ) );
143
mTimer.start( COMPACTIONJOB_TIMERINTERVAL );
149
void MboxCompactionJob::slotDoWork()
151
// No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
152
KMFolderMbox *mbox = static_cast<KMFolderMbox *>( mSrcFolder->storage() );
154
int nbMessages = mImmediate ? -1 /*all*/ : COMPACTIONJOB_NRMESSAGES;
155
int rc = mbox->compact( mCurrentIndex, nbMessages,
156
mTmpFile, mOffset /*in-out*/, bDone /*out*/ );
158
mCurrentIndex += COMPACTIONJOB_NRMESSAGES;
159
if ( rc || bDone ) // error, or finished
163
void MboxCompactionJob::done( int rc )
166
mCancellable = false;
167
KMFolderMbox *mbox = static_cast<KMFolderMbox *>( mSrcFolder->storage() );
169
rc = fflush( mTmpFile );
172
rc = fsync( fileno( mTmpFile ) );
174
rc |= fclose( mTmpFile );
177
bool autoCreate = mbox->autoCreateIndex();
178
QString box( realLocation() );
179
rc = KDE_rename( QFile::encodeName( mTempName ), QFile::encodeName( box ) );
184
mbox->setAutoCreateIndex( false );
185
mbox->close( "mboxcompact", true );
186
mbox->setAutoCreateIndex( autoCreate );
187
mbox->setNeedsCompacting( false ); // We are clean now
188
str = i18n( "Folder \"%1\" successfully compacted", mSrcFolder->label() );
191
mbox->close( "mboxcompact" );
192
str = i18n( "Error occurred while compacting \"%1\". Compaction aborted.", mSrcFolder->label() );
193
kDebug() << "Error occurred while compacting" << mbox->location();
194
kDebug() << "Compaction aborted.";
195
QFile::remove( mTempName );
200
BroadcastStatus::instance()->setStatusMsg( str );
203
deleteLater(); // later, because of the "return mErrorCode"
208
MaildirCompactionJob::MaildirCompactionJob( KMFolder* folder, bool immediate )
209
: ScheduledJob( folder, immediate ), mTimer( this ),
210
mCurrentIndex( 0 ), mFolderOpen( false ), mSilent( false )
214
MaildirCompactionJob::~MaildirCompactionJob()
218
void MaildirCompactionJob::kill()
220
Q_ASSERT( mCancellable );
221
// We must close the folder if we opened it and got interrupted
222
if ( mFolderOpen && mSrcFolder && mSrcFolder->storage() ) {
223
mSrcFolder->storage()->close( "maildircompact" );
229
int MaildirCompactionJob::executeNow( bool silent )
232
KMFolderMaildir *storage =
233
static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
234
kDebug() << "Compacting" << mSrcFolder->idString();
236
mOpeningFolder = true; // Ignore open-notifications while opening the folder
237
storage->open( "maildircompact" );
238
mOpeningFolder = false;
240
QString subdirNew( storage->location() + "/new/" );
242
mEntryList = d.entryList();
245
kDebug() << "MaildirCompactionJob: starting to compact in folder"
246
<< mSrcFolder->location();
247
connect( &mTimer, SIGNAL( timeout() ), SLOT( slotDoWork() ) );
249
mTimer.start( COMPACTIONJOB_TIMERINTERVAL );
255
void MaildirCompactionJob::slotDoWork()
257
// No need to worry about mSrcFolder==0 here. The FolderStorage deletes the jobs on destruction.
258
KMFolderMaildir *storage =
259
static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
261
int nbMessages = mImmediate ? -1 /*all*/ : COMPACTIONJOB_NRMESSAGES;
262
int rc = storage->compact( mCurrentIndex, nbMessages, mEntryList, bDone /*out*/ );
264
mCurrentIndex += COMPACTIONJOB_NRMESSAGES;
265
if ( rc || bDone ) // error, or finished
269
void MaildirCompactionJob::done( int rc )
271
KMFolderMaildir *storage =
272
static_cast<KMFolderMaildir *>( mSrcFolder->storage() );
274
mCancellable = false;
277
str = i18n( "Folder \"%1\" successfully compacted", mSrcFolder->label() );
279
str = i18n( "Error occurred while compacting \"%1\". Compaction aborted.", mSrcFolder->label() );
282
storage->setNeedsCompacting( false );
283
storage->close( "maildircompact" );
284
if ( storage->isOpened() ) {
285
storage->updateIndex();
288
BroadcastStatus::instance()->setStatusMsg( str );
292
deleteLater(); // later, because of the "return mErrorCode"
295
ScheduledJob *ScheduledCompactionTask::run()
297
if ( !folder() || !folder()->needsCompacting() )
299
switch( folder()->storage()->folderType() ) {
300
case KMFolderTypeMbox:
301
return new MboxCompactionJob( folder(), isImmediate() );
302
case KMFolderTypeCachedImap:
303
case KMFolderTypeMaildir:
304
return new MaildirCompactionJob( folder(), isImmediate() );
305
default: // imap, search, unknown...
310
#include "compactionjob.moc"