kmail Library API Documentation

imapjob.cpp

00001 /*  -*- mode: C++; c-file-style: "gnu" -*-
00002  *
00003  *  This file is part of KMail, the KDE mail client.
00004  *  Copyright (c) 2002-2003 Zack Rusin <zack@kde.org>
00005  *                2000-2002 Michael Haeckel <haeckel@kde.org>
00006  *
00007  *  KMail is free software; you can redistribute it and/or modify it
00008  *  under the terms of the GNU General Public License, version 2, as
00009  *  published by the Free Software Foundation.
00010  *
00011  *  KMail is distributed in the hope that it will be useful, but
00012  *  WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  In addition, as a special exception, the copyright holders give
00021  *  permission to link the code of this program with any edition of
00022  *  the Qt library by Trolltech AS, Norway (or with modified versions
00023  *  of Qt that use the same license as Qt), and distribute linked
00024  *  combinations including the two.  You must obey the GNU General
00025  *  Public License in all respects for all of the code used other than
00026  *  Qt.  If you modify this file, you may extend this exception to
00027  *  your version of the file, but you are not obligated to do so.  If
00028  *  you do not wish to do so, delete this exception statement from
00029  *  your version.
00030  */
00031 
00032 #ifdef HAVE_CONFIG_H
00033 #include <config.h>
00034 #endif
00035 
00036 #include "imapjob.h"
00037 #include "kmfolderimap.h"
00038 #include "kmmsgpart.h"
00039 
00040 #include <kio/scheduler.h>
00041 #include <kdebug.h>
00042 #include <mimelib/body.h>
00043 #include <mimelib/bodypart.h>
00044 #include <mimelib/string.h>
00045 
00046 
00047 namespace KMail {
00048 
00049 //-----------------------------------------------------------------------------
00050 ImapJob::ImapJob( KMMessage *msg, JobType jt, KMFolderImap* folder,
00051     QString partSpecifier, const AttachmentStrategy *as )
00052   : FolderJob( msg, jt, folder, partSpecifier ),
00053     mAttachmentStrategy( as )
00054 {
00055 }
00056 
00057 //-----------------------------------------------------------------------------
00058 ImapJob::ImapJob( QPtrList<KMMessage>& msgList, QString sets, JobType jt,
00059                   KMFolderImap* folder )
00060   : FolderJob( msgList, sets, jt, folder ),
00061     mAttachmentStrategy ( 0 )
00062 {
00063 }
00064 
00065 void ImapJob::init( JobType jt, QString sets, KMFolderImap* folder,
00066                     QPtrList<KMMessage>& msgList )
00067 {
00068   mJob = 0;
00069   assert(jt == tGetMessage || folder);
00070   KMMessage* msg = msgList.first();
00071   mType = jt;
00072   mDestFolder = folder;
00073   // refcount++
00074   if (folder) {
00075     folder->open();
00076   }
00077   KMFolder *msg_parent = msg->parent();
00078   if (msg_parent) {
00079     if (!folder || folder!= msg_parent) {
00080       msg_parent->open();
00081     }
00082   }
00083   mSrcFolder = msg_parent;
00084   // If there is a destination folder, this is a copy, move or put to an
00085   // imap folder, use its account for keeping track of the job. Otherwise,
00086   // this is a get job and the src folder is an imap one. Use its account
00087   // then.
00088   KMAcctImap *account;
00089   if (folder) {
00090     account = folder->account();
00091   } else { 
00092     account = static_cast<KMFolderImap*>(msg_parent)->account();
00093   }
00094   if ( !account ||
00095        account->makeConnection() == ImapAccountBase::Error ) {
00096     deleteLater();
00097     return;
00098   }
00099   account->mJobList.append( this );
00100   if ( jt == tPutMessage )
00101   {
00102     // transfers the complete message to the server
00103     KURL url = account->getUrl();
00104     QString flags = KMFolderImap::statusToFlags( msg->status() ); 
00105     url.setPath( folder->imapPath() + ";SECTION=" + flags );
00106     ImapAccountBase::jobData jd;
00107     jd.parent = 0; jd.offset = 0;
00108     jd.total = 1; jd.done = 0;
00109     jd.msgList.append(msg);
00110     QCString cstr( msg->asString() );
00111     int a = cstr.find( "\nX-UID: " );
00112     int b = cstr.find( "\n", a );
00113     if ( a != -1 && b != -1 && cstr.find( "\n\n" ) > a ) cstr.remove( a, b-a );
00114     mData.resize( cstr.length() + cstr.contains( "\n" ) - cstr.contains( "\r\n" ) );
00115     unsigned int i = 0;
00116     char prevChar = '\0';
00117     // according to RFC 2060 we need CRLF
00118     for ( char *ch = cstr.data(); *ch; ch++ )
00119     {
00120       if ( *ch == '\n' && (prevChar != '\r') ) {
00121         mData.at( i ) = '\r';
00122         i++;
00123       }
00124       mData.at( i ) = *ch;
00125       prevChar = *ch;
00126       i++;
00127     }
00128     jd.data = mData;
00129     KIO::SimpleJob *simpleJob = KIO::put( url, 0, FALSE, FALSE, FALSE );
00130     KIO::Scheduler::assignJobToSlave( account->slave(), simpleJob );
00131     mJob = simpleJob;
00132     account->insertJob( mJob, jd );
00133     connect( mJob, SIGNAL(result(KIO::Job *)),
00134              SLOT(slotPutMessageResult(KIO::Job *)) );
00135     connect( mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
00136              SLOT(slotPutMessageDataReq(KIO::Job *, QByteArray &)) );
00137     connect( mJob, SIGNAL(infoMessage(KIO::Job *, const QString &)),
00138              SLOT(slotPutMessageInfoData(KIO::Job *, const QString &)) );
00139   }
00140   else if ( jt == tCopyMessage || jt == tMoveMessage )
00141   {
00142     KURL url = account->getUrl();
00143     KURL destUrl = account->getUrl();
00144     destUrl.setPath(folder->imapPath());
00145     KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(msg_parent);
00146     url.setPath( imapDestFolder->imapPath() + ";UID=" + sets );
00147     ImapAccountBase::jobData jd;
00148     jd.parent = 0; mOffset = 0;
00149     jd.total = 1; jd.done = 0;
00150     jd.msgList = msgList;
00151 
00152     QByteArray packedArgs;
00153     QDataStream stream( packedArgs, IO_WriteOnly );
00154 
00155     stream << (int) 'C' << url << destUrl;
00156 
00157     KIO::SimpleJob *simpleJob = KIO::special( url, packedArgs, FALSE );
00158     KIO::Scheduler::assignJobToSlave( account->slave(), simpleJob );
00159     mJob = simpleJob;
00160     account->insertJob( mJob, jd );
00161     connect( mJob, SIGNAL(result(KIO::Job *)),
00162              SLOT(slotCopyMessageResult(KIO::Job *)) );
00163     if ( jt == tMoveMessage )
00164     {
00165       connect( mJob, SIGNAL(infoMessage(KIO::Job *, const QString &)),
00166                SLOT(slotCopyMessageInfoData(KIO::Job *, const QString &)) );
00167     }
00168   } else {
00169     slotGetNextMessage();
00170   }
00171 }
00172 
00173 
00174 //-----------------------------------------------------------------------------
00175 ImapJob::~ImapJob()
00176 {
00177 
00178   if ( mDestFolder )
00179   {
00180     KMAcctImap *account = static_cast<KMFolderImap*>(mDestFolder)->account();
00181     if ( account ) // just to be sure this job is removed from the list
00182       account->mJobList.remove(this);
00183     if ( account && mJob )
00184     {
00185       ImapAccountBase::JobIterator it = account->findJob( mJob );
00186       if ( it != account->jobsEnd() && !(*it).msgList.isEmpty() )
00187       {
00188         for ( QPtrListIterator<KMMessage> mit( (*it).msgList ); mit.current(); ++mit )
00189           mit.current()->setTransferInProgress(false);
00190       }
00191     }
00192       
00193     mDestFolder->close();
00194   }
00195 
00196   if (mSrcFolder) {
00197     if (!mDestFolder || mDestFolder != mSrcFolder) {
00198       if (! (mSrcFolder->folderType() == KMFolderTypeImap) ) return;
00199       KMAcctImap *account = static_cast<KMFolderImap*>(mSrcFolder)->account();
00200       if ( account ) // just to be sure this job is removed from the list
00201         account->mJobList.remove(this);
00202       if ( account && mJob )
00203       {
00204         ImapAccountBase::JobIterator it = account->findJob( mJob );
00205         if ( it != account->jobsEnd() && !(*it).msgList.isEmpty() )
00206         {
00207           for ( QPtrListIterator<KMMessage> mit( (*it).msgList ); mit.current(); ++mit )
00208             mit.current()->setTransferInProgress(false);
00209         }
00210       }
00211     }
00212       
00213     mSrcFolder->close();
00214   }
00215 }
00216 
00217 
00218 //-----------------------------------------------------------------------------
00219 void ImapJob::slotGetNextMessage()
00220 {
00221   KMMessage *msg = mMsgList.first();
00222   KMFolderImap *msgParent = static_cast<KMFolderImap*>(msg->parent());
00223   KMAcctImap *account = msgParent->account();
00224   if ( msg->headerField("X-UID").isEmpty() )
00225   {
00226     emit messageRetrieved( msg );
00227     account->mJobList.remove( this );
00228     deleteLater();
00229     return;
00230   }
00231   KURL url = account->getUrl();
00232   QString path = msgParent->imapPath() + ";UID=" + msg->headerField("X-UID");
00233   if ( !mPartSpecifier.isEmpty() )
00234   {
00235     if ( mPartSpecifier.find ("STRUCTURE", 0, false) != -1 ) {
00236       path += ";SECTION=STRUCTURE";
00237     } else if ( mPartSpecifier == "HEADER" ) {
00238       path += ";SECTION=HEADER";
00239     } else {
00240       path += ";SECTION=BODY.PEEK[" + mPartSpecifier +"]";
00241     }
00242   } else {
00243       path += ";SECTION=BODY.PEEK[]";
00244   }
00245   url.setPath( path );
00246 //  kdDebug(5006) << "ImapJob::slotGetNextMessage - retrieve " << url.path() << endl;
00247   ImapAccountBase::jobData jd;
00248   jd.parent = 0;
00249   jd.total = 1; jd.done = 0;
00250   // protect the message, otherwise we'll get crashes afterwards
00251   msg->setTransferInProgress( true );
00252   KIO::SimpleJob *simpleJob = KIO::get( url, FALSE, FALSE );
00253   KIO::Scheduler::assignJobToSlave( account->slave(), simpleJob );
00254   mJob = simpleJob;
00255   account->insertJob( mJob, jd );
00256   if ( mPartSpecifier.find( "STRUCTURE", 0, false ) != -1 )
00257   {
00258     connect( mJob, SIGNAL(result(KIO::Job *)),
00259              this, SLOT(slotGetBodyStructureResult(KIO::Job *)) );
00260   } else {
00261     connect( mJob, SIGNAL(result(KIO::Job *)),
00262              this, SLOT(slotGetMessageResult(KIO::Job *)) );
00263   }
00264   connect( mJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
00265            msgParent, SLOT(slotSimpleData(KIO::Job *, const QByteArray &)) );
00266 }
00267 
00268 
00269 //-----------------------------------------------------------------------------
00270 void ImapJob::slotGetMessageResult( KIO::Job * job )
00271 {
00272   KMMessage *msg = mMsgList.first();
00273   if (!msg || !msg->parent() || !job) {
00274     deleteLater();
00275     return;
00276   }
00277   KMFolderImap* parent = static_cast<KMFolderImap*>(msg->parent());
00278   if (msg->transferInProgress())
00279     msg->setTransferInProgress( false );
00280   KMAcctImap *account = parent->account();
00281   if ( !account ) {
00282     deleteLater();
00283     return;
00284   }
00285   ImapAccountBase::JobIterator it = account->findJob( job );
00286   if ( it == account->jobsEnd() ) return;
00287 
00288   bool gotData = true;
00289   if (job->error())
00290   {
00291     account->slotSlaveError( account->slave(), job->error(), job->errorText() );
00292     return;
00293   } else {
00294     if ((*it).data.size() > 0)
00295     {
00296       kdDebug(5006) << "ImapJob::slotGetMessageResult - retrieved part " << mPartSpecifier << endl;
00297       if ( mPartSpecifier.isEmpty() ||
00298            mPartSpecifier == "HEADER" )
00299       {
00300         uint size = msg->headerField("X-Length").toUInt();
00301         QString uid = msg->headerField("X-UID");
00302         msg->fromByteArray( (*it).data );
00303         msg->setHeaderField("X-UID",uid);
00304         // set correct size
00305         if ( size > 0 ) msg->setMsgLength(size);
00306         if ( mPartSpecifier.isEmpty() ) 
00307           msg->setComplete( true );
00308         else
00309           msg->setReadyToShow( false );
00310       } else {
00311         // Update the body of the retrieved part (the message notifies all observers)
00312         msg->updateBodyPart( mPartSpecifier, (*it).data );
00313         msg->setReadyToShow( true );
00314       }
00315     } else {
00316       kdDebug(5006) << "ImapJob::slotGetMessageResult - got no data for " << mPartSpecifier << endl;
00317       gotData = false;
00318       msg->setReadyToShow( true );
00319       // nevertheless give visual feedback
00320       msg->notify();
00321     }
00322   }
00323   if (account->slave()) {
00324       account->removeJob(it);
00325       account->mJobList.remove(this);
00326   }
00327   /* This needs to be emitted last, so the slots that are hooked to it
00328    * don't unGetMsg the msg before we have finished. */
00329   if ( mPartSpecifier.isEmpty() ||
00330        mPartSpecifier == "HEADER" )
00331   {
00332     if ( gotData )
00333       emit messageRetrieved(msg);
00334     else
00335     {
00336       /* we got an answer but not data
00337        * this means that the msg is not on the server anymore so delete it */
00338       parent->ignoreJobsForMessage( msg );
00339       int idx = parent->find( msg );
00340       if (idx != -1) parent->removeMsg( idx, true );
00341       emit messageRetrieved( 0 );
00342     }
00343   } else {
00344     emit messageUpdated(msg, mPartSpecifier);
00345   }
00346   deleteLater();
00347 }
00348 
00349 //-----------------------------------------------------------------------------
00350 void ImapJob::slotGetBodyStructureResult( KIO::Job * job )
00351 {
00352   KMMessage *msg = mMsgList.first();
00353   if (!msg || !msg->parent() || !job) {
00354     deleteLater();
00355     return;
00356   }
00357   KMFolderImap* parent = static_cast<KMFolderImap*>(msg->parent());
00358   if (msg->transferInProgress())
00359     msg->setTransferInProgress( false );
00360   KMAcctImap *account = parent->account();
00361   if ( !account ) {
00362     deleteLater();
00363     return;
00364   }
00365   ImapAccountBase::JobIterator it = account->findJob( job );
00366   if ( it == account->jobsEnd() ) return;
00367   
00368 
00369   if (job->error())
00370   {
00371     account->slotSlaveError( account->slave(), job->error(),
00372         job->errorText() );
00373     return;
00374   } else {
00375     if ((*it).data.size() > 0)
00376     {
00377       QDataStream stream( (*it).data, IO_ReadOnly );
00378       account->handleBodyStructure(stream, msg, mAttachmentStrategy);
00379     }
00380   }
00381   if (account->slave()) {
00382       account->removeJob(it);
00383       account->mJobList.remove(this);
00384   }
00385   deleteLater();
00386 }
00387 
00388 //-----------------------------------------------------------------------------
00389 void ImapJob::slotPutMessageDataReq( KIO::Job *job, QByteArray &data )
00390 {
00391   KMAcctImap *account = static_cast<KMFolderImap*>(mDestFolder)->account();
00392   ImapAccountBase::JobIterator it = account->findJob( job );
00393   if ( it == account->jobsEnd() ) return;
00394 
00395   if ((*it).data.size() - (*it).offset > 0x8000)
00396   {
00397     data.duplicate((*it).data.data() + (*it).offset, 0x8000);
00398     (*it).offset += 0x8000;
00399   }
00400   else if ((*it).data.size() - (*it).offset > 0)
00401   {
00402     data.duplicate((*it).data.data() + (*it).offset, (*it).data.size() - (*it).offset);
00403     (*it).offset = (*it).data.size();
00404   } else data.resize(0);
00405 }
00406 
00407 
00408 //-----------------------------------------------------------------------------
00409 void ImapJob::slotPutMessageResult( KIO::Job *job )
00410 {
00411   KMMessage *msg = mMsgList.first();
00412   KMAcctImap *account = static_cast<KMFolderImap*>(mDestFolder)->account();
00413   ImapAccountBase::JobIterator it = account->findJob( job );
00414   if ( it == account->jobsEnd() ) return;
00415 
00416   if (job->error())
00417   {
00418     account->slotSlaveError( account->slave(), job->error(),
00419         job->errorText() );
00420     return;
00421   } else {
00422     if ( !(*it).msgList.isEmpty() )
00423     {
00424       emit messageStored((*it).msgList.last());
00425       (*it).msgList.removeLast();
00426     } else if (msg)
00427     {
00428       emit messageStored(msg);
00429     }
00430     msg = 0;
00431   }
00432   if (account->slave()) {
00433     account->removeJob(it);
00434     account->mJobList.remove(this);
00435   }
00436   deleteLater();
00437 }
00438 
00439 //-----------------------------------------------------------------------------
00440 void ImapJob::slotCopyMessageInfoData(KIO::Job * job, const QString & data)
00441 {
00442   KMFolderImap * imapFolder = static_cast<KMFolderImap*>(mDestFolder);
00443   KMAcctImap *account = imapFolder->account();
00444   ImapAccountBase::JobIterator it = account->findJob( job );
00445   if ( it == account->jobsEnd() ) return;
00446 
00447   if (data.find("UID") != -1)
00448   {
00449     // split
00450     QString oldUid = data.section(' ', 1, 1);
00451     QString newUid = data.section(' ', 2, 2);
00452 
00453     // get lists of uids
00454     QValueList<int> olduids = KMFolderImap::splitSets(oldUid);
00455     QValueList<int> newuids = KMFolderImap::splitSets(newUid);
00456 
00457     int index = -1;
00458     if ( !(*it).msgList.isEmpty() )
00459     {
00460       KMMessage * msg;
00461       for ( msg = (*it).msgList.first(); msg; msg = (*it).msgList.next() )
00462       {
00463         uint uid = msg->headerField("X-UID").toInt();
00464         index = olduids.findIndex(uid);
00465         if (index > -1)
00466         {
00467           // found, get the new uid
00468           const ulong * sernum = (ulong *)msg->getMsgSerNum();
00469           imapFolder->insertUidSerNumEntry(newuids[index], sernum);
00470         }
00471       }
00472     } else if (mMsgList.first()) {
00473       uint uid = mMsgList.first()->headerField("X-UID").toInt();
00474       index = olduids.findIndex(uid);
00475       if (index > -1)
00476       {
00477         // found, get the new uid
00478         const ulong * sernum = (ulong *)mMsgList.first()->getMsgSerNum();
00479         imapFolder->insertUidSerNumEntry(newuids[index], sernum);
00480       }
00481     }
00482   }
00483 }
00484 
00485 //----------------------------------------------------------------------------
00486 void ImapJob::slotPutMessageInfoData(KIO::Job *job, const QString &data)
00487 {
00488   KMFolderImap * imapFolder = static_cast<KMFolderImap*>(mDestFolder);
00489   KMAcctImap *account = imapFolder->account();
00490   ImapAccountBase::JobIterator it = account->findJob( job );
00491   if ( it == account->jobsEnd() ) return;
00492 
00493   if (data.find("UID") != -1)
00494   {
00495     int uid = (data.right(data.length()-4)).toInt();
00496 
00497     if ( !(*it).msgList.isEmpty() )
00498     {
00499       const ulong * sernum = (ulong *)(*it).msgList.last()->getMsgSerNum();
00500 //      kdDebug(5006) << "insert sernum " << (*it).msgList.last()->getMsgSerNum() << " for " << uid << endl;
00501       imapFolder->insertUidSerNumEntry(uid, sernum);
00502     } else if (mMsgList.first())
00503     {
00504       const ulong * sernum = (ulong *)mMsgList.first()->getMsgSerNum();
00505 //      kdDebug(5006) << "insert sernum " << mMsgList.first()->getMsgSerNum() << " for " << uid << endl;
00506       imapFolder->insertUidSerNumEntry(uid, sernum);
00507     }
00508   }
00509 }
00510 
00511 
00512 //-----------------------------------------------------------------------------
00513 void ImapJob::slotCopyMessageResult( KIO::Job *job )
00514 {
00515   KMAcctImap *account = static_cast<KMFolderImap*>(mDestFolder)->account();
00516   ImapAccountBase::JobIterator it = account->findJob( job );
00517   if ( it == account->jobsEnd() ) return;
00518 
00519   if (job->error())
00520   {
00521     account->slotSlaveError( account->slave(), job->error(),
00522         job->errorText() );
00523     return;
00524   } else {
00525     if ( !(*it).msgList.isEmpty() )
00526     {
00527       emit messageCopied((*it).msgList);
00528     } else if (mMsgList.first()) {
00529       emit messageCopied(mMsgList.first());
00530     }
00531   }
00532   if (account->slave()) {
00533     account->removeJob(it);
00534     account->mJobList.remove(this);
00535   }
00536   deleteLater();
00537 }
00538 
00539 //-----------------------------------------------------------------------------
00540 void ImapJob::execute()
00541 {
00542   init( mType, mSets, static_cast<KMFolderImap*>( mDestFolder ), mMsgList );
00543 }
00544 
00545 //-----------------------------------------------------------------------------
00546 void ImapJob::expireMessages()
00547 {
00548     return;
00549 }
00550 
00551 }//namespace KMail
00552 
00553 #include "imapjob.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:19 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003