kmail Library API Documentation

kmacctexppop.cpp

00001 // KMAcctExpPop.cpp
00002 // Authors: Don Sanders, (based on kmacctpop by)
00003 //          Stefan Taferner and Markus Wuebben
00004 
00005 #ifdef HAVE_CONFIG_H
00006 #include <config.h>
00007 #endif
00008 
00009 #include "kmacctexppop.h"
00010 
00011 #include "broadcaststatus.h"
00012 using KPIM::BroadcastStatus;
00013 #include "progressmanager.h"
00014 #include "kmfoldermgr.h"
00015 #include "kmfiltermgr.h"
00016 #include "kmpopfiltercnfrmdlg.h"
00017 #include "kmkernel.h"
00018 #include "protocols.h"
00019 #include "kmdict.h"
00020 
00021 #include <kdebug.h>
00022 #include <kstandarddirs.h>
00023 #include <klocale.h>
00024 #include <kmessagebox.h>
00025 #include <kmainwindow.h>
00026 #include <kio/scheduler.h>
00027 #include <kio/passdlg.h>
00028 #include <kconfig.h>
00029 using KIO::MetaData;
00030 
00031 static const unsigned short int pop3DefaultPort = 110;
00032 
00033 //-----------------------------------------------------------------------------
00034 KMAcctExpPop::KMAcctExpPop(KMAcctMgr* aOwner, const QString& aAccountName, uint id)
00035   : NetworkAccount(aOwner, aAccountName, id),
00036     headerIt(headersOnServer)
00037 {
00038   init();
00039   job = 0;
00040   mSlave = 0;
00041   mPort = defaultPort();
00042   stage = Idle;
00043   indexOfCurrentMsg = -1;
00044   curMsgStrm = 0;
00045   processingDelay = 2*100;
00046   mProcessing = false;
00047   dataCounter = 0;
00048   mUidsOfSeenMsgsDict.setAutoDelete( false );
00049   mUidsOfNextSeenMsgsDict.setAutoDelete( false );
00050 
00051   headersOnServer.setAutoDelete(true);
00052   connect(&processMsgsTimer,SIGNAL(timeout()),SLOT(slotProcessPendingMsgs()));
00053   KIO::Scheduler::connect(
00054     SIGNAL(slaveError(KIO::Slave *, int, const QString &)),
00055     this, SLOT(slotSlaveError(KIO::Slave *, int, const QString &)));
00056 
00057   mHeaderDeleteUids.clear();
00058   mHeaderDownUids.clear();
00059   mHeaderLaterUids.clear();
00060 }
00061 
00062 
00063 //-----------------------------------------------------------------------------
00064 KMAcctExpPop::~KMAcctExpPop()
00065 {
00066   if (job) {
00067     job->kill();
00068     mMsgsPendingDownload.clear();
00069     processRemainingQueuedMessages();
00070     saveUidList();
00071   }
00072 }
00073 
00074 
00075 //-----------------------------------------------------------------------------
00076 QString KMAcctExpPop::type(void) const
00077 {
00078   return "pop";
00079 }
00080 
00081 QString KMAcctExpPop::protocol() const {
00082   return useSSL() ? POP_SSL_PROTOCOL : POP_PROTOCOL;
00083 }
00084 
00085 unsigned short int KMAcctExpPop::defaultPort() const {
00086   return pop3DefaultPort;
00087 }
00088 
00089 //-----------------------------------------------------------------------------
00090 void KMAcctExpPop::init(void)
00091 {
00092   NetworkAccount::init();
00093 
00094   mUsePipelining = FALSE;
00095   mLeaveOnServer = FALSE;
00096   mFilterOnServer = FALSE;
00097   //tz todo
00098   mFilterOnServerCheckSize = 50000;
00099 }
00100 
00101 //-----------------------------------------------------------------------------
00102 void KMAcctExpPop::pseudoAssign( const KMAccount * a ) {
00103   slotAbortRequested();
00104   NetworkAccount::pseudoAssign( a );
00105 
00106   const KMAcctExpPop * p = dynamic_cast<const KMAcctExpPop*>( a );
00107   if ( !p ) return;
00108 
00109   setUsePipelining( p->usePipelining() );
00110   setLeaveOnServer( p->leaveOnServer() );
00111   setFilterOnServer( p->filterOnServer() );
00112   setFilterOnServerCheckSize( p->filterOnServerCheckSize() );
00113 }
00114 
00115 //-----------------------------------------------------------------------------
00116 void KMAcctExpPop::processNewMail(bool _interactive)
00117 {
00118   if (stage == Idle) {
00119 
00120     if(mAskAgain || mPasswd.isEmpty() || mLogin.isEmpty()) {
00121       QString passwd = decryptStr(mPasswd);
00122       bool b = FALSE;
00123       if (KIO::PasswordDialog::getNameAndPassword(mLogin, passwd, &b,
00124         i18n("You need to supply a username and a password to access this "
00125         "mailbox."), FALSE, QString::null, mName, i18n("Account:"))
00126         != QDialog::Accepted)
00127       {
00128         checkDone( false, CheckAborted );
00129         return;
00130       } else {
00131         mPasswd = encryptStr(passwd);
00132         mAskAgain = FALSE;
00133       }
00134     }
00135 
00136     QString seenUidList = locateLocal( "data", "kmail/" + mLogin + ":" + "@" +
00137                                        mHost + ":" + QString("%1").arg(mPort) );
00138     KConfig config( seenUidList );
00139     QStringList uidsOfSeenMsgs = config.readListEntry( "seenUidList" );
00140     mUidsOfSeenMsgsDict.clear();
00141     mUidsOfSeenMsgsDict.resize( KMail::nextPrime( ( uidsOfSeenMsgs.count() * 11 ) / 10 ) );
00142     for ( QStringList::ConstIterator it = uidsOfSeenMsgs.begin();
00143           it != uidsOfSeenMsgs.end(); ++it ) {
00144       // we use mUidsOfSeenMsgsDict to provide fast random access to the keys,
00145       // so we simply set the values to (const int *)1
00146       mUidsOfSeenMsgsDict.insert( *it, (const int *)1 );
00147     }
00148     QStringList downloadLater = config.readListEntry( "downloadLater" );
00149     for ( QStringList::Iterator it = downloadLater.begin(); it != downloadLater.end(); ++it ) {
00150         mHeaderLaterUids.insert( *it, true );
00151     }
00152     mUidsOfNextSeenMsgsDict.clear();
00153 
00154     interactive = _interactive;
00155     mUidlFinished = FALSE;
00156     startJob();
00157   }
00158   else {
00159     checkDone( false, CheckIgnored );
00160     return;
00161   }
00162 }
00163 
00164 
00165 //-----------------------------------------------------------------------------
00166 void KMAcctExpPop::readConfig(KConfig& config)
00167 {
00168   NetworkAccount::readConfig(config);
00169 
00170   mUsePipelining = config.readNumEntry("pipelining", FALSE);
00171   mLeaveOnServer = config.readNumEntry("leave-on-server", FALSE);
00172   mFilterOnServer = config.readNumEntry("filter-on-server", FALSE);
00173   mFilterOnServerCheckSize = config.readUnsignedNumEntry("filter-os-check-size", 50000);
00174 }
00175 
00176 
00177 //-----------------------------------------------------------------------------
00178 void KMAcctExpPop::writeConfig(KConfig& config)
00179 {
00180   NetworkAccount::writeConfig(config);
00181 
00182   config.writeEntry("pipelining", mUsePipelining);
00183   config.writeEntry("leave-on-server", mLeaveOnServer);
00184   config.writeEntry("filter-on-server", mFilterOnServer);
00185   config.writeEntry("filter-os-check-size", mFilterOnServerCheckSize);
00186 }
00187 
00188 
00189 //-----------------------------------------------------------------------------
00190 void KMAcctExpPop::setUsePipelining(bool b)
00191 {
00192   mUsePipelining = b;
00193 }
00194 
00195 //-----------------------------------------------------------------------------
00196 void KMAcctExpPop::setLeaveOnServer(bool b)
00197 {
00198   mLeaveOnServer = b;
00199 }
00200 
00201 
00202 //---------------------------------------------------------------------------
00203 void KMAcctExpPop::setFilterOnServer(bool b)
00204 {
00205   mFilterOnServer = b;
00206 }
00207 
00208 //---------------------------------------------------------------------------
00209 void KMAcctExpPop::setFilterOnServerCheckSize(unsigned int aSize)
00210 {
00211   mFilterOnServerCheckSize = aSize;
00212 }
00213 
00214 //-----------------------------------------------------------------------------
00215 void KMAcctExpPop::connectJob() {
00216   KIO::Scheduler::assignJobToSlave(mSlave, job);
00217   if (stage != Dele)
00218   connect(job, SIGNAL( data( KIO::Job*, const QByteArray &)),
00219       SLOT( slotData( KIO::Job*, const QByteArray &)));
00220   connect(job, SIGNAL( result( KIO::Job * ) ),
00221       SLOT( slotResult( KIO::Job * ) ) );
00222   connect(job, SIGNAL(infoMessage( KIO::Job*, const QString & )),
00223           SLOT( slotMsgRetrieved(KIO::Job*, const QString &)));
00224 }
00225 
00226 
00227 //-----------------------------------------------------------------------------
00228 void KMAcctExpPop::slotCancel()
00229 {
00230   mMsgsPendingDownload.clear();
00231   processRemainingQueuedMessages();
00232   saveUidList();
00233   slotJobFinished();
00234 }
00235 
00236 
00237 //-----------------------------------------------------------------------------
00238 void KMAcctExpPop::slotProcessPendingMsgs()
00239 {
00240   if (mProcessing) // not reentrant
00241     return;
00242   mProcessing = true;
00243 
00244   bool addedOk;
00245   QValueList<KMMessage*>::Iterator cur = msgsAwaitingProcessing.begin();
00246   QStringList::Iterator curId = msgIdsAwaitingProcessing.begin();
00247   QStringList::Iterator curUid = msgUidsAwaitingProcessing.begin();
00248 
00249   while (cur != msgsAwaitingProcessing.end()) {
00250     // note we can actually end up processing events in processNewMsg
00251     // this happens when send receipts is turned on
00252     // hence the check for re-entry at the start of this method.
00253     // -sanders Update processNewMsg should no longer process events
00254 
00255     addedOk = processNewMsg(*cur); //added ok? Error displayed if not.
00256 
00257     if (!addedOk) {
00258       mMsgsPendingDownload.clear();
00259       msgIdsAwaitingProcessing.clear();
00260       msgUidsAwaitingProcessing.clear();
00261       break;
00262     }
00263     else {
00264       idsOfMsgsToDelete.append( *curId );
00265       mUidsOfNextSeenMsgsDict.insert( *curUid, (const int *)1 );
00266     }
00267     ++cur;
00268     ++curId;
00269     ++curUid;
00270   }
00271 
00272   msgsAwaitingProcessing.clear();
00273   msgIdsAwaitingProcessing.clear();
00274   msgUidsAwaitingProcessing.clear();
00275   mProcessing = false;
00276 }
00277 
00278 
00279 //-----------------------------------------------------------------------------
00280 void KMAcctExpPop::slotAbortRequested()
00281 {
00282   if (stage == Idle) return;
00283   disconnect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ),
00284            this, SLOT( slotAbortRequested() ) );
00285   stage = Quit;
00286   if (job) job->kill();
00287   job = 0;
00288   mSlave = 0;
00289   slotCancel();
00290 }
00291 
00292 
00293 //-----------------------------------------------------------------------------
00294 void KMAcctExpPop::startJob()
00295 {
00296   // Run the precommand
00297   if (!runPrecommand(precommand()))
00298     {
00299       KMessageBox::sorry(0,
00300                          i18n("Could not execute precommand: %1").arg(precommand()),
00301                          i18n("KMail Error Message"));
00302       checkDone( false, CheckError );
00303       return;
00304     }
00305   // end precommand code
00306 
00307   KURL url = getUrl();
00308 
00309   if ( !url.isValid() ) {
00310     KMessageBox::error(0, i18n("Source URL is malformed"),
00311                           i18n("Kioslave Error Message") );
00312     return;
00313   }
00314 
00315   mMsgsPendingDownload.clear();
00316   idsOfMsgs.clear();
00317   mUidForIdMap.clear();
00318   idsOfMsgsToDelete.clear();
00319   //delete any headers if there are some this have to be done because of check again
00320   headersOnServer.clear();
00321   headers = false;
00322   indexOfCurrentMsg = -1;
00323 
00324   Q_ASSERT( !mMailCheckProgressItem );
00325   mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem(
00326     "MailCheck" + mName,
00327     mName,
00328     i18n("Preparing transmission from \"%1\"...").arg(mName),
00329     true, // can be canceled
00330     useSSL() || useTLS() );
00331   connect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( ProgressItem* ) ),
00332            this, SLOT( slotAbortRequested() ) );
00333 
00334   numBytes = 0;
00335   numBytesRead = 0;
00336   stage = List;
00337   mSlave = KIO::Scheduler::getConnectedSlave( url, slaveConfig() );
00338   if (!mSlave)
00339   {
00340     slotSlaveError(0, KIO::ERR_CANNOT_LAUNCH_PROCESS, url.protocol());
00341     return;
00342   }
00343   url.setPath(QString("/index"));
00344   job = KIO::get( url, false, false );
00345   connectJob();
00346 }
00347 
00348 MetaData KMAcctExpPop::slaveConfig() const {
00349   MetaData m = NetworkAccount::slaveConfig();
00350 
00351   m.insert("progress", "off");
00352   m.insert("pipelining", (mUsePipelining) ? "on" : "off");
00353   if (mAuth == "PLAIN" || mAuth == "LOGIN" || mAuth == "CRAM-MD5" ||
00354       mAuth == "DIGEST-MD5") {
00355     m.insert("auth", "SASL");
00356     m.insert("sasl", mAuth);
00357   } else if ( mAuth == "*" )
00358     m.insert("auth", "USER");
00359   else
00360     m.insert("auth", mAuth);
00361 
00362   return m;
00363 }
00364 
00365 //-----------------------------------------------------------------------------
00366 // one message is finished
00367 // add data to a KMMessage
00368 void KMAcctExpPop::slotMsgRetrieved(KIO::Job*, const QString & infoMsg)
00369 {
00370   if (infoMsg != "message complete") return;
00371   KMMessage *msg = new KMMessage;
00372   msg->setComplete(true);
00373   // Make sure to use LF as line ending to make the processing easier
00374   // when piping through external programs
00375   uint newSize = KMFolder::crlf2lf( curMsgData.data(), curMsgData.size() );
00376   curMsgData.resize( newSize );
00377   msg->fromByteArray( curMsgData , true );
00378   if (stage == Head)
00379   {
00380     int size = mMsgsPendingDownload[ headerIt.current()->id() ];
00381     kdDebug(5006) << "Size of Message: " << size << endl;
00382     msg->setMsgLength( size );
00383     headerIt.current()->setHeader(msg);
00384     ++headerIt;
00385     slotGetNextHdr();
00386   } else {
00387     //kdDebug(5006) << kfuncinfo << "stage == Retr" << endl;
00388     //kdDebug(5006) << "curMsgData.size() = " << curMsgData.size() << endl;
00389     msg->setMsgLength( curMsgData.size() );
00390     msgsAwaitingProcessing.append(msg);
00391     msgIdsAwaitingProcessing.append(idsOfMsgs[indexOfCurrentMsg]);
00392     msgUidsAwaitingProcessing.append( mUidForIdMap[idsOfMsgs[indexOfCurrentMsg]] );
00393     slotGetNextMsg();
00394   }
00395 }
00396 
00397 
00398 //-----------------------------------------------------------------------------
00399 // finit state machine to cycle trow the stages
00400 void KMAcctExpPop::slotJobFinished() {
00401   QStringList emptyList;
00402   if (stage == List) {
00403     kdDebug(5006) << k_funcinfo << "stage == List" << endl;
00404     // set the initial size of mUidsOfNextSeenMsgsDict to the number of
00405     // messages on the server + 10%
00406     mUidsOfNextSeenMsgsDict.resize( KMail::nextPrime( ( idsOfMsgs.count() * 11 ) / 10 ) );
00407     KURL url = getUrl();
00408     url.setPath(QString("/uidl"));
00409     job = KIO::get( url, false, false );
00410     connectJob();
00411     stage = Uidl;
00412   }
00413   else if (stage == Uidl) {
00414     kdDebug(5006) << k_funcinfo << "stage == Uidl" << endl;
00415     mUidlFinished = TRUE;
00416 
00417     if ( mLeaveOnServer && mUidForIdMap.isEmpty() &&
00418          mUidsOfNextSeenMsgsDict.isEmpty() && !idsOfMsgs.isEmpty() ) {
00419       KMessageBox::sorry(0, i18n("Your POP3 server does not support the UIDL "
00420       "command: this command is required to determine, in a reliable way, "
00421       "which of the mails on the server KMail has already seen before;\n"
00422       "the feature to leave the mails on the server will therefore not "
00423       "work properly."));
00424       // An attempt to work around buggy pop servers, these seem to be popular.
00425       mUidsOfNextSeenMsgsDict = mUidsOfSeenMsgsDict;
00426     }
00427 
00428     //check if filter on server
00429     if (mFilterOnServer == true) {
00430       QMap<QString, int>::Iterator hids;
00431       for ( hids = mMsgsPendingDownload.begin();
00432             hids != mMsgsPendingDownload.end(); hids++ ) {
00433           kdDebug(5006) << "Length: " << hids.data() << endl;
00434           //check for mails bigger mFilterOnServerCheckSize
00435           if ( (unsigned int)hids.data() >= mFilterOnServerCheckSize ) {
00436             kdDebug(5006) << "bigger than " << mFilterOnServerCheckSize << endl;
00437               headersOnServer.append(new KMPopHeaders( hids.key(),
00438                                                        mUidForIdMap[hids.key()],
00439                                                        Later));//TODO
00440             //set Action if already known
00441             if( mHeaderDeleteUids.contains( headersOnServer.current()->uid() ) ) {
00442               headersOnServer.current()->setAction(Delete);
00443             }
00444             else if( mHeaderDownUids.contains( headersOnServer.current()->uid() ) ) {
00445               headersOnServer.current()->setAction(Down);
00446             }
00447             else if( mHeaderLaterUids.contains( headersOnServer.current()->uid() ) ) {
00448               headersOnServer.current()->setAction(Later);
00449             }
00450           }
00451       }
00452       // delete the uids so that you don't get them twice in the list
00453       mHeaderDeleteUids.clear();
00454       mHeaderDownUids.clear();
00455       mHeaderLaterUids.clear();
00456     }
00457     // kdDebug(5006) << "Num of Msgs to Filter: " << headersOnServer.count() << endl;
00458     // if there are mails which should be checkedc download the headers
00459     if ((headersOnServer.count() > 0) && (mFilterOnServer == true)) {
00460       headerIt.toFirst();
00461       KURL url = getUrl();
00462       QString headerIds;
00463       while (headerIt.current())
00464       {
00465         headerIds += headerIt.current()->id();
00466         if (!headerIt.atLast()) headerIds += ",";
00467         ++headerIt;
00468       }
00469       headerIt.toFirst();
00470       url.setPath(QString("/headers/") + headerIds);
00471       job = KIO::get( url, false, false );
00472       connectJob();
00473       slotGetNextHdr();
00474       stage = Head;
00475     }
00476     else {
00477       stage = Retr;
00478       numMsgs = mMsgsPendingDownload.count();
00479       numBytesToRead = 0;
00480       QMap<QString, int>::Iterator len;
00481       for ( len  = mMsgsPendingDownload.begin();
00482             len != mMsgsPendingDownload.end(); len++ )
00483         numBytesToRead += len.data();
00484       idsOfMsgs = QStringList( mMsgsPendingDownload.keys() );
00485       KURL url = getUrl();
00486       url.setPath( "/download/" + idsOfMsgs.join(",") );
00487       job = KIO::get( url, false, false );
00488       connectJob();
00489       slotGetNextMsg();
00490       processMsgsTimer.start(processingDelay);
00491     }
00492   }
00493   else if (stage == Head) {
00494     kdDebug(5006) << k_funcinfo << "stage == Head" << endl;
00495 
00496     // All headers have been downloaded, check which mail you want to get
00497     // data is in list headersOnServer
00498 
00499     // check if headers apply to a filter
00500     // if set the action of the filter
00501     KMPopFilterAction action;
00502     bool dlgPopup = false;
00503     for (headersOnServer.first(); headersOnServer.current(); headersOnServer.next()) {
00504       action = (KMPopFilterAction)kmkernel->popFilterMgr()->process(headersOnServer.current()->header());
00505       //debug todo
00506       switch ( action ) {
00507         case NoAction:
00508           kdDebug(5006) << "PopFilterAction = NoAction" << endl;
00509           break;
00510         case Later:
00511           kdDebug(5006) << "PopFilterAction = Later" << endl;
00512           break;
00513         case Delete:
00514           kdDebug(5006) << "PopFilterAction = Delete" << endl;
00515           break;
00516         case Down:
00517           kdDebug(5006) << "PopFilterAction = Down" << endl;
00518           break;
00519         default:
00520           kdDebug(5006) << "PopFilterAction = default oops!" << endl;
00521           break;
00522       }
00523       switch ( action ) {
00524         case NoAction:
00525           //kdDebug(5006) << "PopFilterAction = NoAction" << endl;
00526           dlgPopup = true;
00527           break;
00528         case Later:
00529           if (kmkernel->popFilterMgr()->showLaterMsgs())
00530             dlgPopup = true;
00531         default:
00532           headersOnServer.current()->setAction(action);
00533           headersOnServer.current()->setRuleMatched(true);
00534           break;
00535       }
00536     }
00537 
00538     // if there are some messages which are not coverd by a filter
00539     // show the dialog
00540     headers = true;
00541     if (dlgPopup) {
00542       KMPopFilterCnfrmDlg dlg(&headersOnServer, this->name(), kmkernel->popFilterMgr()->showLaterMsgs());
00543       dlg.exec();
00544     }
00545 
00546     for (headersOnServer.first(); headersOnServer.current(); headersOnServer.next()) {
00547       if (headersOnServer.current()->action() == Delete ||
00548           headersOnServer.current()->action() == Later) {
00549         //remove entries from the lists when the mails should not be downloaded
00550         //(deleted or downloaded later)
00551         if ( mMsgsPendingDownload.contains( headersOnServer.current()->id() ) ) {
00552           mMsgsPendingDownload.remove( headersOnServer.current()->id() );
00553         }
00554         if (headersOnServer.current()->action() == Delete) {
00555           mHeaderDeleteUids.insert(headersOnServer.current()->uid(), true);
00556           mUidsOfNextSeenMsgsDict.insert( headersOnServer.current()->uid(),
00557                                           (const int *)1 );
00558           idsOfMsgsToDelete.append(headersOnServer.current()->id());
00559         }
00560         else {
00561           mHeaderLaterUids.insert(headersOnServer.current()->uid(), true);
00562         }
00563       }
00564       else if (headersOnServer.current()->action() == Down) {
00565         mHeaderDownUids.insert(headersOnServer.current()->uid(), true);
00566       }
00567     }
00568 
00569     headersOnServer.clear();
00570     stage = Retr;
00571     numMsgs = mMsgsPendingDownload.count();
00572     numBytesToRead = 0;
00573     QMap<QString, int>::Iterator len;
00574     for (len = mMsgsPendingDownload.begin();
00575          len != mMsgsPendingDownload.end(); len++)
00576       numBytesToRead += len.data();
00577     idsOfMsgs = QStringList( mMsgsPendingDownload.keys() );
00578     KURL url = getUrl();
00579     url.setPath( "/download/" + idsOfMsgs.join(",") );
00580     job = KIO::get( url, false, false );
00581     connectJob();
00582     slotGetNextMsg();
00583     processMsgsTimer.start(processingDelay);
00584   }
00585   else if (stage == Retr) {
00586     mMailCheckProgressItem->setProgress( 100 );
00587     processRemainingQueuedMessages();
00588 
00589     mHeaderDeleteUids.clear();
00590     mHeaderDownUids.clear();
00591     mHeaderLaterUids.clear();
00592 
00593     kmkernel->folderMgr()->syncAllFolders();
00594 
00595     KURL url = getUrl();
00596     if (mLeaveOnServer || idsOfMsgsToDelete.isEmpty()) {
00597       stage = Quit;
00598       mMailCheckProgressItem->setStatus(
00599         i18n( "Fetched 1 message from %1. Terminating transmission...",
00600               "Fetched %n messages from %1. Terminating transmission...",
00601               numMsgs )
00602         .arg( mHost ) );
00603       url.setPath(QString("/commit"));
00604       job = KIO::get(url, false, false );
00605     }
00606     else {
00607       stage = Dele;
00608       mMailCheckProgressItem->setStatus(
00609         i18n( "Fetched 1 message from %1. Deleting messages from server...",
00610               "Fetched %n messages from %1. Deleting messages from server...",
00611               numMsgs )
00612         .arg( mHost ) );
00613       url.setPath("/remove/" + idsOfMsgsToDelete.join(","));
00614       kdDebug(5006) << "url: " << url.prettyURL() << endl;
00615       job = KIO::get( url, false, false );
00616     }
00617     connectJob();
00618   }
00619   else if (stage == Dele) {
00620     kdDebug(5006) << k_funcinfo << "stage == Dele" << endl;
00621     // remove the uids of all messages which have been deleted
00622     for ( QStringList::ConstIterator it = idsOfMsgsToDelete.begin();
00623           it != idsOfMsgsToDelete.end(); ++it ) {
00624       mUidsOfNextSeenMsgsDict.remove( mUidForIdMap[*it] );
00625     }
00626     idsOfMsgsToDelete.clear();
00627     mMailCheckProgressItem->setStatus(
00628       i18n( "Fetched 1 message from %1. Terminating transmission...",
00629             "Fetched %n messages from %1. Terminating transmission...",
00630             numMsgs )
00631       .arg( mHost ) );
00632     KURL url = getUrl();
00633     url.setPath(QString("/commit"));
00634     job = KIO::get( url, false, false );
00635     stage = Quit;
00636     connectJob();
00637   }
00638   else if (stage == Quit) {
00639     kdDebug(5006) << k_funcinfo << "stage == Quit" << endl;
00640     saveUidList();
00641     job = 0;
00642     if (mSlave) KIO::Scheduler::disconnectSlave(mSlave);
00643     mSlave = 0;
00644     stage = Idle;
00645     if( mMailCheckProgressItem ) { // do this only once...
00646       bool canceled = kmkernel->mailCheckAborted() || mMailCheckProgressItem->canceled();
00647       int numMessages = canceled ? indexOfCurrentMsg : idsOfMsgs.count();
00648       BroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
00649         numMessages, numBytes, numBytesRead, numBytesToRead, mLeaveOnServer, mMailCheckProgressItem );
00650       mMailCheckProgressItem->setComplete();
00651       mMailCheckProgressItem = 0;
00652       checkDone( ( numMessages > 0 ), canceled ? CheckAborted : CheckOK );
00653     }
00654   }
00655 }
00656 
00657 
00658 //-----------------------------------------------------------------------------
00659 void KMAcctExpPop::processRemainingQueuedMessages()
00660 {
00661   kdDebug(5006) << k_funcinfo << endl;
00662   slotProcessPendingMsgs(); // Force processing of any messages still in the queue
00663   processMsgsTimer.stop();
00664 
00665   stage = Quit;
00666   kmkernel->folderMgr()->syncAllFolders();
00667 }
00668 
00669 
00670 //-----------------------------------------------------------------------------
00671 void KMAcctExpPop::saveUidList()
00672 {
00673   kdDebug(5006) << k_funcinfo << endl;
00674   // Don't update the seen uid list unless we successfully got
00675   // a new list from the server
00676   if (!mUidlFinished) return;
00677 
00678   QStringList uidsOfNextSeenMsgs;
00679   QDictIterator<int> it( mUidsOfNextSeenMsgsDict );
00680   for( ; it.current(); ++it )
00681     uidsOfNextSeenMsgs.append( it.currentKey() );
00682   QString seenUidList = locateLocal( "data", "kmail/" + mLogin + ":" + "@" +
00683                      mHost + ":" + QString("%1").arg(mPort) );
00684   KConfig config( seenUidList );
00685   config.writeEntry( "seenUidList", uidsOfNextSeenMsgs );
00686   config.writeEntry( "downloadLater", QStringList( mHeaderLaterUids.keys() ) );
00687   config.sync();
00688 }
00689 
00690 
00691 //-----------------------------------------------------------------------------
00692 void KMAcctExpPop::slotGetNextMsg()
00693 {
00694   QMap<QString, int>::Iterator next = mMsgsPendingDownload.begin();
00695 
00696   curMsgData.resize(0);
00697   numMsgBytesRead = 0;
00698   curMsgLen = 0;
00699   delete curMsgStrm;
00700   curMsgStrm = 0;
00701 
00702   if ( next != mMsgsPendingDownload.end() ) {
00703     // get the next message
00704     int nextLen = next.data();
00705     curMsgStrm = new QDataStream( curMsgData, IO_WriteOnly );
00706     curMsgLen = nextLen;
00707     ++indexOfCurrentMsg;
00708     kdDebug(5006) << QString("Length of message about to get %1").arg( nextLen ) << endl;
00709     mMsgsPendingDownload.remove( next.key() );
00710   }
00711 }
00712 
00713 
00714 //-----------------------------------------------------------------------------
00715 void KMAcctExpPop::slotData( KIO::Job* job, const QByteArray &data)
00716 {
00717   if (data.size() == 0) {
00718     kdDebug(5006) << "Data: <End>" << endl;
00719     if ((stage == Retr) && (numMsgBytesRead < curMsgLen))
00720       numBytesRead += curMsgLen - numMsgBytesRead;
00721     else if (stage == Head){
00722       kdDebug(5006) << "Head: <End>" << endl;
00723     }
00724     return;
00725   }
00726 
00727   int oldNumMsgBytesRead = numMsgBytesRead;
00728   if (stage == Retr) {
00729     headers = false;
00730     curMsgStrm->writeRawBytes( data.data(), data.size() );
00731     numMsgBytesRead += data.size();
00732     if (numMsgBytesRead > curMsgLen)
00733       numMsgBytesRead = curMsgLen;
00734     numBytesRead += numMsgBytesRead - oldNumMsgBytesRead;
00735     dataCounter++;
00736     if (dataCounter % 5 == 0)
00737     {
00738       QString msg;
00739       if (numBytes != numBytesToRead && mLeaveOnServer)
00740       {
00741     msg = i18n("Fetching message %1 of %2 (%3 of %4 KB) from %5 "
00742            "(%6 KB remain on the server).")
00743       .arg(indexOfCurrentMsg+1).arg(numMsgs).arg(numBytesRead/1024)
00744       .arg(numBytesToRead/1024).arg(mHost).arg(numBytes/1024);
00745       }
00746       else
00747       {
00748     msg = i18n("Fetching message %1 of %2 (%3 of %4 KB) from %5.")
00749       .arg(indexOfCurrentMsg+1).arg(numMsgs).arg(numBytesRead/1024)
00750       .arg(numBytesToRead/1024).arg(mHost);
00751       }
00752       mMailCheckProgressItem->setStatus( msg );
00753       mMailCheckProgressItem->setProgress(
00754         (numBytesToRead <= 100) ? 50  // We never know what the server tells us
00755         // This way of dividing is required for > 21MB of mail
00756         : (numBytesRead / (numBytesToRead / 100)) );
00757     }
00758     return;
00759   }
00760 
00761   if (stage == Head) {
00762     curMsgStrm->writeRawBytes( data.data(), data.size() );
00763     return;
00764   }
00765 
00766   // otherwise stage is List Or Uidl
00767   QString qdata = data;
00768   qdata = qdata.simplifyWhiteSpace(); // Workaround for Maillennium POP3/UNIBOX
00769   int spc = qdata.find( ' ' );
00770   if (spc > 0) {
00771     if (stage == List) {
00772       QString length = qdata.mid(spc+1);
00773       if (length.find(' ') != -1) length.truncate(length.find(' '));
00774       int len = length.toInt();
00775       numBytes += len;
00776       QString id = qdata.left(spc);
00777       idsOfMsgs.append( id );
00778       mMsgsPendingDownload.insert( id, len );
00779     }
00780     else { // stage == Uidl
00781       const QString id = qdata.left(spc);
00782       const QString uid = qdata.mid(spc + 1);
00783       if ( mUidsOfSeenMsgsDict.find( uid ) != 0 ) {
00784         if ( mMsgsPendingDownload.contains( id ) ) {
00785           mMsgsPendingDownload.remove( id );
00786         }
00787         else
00788           kdDebug(5006) << "KMAcctExpPop::slotData synchronization failure." << endl;
00789         idsOfMsgsToDelete.append( id );
00790         mUidsOfNextSeenMsgsDict.insert( uid, (const int *)1 );
00791       }
00792       mUidForIdMap.insert( id, uid );
00793     }
00794   }
00795   else {
00796     stage = Idle;
00797     if (job) job->kill();
00798     job = 0;
00799     mSlave = 0;
00800     KMessageBox::error(0, i18n( "Unable to complete LIST operation." ),
00801                           i18n("Invalid Response From Server"));
00802     return;
00803   }
00804 }
00805 
00806 
00807 //-----------------------------------------------------------------------------
00808 void KMAcctExpPop::slotResult( KIO::Job* )
00809 {
00810   if (!job) return;
00811   if ( job->error() )
00812   {
00813     if (interactive) {
00814       if (headers) { // nothing to be done for headers
00815         idsOfMsgs.clear();
00816       }
00817       if (stage == Head && job->error() == KIO::ERR_COULD_NOT_READ)
00818       {
00819         KMessageBox::error(0, i18n("Your server does not support the "
00820           "TOP command. Therefore it is not possible to fetch the headers "
00821       "of large emails first, before downloading them."));
00822         slotCancel();
00823         return;
00824       }
00825       // force the dialog to be shown next time the account is checked
00826       if (!mStorePasswd) mPasswd = "";
00827       job->showErrorDialog();
00828     }
00829     slotCancel();
00830   }
00831   else
00832     slotJobFinished();
00833 }
00834 
00835 
00836 //-----------------------------------------------------------------------------
00837 void KMAcctExpPop::slotSlaveError(KIO::Slave *aSlave, int error,
00838   const QString &errorMsg)
00839 {
00840   if (aSlave != mSlave) return;
00841   if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
00842 
00843   // explicitely disconnect the slave if the connection went down
00844   if ( error == KIO::ERR_CONNECTION_BROKEN && mSlave ) {
00845     KIO::Scheduler::disconnectSlave( mSlave );
00846     mSlave = 0;
00847   }
00848 
00849   if (interactive) {
00850     KMessageBox::error(kmkernel->mainWin(), KIO::buildErrorString(error, errorMsg));
00851   }
00852 
00853 
00854   stage = Quit;
00855   if (error == KIO::ERR_COULD_NOT_LOGIN && !mStorePasswd)
00856     mAskAgain = TRUE;
00857   /* We need a timer, otherwise slotSlaveError of the next account is also
00858      executed, if it reuses the slave, because the slave member variable
00859      is changed too early */
00860   QTimer::singleShot(0, this, SLOT(slotCancel()));
00861 }
00862 
00863 //-----------------------------------------------------------------------------
00864 void KMAcctExpPop::slotGetNextHdr(){
00865   kdDebug(5006) << "slotGetNextHeader" << endl;
00866 
00867   curMsgData.resize(0);
00868   delete curMsgStrm;
00869   curMsgStrm = 0;
00870 
00871   curMsgStrm = new QDataStream( curMsgData, IO_WriteOnly );
00872 }
00873 
00874 void KMAcctExpPop::killAllJobs( bool ) {
00875   // must reimpl., but we don't use it yet
00876 }
00877 
00878 #include "kmacctexppop.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Aug 2 09:55:03 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003