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