24 #include <libkdepim/misc/broadcaststatus.h>
26 #include <Akonadi/CollectionDeleteJob>
27 #include <Akonadi/CollectionFetchJob>
28 #include <Akonadi/CollectionFetchScope>
29 #include <Akonadi/ItemFetchJob>
30 #include <Akonadi/ItemFetchScope>
32 #include <KMime/Message>
35 #include <KMessageBox>
38 #include <kio/global.h>
43 using namespace MailCommon;
48 mArchiveTime(
QDateTime::currentDateTime().toTime_t()),
52 mParentWidget( parent ),
53 mArchivedMessages( 0 ),
57 mDeleteFoldersAfterCompletion( false ),
59 mCurrentFolder( Akonadi::Collection() ),
61 mDisplayMessageBox(true)
67 mPendingFolders.clear();
74 mRootFolder = rootFolder;
84 mMailArchivePath = savePath;
94 mDeleteFoldersAfterCompletion = deleteThem;
99 mRecursive = recursive;
102 bool BackupJob::queueFolders(
const Akonadi::Collection &root )
104 mPendingFolders.append( root );
113 Akonadi::CollectionFetchJob *job =
114 new Akonadi::CollectionFetchJob( root, Akonadi::CollectionFetchJob::FirstLevel );
115 job->fetchScope().setAncestorRetrieval( Akonadi::CollectionFetchScope::All );
117 if ( job->error() ) {
118 kWarning() << job->errorString();
119 abort( i18n(
"Unable to retrieve folder list." ) );
123 foreach (
const Akonadi::Collection &collection, job->collections() ) {
124 if ( !queueFolders( collection ) ) {
129 mAllFolders = mPendingFolders;
133 bool BackupJob::hasChildren(
const Akonadi::Collection &collection )
const
135 foreach (
const Akonadi::Collection &curCol, mAllFolders ) {
136 if ( collection == curCol.parentCollection() ) {
143 void BackupJob::cancelJob()
145 abort( i18n(
"The operation was canceled by the user." ) );
148 void BackupJob::abort(
const QString &errorMessage )
157 if ( mCurrentFolder.isValid() ) {
158 mCurrentFolder = Akonadi::Collection();
161 if ( mArchive && mArchive->isOpen() ) {
170 if ( mProgressItem ) {
171 mProgressItem->setComplete();
175 QString text = i18n(
"Failed to archive the folder '%1'.", mRootFolder.name() );
178 if (mDisplayMessageBox)
179 KMessageBox::sorry( mParentWidget, text, i18n(
"Archiving failed" ) );
184 void BackupJob::finish()
186 if ( mArchive->isOpen() ) {
187 if ( !mArchive->close() ) {
188 abort( i18n(
"Unable to finalize the archive file." ) );
193 const QString archivingStr( i18n(
"Archiving finished" ) );
194 KPIM::BroadcastStatus::instance()->setStatusMsg( archivingStr );
196 if ( mProgressItem ) {
197 mProgressItem->setStatus( archivingStr );
198 mProgressItem->setComplete();
202 QFileInfo archiveFileInfo( mMailArchivePath.path() );
203 QString text = i18n(
"Archiving folder '%1' successfully completed. "
204 "The archive was written to the file '%2'.",
205 mRealPath.
isEmpty() ? mRootFolder.name() : mRealPath, mMailArchivePath.path() );
206 text +=
QLatin1Char(
'\n') + i18np(
"1 message of size %2 was archived.",
207 "%1 messages with the total size of %2 were archived.",
208 mArchivedMessages, KIO::convertSize( mArchivedSize ) );
209 text +=
QLatin1Char(
'\n') + i18n(
"The archive file has a size of %1.",
210 KIO::convertSize( archiveFileInfo.size() ) );
211 if (mDisplayMessageBox) {
212 KMessageBox::information( mParentWidget, text, i18n(
"Archiving finished" ) );
215 if ( mDeleteFoldersAfterCompletion ) {
217 if ( archiveFileInfo.exists() && ( mArchivedSize > 0 || mArchivedMessages == 0 ) ) {
219 new Akonadi::CollectionDeleteJob( mRootFolder );
226 void BackupJob::archiveNextMessage()
232 if ( mPendingMessages.isEmpty() ) {
233 kDebug() <<
"===> All messages done in folder " << mCurrentFolder.name();
238 Akonadi::Item item = mPendingMessages.front();
239 mPendingMessages.pop_front();
240 kDebug() <<
"Fetching item with ID" << item.id() <<
"for folder" << mCurrentFolder.name();
242 mCurrentJob =
new Akonadi::ItemFetchJob( item );
243 mCurrentJob->fetchScope().fetchFullPayload(
true );
244 connect( mCurrentJob, SIGNAL(result(KJob*)),
245 this, SLOT(itemFetchJobResult(KJob*)) );
248 void BackupJob::processMessage(
const Akonadi::Item &item )
254 const KMime::Message::Ptr message = item.payload<KMime::Message::Ptr>();
255 kDebug() <<
"Processing message with subject " << message->subject(
false );
256 const QByteArray messageData = message->encodedContent();
257 const qint64 messageSize = messageData.
size();
259 const QString fileName = pathForCollection( mCurrentFolder ) +
QLatin1String(
"/cur/") + messageName;
262 kDebug() <<
"AKONDI PORT: disabled code here!";
264 abort( i18n(
"Failed to write a message into the archive folder '%1'.",
265 mCurrentFolder.name() ) );
270 mArchivedSize += messageSize;
277 void BackupJob::itemFetchJobResult( KJob *job )
283 Q_ASSERT( job == mCurrentJob );
286 if ( job->error() ) {
287 Q_ASSERT( mCurrentFolder.isValid() );
288 kWarning() << job->errorString();
289 abort( i18n(
"Downloading a message in folder '%1' failed.", mCurrentFolder.name() ) );
291 Akonadi::ItemFetchJob *fetchJob =
dynamic_cast<Akonadi::ItemFetchJob*
>( job );
292 Q_ASSERT( fetchJob );
293 Q_ASSERT( fetchJob->items().size() == 1 );
294 processMessage( fetchJob->items().first() );
298 bool BackupJob::writeDirHelper(
const QString &directoryPath )
301 kDebug() <<
"AKONDI PORT: Disabled code here!";
302 return mArchive->writeDir( directoryPath,
QLatin1String(
"user"),
QLatin1String(
"group"), 040755, mArchiveTime, mArchiveTime, mArchiveTime );
305 QString BackupJob::collectionName(
const Akonadi::Collection &collection )
const
307 foreach (
const Akonadi::Collection &curCol, mAllFolders ) {
308 if ( curCol == collection ) {
309 return curCol.name();
316 QString BackupJob::pathForCollection(
const Akonadi::Collection &collection )
const
318 QString fullPath = collectionName( collection );
319 Akonadi::Collection curCol = collection.parentCollection();
320 if ( collection != mRootFolder ) {
321 Q_ASSERT( curCol.isValid() );
322 while ( curCol != mRootFolder ) {
324 curCol = curCol.parentCollection();
326 Q_ASSERT( curCol == mRootFolder );
332 QString BackupJob::subdirPathForCollection(
const Akonadi::Collection &collection )
const
334 QString path = pathForCollection( collection );
335 const int parentDirEndIndex = path.
lastIndexOf( collection.name() );
336 Q_ASSERT( parentDirEndIndex != -1 );
337 path = path.
left( parentDirEndIndex );
342 void BackupJob::archiveNextFolder()
348 if ( mPendingFolders.isEmpty() ) {
353 mCurrentFolder = mPendingFolders.takeAt( 0 );
354 kDebug() <<
"===> Archiving next folder: " << mCurrentFolder.name();
355 const QString archivingStr( i18n(
"Archiving folder %1", mCurrentFolder.name() ) );
356 if ( mProgressItem ) {
357 mProgressItem->setStatus( archivingStr );
359 KPIM::BroadcastStatus::instance()->setStatusMsg( archivingStr );
361 const QString folderName = mCurrentFolder.name();
363 if ( hasChildren( mCurrentFolder ) ) {
364 if ( !writeDirHelper( subdirPathForCollection( mCurrentFolder ) ) ) {
369 if ( !writeDirHelper( pathForCollection( mCurrentFolder ) ) ) {
371 }
else if ( !writeDirHelper( pathForCollection( mCurrentFolder ) +
QLatin1String(
"/cur") ) ) {
373 }
else if ( !writeDirHelper( pathForCollection( mCurrentFolder ) +
QLatin1String(
"/new") ) ) {
375 }
else if ( !writeDirHelper( pathForCollection( mCurrentFolder ) +
QLatin1String(
"/tmp") ) ) {
380 abort( i18n(
"Unable to create folder structure for folder '%1' within archive file.",
381 mCurrentFolder.name() ) );
384 Akonadi::ItemFetchJob *job =
new Akonadi::ItemFetchJob( mCurrentFolder );
385 job->setProperty(
"folderName", folderName );
386 connect( job, SIGNAL(result(KJob*)), SLOT(onArchiveNextFolderDone(KJob*)) );
389 void BackupJob::onArchiveNextFolderDone( KJob *job )
391 if ( job->error() ) {
392 kWarning() << job->errorString();
393 abort( i18n(
"Unable to get message list for folder %1.",
394 job->property(
"folderName" ).toString() ) );
398 Akonadi::ItemFetchJob *fetchJob = qobject_cast<Akonadi::ItemFetchJob*>( job );
399 mPendingMessages += fetchJob->items();
400 archiveNextMessage();
405 Q_ASSERT( !mMailArchivePath.isEmpty() );
406 Q_ASSERT( mRootFolder.isValid() );
408 if ( !queueFolders( mRootFolder ) ) {
412 switch ( mArchiveType ) {
415 KZip *zip =
new KZip( mMailArchivePath.path() );
416 zip->setCompression( KZip::DeflateCompression );
422 mArchive =
new KTar( mMailArchivePath.path(),
QLatin1String(
"application/x-tar") );
427 mArchive =
new KTar( mMailArchivePath.path(),
QLatin1String(
"application/x-gzip") );
432 mArchive =
new KTar( mMailArchivePath.path(),
QLatin1String(
"application/x-bzip2") );
437 kDebug() <<
"Starting backup.";
438 if ( !mArchive->open( QIODevice::WriteOnly ) ) {
439 abort( i18n(
"Unable to open archive for writing." ) );
443 mProgressItem = KPIM::ProgressManager::createProgressItem(
448 mProgressItem->setUsesBusyIndicator(
true );
449 connect( mProgressItem, SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
450 this, SLOT(cancelJob()) );
457 mDisplayMessageBox = display;
QString & append(QChar ch)
void setDeleteFoldersAfterCompletion(bool deleteThem)
void backupDone(const QString &)
void setRootFolder(const Akonadi::Collection &rootFolder)
void error(const QString &)
void setDisplayMessageBox(bool display)
QString & prepend(QChar ch)
void setRealPath(const QString &path)
void setArchiveType(ArchiveType type)
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
void setRecursive(bool recursive)
QString number(int n, int base)
void setSaveLocation(const KUrl &savePath)
static const mode_t archivePerms
QString left(int n) const
QString fromLatin1(const char *str, int size)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
BackupJob(QWidget *parent=0)