00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include <config.h>
00034 #endif
00035
00036 #include "imapjob.h"
00037 #include "kmfolderimap.h"
00038 #include "kmfolder.h"
00039 #include "kmmsgpart.h"
00040 #include "progressmanager.h"
00041 using KPIM::ProgressManager;
00042
00043 #include <kio/scheduler.h>
00044 #include <kdebug.h>
00045 #include <klocale.h>
00046 #include <mimelib/body.h>
00047 #include <mimelib/bodypart.h>
00048 #include <mimelib/string.h>
00049
00050
00051 namespace KMail {
00052
00053
00054 ImapJob::ImapJob( KMMessage *msg, JobType jt, KMFolderImap* folder,
00055 QString partSpecifier, const AttachmentStrategy *as )
00056 : FolderJob( msg, jt, folder? folder->folder() : 0, partSpecifier ),
00057 mAttachmentStrategy( as ), mParentProgressItem(0)
00058 {
00059 }
00060
00061
00062 ImapJob::ImapJob( QPtrList<KMMessage>& msgList, QString sets, JobType jt,
00063 KMFolderImap* folder )
00064 : FolderJob( msgList, sets, jt, folder? folder->folder() : 0 ),
00065 mAttachmentStrategy ( 0 ), mParentProgressItem(0)
00066 {
00067 }
00068
00069 void ImapJob::init( JobType jt, QString sets, KMFolderImap* folder,
00070 QPtrList<KMMessage>& msgList )
00071 {
00072 mJob = 0;
00073
00074 assert(jt == tGetMessage || folder);
00075 KMMessage* msg = msgList.first();
00076
00077 if ( !msg ) {
00078 deleteLater();
00079 return;
00080 }
00081 mType = jt;
00082 mDestFolder = folder? folder->folder() : 0;
00083
00084 if (folder) {
00085 folder->open();
00086 }
00087 KMFolder *msg_parent = msg->parent();
00088 if (msg_parent) {
00089 if (!folder || folder!= msg_parent->storage()) {
00090 msg_parent->open();
00091 }
00092 }
00093 mSrcFolder = msg_parent;
00094
00095
00096
00097
00098 KMAcctImap *account;
00099 if (folder) {
00100 account = folder->account();
00101 } else {
00102 account = static_cast<KMFolderImap*>(msg_parent->storage())->account();
00103 }
00104 if ( !account ||
00105 account->makeConnection() == ImapAccountBase::Error ) {
00106 deleteLater();
00107 return;
00108 }
00109 account->mJobList.append( this );
00110 if ( jt == tPutMessage )
00111 {
00112
00113 KURL url = account->getUrl();
00114 QString flags = KMFolderImap::statusToFlags( msg->status() );
00115 url.setPath( folder->imapPath() + ";SECTION=" + flags );
00116 ImapAccountBase::jobData jd;
00117 jd.parent = 0; jd.offset = 0; jd.done = 0;
00118 jd.total = msg->msgSizeServer();
00119 jd.msgList.append(msg);
00120 QCString cstr( msg->asString() );
00121 int a = cstr.find("\nX-UID: ");
00122 int b = cstr.find('\n', a);
00123 if (a != -1 && b != -1 && cstr.find("\n\n") > a) cstr.remove(a, b-a);
00124 mData.resize( cstr.length() + cstr.contains( "\n" ) - cstr.contains( "\r\n" ) );
00125 unsigned int i = 0;
00126 char prevChar = '\0';
00127
00128 for ( char *ch = cstr.data(); *ch; ch++ )
00129 {
00130 if ( *ch == '\n' && (prevChar != '\r') ) {
00131 mData.at( i ) = '\r';
00132 i++;
00133 }
00134 mData.at( i ) = *ch;
00135 prevChar = *ch;
00136 i++;
00137 }
00138 jd.data = mData;
00139 jd.progressItem = ProgressManager::createProgressItem(
00140 mParentProgressItem,
00141 "ImapJobUploading"+ProgressManager::getUniqueID(),
00142 i18n("Uploading message data"),
00143 i18n("Destination folder: ") + mDestFolder->prettyURL(),
00144 true,
00145 account->useSSL() || account->useTLS() );
00146 jd.progressItem->setTotalItems( jd.total );
00147 connect ( jd.progressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ),
00148 account, SLOT( slotAbortRequested( ProgressItem* ) ) );
00149 KIO::SimpleJob *simpleJob = KIO::put( url, 0, FALSE, FALSE, FALSE );
00150 KIO::Scheduler::assignJobToSlave( account->slave(), simpleJob );
00151 mJob = simpleJob;
00152 account->insertJob( mJob, jd );
00153 connect( mJob, SIGNAL(result(KIO::Job *)),
00154 SLOT(slotPutMessageResult(KIO::Job *)) );
00155 connect( mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)),
00156 SLOT(slotPutMessageDataReq(KIO::Job *, QByteArray &)) );
00157 connect( mJob, SIGNAL(infoMessage(KIO::Job *, const QString &)),
00158 SLOT(slotPutMessageInfoData(KIO::Job *, const QString &)) );
00159 connect( mJob, SIGNAL(processedSize(KIO::Job *, KIO::filesize_t)),
00160 SLOT(slotProcessedSize(KIO::Job *, KIO::filesize_t)));
00161 }
00162 else if ( jt == tCopyMessage || jt == tMoveMessage )
00163 {
00164 KURL url = account->getUrl();
00165 KURL destUrl = account->getUrl();
00166 destUrl.setPath(folder->imapPath());
00167 KMFolderImap *imapDestFolder = static_cast<KMFolderImap*>(msg_parent->storage());
00168 url.setPath( imapDestFolder->imapPath() + ";UID=" + sets );
00169 ImapAccountBase::jobData jd;
00170 jd.parent = 0; jd.offset = 0;
00171 jd.total = 1; jd.done = 0;
00172 jd.msgList = msgList;
00173
00174 QByteArray packedArgs;
00175 QDataStream stream( packedArgs, IO_WriteOnly );
00176
00177 stream << (int) 'C' << url << destUrl;
00178 jd.progressItem = ProgressManager::createProgressItem(
00179 mParentProgressItem,
00180 "ImapJobCopyMove"+ProgressManager::getUniqueID(),
00181 i18n("Server operation"),
00182 i18n("Source folder: ") + msg_parent->prettyURL()
00183 + " - destination folder: " + mDestFolder->prettyURL(),
00184 true,
00185 account->useSSL() || account->useTLS() );
00186 jd.progressItem->setTotalItems( jd.total );
00187 connect ( jd.progressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ),
00188 account, SLOT( slotAbortRequested( ProgressItem* ) ) );
00189 KIO::SimpleJob *simpleJob = KIO::special( url, packedArgs, FALSE );
00190 KIO::Scheduler::assignJobToSlave( account->slave(), simpleJob );
00191 mJob = simpleJob;
00192 account->insertJob( mJob, jd );
00193 connect( mJob, SIGNAL(result(KIO::Job *)),
00194 SLOT(slotCopyMessageResult(KIO::Job *)) );
00195 if ( jt == tMoveMessage )
00196 {
00197 connect( mJob, SIGNAL(infoMessage(KIO::Job *, const QString &)),
00198 SLOT(slotCopyMessageInfoData(KIO::Job *, const QString &)) );
00199 }
00200 }
00201 else {
00202 slotGetNextMessage();
00203 }
00204 }
00205
00206
00207
00208 ImapJob::~ImapJob()
00209 {
00210 if ( mDestFolder )
00211 {
00212 KMAcctImap *account = static_cast<KMFolderImap*>(mDestFolder->storage())->account();
00213 if ( account && mJob ) {
00214 ImapAccountBase::JobIterator it = account->findJob( mJob );
00215 if ( it != account->jobsEnd() ) {
00216 if( (*it).progressItem ) {
00217 (*it).progressItem->setComplete();
00218 (*it).progressItem = 0;
00219 }
00220 if ( !(*it).msgList.isEmpty() ) {
00221 for ( QPtrListIterator<KMMessage> mit( (*it).msgList ); mit.current(); ++mit )
00222 mit.current()->setTransferInProgress( false );
00223 }
00224 }
00225 account->removeJob( mJob );
00226 }
00227 account->mJobList.remove( this );
00228 mDestFolder->close();
00229 }
00230
00231 if ( mSrcFolder ) {
00232 if (!mDestFolder || mDestFolder != mSrcFolder) {
00233 if (! (mSrcFolder->folderType() == KMFolderTypeImap) ) return;
00234 KMAcctImap *account = static_cast<KMFolderImap*>(mSrcFolder->storage())->account();
00235 if ( account && mJob ) {
00236 ImapAccountBase::JobIterator it = account->findJob( mJob );
00237 if ( it != account->jobsEnd() ) {
00238 if( (*it).progressItem ) {
00239 (*it).progressItem->setComplete();
00240 (*it).progressItem = 0;
00241 }
00242 if ( !(*it).msgList.isEmpty() ) {
00243 for ( QPtrListIterator<KMMessage> mit( (*it).msgList ); mit.current(); ++mit )
00244 mit.current()->setTransferInProgress( false );
00245 }
00246 }
00247 account->removeJob( mJob );
00248 }
00249 account->mJobList.remove( this );
00250 }
00251 mSrcFolder->close();
00252 }
00253 }
00254
00255
00256
00257 void ImapJob::slotGetNextMessage()
00258 {
00259 KMMessage *msg = mMsgList.first();
00260 KMFolderImap *msgParent = static_cast<KMFolderImap*>(msg->storage());
00261 KMAcctImap *account = msgParent->account();
00262 if ( msg->UID() == 0 )
00263 {
00264 emit messageRetrieved( msg );
00265 account->mJobList.remove( this );
00266 deleteLater();
00267 return;
00268 }
00269 KURL url = account->getUrl();
00270 QString path = msgParent->imapPath() + ";UID=" + QString::number(msg->UID());
00271 ImapAccountBase::jobData jd;
00272 jd.parent = 0; jd.offset = 0;
00273 jd.total = 1; jd.done = 0;
00274 jd.msgList.append( msg );
00275 if ( !mPartSpecifier.isEmpty() )
00276 {
00277 if ( mPartSpecifier.find ("STRUCTURE", 0, false) != -1 ) {
00278 path += ";SECTION=STRUCTURE";
00279 } else if ( mPartSpecifier == "HEADER" ) {
00280 path += ";SECTION=HEADER";
00281 } else {
00282 path += ";SECTION=BODY.PEEK[" + mPartSpecifier + "]";
00283 DwBodyPart * part = msg->findDwBodyPart( msg->getFirstDwBodyPart(), mPartSpecifier );
00284 if (part)
00285 jd.total = part->BodySize();
00286 }
00287 } else {
00288 path += ";SECTION=BODY.PEEK[]";
00289 if (msg->msgSizeServer() > 0)
00290 jd.total = msg->msgSizeServer();
00291 }
00292 url.setPath( path );
00293
00294
00295 msg->setTransferInProgress( true );
00296 jd.progressItem = ProgressManager::createProgressItem(
00297 mParentProgressItem,
00298 "ImapJobDownloading"+ProgressManager::getUniqueID(),
00299 i18n("Downloading message data"),
00300 i18n("Message with subject: ") + msg->subject(),
00301 true,
00302 account->useSSL() || account->useTLS() );
00303 connect ( jd.progressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ),
00304 account, SLOT( slotAbortRequested( ProgressItem* ) ) );
00305 jd.progressItem->setTotalItems( jd.total );
00306
00307 KIO::SimpleJob *simpleJob = KIO::get( url, FALSE, FALSE );
00308 KIO::Scheduler::assignJobToSlave( account->slave(), simpleJob );
00309 mJob = simpleJob;
00310 account->insertJob( mJob, jd );
00311 if ( mPartSpecifier.find( "STRUCTURE", 0, false ) != -1 )
00312 {
00313 connect( mJob, SIGNAL(result(KIO::Job *)),
00314 this, SLOT(slotGetBodyStructureResult(KIO::Job *)) );
00315 } else {
00316 connect( mJob, SIGNAL(result(KIO::Job *)),
00317 this, SLOT(slotGetMessageResult(KIO::Job *)) );
00318 }
00319 connect( mJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
00320 msgParent, SLOT(slotSimpleData(KIO::Job *, const QByteArray &)) );
00321 if ( jd.total > 1 )
00322 {
00323 connect(mJob, SIGNAL(processedSize(KIO::Job *, KIO::filesize_t)),
00324 this, SLOT(slotProcessedSize(KIO::Job *, KIO::filesize_t)));
00325 }
00326 }
00327
00328
00329
00330 void ImapJob::slotGetMessageResult( KIO::Job * job )
00331 {
00332 KMMessage *msg = mMsgList.first();
00333 if (!msg || !msg->parent() || !job) {
00334 deleteLater();
00335 return;
00336 }
00337 KMFolderImap* parent = static_cast<KMFolderImap*>(msg->storage());
00338 if (msg->transferInProgress())
00339 msg->setTransferInProgress( false );
00340 KMAcctImap *account = parent->account();
00341 if ( !account ) {
00342 deleteLater();
00343 return;
00344 }
00345 ImapAccountBase::JobIterator it = account->findJob( job );
00346 if ( it == account->jobsEnd() ) return;
00347
00348 bool gotData = true;
00349 if (job->error())
00350 {
00351 QString errorStr = i18n( "Error while retrieving messages from the server." );
00352 if ( (*it).progressItem )
00353 (*it).progressItem->setStatus( errorStr );
00354 account->handleJobError( job, errorStr );
00355 return;
00356 } else {
00357 if ((*it).data.size() > 0)
00358 {
00359 kdDebug(5006) << "ImapJob::slotGetMessageResult - retrieved part " << mPartSpecifier << endl;
00360 if ( mPartSpecifier.isEmpty() ||
00361 mPartSpecifier == "HEADER" )
00362 {
00363 uint size = msg->msgSizeServer();
00364 if ( size > 0 && mPartSpecifier.isEmpty() )
00365 (*it).done = size;
00366 ulong uid = msg->UID();
00367
00368 if ( mPartSpecifier.isEmpty() )
00369 msg->setComplete( true );
00370 else
00371 msg->setReadyToShow( false );
00372
00373
00374 size_t dataSize = (*it).data.size();
00375 dataSize = FolderStorage::crlf2lf( (*it).data.data(), dataSize );
00376 (*it).data.resize( dataSize );
00377
00378 msg->fromByteArray( (*it).data );
00379
00380 msg->setUID(uid);
00381 if ( size > 0 && msg->msgSizeServer() == 0 )
00382 msg->setMsgSizeServer(size);
00383
00384 } else {
00385
00386 msg->updateBodyPart( mPartSpecifier, (*it).data );
00387 msg->setReadyToShow( true );
00388
00389
00390 if (msg->attachmentState() != KMMsgHasAttachment)
00391 msg->updateAttachmentState();
00392 }
00393 } else {
00394 kdDebug(5006) << "ImapJob::slotGetMessageResult - got no data for " << mPartSpecifier << endl;
00395 gotData = false;
00396 msg->setReadyToShow( true );
00397
00398 msg->notify();
00399 }
00400 }
00401 if (account->slave()) {
00402 account->removeJob(it);
00403 account->mJobList.remove(this);
00404 }
00405
00406
00407 if ( mPartSpecifier.isEmpty() ||
00408 mPartSpecifier == "HEADER" )
00409 {
00410 if ( gotData )
00411 emit messageRetrieved(msg);
00412 else
00413 {
00414
00415
00416 emit messageRetrieved( 0 );
00417 parent->ignoreJobsForMessage( msg );
00418 int idx = parent->find( msg );
00419 if (idx != -1) parent->removeMsg( idx, true );
00420
00421
00422 return;
00423 }
00424 } else {
00425 emit messageUpdated(msg, mPartSpecifier);
00426 }
00427 deleteLater();
00428 }
00429
00430
00431 void ImapJob::slotGetBodyStructureResult( KIO::Job * job )
00432 {
00433 KMMessage *msg = mMsgList.first();
00434 if (!msg || !msg->parent() || !job) {
00435 deleteLater();
00436 return;
00437 }
00438 KMFolderImap* parent = static_cast<KMFolderImap*>(msg->storage());
00439 if (msg->transferInProgress())
00440 msg->setTransferInProgress( false );
00441 KMAcctImap *account = parent->account();
00442 if ( !account ) {
00443 deleteLater();
00444 return;
00445 }
00446 ImapAccountBase::JobIterator it = account->findJob( job );
00447 if ( it == account->jobsEnd() ) return;
00448
00449
00450 if (job->error())
00451 {
00452 account->handleJobError( job, i18n( "Error while retrieving information on the structure of a message." ) );
00453 return;
00454 } else {
00455 if ((*it).data.size() > 0)
00456 {
00457 QDataStream stream( (*it).data, IO_ReadOnly );
00458 account->handleBodyStructure(stream, msg, mAttachmentStrategy);
00459 }
00460 }
00461 if (account->slave()) {
00462 account->removeJob(it);
00463 account->mJobList.remove(this);
00464 }
00465 deleteLater();
00466 }
00467
00468
00469 void ImapJob::slotPutMessageDataReq( KIO::Job *job, QByteArray &data )
00470 {
00471 KMAcctImap *account = static_cast<KMFolderImap*>(mDestFolder->storage())->account();
00472 ImapAccountBase::JobIterator it = account->findJob( job );
00473 if ( it == account->jobsEnd() ) return;
00474
00475 if ((*it).data.size() - (*it).offset > 0x8000)
00476 {
00477 data.duplicate((*it).data.data() + (*it).offset, 0x8000);
00478 (*it).offset += 0x8000;
00479 }
00480 else if ((*it).data.size() - (*it).offset > 0)
00481 {
00482 data.duplicate((*it).data.data() + (*it).offset, (*it).data.size() - (*it).offset);
00483 (*it).offset = (*it).data.size();
00484 } else data.resize(0);
00485 }
00486
00487
00488
00489 void ImapJob::slotPutMessageResult( KIO::Job *job )
00490 {
00491 KMMessage *msg = mMsgList.first();
00492 KMAcctImap *account = static_cast<KMFolderImap*>(mDestFolder->storage())->account();
00493 ImapAccountBase::JobIterator it = account->findJob( job );
00494 if ( it == account->jobsEnd() ) return;
00495 if (job->error())
00496 {
00497 if ( (*it).progressItem )
00498 (*it).progressItem->setStatus( i18n("Uploading message data failed.") );
00499 account->handlePutError( job, *it, mDestFolder );
00500 return;
00501 } else {
00502 if ( !(*it).msgList.isEmpty() )
00503 {
00504 emit messageStored((*it).msgList.last());
00505 (*it).msgList.removeLast();
00506 } else if (msg)
00507 {
00508 emit messageStored(msg);
00509 }
00510 msg = 0;
00511 if ( (*it).progressItem )
00512 (*it).progressItem->setStatus( i18n("Uploading message data completed.") );
00513 }
00514 if (account->slave()) {
00515 account->removeJob(it);
00516 account->mJobList.remove(this);
00517 }
00518 deleteLater();
00519 }
00520
00521
00522 void ImapJob::slotCopyMessageInfoData(KIO::Job * job, const QString & data)
00523 {
00524 KMFolderImap * imapFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
00525 KMAcctImap *account = imapFolder->account();
00526 ImapAccountBase::JobIterator it = account->findJob( job );
00527 if ( it == account->jobsEnd() ) return;
00528
00529 if (data.find("UID") != -1)
00530 {
00531
00532 QString oldUid = data.section(' ', 1, 1);
00533 QString newUid = data.section(' ', 2, 2);
00534
00535
00536 QValueList<ulong> olduids = KMFolderImap::splitSets(oldUid);
00537 QValueList<ulong> newuids = KMFolderImap::splitSets(newUid);
00538
00539 int index = -1;
00540 if ( !(*it).msgList.isEmpty() )
00541 {
00542 KMMessage * msg;
00543 for ( msg = (*it).msgList.first(); msg; msg = (*it).msgList.next() )
00544 {
00545 ulong uid = msg->UID();
00546 index = olduids.findIndex(uid);
00547 if (index > -1)
00548 {
00549
00550 const ulong * sernum = (ulong *)msg->getMsgSerNum();
00551 imapFolder->insertUidSerNumEntry(newuids[index], sernum);
00552 }
00553 }
00554 } else if (mMsgList.first()) {
00555 ulong uid = mMsgList.first()->UID();
00556 index = olduids.findIndex(uid);
00557 if (index > -1)
00558 {
00559
00560 const ulong * sernum = (ulong *)mMsgList.first()->getMsgSerNum();
00561 imapFolder->insertUidSerNumEntry(newuids[index], sernum);
00562 }
00563 }
00564 }
00565 }
00566
00567
00568 void ImapJob::slotPutMessageInfoData(KIO::Job *job, const QString &data)
00569 {
00570 KMFolderImap * imapFolder = static_cast<KMFolderImap*>(mDestFolder->storage());
00571 KMAcctImap *account = imapFolder->account();
00572 ImapAccountBase::JobIterator it = account->findJob( job );
00573 if ( it == account->jobsEnd() ) return;
00574
00575 if (data.find("UID") != -1)
00576 {
00577 ulong uid = (data.right(data.length()-4)).toInt();
00578
00579 if ( !(*it).msgList.isEmpty() )
00580 {
00581 const ulong * sernum = (ulong *)(*it).msgList.last()->getMsgSerNum();
00582 imapFolder->insertUidSerNumEntry(uid, sernum);
00583 } else if (mMsgList.first())
00584 {
00585 const ulong * sernum = (ulong *)mMsgList.first()->getMsgSerNum();
00586 imapFolder->insertUidSerNumEntry(uid, sernum);
00587 }
00588 }
00589 }
00590
00591
00592
00593 void ImapJob::slotCopyMessageResult( KIO::Job *job )
00594 {
00595 KMAcctImap *account = static_cast<KMFolderImap*>(mDestFolder->storage())->account();
00596 ImapAccountBase::JobIterator it = account->findJob( job );
00597 if ( it == account->jobsEnd() ) return;
00598
00599 if (job->error())
00600 {
00601 account->handleJobError( job, i18n("Error while copying messages.") );
00602 return;
00603 } else {
00604 if ( !(*it).msgList.isEmpty() )
00605 {
00606 emit messageCopied((*it).msgList);
00607 } else if (mMsgList.first()) {
00608 emit messageCopied(mMsgList.first());
00609 }
00610 }
00611 if (account->slave()) {
00612 account->removeJob(it);
00613 account->mJobList.remove(this);
00614 }
00615 deleteLater();
00616 }
00617
00618
00619 void ImapJob::execute()
00620 {
00621 init( mType, mSets, mDestFolder?
00622 dynamic_cast<KMFolderImap*>( mDestFolder->storage() ):0, mMsgList );
00623 }
00624
00625
00626 void ImapJob::setParentFolder( const KMFolderImap* parent )
00627 {
00628 mParentFolder = const_cast<KMFolderImap*>( parent );
00629 }
00630
00631
00632 void ImapJob::slotProcessedSize(KIO::Job * job, KIO::filesize_t processed)
00633 {
00634 KMMessage *msg = mMsgList.first();
00635 if (!msg || !job) {
00636 return;
00637 }
00638 KMFolderImap* parent = 0;
00639 if ( msg->parent() && msg->parent()->folderType() == KMFolderTypeImap )
00640 parent = static_cast<KMFolderImap*>(msg->parent()->storage());
00641 else if (mDestFolder)
00642 parent = static_cast<KMFolderImap*>(mDestFolder->storage());
00643 if (!parent) return;
00644 KMAcctImap *account = parent->account();
00645 if ( !account ) return;
00646 ImapAccountBase::JobIterator it = account->findJob( job );
00647 if ( it == account->jobsEnd() ) return;
00648 (*it).done = processed;
00649 if ( (*it).progressItem ) {
00650 (*it).progressItem->setCompletedItems( processed );
00651 (*it).progressItem->updateProgress();
00652 }
00653 emit progress( (*it).done, (*it).total );
00654 }
00655
00656 }
00657
00658 #include "imapjob.moc"