kmail

cachedimapjob.cpp

00001 /*  -*- mode: C++; c-file-style: "gnu" -*-
00002  *
00003  *  This file is part of KMail, the KDE mail client.
00004  *  Copyright (c) 2002-2004  Bo Thorsen <bo@sonofthor.dk>
00005  *                2002-2003  Steffen Hansen <hansen@kde.org>
00006  *                2002-2003  Zack Rusin <zack@kde.org>
00007  *
00008  *  KMail is free software; you can redistribute it and/or modify it
00009  *  under the terms of the GNU General Public License, version 2, as
00010  *  published by the Free Software Foundation.
00011  *
00012  *  KMail is distributed in the hope that it will be useful, but
00013  *  WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  *  General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020  *
00021  *  In addition, as a special exception, the copyright holders give
00022  *  permission to link the code of this program with any edition of
00023  *  the Qt library by Trolltech AS, Norway (or with modified versions
00024  *  of Qt that use the same license as Qt), and distribute linked
00025  *  combinations including the two.  You must obey the GNU General
00026  *  Public License in all respects for all of the code used other than
00027  *  Qt.  If you modify this file, you may extend this exception to
00028  *  your version of the file, but you are not obligated to do so.  If
00029  *  you do not wish to do so, delete this exception statement from
00030  *  your version.
00031  */
00032 
00033 #ifdef HAVE_CONFIG_H
00034 #include <config.h>
00035 #endif
00036 
00037 #include "cachedimapjob.h"
00038 #include "imapaccountbase.h"
00039 
00040 #include "kmfoldermgr.h"
00041 #include "kmfolder.h"
00042 #include "kmfoldercachedimap.h"
00043 #include "kmailicalifaceimpl.h"
00044 #include "kmacctcachedimap.h"
00045 #include "kmmsgdict.h"
00046 #include "maildirjob.h"
00047 #include "scalix.h"
00048 #include "util.h"
00049 
00050 #include <kio/scheduler.h>
00051 #include <kio/job.h>
00052 
00053 #include <klocale.h>
00054 #include <kdebug.h>
00055 
00056 
00057 namespace KMail {
00058 
00059 // Get messages
00060 CachedImapJob::CachedImapJob( const QValueList<MsgForDownload>& msgs,
00061                               JobType type, KMFolderCachedImap* folder )
00062   : FolderJob( type ), mFolder( folder ), mMsgsForDownload( msgs ),
00063     mTotalBytes(0), mMsg(0), mParentFolder( 0 )
00064 {
00065   QValueList<MsgForDownload>::ConstIterator it = msgs.begin();
00066   for ( ; it != msgs.end() ; ++it )
00067     mTotalBytes += (*it).size;
00068 }
00069 
00070 // Put messages
00071 CachedImapJob::CachedImapJob( const QPtrList<KMMessage>& msgs, JobType type,
00072                               KMFolderCachedImap* folder )
00073   : FolderJob( msgs, QString::null, type, folder?folder->folder():0 ), mFolder( folder ),
00074     mTotalBytes( msgs.count() ), // we abuse it as "total number of messages"
00075     mMsg( 0 ), mParentFolder( 0 )
00076 {
00077 }
00078 
00079 CachedImapJob::CachedImapJob( const QValueList<unsigned long>& msgs,
00080                   JobType type, KMFolderCachedImap* folder )
00081   : FolderJob( QPtrList<KMMessage>(), QString::null, type, folder?folder->folder():0 ),
00082     mFolder( folder ), mSerNumMsgList( msgs ), mTotalBytes( msgs.count() ), mMsg( 0 ),
00083     mParentFolder ( 0 )
00084 {
00085 }
00086 
00087 // Add sub folders
00088 CachedImapJob::CachedImapJob( const QValueList<KMFolderCachedImap*>& fList,
00089                               JobType type, KMFolderCachedImap* folder )
00090   : FolderJob( type ), mFolder( folder ), mFolderList( fList ), mMsg( 0 ),
00091     mParentFolder ( 0 )
00092 {
00093 }
00094 
00095 // Rename folder
00096 CachedImapJob::CachedImapJob( const QString& string1, JobType type,
00097                               KMFolderCachedImap* folder )
00098   : FolderJob( type ), mFolder(folder), mMsg( 0 ), mString( string1 ),
00099     mParentFolder ( 0 )
00100 {
00101   assert( folder );
00102   assert( type != tDeleteMessage ); // moved to another ctor
00103 }
00104 
00105 // Delete folders or messages
00106 CachedImapJob::CachedImapJob( const QStringList& foldersOrMsgs, JobType type,
00107                               KMFolderCachedImap* folder )
00108   : FolderJob( type ), mFolder( folder ), mFoldersOrMessages( foldersOrMsgs ),
00109     mMsg( 0 ), mParentFolder( 0 )
00110 {
00111   assert( folder );
00112 }
00113 
00114 // Other jobs (list messages,expunge folder, check uid validity)
00115 CachedImapJob::CachedImapJob( JobType type, KMFolderCachedImap* folder )
00116   : FolderJob( type ), mFolder( folder ), mMsg( 0 ), mParentFolder ( 0 )
00117 {
00118   assert( folder );
00119 }
00120 
00121 CachedImapJob::~CachedImapJob()
00122 {
00123   mAccount->mJobList.remove(this);
00124 }
00125 
00126 void CachedImapJob::execute()
00127 {
00128   mSentBytes = 0;
00129 
00130   if( !mFolder ) {
00131     if( !mMsgList.isEmpty() ) {
00132       mFolder = static_cast<KMFolderCachedImap*>(mMsgList.first()->storage());
00133     }
00134   }
00135   assert( mFolder );
00136   mAccount = mFolder->account();
00137   assert( mAccount != 0 );
00138   if( mAccount->makeConnection() != ImapAccountBase::Connected ) {
00139     // No connection to the IMAP server
00140     kdDebug(5006) << "mAccount->makeConnection() failed" << endl;
00141     mPassiveDestructor = true;
00142     delete this;
00143     return;
00144   } else
00145     mPassiveDestructor = false;
00146 
00147   // All necessary conditions have been met. Register this job
00148   mAccount->mJobList.append(this);
00149 
00156   if ( mAccount->groupwareType() == KMAcctCachedImap::GroupwareScalix ) {
00157     if ( !mAccount->sentCustomLoginCommand() ) {
00158       QByteArray packedArgs;
00159       QDataStream stream( packedArgs, IO_WriteOnly );
00160 
00161       const QString command = QString( "X-SCALIX-ID " );
00162       const QString argument = QString( "(\"name\" \"Evolution\" \"version\" \"2.10.0\")" );
00163 
00164       stream << (int) 'X' << 'N' << command << argument;
00165 
00166       const KURL url = mAccount->getUrl();
00167 
00168       ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00169       jd.items << mFolder->label(); // for the err msg
00170       KIO::SimpleJob *simpleJob = KIO::special( url.url(), packedArgs, false );
00171       KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00172       mAccount->insertJob(simpleJob, jd);
00173 
00174       mAccount->setSentCustomLoginCommand( true );
00175     }
00176   }
00177 
00178   switch( mType ) {
00179   case tGetMessage:       slotGetNextMessage();     break;
00180   case tPutMessage:       slotPutNextMessage();     break;
00181   case tDeleteMessage:    slotDeleteNextMessages(); break;
00182   case tExpungeFolder:    expungeFolder();          break;
00183   case tAddSubfolders:    slotAddNextSubfolder();   break;
00184   case tDeleteFolders:    slotDeleteNextFolder();   break;
00185   case tCheckUidValidity: checkUidValidity();       break;
00186   case tRenameFolder:     renameFolder(mString);    break;
00187   case tListMessages:     listMessages();           break;
00188   default:
00189     assert( 0 );
00190   }
00191 }
00192 
00193 void CachedImapJob::listMessages()
00194 {
00195   KURL url = mAccount->getUrl();
00196   url.setPath( mFolder->imapPath() + ";UID=1:*;SECTION=FLAGS RFC822.SIZE");
00197 
00198   KIO::SimpleJob *job = KIO::get(url, false, false);
00199   KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
00200   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00201   jd.cancellable = true;
00202   mAccount->insertJob( job, jd );
00203   connect( job, SIGNAL( result(KIO::Job *) ),
00204            this, SLOT( slotListMessagesResult( KIO::Job* ) ) );
00205   // send the data directly for KMFolderCachedImap
00206   connect( job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
00207            mFolder, SLOT( slotGetMessagesData( KIO::Job* , const QByteArray& ) ) );
00208 }
00209 
00210 void CachedImapJob::slotDeleteNextMessages( KIO::Job* job )
00211 {
00212   if (job) {
00213     KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00214     if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00215       delete this;
00216       return;
00217     }
00218 
00219     if( job->error() ) {
00220       mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
00221       delete this;
00222       return;
00223     }
00224     mAccount->removeJob(it);
00225   }
00226 
00227   if( mFoldersOrMessages.isEmpty() ) {
00228     // No more messages to delete
00229     delete this;
00230     return;
00231   }
00232 
00233   QString uids = mFoldersOrMessages.front(); mFoldersOrMessages.pop_front();
00234 
00235   KURL url = mAccount->getUrl();
00236   url.setPath( mFolder->imapPath() +
00237                QString::fromLatin1(";UID=%1").arg(uids) );
00238 
00239   KIO::SimpleJob *simpleJob = KIO::file_delete( url, false );
00240   KIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
00241   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00242   mAccount->insertJob( simpleJob, jd );
00243   connect( simpleJob, SIGNAL( result(KIO::Job *) ),
00244            this, SLOT( slotDeleteNextMessages(KIO::Job *) ) );
00245 }
00246 
00247 void CachedImapJob::expungeFolder()
00248 {
00249   KURL url = mAccount->getUrl();
00250   // Special URL that means EXPUNGE
00251   url.setPath( mFolder->imapPath() + QString::fromLatin1(";UID=*") );
00252 
00253   KIO::SimpleJob *job = KIO::file_delete( url, false );
00254   KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
00255   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00256   mAccount->insertJob( job, jd );
00257   connect( job, SIGNAL( result(KIO::Job *) ),
00258            this, SLOT( slotExpungeResult(KIO::Job *) ) );
00259 }
00260 
00261 void CachedImapJob::slotExpungeResult( KIO::Job * job )
00262 {
00263   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00264   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00265     delete this;
00266     return;
00267   }
00268 
00269   if (job->error()) {
00270     mErrorCode = job->error();
00271     mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
00272   }
00273   else
00274     mAccount->removeJob(it);
00275 
00276   delete this;
00277 }
00278 
00279 void CachedImapJob::slotGetNextMessage(KIO::Job * job)
00280 {
00281   if (job) {
00282     KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00283     if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00284       delete this;
00285       return;
00286     }
00287 
00288     if (job->error()) {
00289       mErrorCode = job->error();
00290       mAccount->handleJobError( job, i18n( "Error while retrieving message on the server: " ) + '\n' );
00291       delete this;
00292       return;
00293     }
00294 
00295     ulong size = 0;
00296     if ((*it).data.size() > 0) {
00297       ulong uid = mMsg->UID();
00298       size = mMsg->msgSizeServer();
00299 
00300       // Convert CR/LF to LF.
00301       size_t dataSize = (*it).data.size();
00302       dataSize = Util::crlf2lf( (*it).data.data(), dataSize ); // always <=
00303       (*it).data.resize( dataSize );
00304 
00305       mMsg->setComplete( true );
00306       mMsg->fromByteArray( (*it).data );
00307       mMsg->setUID(uid);
00308       mMsg->setMsgSizeServer(size);
00309       mMsg->setTransferInProgress( false );
00310       int index = -1;
00311       mFolder->open( "KMFolderCachedImap::slotGetNextMessage" );
00312       mFolder->addMsgInternal( mMsg, true, &index );
00313 
00314       if ( kmkernel->iCalIface().isResourceFolder( mFolder->folder() ) ) {
00315         mFolder->setStatus( index, KMMsgStatusRead, false );
00316       }
00317       mFolder->close( "KMFolderCachedImap::slotGetNextMessage" );
00318 
00319       emit messageRetrieved( mMsg );
00320       if ( index >= 0 ) mFolder->unGetMsg( index );
00321     } else {
00322       emit messageRetrieved( 0 );
00323     }
00324     mMsg = 0;
00325 
00326     mSentBytes += size;
00327     emit progress( mSentBytes, mTotalBytes );
00328     mAccount->removeJob(it);
00329   } else
00330     mFolder->quiet( true );
00331 
00332   if( mMsgsForDownload.isEmpty() ) {
00333     mFolder->quiet( false );
00334     delete this;
00335     return;
00336   }
00337 
00338   MsgForDownload mfd = mMsgsForDownload.front(); mMsgsForDownload.pop_front();
00339 
00340   mMsg = new KMMessage;
00341   mMsg->setUID(mfd.uid);
00342   mMsg->setMsgSizeServer(mfd.size);
00343   if( mfd.flags > 0 )
00344     KMFolderImap::flagsToStatus(mMsg, mfd.flags, true, GlobalSettings::allowLocalFlags() ? mFolder->permanentFlags() : INT_MAX);
00345   KURL url = mAccount->getUrl();
00346   url.setPath(mFolder->imapPath() + QString(";UID=%1;SECTION=BODY.PEEK[]").arg(mfd.uid));
00347 
00348   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00349   jd.cancellable = true;
00350   mMsg->setTransferInProgress(true);
00351   KIO::SimpleJob *simpleJob = KIO::get(url, false, false);
00352   KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00353   mAccount->insertJob(simpleJob, jd);
00354   connect(simpleJob, SIGNAL(processedSize(KIO::Job *, KIO::filesize_t)),
00355           this, SLOT(slotProcessedSize(KIO::Job *, KIO::filesize_t)));
00356   connect(simpleJob, SIGNAL(result(KIO::Job *)),
00357           this, SLOT(slotGetNextMessage(KIO::Job *)));
00358   connect(simpleJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
00359           mFolder, SLOT(slotSimpleData(KIO::Job *, const QByteArray &)));
00360 }
00361 
00362 void CachedImapJob::slotProcessedSize(KIO::Job *, KIO::filesize_t processed)
00363 {
00364   emit progress( mSentBytes + processed, mTotalBytes );
00365 }
00366 
00367 void CachedImapJob::slotPutNextMessage()
00368 {
00369   mMsg = 0;
00370 
00371   // First try the message list
00372   if( !mMsgList.isEmpty() ) {
00373     mMsg = mMsgList.first();
00374     mMsgList.removeFirst();
00375   }
00376 
00377   // Now try the serial number list
00378   while( mMsg == 0 && !mSerNumMsgList.isEmpty() ) {
00379     unsigned long serNum = mSerNumMsgList.first();
00380     mSerNumMsgList.pop_front();
00381 
00382     // Find the message with this serial number
00383     int i = 0;
00384     KMFolder* aFolder = 0;
00385     KMMsgDict::instance()->getLocation( serNum, &aFolder, &i );
00386     if( mFolder->folder() != aFolder )
00387       // This message was moved or something
00388       continue;
00389     mMsg = mFolder->getMsg( i );
00390   }
00391 
00392   if( !mMsg ) {
00393     // No message found for upload
00394     delete this;
00395     return;
00396   }
00397 
00398   KURL url = mAccount->getUrl();
00399   QString flags = KMFolderImap::statusToFlags( mMsg->status(), mFolder->permanentFlags() );
00400   url.setPath( mFolder->imapPath() + ";SECTION=" + flags );
00401 
00402   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00403 
00404   mMsg->setUID( 0 ); // for the index
00405   QCString cstr(mMsg->asString());
00406   int a = cstr.find("\nX-UID: ");
00407   int b = cstr.find('\n', a);
00408   if (a != -1 && b != -1 && cstr.find("\n\n") > a) cstr.remove(a, b-a);
00409   QCString mData(cstr.length() + cstr.contains('\n'));
00410   unsigned int i = 0;
00411   for( char *ch = cstr.data(); *ch; ch++ ) {
00412     if ( *ch == '\n' ) {
00413       mData.at(i) = '\r';
00414       i++;
00415     }
00416     mData.at(i) = *ch; i++;
00417   }
00418   jd.data = mData;
00419   jd.msgList.append( mMsg );
00420 
00421   mMsg->setTransferInProgress(true);
00422   KIO::SimpleJob *simpleJob = KIO::put(url, 0, false, false, false);
00423   KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00424   mAccount->insertJob(simpleJob, jd);
00425   connect( simpleJob, SIGNAL( result(KIO::Job *) ),
00426            SLOT( slotPutMessageResult(KIO::Job *) ) );
00427   connect( simpleJob, SIGNAL( dataReq(KIO::Job *, QByteArray &) ),
00428            SLOT( slotPutMessageDataReq(KIO::Job *, QByteArray &) ) );
00429   connect( simpleJob, SIGNAL( data(KIO::Job *, const QByteArray &) ),
00430            mFolder, SLOT( slotSimpleData(KIO::Job *, const QByteArray &) ) );
00431   connect( simpleJob, SIGNAL(infoMessage(KIO::Job *, const QString &)),
00432              SLOT(slotPutMessageInfoData(KIO::Job *, const QString &)) );
00433 
00434 }
00435 
00436 //-----------------------------------------------------------------------------
00437 // TODO: port to KIO::StoredTransferJob once it's ok to require kdelibs-3.3
00438 void CachedImapJob::slotPutMessageDataReq(KIO::Job *job, QByteArray &data)
00439 {
00440   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00441   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00442     delete this;
00443     return;
00444   }
00445   if ((*it).data.size() - (*it).offset > 0x8000) {
00446     data.duplicate((*it).data.data() + (*it).offset, 0x8000);
00447     (*it).offset += 0x8000;
00448   } else if ((*it).data.size() - (*it).offset > 0) {
00449     data.duplicate((*it).data.data() + (*it).offset,
00450                    (*it).data.size() - (*it).offset);
00451     (*it).offset = (*it).data.size();
00452   } else
00453     data.resize(0);
00454 }
00455 
00456 //----------------------------------------------------------------------------
00457 void CachedImapJob::slotPutMessageInfoData( KIO::Job *job, const QString &data )
00458 {
00459   KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( mDestFolder->storage() );
00460   if ( imapFolder ) {
00461     KMAcctCachedImap *account = imapFolder->account();
00462     ImapAccountBase::JobIterator it = account->findJob( job );
00463     if ( it == account->jobsEnd() ) {
00464       return;
00465     }
00466 
00467     if ( data.find( "UID" ) != -1 && mMsg ) {
00468       int uid = ( data.right( data.length() - 4 ) ).toInt();
00469       kdDebug( 5006 ) << k_funcinfo << "Server told us uid is: " << uid << endl;
00470       mMsg->setUID( uid );
00471     }
00472   }
00473 }
00474 
00475 
00476 //-----------------------------------------------------------------------------
00477 void CachedImapJob::slotPutMessageResult(KIO::Job *job)
00478 {
00479   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00480   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00481     delete this;
00482     return;
00483   }
00484 
00485   if ( job->error() ) {
00486     bool cont = mAccount->handlePutError( job, *it, mFolder->folder() );
00487     if ( !cont ) {
00488       delete this;
00489     } else {
00490       mMsg = 0;
00491       slotPutNextMessage();
00492     }
00493     return;
00494   }
00495 
00496   emit messageStored( mMsg );
00497 
00498   // we abuse those fields, the unit is the number of messages, here
00499   ++mSentBytes;
00500   emit progress( mSentBytes, mTotalBytes );
00501 
00502   int i;
00503   if( ( i = mFolder->find(mMsg) ) != -1 ) {
00504      /*
00505       * If we have aquired a uid during upload the server supports the uidnext
00506       * extension and there is no need to redownload this mail, we already have
00507       * it. Otherwise remove it, it will be redownloaded.
00508       */
00509      if ( mMsg->UID() == 0 ) {
00510         mFolder->removeMsg(i);
00511      } else {
00512         // When removing+readding, no point in telling the imap resources about it
00513         bool b = kmkernel->iCalIface().isResourceQuiet();
00514         kmkernel->iCalIface().setResourceQuiet( true );
00515 
00516         mFolder->takeTemporarily( i );
00517         mFolder->addMsgKeepUID( mMsg );
00518         mMsg->setTransferInProgress( false );
00519 
00520         kmkernel->iCalIface().setResourceQuiet( b );
00521      }
00522   }
00523   mMsg = NULL;
00524   mAccount->removeJob( it );
00525   slotPutNextMessage();
00526 }
00527 
00528 
00529 void CachedImapJob::slotAddNextSubfolder( KIO::Job * job )
00530 {
00531   if (job) {
00532     KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00533     if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00534       delete this;
00535       return;
00536     }
00537 
00538     // make copy of setting, to reset it before potentially destroying 'it'
00539     bool silentUpload = static_cast<KMFolderCachedImap*>((*it).parent->storage())->silentUpload();
00540     static_cast<KMFolderCachedImap*>((*it).parent->storage())->setSilentUpload( false );
00541 
00542     if ( job->error() && !silentUpload ) {
00543       QString myError = "<p><b>" + i18n("Error while uploading folder")
00544         + "</b></p><p>" + i18n("Could not make the folder <b>%1</b> on the server.").arg((*it).items[0])
00545         + "</p><p>" + i18n("This could be because you do not have permission to do this, or because the folder is already present on the server; the error message from the server communication is here:") + "</p>";
00546       mAccount->handleJobError( job, myError );
00547     }
00548 
00549     if( job->error() ) {
00550       delete this;
00551       return;
00552     } else {
00553       KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>( (*it).current->storage() );
00554       KMFolderCachedImap* parentStorage = static_cast<KMFolderCachedImap*>( (*it).parent->storage() );
00555       Q_ASSERT( storage );
00556       Q_ASSERT( parentStorage );
00557       if ( storage->imapPath().isEmpty() ) {
00558         QString path = mAccount->createImapPath( parentStorage->imapPath(), storage->folder()->name() );
00559         if ( !storage->imapPathForCreation().isEmpty() )
00560           path = storage->imapPathForCreation();
00561         storage->setImapPath( path );
00562         storage->writeConfig();
00563       }
00564     }
00565     mAccount->removeJob( it );
00566   }
00567 
00568   if (mFolderList.isEmpty()) {
00569     // No more folders to add
00570     delete this;
00571     return;
00572   }
00573 
00574   KMFolderCachedImap *folder = mFolderList.front();
00575   mFolderList.pop_front();
00576   KURL url = mAccount->getUrl();
00577   QString path = mAccount->createImapPath( mFolder->imapPath(),
00578       folder->folder()->name() );
00579   if ( !folder->imapPathForCreation().isEmpty() ) {
00580     // the folder knows it's namespace
00581     path = folder->imapPathForCreation();
00582   }
00583   url.setPath( path );
00584 
00585   if ( mAccount->groupwareType() != KMAcctCachedImap::GroupwareScalix ) {
00586     // Associate the jobData with the parent folder, not with the child
00587     // This is necessary in case of an error while creating the subfolder,
00588     // so that folderComplete is called on the parent (and the sync resetted).
00589     ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00590     jd.items << folder->label(); // for the err msg
00591     jd.current = folder->folder();
00592     KIO::SimpleJob *simpleJob = KIO::mkdir(url);
00593     KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00594     mAccount->insertJob(simpleJob, jd);
00595     connect( simpleJob, SIGNAL(result(KIO::Job *)),
00596              this, SLOT(slotAddNextSubfolder(KIO::Job *)) );
00597   } else {
00598     QByteArray packedArgs;
00599     QDataStream stream( packedArgs, IO_WriteOnly );
00600 
00601     const QString command = QString( "X-CREATE-SPECIAL" );
00602     const QString argument = QString( "%1 %2" ).arg( Scalix::Utils::contentsTypeToScalixId( folder->contentsType() ) )
00603                                                .arg( path );
00604 
00605     stream << (int) 'X' << 'N' << command << argument;
00606 
00607     ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00608     jd.items << folder->label(); // for the err msg
00609     jd.current = folder->folder();
00610     KIO::SimpleJob *simpleJob = KIO::special( url.url(), packedArgs, false );
00611     KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00612     mAccount->insertJob(simpleJob, jd);
00613     connect( simpleJob, SIGNAL(result(KIO::Job *)),
00614              this, SLOT(slotAddNextSubfolder(KIO::Job *)) );
00615   }
00616 }
00617 
00618 
00619 void CachedImapJob::slotDeleteNextFolder( KIO::Job *job )
00620 {
00621   if (job) {
00622     KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00623     if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00624       delete this;
00625       return;
00626     }
00627 
00628     mAccount->removeDeletedFolder( (*it).path );
00629 
00630     if( job->error() ) {
00631       mAccount->handleJobError( job, i18n( "Error while deleting folder %1 on the server: " ).arg( (*it).path ) + '\n' );
00632       delete this;
00633       return;
00634     }
00635     mAccount->removeJob(it);
00636   }
00637 
00638   if( mFoldersOrMessages.isEmpty() ) {
00639     // No more folders to delete
00640     delete this;
00641     return;
00642   }
00643 
00644   QString folderPath = mFoldersOrMessages.front();
00645   mFoldersOrMessages.pop_front();
00646   KURL url = mAccount->getUrl();
00647   url.setPath(folderPath);
00648   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00649   jd.path = url.path();
00650   KIO::SimpleJob *simpleJob = KIO::file_delete(url, false);
00651   KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00652   mAccount->insertJob(simpleJob, jd);
00653   connect( simpleJob, SIGNAL( result(KIO::Job *) ),
00654            SLOT( slotDeleteNextFolder(KIO::Job *) ) );
00655 }
00656 
00657 void CachedImapJob::checkUidValidity()
00658 {
00659   KURL url = mAccount->getUrl();
00660   url.setPath( mFolder->imapPath() + ";UID=0:0" );
00661 
00662   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00663   jd.cancellable = true;
00664 
00665   KIO::SimpleJob *job = KIO::get( url, false, false );
00666   KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
00667   mAccount->insertJob( job, jd );
00668   connect( job, SIGNAL(result(KIO::Job *)),
00669            SLOT(slotCheckUidValidityResult(KIO::Job *)) );
00670   connect( job, SIGNAL(data(KIO::Job *, const QByteArray &)),
00671            mFolder, SLOT(slotSimpleData(KIO::Job *, const QByteArray &)));
00672 }
00673 
00674 void CachedImapJob::slotCheckUidValidityResult(KIO::Job * job)
00675 {
00676   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00677   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00678     delete this;
00679     return;
00680   }
00681 
00682   if( job->error() ) {
00683     mErrorCode = job->error();
00684     mAccount->handleJobError( job, i18n( "Error while reading folder %1 on the server: " ).arg( (*it).parent->label() ) + '\n' );
00685     delete this;
00686     return;
00687   }
00688 
00689   // Check the uidValidity
00690   QCString cstr((*it).data.data(), (*it).data.size() + 1);
00691   int a = cstr.find("X-uidValidity: ");
00692   if (a < 0) {
00693     // Something is seriously rotten here!
00694     // TODO: Tell the user that he has a problem
00695     kdDebug(5006) << "No uidvalidity available for folder "
00696                   << mFolder->name() << endl;
00697   }
00698   else {
00699     int b = cstr.find("\r\n", a);
00700     if ( (b - a - 15) >= 0 ) {
00701       QString uidv = cstr.mid(a + 15, b - a - 15);
00702       // kdDebug(5006) << "New uidv = " << uidv << ", old uidv = "
00703       //               << mFolder->uidValidity() << endl;
00704       if( !mFolder->uidValidity().isEmpty() && mFolder->uidValidity() != uidv ) {
00705         // kdDebug(5006) << "Expunging the mailbox " << mFolder->name()
00706         //               << "!" << endl;
00707         mFolder->expunge();
00708         mFolder->setLastUid( 0 );
00709         mFolder->clearUidMap();
00710       }
00711     } else
00712       kdDebug(5006) << "No uidvalidity available for folder "
00713                     << mFolder->name() << endl;
00714   }
00715 
00716   a = cstr.find( "X-PermanentFlags: " );
00717   if ( a < 0 ) {
00718     kdDebug(5006) << "no PERMANENTFLAGS response? assumming custom flags are not available" << endl;
00719   } else {
00720     int b = cstr.find( "\r\n", a );
00721     if ( (b - a - 18) >= 0 ) {
00722       int flags = cstr.mid( a + 18, b - a - 18 ).toInt();
00723       emit permanentFlags( flags );
00724     } else {
00725       kdDebug(5006) << "PERMANENTFLAGS response broken, assumming custom flags are not available" << endl;
00726     }
00727   }
00728 
00729   mAccount->removeJob(it);
00730   delete this;
00731 }
00732 
00733 
00734 void CachedImapJob::renameFolder( const QString &newName )
00735 {
00736   mNewName = newName;
00737 
00738   // Set the source URL
00739   KURL urlSrc = mAccount->getUrl();
00740   mOldImapPath = mFolder->imapPath();
00741   urlSrc.setPath( mOldImapPath );
00742 
00743   // Set the destination URL - this is a bit trickier
00744   KURL urlDst = mAccount->getUrl();
00745   mNewImapPath = mFolder->imapPath();
00746   // Destination url = old imappath - oldname + new name
00747   mNewImapPath.truncate( mNewImapPath.length() - mFolder->folder()->name().length() - 1);
00748   mNewImapPath += newName + '/';
00749   urlDst.setPath( mNewImapPath );
00750 
00751   ImapAccountBase::jobData jd( newName, mFolder->folder() );
00752   jd.path = mNewImapPath;
00753 
00754   KIO::SimpleJob *simpleJob = KIO::rename( urlSrc, urlDst, false );
00755   KIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
00756   mAccount->insertJob( simpleJob, jd );
00757   connect( simpleJob, SIGNAL(result(KIO::Job *)),
00758            SLOT(slotRenameFolderResult(KIO::Job *)) );
00759 }
00760 
00761 static void renameChildFolders( KMFolderDir* dir, const QString& oldPath,
00762                                 const QString& newPath )
00763 {
00764   if( dir ) {
00765     KMFolderNode *node = dir->first();
00766     while( node ) {
00767       if( !node->isDir() ) {
00768         KMFolderCachedImap* imapFolder =
00769           static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
00770         if ( !imapFolder->imapPath().isEmpty() )
00771           // Only rename folders that have been accepted by the server
00772           if( imapFolder->imapPath().find( oldPath ) == 0 ) {
00773             QString p = imapFolder->imapPath();
00774             p = p.mid( oldPath.length() );
00775             p.prepend( newPath );
00776             imapFolder->setImapPath( p );
00777             renameChildFolders( imapFolder->folder()->child(), oldPath, newPath );
00778           }
00779       }
00780       node = dir->next();
00781     }
00782   }
00783 }
00784 
00785 void CachedImapJob::revertLabelChange()
00786 {
00787   QMap<QString, KMAcctCachedImap::RenamedFolder>::ConstIterator renit = mAccount->renamedFolders().find( mFolder->imapPath() );
00788   Q_ASSERT( renit != mAccount->renamedFolders().end() );
00789   if ( renit != mAccount->renamedFolders().end() ) {
00790     mFolder->folder()->setLabel( (*renit).mOldLabel );
00791     mAccount->removeRenamedFolder( mFolder->imapPath() );
00792     kmkernel->dimapFolderMgr()->contentsChanged();
00793   }
00794 }
00795 
00796 void CachedImapJob::renameOnDisk()
00797 {
00798   QString oldName = mFolder->name();
00799   QString oldPath = mFolder->imapPath();
00800   mAccount->removeRenamedFolder( oldPath );
00801   mFolder->setImapPath( mNewImapPath );
00802   mFolder->FolderStorage::rename( mNewName );
00803 
00804   if( oldPath.endsWith( "/" ) ) oldPath.truncate( oldPath.length() -1 );
00805   QString newPath = mFolder->imapPath();
00806   if( newPath.endsWith( "/" ) ) newPath.truncate( newPath.length() -1 );
00807   renameChildFolders( mFolder->folder()->child(), oldPath, newPath );
00808   kmkernel->dimapFolderMgr()->contentsChanged();
00809 }
00810 
00811 void CachedImapJob::slotSubscribtionChange1Failed( const QString &errorMessage )
00812 {
00813   KMessageBox::sorry( 0, i18n( "Error while trying to subscribe to the renamed folder %1.\n"
00814                                "Renaming itself was successful, but the renamed folder might disappear "
00815                                "from the folder list after the next sync since it is unsubscribed on the server.\n"
00816                                "You can try to manually subscribe to the folder yourself.\n\n"
00817                                "%2" )
00818       .arg( mFolder->label() ).arg( errorMessage ) );
00819   delete this;
00820 }
00821 
00822 void CachedImapJob::slotSubscribtionChange2Failed( const QString &errorMessage )
00823 {
00824   kdWarning(5006) << k_funcinfo << errorMessage << endl;
00825   // Ignore this error, not something user-visible anyway
00826   delete this;
00827 }
00828 
00829 void CachedImapJob::slotSubscribtionChange1Done( const QString&, bool )
00830 {
00831   disconnect( mAccount, SIGNAL( subscriptionChanged( const QString&, bool ) ),
00832               this, SLOT( slotSubscribtionChange1Done( const QString&, bool ) ) );
00833   connect( mAccount, SIGNAL( subscriptionChanged( const QString&, bool ) ),
00834            this, SLOT( slotSubscribtionChange2Done( const QString&, bool ) ) );
00835   disconnect( mAccount, SIGNAL( subscriptionChangeFailed( const QString& ) ),
00836               this, SLOT( slotSubscribtionChange1Failed( const QString& ) ) );
00837   connect( mAccount, SIGNAL( subscriptionChangeFailed( const QString& ) ),
00838            this, SLOT( slotSubscribtionChange2Failed( const QString& ) ) );
00839 
00840   mAccount->changeSubscription( false, mOldImapPath, true /* quiet */ );
00841 }
00842 
00843 void CachedImapJob::slotSubscribtionChange2Done( const QString&, bool )
00844 {
00845   // Finally done with everything!
00846   delete this;
00847 }
00848 
00849 void CachedImapJob::slotRenameFolderResult( KIO::Job *job )
00850 {
00851   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00852   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00853     delete this;
00854     return;
00855   }
00856 
00857   if( job->error() ) {
00858     revertLabelChange();
00859     const QString errorMessage = i18n( "Error while trying to rename folder %1" ).arg( mFolder->label() );
00860     mAccount->handleJobError( job, errorMessage );
00861     delete this;
00862   } else {
00863 
00864     mAccount->removeJob( it );
00865     renameOnDisk();
00866 
00867     // Okay, the folder seems to be renamed on the server and on disk.
00868     // Now unsubscribe from the old folder name and subscribe to the new folder name,
00869     // so that the folder doesn't suddenly disappear after renaming it
00870     connect( mAccount, SIGNAL( subscriptionChangeFailed( const QString& ) ),
00871              this, SLOT( slotSubscribtionChange1Failed( const QString& ) ) );
00872     connect( mAccount, SIGNAL( subscriptionChanged( const QString&, bool ) ),
00873              this, SLOT( slotSubscribtionChange1Done( const QString&, bool ) ) );
00874     mAccount->changeSubscription( true, mNewImapPath, true /* quiet */ );
00875   }
00876 }
00877 
00878 void CachedImapJob::slotListMessagesResult( KIO::Job * job )
00879 {
00880   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00881   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00882     delete this;
00883     return;
00884   }
00885 
00886   if (job->error()) {
00887     mErrorCode = job->error();
00888     mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
00889   }
00890   else
00891     mAccount->removeJob(it);
00892 
00893   delete this;
00894 }
00895 
00896 //-----------------------------------------------------------------------------
00897 void CachedImapJob::setParentFolder( const KMFolderCachedImap* parent )
00898 {
00899   mParentFolder = const_cast<KMFolderCachedImap*>( parent );
00900 }
00901 
00902 }
00903 
00904 #include "cachedimapjob.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys