19 #include "backupjob.h"
21 #include "kmmsgdict.h"
23 #include "kmfoldercachedimap.h"
24 #include "kmfolderdir.h"
25 #include "folderutil.h"
27 #include "progressmanager.h"
31 #include "tdemessagebox.h"
34 #include "tqfileinfo.h"
35 #include "tqstringlist.h"
37 using namespace KMail;
39 BackupJob::BackupJob( TQWidget *parent )
44 mParentWidget( parent ),
45 mCurrentFolderOpen( false ),
46 mArchivedMessages( 0 ),
50 mDeleteFoldersAfterCompletion( false ),
57 BackupJob::~BackupJob()
59 mPendingFolders.clear();
66 void BackupJob::setRootFolder(
KMFolder *rootFolder )
68 mRootFolder = rootFolder;
71 void BackupJob::setSaveLocation(
const KURL &savePath )
73 mMailArchivePath = savePath;
76 void BackupJob::setArchiveType( ArchiveType type )
81 void BackupJob::setDeleteFoldersAfterCompletion(
bool deleteThem )
83 mDeleteFoldersAfterCompletion = deleteThem;
86 TQString BackupJob::stripRootPath(
const TQString &path )
const
89 ret = ret.remove( mRootFolder->path() );
90 if ( ret.startsWith(
"/" ) )
91 ret = ret.right( ret.length() - 1 );
95 void BackupJob::queueFolders(
KMFolder *root )
97 mPendingFolders.append( root );
100 for ( KMFolderNode * node = dir->first() ; node ; node = dir->next() ) {
104 queueFolders( folder );
109 bool BackupJob::hasChildren(
KMFolder *folder )
const
113 for ( KMFolderNode * node = dir->first() ; node ; node = dir->next() ) {
114 if ( !node->isDir() )
121 void BackupJob::cancelJob()
123 abort( i18n(
"The operation was canceled by the user." ) );
126 void BackupJob::abort(
const TQString &errorMessage )
134 if ( mCurrentFolderOpen && mCurrentFolder ) {
135 mCurrentFolder->
close(
"BackupJob" );
138 if ( mArchive && mArchive->isOpened() ) {
145 if ( mProgressItem ) {
146 mProgressItem->setComplete();
151 TQString text = i18n(
"Failed to archive the folder '%1'." ).arg( mRootFolder->name() );
152 text +=
"\n" + errorMessage;
153 KMessageBox::sorry( mParentWidget, text, i18n(
"Archiving failed." ) );
158 void BackupJob::finish()
160 if ( mArchive->isOpened() ) {
162 if ( !mArchive->closeSucceeded() ) {
163 abort( i18n(
"Unable to finalize the archive file." ) );
168 mProgressItem->setStatus( i18n(
"Archiving finished" ) );
169 mProgressItem->setComplete();
172 TQFileInfo archiveFileInfo( mMailArchivePath.path() );
173 TQString text = i18n(
"Archiving folder '%1' successfully completed. "
174 "The archive was written to the file '%2'." )
175 .arg( mRootFolder->name() ).arg( mMailArchivePath.path() );
176 text +=
"\n" + i18n(
"1 message of size %1 was archived.",
177 "%n messages with the total size of %1 were archived.", mArchivedMessages )
178 .arg( TDEIO::convertSize( mArchivedSize ) );
179 text +=
"\n" + i18n(
"The archive file has a size of %1." )
180 .arg( TDEIO::convertSize( archiveFileInfo.size() ) );
181 KMessageBox::information( mParentWidget, text, i18n(
"Archiving finished." ) );
183 if ( mDeleteFoldersAfterCompletion ) {
185 if ( archiveFileInfo.size() > 0 && ( mArchivedSize > 0 || mArchivedMessages == 0 ) ) {
187 FolderUtil::deleteFolder( mRootFolder, mParentWidget );
194 void BackupJob::archiveNextMessage()
200 if ( mPendingMessages.isEmpty() ) {
201 kdDebug(5006) <<
"===> All messages done in folder " << mCurrentFolder->name() << endl;
202 mCurrentFolder->
close(
"BackupJob" );
203 mCurrentFolderOpen =
false;
208 unsigned long serNum = mPendingMessages.front();
209 mPendingMessages.pop_front();
214 if ( mMessageIndex == -1 ) {
215 kdWarning(5006) <<
"Failed to get message location for sernum " << serNum << endl;
216 abort( i18n(
"Unable to retrieve a message for folder '%1'." ).arg( mCurrentFolder->name() ) );
220 Q_ASSERT( folder == mCurrentFolder );
221 const KMMsgBase *base = mCurrentFolder->
getMsgBase( mMessageIndex );
222 mUnget = base && !base->isMessage();
225 kdWarning(5006) <<
"Failed to retrieve message with index " << mMessageIndex << endl;
226 abort( i18n(
"Unable to retrieve a message for folder '%1'." ).arg( mCurrentFolder->name() ) );
230 kdDebug(5006) <<
"Going to get next message with subject " << message->
subject() <<
", "
231 << mPendingMessages.size() <<
" messages left in the folder." << endl;
236 mCurrentMessage = message;
237 TQTimer::singleShot( 0,
this, TQ_SLOT( processCurrentMessage() ) );
239 else if ( message->parent() ) {
240 mCurrentJob = message->parent()->createJob( message );
241 mCurrentJob->setCancellable(
false );
242 connect( mCurrentJob, TQ_SIGNAL( messageRetrieved(
KMMessage* ) ),
243 this, TQ_SLOT( messageRetrieved(
KMMessage* ) ) );
244 connect( mCurrentJob, TQ_SIGNAL( result( KMail::FolderJob* ) ),
245 this, TQ_SLOT( folderJobFinished( KMail::FolderJob* ) ) );
246 mCurrentJob->start();
249 kdWarning(5006) <<
"Message with subject " << mCurrentMessage->
subject()
250 <<
" is neither complete nor has a parent!" << endl;
251 abort( i18n(
"Internal error while trying to retrieve a message from folder '%1'." )
252 .arg( mCurrentFolder->name() ) );
255 mProgressItem->setProgress( ( mProgressItem->progress() + 5 ) );
258 static int fileInfoToUnixPermissions(
const TQFileInfo &fileInfo )
261 if ( fileInfo.permission( TQFileInfo::ExeOther ) ) perm += S_IXOTH;
262 if ( fileInfo.permission( TQFileInfo::WriteOther ) ) perm += S_IWOTH;
263 if ( fileInfo.permission( TQFileInfo::ReadOther ) ) perm += S_IROTH;
264 if ( fileInfo.permission( TQFileInfo::ExeGroup ) ) perm += S_IXGRP;
265 if ( fileInfo.permission( TQFileInfo::WriteGroup ) ) perm += S_IWGRP;
266 if ( fileInfo.permission( TQFileInfo::ReadGroup ) ) perm += S_IRGRP;
267 if ( fileInfo.permission( TQFileInfo::ExeOwner ) ) perm += S_IXUSR;
268 if ( fileInfo.permission( TQFileInfo::WriteOwner ) ) perm += S_IWUSR;
269 if ( fileInfo.permission( TQFileInfo::ReadOwner ) ) perm += S_IRUSR;
273 void BackupJob::processCurrentMessage()
278 if ( mCurrentMessage ) {
279 kdDebug(5006) <<
"Processing message with subject " << mCurrentMessage->
subject() << endl;
280 const DwString &messageDWString = mCurrentMessage->
asDwString();
281 const uint messageSize = messageDWString.size();
282 const char *messageString = mCurrentMessage->
asDwString().c_str();
283 TQString messageName;
285 if ( messageName.isEmpty() ) {
286 messageName = TQString::number( mCurrentMessage->getMsgSerNum() );
287 if ( mCurrentMessage->storage() ) {
288 fileInfo.setFile( mCurrentMessage->storage()->location() );
294 fileInfo.setFile( mCurrentFolder->
location() +
"/cur/" + mCurrentMessage->
fileName() );
295 messageName = mCurrentMessage->
fileName();
298 const TQString fileName = stripRootPath( mCurrentFolder->
location() ) +
299 "/cur/" + messageName;
303 mode_t permissions = 0700;
304 time_t creationTime = time( 0 );
305 time_t modificationTime = time( 0 );
306 time_t accessTime = time( 0 );
307 if ( !fileInfo.fileName().isEmpty() ) {
308 user = fileInfo.owner();
309 group = fileInfo.group();
310 permissions = fileInfoToUnixPermissions( fileInfo );
311 creationTime = fileInfo.created().toTime_t();
312 modificationTime = fileInfo.lastModified().toTime_t();
313 accessTime = fileInfo.lastRead().toTime_t();
316 kdWarning(5006) <<
"Unable to find file for message " << fileName << endl;
319 if ( !mArchive->writeFile( fileName, user, group, messageSize, permissions, accessTime,
320 modificationTime, creationTime, messageString ) ) {
321 abort( i18n(
"Failed to write a message into the archive folder '%1'." ).arg( mCurrentFolder->name() ) );
326 Q_ASSERT( mMessageIndex >= 0 );
327 mCurrentFolder->
unGetMsg( mMessageIndex );
331 mArchivedSize += messageSize;
336 kdWarning(5006) <<
"Unable to download a message for folder " << mCurrentFolder->name() << endl;
338 archiveNextMessage();
341 void BackupJob::messageRetrieved(
KMMessage *message )
343 mCurrentMessage = message;
344 processCurrentMessage();
347 void BackupJob::folderJobFinished( KMail::FolderJob *job )
354 if ( job == mCurrentJob ) {
358 if ( job->error() ) {
359 if ( mCurrentFolder )
360 abort( i18n(
"Downloading a message in folder '%1' failed." ).arg( mCurrentFolder->name() ) );
362 abort( i18n(
"Downloading a message in the current folder failed." ) );
366 bool BackupJob::writeDirHelper(
const TQString &directoryPath,
const TQString &permissionPath )
368 TQFileInfo fileInfo( permissionPath );
369 TQString user = fileInfo.owner();
370 TQString group = fileInfo.group();
371 mode_t permissions = fileInfoToUnixPermissions( fileInfo );
372 time_t creationTime = fileInfo.created().toTime_t();
373 time_t modificationTime = fileInfo.lastModified().toTime_t();
374 time_t accessTime = fileInfo.lastRead().toTime_t();
375 return mArchive->writeDir( stripRootPath( directoryPath ), user, group, permissions, accessTime,
376 modificationTime, creationTime );
379 void BackupJob::archiveNextFolder()
384 if ( mPendingFolders.isEmpty() ) {
389 mCurrentFolder = mPendingFolders.take( 0 );
390 kdDebug(5006) <<
"===> Archiving next folder: " << mCurrentFolder->name() << endl;
391 mProgressItem->setStatus( i18n(
"Archiving folder %1" ).arg( mCurrentFolder->name() ) );
392 if ( mCurrentFolder->
open(
"BackupJob" ) != 0 ) {
393 abort( i18n(
"Unable to open folder '%1'.").arg( mCurrentFolder->name() ) );
396 mCurrentFolderOpen =
true;
398 const TQString folderName = mCurrentFolder->name();
400 if ( hasChildren( mCurrentFolder ) ) {
404 if ( !writeDirHelper( mCurrentFolder->
location(), mCurrentFolder->
location() ) )
406 if ( !writeDirHelper( mCurrentFolder->
location() +
"/cur", mCurrentFolder->
location() ) )
408 if ( !writeDirHelper( mCurrentFolder->
location() +
"/new", mCurrentFolder->
location() ) )
410 if ( !writeDirHelper( mCurrentFolder->
location() +
"/tmp", mCurrentFolder->
location() ) )
413 abort( i18n(
"Unable to create folder structure for folder '%1' within archive file." )
414 .arg( mCurrentFolder->name() ) );
418 for (
int i = 0; i < mCurrentFolder->
count(
false ); i++ ) {
422 kdWarning(5006) <<
"Got serial number zero in " << mCurrentFolder->name()
423 <<
" at index " << i <<
"!" << endl;
425 abort( i18n(
"Unable to backup messages in folder '%1', the index file is corrupted." )
426 .arg( mCurrentFolder->name() ) );
430 mPendingMessages.append( serNum );
432 archiveNextMessage();
453 void BackupJob::start()
455 Q_ASSERT( !mMailArchivePath.isEmpty() );
456 Q_ASSERT( mRootFolder );
458 queueFolders( mRootFolder );
460 switch ( mArchiveType ) {
462 KZip *zip =
new KZip( mMailArchivePath.path() );
463 zip->setCompression( KZip::DeflateCompression );
468 mArchive =
new KTar( mMailArchivePath.path(),
"application/x-tar" );
472 mArchive =
new KTar( mMailArchivePath.path(),
"application/x-gzip" );
476 mArchive =
new KTar( mMailArchivePath.path(),
"application/x-bzip2" );
481 kdDebug(5006) <<
"Starting backup." << endl;
482 if ( !mArchive->open( IO_WriteOnly ) ) {
483 abort( i18n(
"Unable to open archive for writing." ) );
487 mProgressItem = KPIM::ProgressManager::createProgressItem(
492 mProgressItem->setUsesBusyIndicator(
true );
493 connect( mProgressItem, TQ_SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
494 this, TQ_SLOT(cancelJob()) );
499 #include "backupjob.moc"
KMail list that manages the contents of one directory that may contain folders and/or other directori...
TQString subdirLocation() const
Returns full path to sub directory file.
KMFolderDir * child() const
Returns the folder directory associated with this node or 0 if no such directory exists.
KMMsgInfo * unGetMsg(int idx)
Replace KMMessage with KMMsgInfo and delete KMMessage
void close(const char *owner, bool force=false)
Close folder.
KMMessage * getMsg(int idx)
Read message at given index.
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
int count(bool cache=false) const
Number of messages in this folder.
int open(const char *owner)
Open folder for access.
TQString location() const
Returns full path to folder file.
TQString subject() const
Get or set the 'Subject' header field.
const DwString & asDwString() const
Return the entire message contents in the DwString.
TQString fileName() const
Get/set filename in mail folder.
bool isComplete() const
Return true if the complete message is available without referring to the backing store.
unsigned long getMsgSerNum(KMFolder *folder, int index) const
Find the message serial number for the message located at index index in folder folder.
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
static const KMMsgDict * instance()
Access the globally unique MessageDict.