00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "importjob.h"
00020
00021 #include "kmfolder.h"
00022 #include "folderutil.h"
00023 #include "kmfolderdir.h"
00024 #include "kmfolderimap.h"
00025 #include "imapjob.h"
00026
00027 #include "progressmanager.h"
00028
00029 #include <kdebug.h>
00030 #include <kzip.h>
00031 #include <ktar.h>
00032 #include <klocale.h>
00033 #include <kmessagebox.h>
00034 #include <kmimetype.h>
00035
00036 #include <qwidget.h>
00037 #include <qtimer.h>
00038 #include <qfile.h>
00039
00040 using namespace KMail;
00041
00042 ImportJob::ImportJob( QWidget *parentWidget )
00043 : QObject( parentWidget ),
00044 mArchive( 0 ),
00045 mRootFolder( 0 ),
00046 mParentWidget( parentWidget ),
00047 mNumberOfImportedMessages( 0 ),
00048 mCurrentFolder( 0 ),
00049 mCurrentMessage( 0 ),
00050 mCurrentMessageFile( 0 ),
00051 mProgressItem( 0 ),
00052 mAborted( false )
00053 {
00054 }
00055
00056 ImportJob::~ImportJob()
00057 {
00058 if ( mArchive && mArchive->isOpened() ) {
00059 mArchive->close();
00060 }
00061 delete mArchive;
00062 mArchive = 0;
00063 }
00064
00065 void ImportJob::setFile( const KURL &archiveFile )
00066 {
00067 mArchiveFile = archiveFile;
00068 }
00069
00070 void ImportJob::setRootFolder( KMFolder *rootFolder )
00071 {
00072 mRootFolder = rootFolder;
00073 }
00074
00075 void ImportJob::finish()
00076 {
00077 kdDebug(5006) << "Finished import job." << endl;
00078 mProgressItem->setComplete();
00079 mProgressItem = 0;
00080 QString text = i18n( "Importing the archive file '%1' into the folder '%2' succeeded." )
00081 .arg( mArchiveFile.path() ).arg( mRootFolder->name() );
00082 text += "\n" + i18n( "1 message was imported.", "%n messages were imported.", mNumberOfImportedMessages );
00083 KMessageBox::information( mParentWidget, text, i18n( "Import finished." ) );
00084 deleteLater();
00085 }
00086
00087 void ImportJob::cancelJob()
00088 {
00089 abort( i18n( "The operation was canceled by the user." ) );
00090 }
00091
00092 void ImportJob::abort( const QString &errorMessage )
00093 {
00094 if ( mAborted )
00095 return;
00096
00097 mAborted = true;
00098 QString text = i18n( "Failed to import the archive into folder '%1'." ).arg( mRootFolder->name() );
00099 text += "\n" + errorMessage;
00100 if ( mProgressItem ) {
00101 mProgressItem->setComplete();
00102 mProgressItem = 0;
00103
00104 }
00105 KMessageBox::sorry( mParentWidget, text, i18n( "Importing archive failed." ) );
00106 deleteLater();
00107 }
00108
00109 KMFolder * ImportJob::createSubFolder( KMFolder *parent, const QString &folderName, mode_t permissions )
00110 {
00111 KMFolder *newFolder = FolderUtil::createSubFolder( parent, parent->child(), folderName, QString(),
00112 KMFolderTypeMaildir );
00113 if ( !newFolder ) {
00114 abort( i18n( "Unable to create subfolder for folder '%1'." ).arg( parent->name() ) );
00115 return 0;
00116 }
00117 else {
00118 newFolder->createChildFolder();
00119
00120
00121 chmod( newFolder->location().latin1(), permissions );
00122 chmod( newFolder->subdirLocation().latin1(), permissions );
00123
00124
00125 return newFolder;
00126 }
00127 }
00128
00129 void ImportJob::enqueueMessages( const KArchiveDirectory *dir, KMFolder *folder )
00130 {
00131 const KArchiveDirectory *messageDir = dynamic_cast<const KArchiveDirectory*>( dir->entry( "cur" ) );
00132 if ( messageDir ) {
00133 Messages messagesToQueue;
00134 messagesToQueue.parent = folder;
00135 const QStringList entries = messageDir->entries();
00136 for ( uint i = 0; i < entries.size(); i++ ) {
00137 const KArchiveEntry *entry = messageDir->entry( entries[i] );
00138 Q_ASSERT( entry );
00139 if ( entry->isDirectory() ) {
00140 kdWarning(5006) << "Unexpected subdirectory in archive folder " << dir->name() << endl;
00141 }
00142 else {
00143 kdDebug(5006) << "Queueing message " << entry->name() << endl;
00144 const KArchiveFile *file = static_cast<const KArchiveFile*>( entry );
00145 messagesToQueue.files.append( file );
00146 }
00147 }
00148 mQueuedMessages.append( messagesToQueue );
00149 }
00150 else {
00151 kdWarning(5006) << "No 'cur' subdirectory for archive directory " << dir->name() << endl;
00152 }
00153 }
00154
00155 void ImportJob::messageAdded()
00156 {
00157 mNumberOfImportedMessages++;
00158 if ( mCurrentFolder->folderType() == KMFolderTypeMaildir ||
00159 mCurrentFolder->folderType() == KMFolderTypeCachedImap ) {
00160 const QString messageFile = mCurrentFolder->location() + "/cur/" + mCurrentMessage->fileName();
00161
00162 if ( QFile::exists( messageFile ) ) {
00163 chmod( messageFile.latin1(), mCurrentMessageFile->permissions() );
00164
00165
00166
00167
00168 }
00169 else {
00170 kdWarning(5006) << "Unable to change permissions for newly created file: " << messageFile << endl;
00171 }
00172 }
00173
00174
00175 mCurrentMessage = 0;
00176 mCurrentMessageFile = 0;
00177 QTimer::singleShot( 0, this, SLOT( importNextMessage() ) );
00178 }
00179
00180 void ImportJob::importNextMessage()
00181 {
00182 if ( mAborted )
00183 return;
00184
00185 if ( mQueuedMessages.isEmpty() ) {
00186 kdDebug(5006) << "importNextMessage(): Processed all messages in the queue." << endl;
00187 if ( mCurrentFolder ) {
00188 mCurrentFolder->close( "ImportJob" );
00189 }
00190 mCurrentFolder = 0;
00191 importNextDirectory();
00192 return;
00193 }
00194
00195 Messages &messages = mQueuedMessages.front();
00196 if ( messages.files.isEmpty() ) {
00197 mQueuedMessages.pop_front();
00198 importNextMessage();
00199 return;
00200 }
00201
00202 KMFolder *folder = messages.parent;
00203 if ( folder != mCurrentFolder ) {
00204 kdDebug(5006) << "importNextMessage(): Processed all messages in the current folder of the queue." << endl;
00205 if ( mCurrentFolder ) {
00206 mCurrentFolder->close( "ImportJob" );
00207 }
00208 mCurrentFolder = folder;
00209 if ( mCurrentFolder->open( "ImportJob" ) != 0 ) {
00210 abort( i18n( "Unable to open folder '%1'." ).arg( mCurrentFolder->name() ) );
00211 return;
00212 }
00213 kdDebug(5006) << "importNextMessage(): Current folder of queue is now: " << mCurrentFolder->name() << endl;
00214 mProgressItem->setStatus( i18n( "Importing folder %1" ).arg( mCurrentFolder->name() ) );
00215 }
00216
00217 mProgressItem->setProgress( ( mProgressItem->progress() + 5 ) );
00218
00219 mCurrentMessageFile = messages.files.first();
00220 Q_ASSERT( mCurrentMessageFile );
00221 messages.files.removeFirst();
00222
00223 mCurrentMessage = new KMMessage();
00224 mCurrentMessage->fromByteArray( mCurrentMessageFile->data(), true );
00225 int retIndex;
00226
00227
00228
00229
00230
00231
00232 if ( mCurrentFolder->folderType() != KMFolderTypeImap ) {
00233 if ( mCurrentFolder->addMsg( mCurrentMessage, &retIndex ) != 0 ) {
00234 abort( i18n( "Failed to add a message to the folder '%1'." ).arg( mCurrentFolder->name() ) );
00235 return;
00236 }
00237 messageAdded();
00238 }
00239 else {
00240 ImapJob *imapJob = new ImapJob( mCurrentMessage, ImapJob::tPutMessage,
00241 dynamic_cast<KMFolderImap*>( mCurrentFolder->storage() ) );
00242 connect( imapJob, SIGNAL(result(KMail::FolderJob*)),
00243 SLOT(messagePutResult(KMail::FolderJob*)) );
00244 imapJob->start();
00245 }
00246 }
00247
00248 void ImportJob::messagePutResult( KMail::FolderJob *job )
00249 {
00250 if ( mAborted )
00251 return;
00252
00253 if ( job->error() ) {
00254 abort( i18n( "Failed to upload a message to the IMAP server." ) );
00255 return;
00256 } else {
00257
00258 KMFolderImap *imap = dynamic_cast<KMFolderImap*>( mCurrentFolder->storage() );
00259 Q_ASSERT( imap );
00260
00261
00262
00263 imap->addMsgQuiet( mCurrentMessage );
00264 messageAdded();
00265 }
00266 }
00267
00268
00269
00270
00271 static QString folderNameForDirectoryName( const QString &dirName )
00272 {
00273 Q_ASSERT( dirName.startsWith( "." ) );
00274 const QString end = ".directory";
00275 const int expectedIndex = dirName.length() - end.length();
00276 if ( dirName.lower().find( end ) != expectedIndex )
00277 return QString();
00278 QString returnName = dirName.left( dirName.length() - end.length() );
00279 returnName = returnName.right( returnName.length() - 1 );
00280 return returnName;
00281 }
00282
00283 KMFolder* ImportJob::getOrCreateSubFolder( KMFolder *parentFolder, const QString &subFolderName,
00284 mode_t subFolderPermissions )
00285 {
00286 if ( !parentFolder->createChildFolder() ) {
00287 abort( i18n( "Unable to create subfolder for folder '%1'." ).arg( parentFolder->name() ) );
00288 return 0;
00289 }
00290
00291 KMFolder *subFolder = 0;
00292 subFolder = dynamic_cast<KMFolder*>( parentFolder->child()->hasNamedFolder( subFolderName ) );
00293
00294 if ( !subFolder ) {
00295 subFolder = createSubFolder( parentFolder, subFolderName, subFolderPermissions );
00296 }
00297 return subFolder;
00298 }
00299
00300 void ImportJob::importNextDirectory()
00301 {
00302 if ( mAborted )
00303 return;
00304
00305 if ( mQueuedDirectories.isEmpty() ) {
00306 finish();
00307 return;
00308 }
00309
00310 Folder folder = mQueuedDirectories.first();
00311 KMFolder *currentFolder = folder.parent;
00312 mQueuedDirectories.pop_front();
00313 kdDebug(5006) << "importNextDirectory(): Working on directory " << folder.archiveDir->name() << endl;
00314
00315 QStringList entries = folder.archiveDir->entries();
00316 for ( uint i = 0; i < entries.size(); i++ ) {
00317 const KArchiveEntry *entry = folder.archiveDir->entry( entries[i] );
00318 Q_ASSERT( entry );
00319 kdDebug(5006) << "Queueing entry " << entry->name() << endl;
00320 if ( entry->isDirectory() ) {
00321 const KArchiveDirectory *dir = static_cast<const KArchiveDirectory*>( entry );
00322 if ( !dir->name().startsWith( "." ) ) {
00323
00324 kdDebug(5006) << "Queueing messages in folder " << entry->name() << endl;
00325 KMFolder *subFolder = getOrCreateSubFolder( currentFolder, entry->name(), entry->permissions() );
00326 if ( !subFolder )
00327 return;
00328
00329 enqueueMessages( dir, subFolder );
00330 }
00331
00332
00333 else {
00334
00335 const QString folderName = folderNameForDirectoryName( entry->name() );
00336 if ( folderName.isEmpty() ) {
00337 abort( i18n( "Unexpected subdirectory named '%1'." ).arg( entry->name() ) );
00338 return;
00339 }
00340 KMFolder *subFolder = getOrCreateSubFolder( currentFolder, folderName, entry->permissions() );
00341 if ( !subFolder )
00342 return;
00343
00344 Folder newFolder;
00345 newFolder.archiveDir = dir;
00346 newFolder.parent = subFolder;
00347 kdDebug(5006) << "Enqueueing directory " << entry->name() << endl;
00348 mQueuedDirectories.push_back( newFolder );
00349 }
00350 }
00351 }
00352
00353 importNextMessage();
00354 }
00355
00356
00357
00358
00359
00360 void ImportJob::start()
00361 {
00362 Q_ASSERT( mRootFolder );
00363 Q_ASSERT( mArchiveFile.isValid() );
00364
00365 KMimeType::Ptr mimeType = KMimeType::findByURL( mArchiveFile, 0, true );
00366 if ( !mimeType->patterns().grep( "tar", false ).isEmpty() )
00367 mArchive = new KTar( mArchiveFile.path() );
00368 else if ( !mimeType->patterns().grep( "zip", false ).isEmpty() )
00369 mArchive = new KZip( mArchiveFile.path() );
00370 else {
00371 abort( i18n( "The file '%1' does not appear to be a valid archive." ).arg( mArchiveFile.path() ) );
00372 return;
00373 }
00374
00375 if ( !mArchive->open( IO_ReadOnly ) ) {
00376 abort( i18n( "Unable to open archive file '%1'" ).arg( mArchiveFile.path() ) );
00377 return;
00378 }
00379
00380 mProgressItem = KPIM::ProgressManager::createProgressItem(
00381 "ImportJob",
00382 i18n( "Importing Archive" ),
00383 QString(),
00384 true );
00385 mProgressItem->setUsesBusyIndicator( true );
00386 connect( mProgressItem, SIGNAL(progressItemCanceled(KPIM::ProgressItem*)),
00387 this, SLOT(cancelJob()) );
00388
00389 Folder nextFolder;
00390 nextFolder.archiveDir = mArchive->directory();
00391 nextFolder.parent = mRootFolder;
00392 mQueuedDirectories.push_back( nextFolder );
00393 importNextDirectory();
00394 }
00395
00396 #include "importjob.moc"