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