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();
223 KMMessage *message = mCurrentFolder->getMsg( mMessageIndex );
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, TQT_SLOT( processCurrentMessage() ) );
239 else if ( message->parent() ) {
240 mCurrentJob = message->parent()->createJob( message );
241 mCurrentJob->setCancellable(
false );
242 connect( mCurrentJob, TQT_SIGNAL( messageRetrieved(
KMMessage* ) ),
243 this, TQT_SLOT( messageRetrieved(
KMMessage* ) ) );
244 connect( mCurrentJob, TQT_SIGNAL( result( KMail::FolderJob* ) ),
245 this, TQT_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 ) ) {
401 if ( !writeDirHelper( mCurrentFolder->subdirLocation(), mCurrentFolder->subdirLocation() ) )
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, TQT_SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
494 this, TQT_SLOT(cancelJob()) );
499 #include "backupjob.moc" 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.
KMail list that manages the contents of one directory that may contain folders and/or other directori...
void remove()
Removes the folder physically from disk and empties the contents of the folder in memory...
bool isComplete() const
Return true if the complete message is available without referring to the backing store...
TQString subject() const
Get or set the 'Subject' header field.
unsigned long getMsgSerNum(KMFolder *folder, int index) const
Find the message serial number for the message located at index index in folder folder.
KMFolderDir * child() const
Returns the folder directory associated with this node or 0 if no such directory exists.