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 TQValueList<MsgForDownload>& msgs,
00061                               JobType type, KMFolderCachedImap* folder )
00062   : FolderJob( type ), mFolder( folder ), mMsgsForDownload( msgs ),
00063     mTotalBytes(0), mMsg(0), mParentFolder( 0 )
00064 {
00065   TQValueList<MsgForDownload>::ConstIterator it = msgs.begin();
00066   for ( ; it != msgs.end() ; ++it )
00067     mTotalBytes += (*it).size;
00068 }
00069 
00070 // Put messages
00071 CachedImapJob::CachedImapJob( const TQPtrList<KMMessage>& msgs, JobType type,
00072                               KMFolderCachedImap* folder )
00073   : FolderJob( msgs, TQString::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 TQValueList<unsigned long>& msgs,
00080                   JobType type, KMFolderCachedImap* folder )
00081   : FolderJob( TQPtrList<KMMessage>(), TQString::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 TQValueList<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 TQString& 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 TQStringList& 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       TQByteArray packedArgs;
00159       TQDataStream stream( packedArgs, IO_WriteOnly );
00160 
00161       const TQString command = TQString( "X-SCALIX-ID " );
00162       const TQString argument = TQString( "(\"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, TQT_SIGNAL( result(KIO::Job *) ),
00204            this, TQT_SLOT( slotListMessagesResult( KIO::Job* ) ) );
00205   // send the data directly for KMFolderCachedImap
00206   connect( job, TQT_SIGNAL( data( KIO::Job*, const TQByteArray& ) ),
00207            mFolder, TQT_SLOT( slotGetMessagesData( KIO::Job* , const TQByteArray& ) ) );
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   TQString uids = mFoldersOrMessages.front(); mFoldersOrMessages.pop_front();
00234 
00235   KURL url = mAccount->getUrl();
00236   url.setPath( mFolder->imapPath() +
00237                TQString::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, TQT_SIGNAL( result(KIO::Job *) ),
00244            this, TQT_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() + TQString::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, TQT_SIGNAL( result(KIO::Job *) ),
00258            this, TQT_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 = 0;
00311       mFolder->addMsgInternal( mMsg, true, &index );
00312 
00313       if ( kmkernel->iCalIface().isResourceFolder( mFolder->folder() ) ) {
00314         mFolder->setStatus( index, KMMsgStatusRead, false );
00315       }
00316 
00317       emit messageRetrieved( mMsg );
00318       if ( index > 0 ) mFolder->unGetMsg( index );
00319     } else {
00320       emit messageRetrieved( 0 );
00321     }
00322     mMsg = 0;
00323 
00324     mSentBytes += size;
00325     emit progress( mSentBytes, mTotalBytes );
00326     mAccount->removeJob(it);
00327   } else
00328     mFolder->quiet( true );
00329 
00330   if( mMsgsForDownload.isEmpty() ) {
00331     mFolder->quiet( false );
00332     delete this;
00333     return;
00334   }
00335 
00336   MsgForDownload mfd = mMsgsForDownload.front(); mMsgsForDownload.pop_front();
00337 
00338   mMsg = new KMMessage;
00339   mMsg->setUID(mfd.uid);
00340   mMsg->setMsgSizeServer(mfd.size);
00341   if( mfd.flags > 0 )
00342     KMFolderImap::flagsToStatus(mMsg, mfd.flags, true, GlobalSettings::allowLocalFlags() ? mFolder->permanentFlags() : INT_MAX);
00343   KURL url = mAccount->getUrl();
00344   url.setPath(mFolder->imapPath() + TQString(";UID=%1;SECTION=BODY.PEEK[]").arg(mfd.uid));
00345 
00346   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00347   jd.cancellable = true;
00348   mMsg->setTransferInProgress(true);
00349   KIO::SimpleJob *simpleJob = KIO::get(url, false, false);
00350   KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00351   mAccount->insertJob(simpleJob, jd);
00352   connect(simpleJob, TQT_SIGNAL(processedSize(KIO::Job *, KIO::filesize_t)),
00353           this, TQT_SLOT(slotProcessedSize(KIO::Job *, KIO::filesize_t)));
00354   connect(simpleJob, TQT_SIGNAL(result(KIO::Job *)),
00355           this, TQT_SLOT(slotGetNextMessage(KIO::Job *)));
00356   connect(simpleJob, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)),
00357           mFolder, TQT_SLOT(slotSimpleData(KIO::Job *, const TQByteArray &)));
00358 }
00359 
00360 void CachedImapJob::slotProcessedSize(KIO::Job *, KIO::filesize_t processed)
00361 {
00362   emit progress( mSentBytes + processed, mTotalBytes );
00363 }
00364 
00365 void CachedImapJob::slotPutNextMessage()
00366 {
00367   mMsg = 0;
00368 
00369   // First try the message list
00370   if( !mMsgList.isEmpty() ) {
00371     mMsg = mMsgList.first();
00372     mMsgList.removeFirst();
00373   }
00374 
00375   // Now try the serial number list
00376   while( mMsg == 0 && !mSerNumMsgList.isEmpty() ) {
00377     unsigned long serNum = mSerNumMsgList.first();
00378     mSerNumMsgList.pop_front();
00379 
00380     // Find the message with this serial number
00381     int i = 0;
00382     KMFolder* aFolder = 0;
00383     KMMsgDict::instance()->getLocation( serNum, &aFolder, &i );
00384     if( mFolder->folder() != aFolder )
00385       // This message was moved or something
00386       continue;
00387     mMsg = mFolder->getMsg( i );
00388   }
00389 
00390   if( !mMsg ) {
00391     // No message found for upload
00392     delete this;
00393     return;
00394   }
00395 
00396   KURL url = mAccount->getUrl();
00397   TQString flags = KMFolderImap::statusToFlags( mMsg->status(), mFolder->permanentFlags() );
00398   url.setPath( mFolder->imapPath() + ";SECTION=" + flags );
00399 
00400   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00401 
00402   mMsg->setUID( 0 ); // for the index
00403   TQCString cstr(mMsg->asString());
00404   int a = cstr.find("\nX-UID: ");
00405   int b = cstr.find('\n', a);
00406   if (a != -1 && b != -1 && cstr.find("\n\n") > a) cstr.remove(a, b-a);
00407   TQCString mData(cstr.length() + cstr.contains('\n'));
00408   unsigned int i = 0;
00409   for( char *ch = cstr.data(); *ch; ch++ ) {
00410     if ( *ch == '\n' ) {
00411       mData.at(i) = '\r';
00412       i++;
00413     }
00414     mData.at(i) = *ch; i++;
00415   }
00416   jd.data = mData;
00417   jd.msgList.append( mMsg );
00418 
00419   mMsg->setTransferInProgress(true);
00420   KIO::SimpleJob *simpleJob = KIO::put(url, 0, false, false, false);
00421   KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00422   mAccount->insertJob(simpleJob, jd);
00423   connect( simpleJob, TQT_SIGNAL( result(KIO::Job *) ),
00424            TQT_SLOT( slotPutMessageResult(KIO::Job *) ) );
00425   connect( simpleJob, TQT_SIGNAL( dataReq(KIO::Job *, TQByteArray &) ),
00426            TQT_SLOT( slotPutMessageDataReq(KIO::Job *, TQByteArray &) ) );
00427   connect( simpleJob, TQT_SIGNAL( data(KIO::Job *, const TQByteArray &) ),
00428            mFolder, TQT_SLOT( slotSimpleData(KIO::Job *, const TQByteArray &) ) );
00429   connect( simpleJob, TQT_SIGNAL(infoMessage(KIO::Job *, const TQString &)),
00430              TQT_SLOT(slotPutMessageInfoData(KIO::Job *, const TQString &)) );
00431 
00432 }
00433 
00434 //-----------------------------------------------------------------------------
00435 // TODO: port to KIO::StoredTransferJob once it's ok to require kdelibs-3.3
00436 void CachedImapJob::slotPutMessageDataReq(KIO::Job *job, TQByteArray &data)
00437 {
00438   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00439   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00440     delete this;
00441     return;
00442   }
00443   if ((*it).data.size() - (*it).offset > 0x8000) {
00444     data.duplicate((*it).data.data() + (*it).offset, 0x8000);
00445     (*it).offset += 0x8000;
00446   } else if ((*it).data.size() - (*it).offset > 0) {
00447     data.duplicate((*it).data.data() + (*it).offset,
00448                    (*it).data.size() - (*it).offset);
00449     (*it).offset = (*it).data.size();
00450   } else
00451     data.resize(0);
00452 }
00453 
00454 //----------------------------------------------------------------------------
00455 void CachedImapJob::slotPutMessageInfoData( KIO::Job *job, const TQString &data )
00456 {
00457   KMFolderCachedImap *imapFolder = static_cast<KMFolderCachedImap*>( mDestFolder->storage() );
00458   if ( imapFolder ) {
00459     KMAcctCachedImap *account = imapFolder->account();
00460     ImapAccountBase::JobIterator it = account->findJob( job );
00461     if ( it == account->jobsEnd() ) {
00462       return;
00463     }
00464 
00465     if ( data.find( "UID" ) != -1 && mMsg ) {
00466       int uid = ( data.right( data.length() - 4 ) ).toInt();
00467       kdDebug( 5006 ) << k_funcinfo << "Server told us uid is: " << uid << endl;
00468       mMsg->setUID( uid );
00469     }
00470   }
00471 }
00472 
00473 
00474 //-----------------------------------------------------------------------------
00475 void CachedImapJob::slotPutMessageResult(KIO::Job *job)
00476 {
00477   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00478   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00479     delete this;
00480     return;
00481   }
00482 
00483   if ( job->error() ) {
00484     bool cont = mAccount->handlePutError( job, *it, mFolder->folder() );
00485     if ( !cont ) {
00486       delete this;
00487     } else {
00488       mMsg = 0;
00489       slotPutNextMessage();
00490     }
00491     return;
00492   }
00493 
00494   emit messageStored( mMsg );
00495 
00496   // we abuse those fields, the unit is the number of messages, here
00497   ++mSentBytes;
00498   emit progress( mSentBytes, mTotalBytes );
00499 
00500   int i;
00501   if( ( i = mFolder->find(mMsg) ) != -1 ) {
00502      /*
00503       * If we have aquired a uid during upload the server supports the uidnext
00504       * extension and there is no need to redownload this mail, we already have
00505       * it. Otherwise remove it, it will be redownloaded.
00506       */
00507      if ( mMsg->UID() == 0 ) {
00508         mFolder->removeMsg(i);
00509      } else {
00510         // When removing+readding, no point in telling the imap resources about it
00511         bool b = kmkernel->iCalIface().isResourceQuiet();
00512         kmkernel->iCalIface().setResourceQuiet( true );
00513 
00514         mFolder->takeTemporarily( i );
00515         mFolder->addMsgKeepUID( mMsg );
00516         mMsg->setTransferInProgress( false );
00517 
00518         kmkernel->iCalIface().setResourceQuiet( b );
00519      }
00520   }
00521   mMsg = NULL;
00522   mAccount->removeJob( it );
00523   slotPutNextMessage();
00524 }
00525 
00526 
00527 void CachedImapJob::slotAddNextSubfolder( KIO::Job * job )
00528 {
00529   if (job) {
00530     KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00531     if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00532       delete this;
00533       return;
00534     }
00535 
00536     // make copy of setting, to reset it before potentially destroying 'it'
00537     bool silentUpload = static_cast<KMFolderCachedImap*>((*it).parent->storage())->silentUpload();
00538     static_cast<KMFolderCachedImap*>((*it).parent->storage())->setSilentUpload( false );
00539 
00540     if ( job->error() && !silentUpload ) {
00541       TQString myError = "<p><b>" + i18n("Error while uploading folder")
00542         + "</b></p><p>" + i18n("Could not make the folder <b>%1</b> on the server.").arg((*it).items[0])
00543         + "</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>";
00544       mAccount->handleJobError( job, myError );
00545     }
00546 
00547     if( job->error() ) {
00548       delete this;
00549       return;
00550     } else {
00551       KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>( (*it).current->storage() );
00552       KMFolderCachedImap* parentStorage = static_cast<KMFolderCachedImap*>( (*it).parent->storage() );
00553       Q_ASSERT( storage );
00554       Q_ASSERT( parentStorage );
00555       if ( storage->imapPath().isEmpty() ) {
00556         TQString path = mAccount->createImapPath( parentStorage->imapPath(), storage->folder()->name() );
00557         if ( !storage->imapPathForCreation().isEmpty() )
00558           path = storage->imapPathForCreation();
00559         storage->setImapPath( path );
00560         storage->writeConfig();
00561       }
00562     }
00563     mAccount->removeJob( it );
00564   }
00565 
00566   if (mFolderList.isEmpty()) {
00567     // No more folders to add
00568     delete this;
00569     return;
00570   }
00571 
00572   KMFolderCachedImap *folder = mFolderList.front();
00573   mFolderList.pop_front();
00574   KURL url = mAccount->getUrl();
00575   TQString path = mAccount->createImapPath( mFolder->imapPath(),
00576       folder->folder()->name() );
00577   if ( !folder->imapPathForCreation().isEmpty() ) {
00578     // the folder knows it's namespace
00579     path = folder->imapPathForCreation();
00580   }
00581   url.setPath( path );
00582 
00583   if ( mAccount->groupwareType() != KMAcctCachedImap::GroupwareScalix ) {
00584     // Associate the jobData with the parent folder, not with the child
00585     // This is necessary in case of an error while creating the subfolder,
00586     // so that folderComplete is called on the parent (and the sync resetted).
00587     ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00588     jd.items << folder->label(); // for the err msg
00589     jd.current = folder->folder();
00590     KIO::SimpleJob *simpleJob = KIO::mkdir(url);
00591     KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00592     mAccount->insertJob(simpleJob, jd);
00593     connect( simpleJob, TQT_SIGNAL(result(KIO::Job *)),
00594              this, TQT_SLOT(slotAddNextSubfolder(KIO::Job *)) );
00595   } else {
00596     TQByteArray packedArgs;
00597     TQDataStream stream( packedArgs, IO_WriteOnly );
00598 
00599     const TQString command = TQString( "X-CREATE-SPECIAL" );
00600     const TQString argument = TQString( "%1 %2" ).arg( Scalix::Utils::contentsTypeToScalixId( folder->contentsType() ) )
00601                                                .arg( path );
00602 
00603     stream << (int) 'X' << 'N' << command << argument;
00604 
00605     ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00606     jd.items << folder->label(); // for the err msg
00607     jd.current = folder->folder();
00608     KIO::SimpleJob *simpleJob = KIO::special( url.url(), packedArgs, false );
00609     KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00610     mAccount->insertJob(simpleJob, jd);
00611     connect( simpleJob, TQT_SIGNAL(result(KIO::Job *)),
00612              this, TQT_SLOT(slotAddNextSubfolder(KIO::Job *)) );
00613   }
00614 }
00615 
00616 
00617 void CachedImapJob::slotDeleteNextFolder( KIO::Job *job )
00618 {
00619   if (job) {
00620     KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00621     if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00622       delete this;
00623       return;
00624     }
00625 
00626     mAccount->removeDeletedFolder( (*it).path );
00627 
00628     if( job->error() ) {
00629       mAccount->handleJobError( job, i18n( "Error while deleting folder %1 on the server: " ).arg( (*it).path ) + '\n' );
00630       delete this;
00631       return;
00632     }
00633     mAccount->removeJob(it);
00634   }
00635 
00636   if( mFoldersOrMessages.isEmpty() ) {
00637     // No more folders to delete
00638     delete this;
00639     return;
00640   }
00641 
00642   TQString folderPath = mFoldersOrMessages.front();
00643   mFoldersOrMessages.pop_front();
00644   KURL url = mAccount->getUrl();
00645   url.setPath(folderPath);
00646   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00647   jd.path = url.path();
00648   KIO::SimpleJob *simpleJob = KIO::file_delete(url, false);
00649   KIO::Scheduler::assignJobToSlave(mAccount->slave(), simpleJob);
00650   mAccount->insertJob(simpleJob, jd);
00651   connect( simpleJob, TQT_SIGNAL( result(KIO::Job *) ),
00652            TQT_SLOT( slotDeleteNextFolder(KIO::Job *) ) );
00653 }
00654 
00655 void CachedImapJob::checkUidValidity()
00656 {
00657   KURL url = mAccount->getUrl();
00658   url.setPath( mFolder->imapPath() + ";UID=0:0" );
00659 
00660   ImapAccountBase::jobData jd( url.url(), mFolder->folder() );
00661   jd.cancellable = true;
00662 
00663   KIO::SimpleJob *job = KIO::get( url, false, false );
00664   KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
00665   mAccount->insertJob( job, jd );
00666   connect( job, TQT_SIGNAL(result(KIO::Job *)),
00667            TQT_SLOT(slotCheckUidValidityResult(KIO::Job *)) );
00668   connect( job, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)),
00669            mFolder, TQT_SLOT(slotSimpleData(KIO::Job *, const TQByteArray &)));
00670 }
00671 
00672 void CachedImapJob::slotCheckUidValidityResult(KIO::Job * job)
00673 {
00674   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00675   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00676     delete this;
00677     return;
00678   }
00679 
00680   if( job->error() ) {
00681     mErrorCode = job->error();
00682     mAccount->handleJobError( job, i18n( "Error while reading folder %1 on the server: " ).arg( (*it).parent->label() ) + '\n' );
00683     delete this;
00684     return;
00685   }
00686 
00687   // Check the uidValidity
00688   TQCString cstr((*it).data.data(), (*it).data.size() + 1);
00689   int a = cstr.find("X-uidValidity: ");
00690   if (a < 0) {
00691     // Something is seriously rotten here!
00692     // TODO: Tell the user that he has a problem
00693     kdDebug(5006) << "No uidvalidity available for folder "
00694                   << mFolder->name() << endl;
00695   }
00696   else {
00697     int b = cstr.find("\r\n", a);
00698     if ( (b - a - 15) >= 0 ) {
00699       TQString uidv = cstr.mid(a + 15, b - a - 15);
00700       // kdDebug(5006) << "New uidv = " << uidv << ", old uidv = "
00701       //               << mFolder->uidValidity() << endl;
00702       if( !mFolder->uidValidity().isEmpty() && mFolder->uidValidity() != uidv ) {
00703         // kdDebug(5006) << "Expunging the mailbox " << mFolder->name()
00704         //               << "!" << endl;
00705         mFolder->expunge();
00706         mFolder->setLastUid( 0 );
00707         mFolder->clearUidMap();
00708       }
00709     } else
00710       kdDebug(5006) << "No uidvalidity available for folder "
00711                     << mFolder->name() << endl;
00712   }
00713 
00714   a = cstr.find( "X-PermanentFlags: " );
00715   if ( a < 0 ) {
00716     kdDebug(5006) << "no PERMANENTFLAGS response? assumming custom flags are not available" << endl;
00717   } else {
00718     int b = cstr.find( "\r\n", a );
00719     if ( (b - a - 18) >= 0 ) {
00720       int flags = cstr.mid( a + 18, b - a - 18 ).toInt();
00721       emit permanentFlags( flags );
00722     } else {
00723       kdDebug(5006) << "PERMANENTFLAGS response broken, assumming custom flags are not available" << endl;
00724     }
00725   }
00726 
00727   mAccount->removeJob(it);
00728   delete this;
00729 }
00730 
00731 
00732 void CachedImapJob::renameFolder( const TQString &newName )
00733 {
00734   mNewName = newName;
00735 
00736   // Set the source URL
00737   KURL urlSrc = mAccount->getUrl();
00738   mOldImapPath = mFolder->imapPath();
00739   urlSrc.setPath( mOldImapPath );
00740 
00741   // Set the destination URL - this is a bit trickier
00742   KURL urlDst = mAccount->getUrl();
00743   mNewImapPath = mFolder->imapPath();
00744   // Destination url = old imappath - oldname + new name
00745   mNewImapPath.truncate( mNewImapPath.length() - mFolder->folder()->name().length() - 1);
00746   mNewImapPath += newName + '/';
00747   urlDst.setPath( mNewImapPath );
00748 
00749   ImapAccountBase::jobData jd( newName, mFolder->folder() );
00750   jd.path = mNewImapPath;
00751 
00752   KIO::SimpleJob *simpleJob = KIO::rename( urlSrc, urlDst, false );
00753   KIO::Scheduler::assignJobToSlave( mAccount->slave(), simpleJob );
00754   mAccount->insertJob( simpleJob, jd );
00755   connect( simpleJob, TQT_SIGNAL(result(KIO::Job *)),
00756            TQT_SLOT(slotRenameFolderResult(KIO::Job *)) );
00757 }
00758 
00759 static void renameChildFolders( KMFolderDir* dir, const TQString& oldPath,
00760                                 const TQString& newPath )
00761 {
00762   if( dir ) {
00763     KMFolderNode *node = dir->first();
00764     while( node ) {
00765       if( !node->isDir() ) {
00766         KMFolderCachedImap* imapFolder =
00767           static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
00768         if ( !imapFolder->imapPath().isEmpty() )
00769           // Only rename folders that have been accepted by the server
00770           if( imapFolder->imapPath().find( oldPath ) == 0 ) {
00771             TQString p = imapFolder->imapPath();
00772             p = p.mid( oldPath.length() );
00773             p.prepend( newPath );
00774             imapFolder->setImapPath( p );
00775             renameChildFolders( imapFolder->folder()->child(), oldPath, newPath );
00776           }
00777       }
00778       node = dir->next();
00779     }
00780   }
00781 }
00782 
00783 void CachedImapJob::revertLabelChange()
00784 {
00785   TQMap<TQString, KMAcctCachedImap::RenamedFolder>::ConstIterator renit = mAccount->renamedFolders().find( mFolder->imapPath() );
00786   Q_ASSERT( renit != mAccount->renamedFolders().end() );
00787   if ( renit != mAccount->renamedFolders().end() ) {
00788     mFolder->folder()->setLabel( (*renit).mOldLabel );
00789     mAccount->removeRenamedFolder( mFolder->imapPath() );
00790     kmkernel->dimapFolderMgr()->contentsChanged();
00791   }
00792 }
00793 
00794 void CachedImapJob::renameOnDisk()
00795 {
00796   TQString oldName = mFolder->name();
00797   TQString oldPath = mFolder->imapPath();
00798   mAccount->removeRenamedFolder( oldPath );
00799   mFolder->setImapPath( mNewImapPath );
00800   mFolder->FolderStorage::rename( mNewName );
00801 
00802   if( oldPath.endsWith( "/" ) ) oldPath.truncate( oldPath.length() -1 );
00803   TQString newPath = mFolder->imapPath();
00804   if( newPath.endsWith( "/" ) ) newPath.truncate( newPath.length() -1 );
00805   renameChildFolders( mFolder->folder()->child(), oldPath, newPath );
00806   kmkernel->dimapFolderMgr()->contentsChanged();
00807 }
00808 
00809 void CachedImapJob::slotSubscribtionChange1Failed( const TQString &errorMessage )
00810 {
00811   KMessageBox::sorry( 0, i18n( "Error while trying to subscribe to the renamed folder %1.\n"
00812                                "Renaming itself was successful, but the renamed folder might disappear "
00813                                "from the folder list after the next sync since it is unsubscribed on the server.\n"
00814                                "You can try to manually subscribe to the folder yourself.\n\n"
00815                                "%2" )
00816       .arg( mFolder->label() ).arg( errorMessage ) );
00817   delete this;
00818 }
00819 
00820 void CachedImapJob::slotSubscribtionChange2Failed( const TQString &errorMessage )
00821 {
00822   kdWarning(5006) << k_funcinfo << errorMessage << endl;
00823   // Ignore this error, not something user-visible anyway
00824   delete this;
00825 }
00826 
00827 void CachedImapJob::slotSubscribtionChange1Done( const TQString&, bool )
00828 {
00829   disconnect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
00830               this, TQT_SLOT( slotSubscribtionChange1Done( const TQString&, bool ) ) );
00831   connect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
00832            this, TQT_SLOT( slotSubscribtionChange2Done( const TQString&, bool ) ) );
00833   disconnect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
00834               this, TQT_SLOT( slotSubscribtionChange1Failed( const TQString& ) ) );
00835   connect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
00836            this, TQT_SLOT( slotSubscribtionChange2Failed( const TQString& ) ) );
00837 
00838   mAccount->changeSubscription( false, mOldImapPath, true /* quiet */ );
00839 }
00840 
00841 void CachedImapJob::slotSubscribtionChange2Done( const TQString&, bool )
00842 {
00843   // Finally done with everything!
00844   delete this;
00845 }
00846 
00847 void CachedImapJob::slotRenameFolderResult( KIO::Job *job )
00848 {
00849   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00850   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00851     delete this;
00852     return;
00853   }
00854 
00855   if( job->error() ) {
00856     revertLabelChange();
00857     const TQString errorMessage = i18n( "Error while trying to rename folder %1" ).arg( mFolder->label() );
00858     mAccount->handleJobError( job, errorMessage );
00859     delete this;
00860   } else {
00861 
00862     mAccount->removeJob( it );
00863     renameOnDisk();
00864 
00865     // Okay, the folder seems to be renamed on the server and on disk.
00866     // Now unsubscribe from the old folder name and subscribe to the new folder name,
00867     // so that the folder doesn't suddenly disappear after renaming it
00868     connect( mAccount, TQT_SIGNAL( subscriptionChangeFailed( const TQString& ) ),
00869              this, TQT_SLOT( slotSubscribtionChange1Failed( const TQString& ) ) );
00870     connect( mAccount, TQT_SIGNAL( subscriptionChanged( const TQString&, bool ) ),
00871              this, TQT_SLOT( slotSubscribtionChange1Done( const TQString&, bool ) ) );
00872     mAccount->changeSubscription( true, mNewImapPath, true /* quiet */ );
00873   }
00874 }
00875 
00876 void CachedImapJob::slotListMessagesResult( KIO::Job * job )
00877 {
00878   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00879   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00880     delete this;
00881     return;
00882   }
00883 
00884   if (job->error()) {
00885     mErrorCode = job->error();
00886     mAccount->handleJobError( job, i18n( "Error while deleting messages on the server: " ) + '\n' );
00887   }
00888   else
00889     mAccount->removeJob(it);
00890 
00891   delete this;
00892 }
00893 
00894 //-----------------------------------------------------------------------------
00895 void CachedImapJob::setParentFolder( const KMFolderCachedImap* parent )
00896 {
00897   mParentFolder = const_cast<KMFolderCachedImap*>( parent );
00898 }
00899 
00900 }
00901 
00902 #include "cachedimapjob.moc"