00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include "popaccount.h"
00028 
00029 #include "broadcaststatus.h"
00030 using KPIM::BroadcastStatus;
00031 #include "progressmanager.h"
00032 #include "kmfoldermgr.h"
00033 #include "kmfiltermgr.h"
00034 #include "kmpopfiltercnfrmdlg.h"
00035 #include "protocols.h"
00036 #include "kmglobal.h"
00037 #include "util.h"
00038 #include "accountmanager.h"
00039 
00040 #include <kdebug.h>
00041 #include <kstandarddirs.h>
00042 #include <klocale.h>
00043 #include <kmessagebox.h>
00044 #include <kmainwindow.h>
00045 #include <kio/scheduler.h>
00046 #include <kio/passdlg.h>
00047 #include <kconfig.h>
00048 using KIO::MetaData;
00049 
00050 #include <qstylesheet.h>
00051 
00052 static const unsigned short int pop3DefaultPort = 110;
00053 
00054 namespace KMail {
00055 
00056 PopAccount::PopAccount(AccountManager* aOwner, const QString& aAccountName, uint id)
00057   : NetworkAccount(aOwner, aAccountName, id),
00058     headerIt(headersOnServer),
00059     processMsgsTimer( 0, "processMsgsTimer" )
00060 {
00061   init();
00062   job = 0;
00063   mSlave = 0;
00064   mPort = defaultPort();
00065   stage = Idle;
00066   indexOfCurrentMsg = -1;
00067   curMsgStrm = 0;
00068   processingDelay = 2*100;
00069   mProcessing = false;
00070   dataCounter = 0;
00071   mUidsOfSeenMsgsDict.setAutoDelete( false );
00072   mUidsOfNextSeenMsgsDict.setAutoDelete( false );
00073 
00074   headersOnServer.setAutoDelete(true);
00075   connect(&processMsgsTimer,SIGNAL(timeout()),SLOT(slotProcessPendingMsgs()));
00076   KIO::Scheduler::connect(
00077     SIGNAL(slaveError(KIO::Slave *, int, const QString &)),
00078     this, SLOT(slotSlaveError(KIO::Slave *, int, const QString &)));
00079 
00080   mHeaderDeleteUids.clear();
00081   mHeaderDownUids.clear();
00082   mHeaderLaterUids.clear();
00083 }
00084 
00085 
00086 
00087 PopAccount::~PopAccount()
00088 {
00089   if (job) {
00090     job->kill();
00091     mMsgsPendingDownload.clear();
00092     processRemainingQueuedMessages();
00093     saveUidList();
00094   }
00095 }
00096 
00097 
00098 
00099 QString PopAccount::type(void) const
00100 {
00101   return "pop";
00102 }
00103 
00104 QString PopAccount::protocol() const {
00105   return useSSL() ? POP_SSL_PROTOCOL : POP_PROTOCOL;
00106 }
00107 
00108 unsigned short int PopAccount::defaultPort() const {
00109   return pop3DefaultPort;
00110 }
00111 
00112 
00113 void PopAccount::init(void)
00114 {
00115   NetworkAccount::init();
00116 
00117   mUsePipelining = false;
00118   mLeaveOnServer = false;
00119   mLeaveOnServerDays = -1;
00120   mLeaveOnServerCount = -1;
00121   mLeaveOnServerSize = -1;
00122   mFilterOnServer = false;
00123   
00124   mFilterOnServerCheckSize = 50000;
00125 }
00126 
00127 
00128 void PopAccount::pseudoAssign( const KMAccount * a ) {
00129   slotAbortRequested();
00130   NetworkAccount::pseudoAssign( a );
00131 
00132   const PopAccount * p = dynamic_cast<const PopAccount*>( a );
00133   if ( !p ) return;
00134 
00135   setUsePipelining( p->usePipelining() );
00136   setLeaveOnServer( p->leaveOnServer() );
00137   setLeaveOnServerDays( p->leaveOnServerDays() );
00138   setLeaveOnServerCount( p->leaveOnServerCount() );
00139   setLeaveOnServerSize( p->leaveOnServerSize() );
00140   setFilterOnServer( p->filterOnServer() );
00141   setFilterOnServerCheckSize( p->filterOnServerCheckSize() );
00142 }
00143 
00144 
00145 void PopAccount::processNewMail(bool _interactive)
00146 {
00147   if (stage == Idle) {
00148 
00149     if ( (mAskAgain || passwd().isEmpty() || mLogin.isEmpty()) &&
00150       mAuth != "GSSAPI" ) {
00151       QString passwd = NetworkAccount::passwd();
00152       bool b = storePasswd();
00153       if (KIO::PasswordDialog::getNameAndPassword(mLogin, passwd, &b,
00154         i18n("You need to supply a username and a password to access this "
00155         "mailbox."), false, QString::null, mName, i18n("Account:"))
00156         != QDialog::Accepted)
00157       {
00158         checkDone( false, CheckAborted );
00159         return;
00160       } else {
00161         setPasswd( passwd, b );
00162         if ( b ) {
00163           kmkernel->acctMgr()->writeConfig( true );
00164         }
00165         mAskAgain = false;
00166       }
00167     }
00168 
00169     QString seenUidList = locateLocal( "data", "kmail/" + mLogin + ":" + "@" +
00170                                        mHost + ":" + QString("%1").arg(mPort) );
00171     KConfig config( seenUidList );
00172     QStringList uidsOfSeenMsgs = config.readListEntry( "seenUidList" );
00173     QValueList<int> timeOfSeenMsgs = config.readIntListEntry( "seenUidTimeList" );
00174     mUidsOfSeenMsgsDict.clear();
00175     mUidsOfSeenMsgsDict.resize( KMail::nextPrime( ( uidsOfSeenMsgs.count() * 11 ) / 10 ) );
00176     int idx = 1;
00177     for ( QStringList::ConstIterator it = uidsOfSeenMsgs.begin();
00178           it != uidsOfSeenMsgs.end(); ++it, idx++ ) {
00179       
00180       
00181       
00182       mUidsOfSeenMsgsDict.insert( *it, (const int *)idx );
00183     }
00184     mTimeOfSeenMsgsVector.clear();
00185     mTimeOfSeenMsgsVector.reserve( timeOfSeenMsgs.size() );
00186     for ( QValueList<int>::ConstIterator it = timeOfSeenMsgs.begin();
00187           it != timeOfSeenMsgs.end(); ++it) {
00188       mTimeOfSeenMsgsVector.append( *it );
00189     }
00190     
00191     
00192     
00193     if ( mTimeOfSeenMsgsVector.count() != mUidsOfSeenMsgsDict.count() )
00194       mTimeOfSeenMsgsVector.clear();
00195     QStringList downloadLater = config.readListEntry( "downloadLater" );
00196     for ( QStringList::Iterator it = downloadLater.begin(); it != downloadLater.end(); ++it ) {
00197         mHeaderLaterUids.insert( *it, true );
00198     }
00199     mUidsOfNextSeenMsgsDict.clear();
00200     mTimeOfNextSeenMsgsMap.clear();
00201     mSizeOfNextSeenMsgsDict.clear();
00202 
00203     interactive = _interactive;
00204     mUidlFinished = false;
00205     startJob();
00206   }
00207   else {
00208     checkDone( false, CheckIgnored );
00209     return;
00210   }
00211 }
00212 
00213 
00214 
00215 void PopAccount::readConfig(KConfig& config)
00216 {
00217   NetworkAccount::readConfig(config);
00218 
00219   mUsePipelining = config.readNumEntry("pipelining", false);
00220   mLeaveOnServer = config.readNumEntry("leave-on-server", false);
00221   mLeaveOnServerDays = config.readNumEntry("leave-on-server-days", -1);
00222   mLeaveOnServerCount = config.readNumEntry("leave-on-server-count", -1);
00223   mLeaveOnServerSize = config.readNumEntry("leave-on-server-size", -1);
00224   mFilterOnServer = config.readNumEntry("filter-on-server", false);
00225   mFilterOnServerCheckSize = config.readUnsignedNumEntry("filter-os-check-size", 50000);
00226 }
00227 
00228 
00229 
00230 void PopAccount::writeConfig(KConfig& config)
00231 {
00232   NetworkAccount::writeConfig(config);
00233 
00234   config.writeEntry("pipelining", mUsePipelining);
00235   config.writeEntry("leave-on-server", mLeaveOnServer);
00236   config.writeEntry("leave-on-server-days", mLeaveOnServerDays);
00237   config.writeEntry("leave-on-server-count", mLeaveOnServerCount);
00238   config.writeEntry("leave-on-server-size", mLeaveOnServerSize);
00239   config.writeEntry("filter-on-server", mFilterOnServer);
00240   config.writeEntry("filter-os-check-size", mFilterOnServerCheckSize);
00241 }
00242 
00243 
00244 
00245 void PopAccount::setUsePipelining(bool b)
00246 {
00247   mUsePipelining = b;
00248 }
00249 
00250 
00251 void PopAccount::setLeaveOnServer(bool b)
00252 {
00253   mLeaveOnServer = b;
00254 }
00255 
00256 
00257 void PopAccount::setLeaveOnServerDays(int days)
00258 {
00259   mLeaveOnServerDays = days;
00260 }
00261 
00262 
00263 void PopAccount::setLeaveOnServerCount(int count)
00264 {
00265   mLeaveOnServerCount = count;
00266 }
00267 
00268 
00269 void PopAccount::setLeaveOnServerSize(int size)
00270 {
00271   mLeaveOnServerSize = size;
00272 }
00273 
00274 
00275 void PopAccount::setFilterOnServer(bool b)
00276 {
00277   mFilterOnServer = b;
00278 }
00279 
00280 
00281 void PopAccount::setFilterOnServerCheckSize(unsigned int aSize)
00282 {
00283   mFilterOnServerCheckSize = aSize;
00284 }
00285 
00286 
00287 void PopAccount::connectJob() {
00288   KIO::Scheduler::assignJobToSlave(mSlave, job);
00289   connect(job, SIGNAL( data( KIO::Job*, const QByteArray &)),
00290          SLOT( slotData( KIO::Job*, const QByteArray &)));
00291   connect(job, SIGNAL( result( KIO::Job * ) ),
00292          SLOT( slotResult( KIO::Job * ) ) );
00293   connect(job, SIGNAL(infoMessage( KIO::Job*, const QString & )),
00294          SLOT( slotMsgRetrieved(KIO::Job*, const QString &)));
00295 }
00296 
00297 
00298 
00299 void PopAccount::slotCancel()
00300 {
00301   mMsgsPendingDownload.clear();
00302   processRemainingQueuedMessages();
00303   saveUidList();
00304   slotJobFinished();
00305 }
00306 
00307 
00308 
00309 void PopAccount::slotProcessPendingMsgs()
00310 {
00311   if (mProcessing) 
00312     return;
00313   mProcessing = true;
00314 
00315   bool addedOk;
00316   QValueList<KMMessage*>::Iterator cur = msgsAwaitingProcessing.begin();
00317   QStringList::Iterator curId = msgIdsAwaitingProcessing.begin();
00318   QStringList::Iterator curUid = msgUidsAwaitingProcessing.begin();
00319 
00320   while (cur != msgsAwaitingProcessing.end()) {
00321     
00322     
00323     
00324     
00325 
00326     addedOk = processNewMsg(*cur); 
00327 
00328     if (!addedOk) {
00329       mMsgsPendingDownload.clear();
00330       msgIdsAwaitingProcessing.clear();
00331       msgUidsAwaitingProcessing.clear();
00332       break;
00333     }
00334     else {
00335       idsOfMsgsToDelete.append( *curId );
00336       mUidsOfNextSeenMsgsDict.insert( *curUid, (const int *)1 );
00337       mTimeOfNextSeenMsgsMap.insert( *curUid, time(0) );
00338     }
00339     ++cur;
00340     ++curId;
00341     ++curUid;
00342   }
00343 
00344   msgsAwaitingProcessing.clear();
00345   msgIdsAwaitingProcessing.clear();
00346   msgUidsAwaitingProcessing.clear();
00347   mProcessing = false;
00348 }
00349 
00350 
00351 
00352 void PopAccount::slotAbortRequested()
00353 {
00354   if (stage == Idle) return;
00355   if ( mMailCheckProgressItem )
00356     disconnect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
00357                 this, SLOT( slotAbortRequested() ) );
00358   stage = Quit;
00359   if (job) job->kill();
00360   job = 0;
00361   mSlave = 0;
00362   slotCancel();
00363 }
00364 
00365 
00366 
00367 void PopAccount::startJob()
00368 {
00369   
00370   if (!runPrecommand(precommand()))
00371     {
00372       KMessageBox::sorry(0,
00373                          i18n("Could not execute precommand: %1").arg(precommand()),
00374                          i18n("KMail Error Message"));
00375       checkDone( false, CheckError );
00376       return;
00377     }
00378   
00379 
00380   KURL url = getUrl();
00381 
00382   if ( !url.isValid() ) {
00383     KMessageBox::error(0, i18n("Source URL is malformed"),
00384                           i18n("Kioslave Error Message") );
00385     return;
00386   }
00387 
00388   mMsgsPendingDownload.clear();
00389   idsOfMsgs.clear();
00390   mUidForIdMap.clear();
00391   idsOfMsgsToDelete.clear();
00392   idsOfForcedDeletes.clear();
00393 
00394   
00395   headersOnServer.clear();
00396   headers = false;
00397   indexOfCurrentMsg = -1;
00398 
00399   Q_ASSERT( !mMailCheckProgressItem );
00400   QString escapedName = QStyleSheet::escape( mName );
00401   mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem(
00402     "MailCheck" + mName,
00403     escapedName,
00404     i18n("Preparing transmission from \"%1\"...").arg( escapedName ),
00405     true, 
00406     useSSL() || useTLS() );
00407   connect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
00408            this, SLOT( slotAbortRequested() ) );
00409 
00410   numBytes = 0;
00411   numBytesRead = 0;
00412   stage = List;
00413   mSlave = KIO::Scheduler::getConnectedSlave( url, slaveConfig() );
00414   if (!mSlave)
00415   {
00416     slotSlaveError(0, KIO::ERR_CANNOT_LAUNCH_PROCESS, url.protocol());
00417     return;
00418   }
00419   url.setPath(QString("/index"));
00420   job = KIO::get( url, false, false );
00421   connectJob();
00422 }
00423 
00424 MetaData PopAccount::slaveConfig() const {
00425   MetaData m = NetworkAccount::slaveConfig();
00426 
00427   m.insert("progress", "off");
00428   m.insert("pipelining", (mUsePipelining) ? "on" : "off");
00429   if (mAuth == "PLAIN" || mAuth == "LOGIN" || mAuth == "CRAM-MD5" ||
00430       mAuth == "DIGEST-MD5" || mAuth == "NTLM" || mAuth == "GSSAPI") {
00431     m.insert("auth", "SASL");
00432     m.insert("sasl", mAuth);
00433   } else if ( mAuth == "*" )
00434     m.insert("auth", "USER");
00435   else
00436     m.insert("auth", mAuth);
00437 
00438   return m;
00439 }
00440 
00441 
00442 
00443 
00444 void PopAccount::slotMsgRetrieved(KIO::Job*, const QString & infoMsg)
00445 {
00446   if (infoMsg != "message complete") return;
00447   KMMessage *msg = new KMMessage;
00448   msg->setComplete(true);
00449   
00450   
00451   uint newSize = Util::crlf2lf( curMsgData.data(), curMsgData.size() );
00452   curMsgData.resize( newSize );
00453   msg->fromByteArray( curMsgData , true );
00454   if (stage == Head)
00455   {
00456     int size = mMsgsPendingDownload[ headerIt.current()->id() ];
00457     kdDebug(5006) << "Size of Message: " << size << endl;
00458     msg->setMsgLength( size );
00459     headerIt.current()->setHeader(msg);
00460     ++headerIt;
00461     slotGetNextHdr();
00462   } else {
00463     
00464     
00465     msg->setMsgLength( curMsgData.size() );
00466     msgsAwaitingProcessing.append(msg);
00467     msgIdsAwaitingProcessing.append(idsOfMsgs[indexOfCurrentMsg]);
00468     msgUidsAwaitingProcessing.append( mUidForIdMap[idsOfMsgs[indexOfCurrentMsg]] );
00469     slotGetNextMsg();
00470   }
00471 }
00472 
00473 
00474 
00475 
00476 void PopAccount::slotJobFinished() {
00477   QStringList emptyList;
00478   if (stage == List) {
00479     kdDebug(5006) << k_funcinfo << "stage == List" << endl;
00480     
00481     
00482     mUidsOfNextSeenMsgsDict.resize( KMail::nextPrime( ( idsOfMsgs.count() * 11 ) / 10 ) );
00483     KURL url = getUrl();
00484     url.setPath(QString("/uidl"));
00485     job = KIO::get( url, false, false );
00486     connectJob();
00487     stage = Uidl;
00488   }
00489   else if (stage == Uidl) {
00490     kdDebug(5006) << k_funcinfo << "stage == Uidl" << endl;
00491     mUidlFinished = true;
00492 
00493     if ( mLeaveOnServer && mUidForIdMap.isEmpty() &&
00494         mUidsOfNextSeenMsgsDict.isEmpty() && !idsOfMsgs.isEmpty() ) {
00495       KMessageBox::sorry(0, i18n("Your POP3 server (Account: %1) does not support "
00496       "the UIDL command: this command is required to determine, in a reliable way, "
00497       "which of the mails on the server KMail has already seen before;\n"
00498       "the feature to leave the mails on the server will therefore not "
00499       "work properly.").arg(NetworkAccount::name()) );
00500       
00501       mUidsOfNextSeenMsgsDict = mUidsOfSeenMsgsDict;
00502     }
00503 
00504     
00505     if (mFilterOnServer == true) {
00506       QMap<QString, int>::Iterator hids;
00507       for ( hids = mMsgsPendingDownload.begin();
00508             hids != mMsgsPendingDownload.end(); hids++ ) {
00509           kdDebug(5006) << "Length: " << hids.data() << endl;
00510           
00511           if ( (unsigned int)hids.data() >= mFilterOnServerCheckSize ) {
00512             kdDebug(5006) << "bigger than " << mFilterOnServerCheckSize << endl;
00513             headersOnServer.append(new KMPopHeaders( hids.key(),
00514                                                      mUidForIdMap[hids.key()],
00515                                                      Later));
00516             
00517             if( mHeaderDeleteUids.contains( headersOnServer.current()->uid() ) ) {
00518               headersOnServer.current()->setAction(Delete);
00519             }
00520             else if( mHeaderDownUids.contains( headersOnServer.current()->uid() ) ) {
00521               headersOnServer.current()->setAction(Down);
00522             }
00523             else if( mHeaderLaterUids.contains( headersOnServer.current()->uid() ) ) {
00524               headersOnServer.current()->setAction(Later);
00525             }
00526           }
00527       }
00528       
00529       mHeaderDeleteUids.clear();
00530       mHeaderDownUids.clear();
00531       mHeaderLaterUids.clear();
00532     }
00533     
00534     
00535     if ((headersOnServer.count() > 0) && (mFilterOnServer == true)) {
00536       headerIt.toFirst();
00537       KURL url = getUrl();
00538       QString headerIds;
00539       while (headerIt.current())
00540       {
00541         headerIds += headerIt.current()->id();
00542         if (!headerIt.atLast()) headerIds += ",";
00543         ++headerIt;
00544       }
00545       headerIt.toFirst();
00546       url.setPath(QString("/headers/") + headerIds);
00547       job = KIO::get( url, false, false );
00548       connectJob();
00549       slotGetNextHdr();
00550       stage = Head;
00551     }
00552     else {
00553       stage = Retr;
00554       numMsgs = mMsgsPendingDownload.count();
00555       numBytesToRead = 0;
00556       QMap<QString, int>::Iterator len;
00557       for ( len  = mMsgsPendingDownload.begin();
00558             len != mMsgsPendingDownload.end(); len++ )
00559         numBytesToRead += len.data();
00560       idsOfMsgs = QStringList( mMsgsPendingDownload.keys() );
00561       KURL url = getUrl();
00562       url.setPath( "/download/" + idsOfMsgs.join(",") );
00563       job = KIO::get( url, false, false );
00564       connectJob();
00565       slotGetNextMsg();
00566       processMsgsTimer.start(processingDelay);
00567     }
00568   }
00569   else if (stage == Head) {
00570     kdDebug(5006) << k_funcinfo << "stage == Head" << endl;
00571 
00572     
00573     
00574 
00575     
00576     
00577     KMPopFilterAction action;
00578     bool dlgPopup = false;
00579     for (headersOnServer.first(); headersOnServer.current(); headersOnServer.next()) {
00580       action = (KMPopFilterAction)kmkernel->popFilterMgr()->process(headersOnServer.current()->header());
00581       
00582       switch ( action ) {
00583         case NoAction:
00584           kdDebug(5006) << "PopFilterAction = NoAction" << endl;
00585           break;
00586         case Later:
00587           kdDebug(5006) << "PopFilterAction = Later" << endl;
00588           break;
00589         case Delete:
00590           kdDebug(5006) << "PopFilterAction = Delete" << endl;
00591           break;
00592         case Down:
00593           kdDebug(5006) << "PopFilterAction = Down" << endl;
00594           break;
00595         default:
00596           kdDebug(5006) << "PopFilterAction = default oops!" << endl;
00597           break;
00598       }
00599       switch ( action ) {
00600         case NoAction:
00601           
00602           dlgPopup = true;
00603           break;
00604         case Later:
00605           if (kmkernel->popFilterMgr()->showLaterMsgs())
00606             dlgPopup = true;
00607           
00608         default:
00609           headersOnServer.current()->setAction(action);
00610           headersOnServer.current()->setRuleMatched(true);
00611           break;
00612       }
00613     }
00614 
00615     
00616     
00617     headers = true;
00618     if (dlgPopup) {
00619       KMPopFilterCnfrmDlg dlg(&headersOnServer, this->name(), kmkernel->popFilterMgr()->showLaterMsgs());
00620       dlg.exec();
00621     }
00622 
00623     for (headersOnServer.first(); headersOnServer.current(); headersOnServer.next()) {
00624       if (headersOnServer.current()->action() == Delete ||
00625           headersOnServer.current()->action() == Later) {
00626         
00627         
00628         if ( mMsgsPendingDownload.contains( headersOnServer.current()->id() ) ) {
00629           mMsgsPendingDownload.remove( headersOnServer.current()->id() );
00630         }
00631         if (headersOnServer.current()->action() == Delete) {
00632           mHeaderDeleteUids.insert(headersOnServer.current()->uid(), true);
00633           mUidsOfNextSeenMsgsDict.insert( headersOnServer.current()->uid(),
00634                                           (const int *)1 );
00635           idsOfMsgsToDelete.append(headersOnServer.current()->id());
00636           mTimeOfNextSeenMsgsMap.insert( headersOnServer.current()->uid(),
00637                                           time(0) );
00638         }
00639         else {
00640           mHeaderLaterUids.insert(headersOnServer.current()->uid(), true);
00641         }
00642       }
00643       else if (headersOnServer.current()->action() == Down) {
00644         mHeaderDownUids.insert(headersOnServer.current()->uid(), true);
00645       }
00646     }
00647 
00648     headersOnServer.clear();
00649     stage = Retr;
00650     numMsgs = mMsgsPendingDownload.count();
00651     numBytesToRead = 0;
00652     QMap<QString, int>::Iterator len;
00653     for (len = mMsgsPendingDownload.begin();
00654          len != mMsgsPendingDownload.end(); len++)
00655       numBytesToRead += len.data();
00656     idsOfMsgs = QStringList( mMsgsPendingDownload.keys() );
00657     KURL url = getUrl();
00658     url.setPath( "/download/" + idsOfMsgs.join(",") );
00659     job = KIO::get( url, false, false );
00660     connectJob();
00661     slotGetNextMsg();
00662     processMsgsTimer.start(processingDelay);
00663   }
00664   else if (stage == Retr) {
00665     if ( mMailCheckProgressItem )
00666       mMailCheckProgressItem->setProgress( 100 );
00667     processRemainingQueuedMessages();
00668 
00669     mHeaderDeleteUids.clear();
00670     mHeaderDownUids.clear();
00671     mHeaderLaterUids.clear();
00672 
00673     kmkernel->folderMgr()->syncAllFolders();
00674 
00675     KURL url = getUrl();
00676     QMap< QPair<time_t, QString>, int > idsToSave;
00677     idsToSave.clear();
00678     
00679     if ( mLeaveOnServer && !idsOfMsgsToDelete.isEmpty() ) {
00680       
00681       if ( mLeaveOnServerDays == -1 && mLeaveOnServerCount <= 0 &&
00682            mLeaveOnServerSize <= 0)
00683         idsOfMsgsToDelete.clear();
00684       
00685       else if ( mLeaveOnServerDays > 0 && !mTimeOfNextSeenMsgsMap.isEmpty() ) {
00686         time_t timeLimit = time(0) - (86400 * mLeaveOnServerDays);
00687         kdDebug() << "timeLimit is " << timeLimit << endl;
00688         QStringList::Iterator cur = idsOfMsgsToDelete.begin();
00689         for ( ; cur != idsOfMsgsToDelete.end(); ++cur) {
00690           time_t msgTime = mTimeOfNextSeenMsgsMap[mUidForIdMap[*cur]];
00691           kdDebug() << "id: " << *cur << " msgTime: " << msgTime << endl;
00692           if (msgTime >= timeLimit ||
00693                 !mTimeOfNextSeenMsgsMap[mUidForIdMap[*cur]]) {
00694             kdDebug() << "Saving msg id " << *cur << endl;
00695             QPair<time_t, QString> msg(msgTime, *cur);
00696             idsToSave.insert( msg, 1 );
00697           }
00698         }
00699       }
00700       
00701       if ( mLeaveOnServerCount > 0 ) {
00702         int numToDelete = idsToSave.count() - mLeaveOnServerCount;
00703         kdDebug() << "numToDelete is " << numToDelete << endl;
00704         if ( numToDelete > 0 && (unsigned)numToDelete < idsToSave.count() ) {
00705           QMap< QPair<time_t, QString>, int >::Iterator cur = idsToSave.begin();
00706           for ( int deleted = 0; deleted < numToDelete && cur != idsToSave.end()
00707                 ; deleted++, cur++ ) {
00708             kdDebug() << "deleting msg id " << cur.key().second << endl;
00709             idsToSave.remove( cur );
00710           }
00711         }
00712         else if ( numToDelete > 0 && (unsigned)numToDelete >= idsToSave.count() )
00713           idsToSave.clear();
00714       }
00715       
00716       if ( mLeaveOnServerSize > 0 ) {
00717         double sizeOnServer = 0;
00718         QMap< QPair<time_t, QString>, int >::Iterator cur = idsToSave.begin();
00719         for ( ; cur != idsToSave.end(); cur++ ) {
00720           sizeOnServer +=
00721             *mSizeOfNextSeenMsgsDict[ mUidForIdMap[ cur.key().second ] ];
00722         }
00723         kdDebug() << "sizeOnServer is " << sizeOnServer/(1024*1024) << "MB" << endl;
00724         long limitInBytes = mLeaveOnServerSize * ( 1024 * 1024 );
00725         for ( cur = idsToSave.begin(); cur != idsToSave.end()
00726                 && sizeOnServer > limitInBytes; cur++ ) {
00727           sizeOnServer -=
00728             *mSizeOfNextSeenMsgsDict[ mUidForIdMap[ cur.key().second ] ];
00729           idsToSave.remove( cur );
00730         }
00731       }
00732       
00733       QMap< QPair<time_t, QString>, int >::Iterator it = idsToSave.begin();
00734       kdDebug() << "Going to save " << idsToSave.count() << endl;
00735       for ( ; it != idsToSave.end(); ++it ) {
00736         kdDebug() << "saving msg id " << it.key().second << endl;
00737         idsOfMsgsToDelete.remove( it.key().second );
00738       }
00739     }
00740 
00741     if ( !idsOfForcedDeletes.isEmpty() ) {
00742       idsOfMsgsToDelete += idsOfForcedDeletes;
00743       idsOfForcedDeletes.clear();
00744     }
00745 
00746     
00747     if ( !idsOfMsgsToDelete.isEmpty() ) {
00748       stage = Dele;
00749       if ( mMailCheckProgressItem )
00750         mMailCheckProgressItem->setStatus(
00751           i18n( "Fetched 1 message from %1. Deleting messages from server...",
00752                 "Fetched %n messages from %1. Deleting messages from server...",
00753                 numMsgs )
00754           .arg( mHost ) );
00755       url.setPath("/remove/" + idsOfMsgsToDelete.join(","));
00756       kdDebug(5006) << "url: " << url.prettyURL() << endl;
00757     } else {
00758       stage = Quit;
00759       if ( mMailCheckProgressItem )
00760         mMailCheckProgressItem->setStatus(
00761           i18n( "Fetched 1 message from %1. Terminating transmission...",
00762                 "Fetched %n messages from %1. Terminating transmission...",
00763                 numMsgs )
00764           .arg( mHost ) );
00765       url.setPath(QString("/commit"));
00766       kdDebug(5006) << "url: " << url.prettyURL() << endl;
00767     }
00768     job = KIO::get( url, false, false );
00769     connectJob();
00770   }
00771   else if (stage == Dele) {
00772     kdDebug(5006) << k_funcinfo << "stage == Dele" << endl;
00773     
00774     for ( QStringList::ConstIterator it = idsOfMsgsToDelete.begin();
00775           it != idsOfMsgsToDelete.end(); ++it ) {
00776       mUidsOfNextSeenMsgsDict.remove( mUidForIdMap[*it] );
00777     }
00778     idsOfMsgsToDelete.clear();
00779     if ( mMailCheckProgressItem )
00780       mMailCheckProgressItem->setStatus(
00781         i18n( "Fetched 1 message from %1. Terminating transmission...",
00782               "Fetched %n messages from %1. Terminating transmission...",
00783               numMsgs )
00784         .arg( mHost ) );
00785     KURL url = getUrl();
00786     url.setPath(QString("/commit"));
00787     job = KIO::get( url, false, false );
00788     stage = Quit;
00789     connectJob();
00790   }
00791   else if (stage == Quit) {
00792     kdDebug(5006) << k_funcinfo << "stage == Quit" << endl;
00793     saveUidList();
00794     job = 0;
00795     if (mSlave) KIO::Scheduler::disconnectSlave(mSlave);
00796     mSlave = 0;
00797     stage = Idle;
00798     if( mMailCheckProgressItem ) { 
00799       bool canceled = !kmkernel || kmkernel->mailCheckAborted() || mMailCheckProgressItem->canceled();
00800       int numMessages = canceled ? indexOfCurrentMsg : idsOfMsgs.count();
00801       BroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
00802         this->name(), numMessages, numBytes, numBytesRead, numBytesToRead, mLeaveOnServer, mMailCheckProgressItem );
00803       
00804       
00805       ProgressItem *savedMailCheckProgressItem = mMailCheckProgressItem;
00806       mMailCheckProgressItem = 0;
00807       savedMailCheckProgressItem->setComplete(); 
00808       checkDone( ( numMessages > 0 ), canceled ? CheckAborted : CheckOK );
00809     }
00810   }
00811 }
00812 
00813 
00814 
00815 void PopAccount::processRemainingQueuedMessages()
00816 {
00817   kdDebug(5006) << k_funcinfo << endl;
00818   slotProcessPendingMsgs(); 
00819   processMsgsTimer.stop();
00820 
00821   stage = Quit;
00822   if ( kmkernel && kmkernel->folderMgr() ) {
00823     kmkernel->folderMgr()->syncAllFolders();
00824   }
00825 }
00826 
00827 
00828 
00829 void PopAccount::saveUidList()
00830 {
00831   kdDebug(5006) << k_funcinfo << endl;
00832   
00833   
00834   if (!mUidlFinished) return;
00835 
00836   QStringList uidsOfNextSeenMsgs;
00837   QValueList<int> seenUidTimeList;
00838   QDictIterator<int> it( mUidsOfNextSeenMsgsDict );
00839   for( ; it.current(); ++it ) {
00840     uidsOfNextSeenMsgs.append( it.currentKey() );
00841     seenUidTimeList.append( mTimeOfNextSeenMsgsMap[it.currentKey()] );
00842   }
00843   QString seenUidList = locateLocal( "data", "kmail/" + mLogin + ":" + "@" +
00844                                       mHost + ":" + QString("%1").arg(mPort) );
00845   KConfig config( seenUidList );
00846   config.writeEntry( "seenUidList", uidsOfNextSeenMsgs );
00847   config.writeEntry( "seenUidTimeList", seenUidTimeList );
00848   config.writeEntry( "downloadLater", QStringList( mHeaderLaterUids.keys() ) );
00849   config.sync();
00850 }
00851 
00852 
00853 
00854 void PopAccount::slotGetNextMsg()
00855 {
00856   QMap<QString, int>::Iterator next = mMsgsPendingDownload.begin();
00857 
00858   curMsgData.resize(0);
00859   numMsgBytesRead = 0;
00860   curMsgLen = 0;
00861   delete curMsgStrm;
00862   curMsgStrm = 0;
00863 
00864   if ( next != mMsgsPendingDownload.end() ) {
00865     
00866     int nextLen = next.data();
00867     curMsgStrm = new QDataStream( curMsgData, IO_WriteOnly );
00868     curMsgLen = nextLen;
00869     ++indexOfCurrentMsg;
00870     kdDebug(5006) << QString("Length of message about to get %1").arg( nextLen ) << endl;
00871     mMsgsPendingDownload.remove( next.key() );
00872   }
00873 }
00874 
00875 
00876 
00877 void PopAccount::slotData( KIO::Job* job, const QByteArray &data)
00878 {
00879   if (data.size() == 0) {
00880     kdDebug(5006) << "Data: <End>" << endl;
00881     if ((stage == Retr) && (numMsgBytesRead < curMsgLen))
00882       numBytesRead += curMsgLen - numMsgBytesRead;
00883     else if (stage == Head){
00884       kdDebug(5006) << "Head: <End>" << endl;
00885     }
00886     return;
00887   }
00888 
00889   int oldNumMsgBytesRead = numMsgBytesRead;
00890   if (stage == Retr) {
00891     headers = false;
00892     curMsgStrm->writeRawBytes( data.data(), data.size() );
00893     numMsgBytesRead += data.size();
00894     if (numMsgBytesRead > curMsgLen)
00895       numMsgBytesRead = curMsgLen;
00896     numBytesRead += numMsgBytesRead - oldNumMsgBytesRead;
00897     dataCounter++;
00898     if ( mMailCheckProgressItem &&
00899          ( dataCounter % 5 == 0 ||
00900            ( indexOfCurrentMsg + 1 == numMsgs && numMsgBytesRead == curMsgLen ) ) )
00901     {
00902       QString msg;
00903       if (numBytes != numBytesToRead && mLeaveOnServer)
00904       {
00905         msg = i18n("Fetching message %1 of %2 (%3 of %4 KB) for %5@%6 "
00906                    "(%7 KB remain on the server).")
00907           .arg(indexOfCurrentMsg+1).arg(numMsgs).arg(numBytesRead/1024)
00908           .arg(numBytesToRead/1024).arg(mLogin).arg(mHost).arg(numBytes/1024);
00909       }
00910       else
00911       {
00912         msg = i18n("Fetching message %1 of %2 (%3 of %4 KB) for %5@%6.")
00913           .arg(indexOfCurrentMsg+1).arg(numMsgs).arg(numBytesRead/1024)
00914           .arg(numBytesToRead/1024).arg(mLogin).arg(mHost);
00915       }
00916       mMailCheckProgressItem->setStatus( msg );
00917       mMailCheckProgressItem->setProgress(
00918         (numBytesToRead <= 100) ? 50  
00919         
00920         : (numBytesRead / (numBytesToRead / 100)) );
00921     }
00922     return;
00923   }
00924 
00925   if (stage == Head) {
00926     curMsgStrm->writeRawBytes( data.data(), data.size() );
00927     return;
00928   }
00929 
00930   
00931   QString qdata = data;
00932   qdata = qdata.simplifyWhiteSpace(); 
00933   int spc = qdata.find( ' ' );
00934   if ( stage == List ) {
00935     if ( spc > 0 ) {
00936       QString length = qdata.mid(spc+1);
00937       if (length.find(' ') != -1) length.truncate(length.find(' '));
00938       int len = length.toInt();
00939       numBytes += len;
00940       QString id = qdata.left(spc);
00941       idsOfMsgs.append( id );
00942       mMsgsPendingDownload.insert( id, len );
00943     }
00944     else {
00945       stage = Idle;
00946       if ( job ) job->kill();
00947       job = 0;
00948       mSlave = 0;
00949       KMessageBox::error( 0, i18n( "Unable to complete LIST operation." ),
00950                              i18n( "Invalid Response From Server") );
00951       return;
00952     }
00953   }
00954   else { 
00955     Q_ASSERT ( stage == Uidl);
00956 
00957     QString id;
00958     QString uid;
00959 
00960     if ( spc <= 0 ) {
00961       
00962       
00963       
00964       
00965 
00966       int testid = atoi ( qdata.ascii() );
00967       if ( testid < 1 ) {
00968         
00969         kdDebug(5006) << "PopAccount::slotData skipping UIDL entry due to parse error "
00970                       << endl << qdata.ascii() << endl;
00971         return;
00972       }
00973       id.setNum (testid, 10);
00974 
00975       QString datestring, serialstring;
00976 
00977       serialstring.setNum ( ++dataCounter, 10 );
00978       datestring.setNum ( time(NULL),10 );
00979       uid = QString( "uidlgen" ) + datestring + QString( "." ) + serialstring;
00980       kdDebug(5006) << "PopAccount::slotData message " << id.ascii()
00981                     <<  "%d has bad UIDL, cannot keep a copy on server" << endl;
00982       idsOfForcedDeletes.append( id );
00983     }
00984     else {
00985       id = qdata.left( spc );
00986       uid = qdata.mid( spc + 1 );
00987     }
00988 
00989     int *size = new int; 
00990     *size = mMsgsPendingDownload[id];
00991     mSizeOfNextSeenMsgsDict.insert( uid, size );
00992     if ( mUidsOfSeenMsgsDict.find( uid ) != 0 ) {
00993       if ( mMsgsPendingDownload.contains( id ) ) {
00994         mMsgsPendingDownload.remove( id );
00995       }
00996       else
00997         kdDebug(5006) << "PopAccount::slotData synchronization failure." << endl;
00998       idsOfMsgsToDelete.append( id );
00999       mUidsOfNextSeenMsgsDict.insert( uid, (const int *)1 );
01000       if ( mTimeOfSeenMsgsVector.empty() ) {
01001         mTimeOfNextSeenMsgsMap.insert( uid, time(0) );
01002       }
01003       else {
01004         
01005         
01006         mTimeOfNextSeenMsgsMap.insert( uid, mTimeOfSeenMsgsVector[(int)( long )
01007                                                  mUidsOfSeenMsgsDict[uid] - 1] );
01008       }
01009     }
01010     mUidForIdMap.insert( id, uid );
01011   }
01012 }
01013 
01014 
01015 void PopAccount::slotResult( KIO::Job* )
01016 {
01017   if (!job) return;
01018   if ( job->error() )
01019   {
01020     if (interactive) {
01021       if (headers) { 
01022         idsOfMsgs.clear();
01023       }
01024       if (stage == Head && job->error() == KIO::ERR_COULD_NOT_READ)
01025       {
01026         KMessageBox::error(0, i18n("Your server does not support the "
01027           "TOP command. Therefore it is not possible to fetch the headers "
01028           "of large emails first, before downloading them."));
01029         slotCancel();
01030         return;
01031       }
01032       
01033       if (!mStorePasswd) mPasswd = "";
01034       job->showErrorDialog();
01035     }
01036     slotCancel();
01037   }
01038   else
01039     slotJobFinished();
01040 }
01041 
01042 
01043 
01044 void PopAccount::slotSlaveError(KIO::Slave *aSlave, int error,
01045   const QString &errorMsg)
01046 {
01047   if (aSlave != mSlave) return;
01048   if (error == KIO::ERR_SLAVE_DIED) mSlave = 0;
01049 
01050   
01051   if ( error == KIO::ERR_CONNECTION_BROKEN && mSlave ) {
01052     KIO::Scheduler::disconnectSlave( mSlave );
01053     mSlave = 0;
01054   }
01055 
01056   if (interactive && kmkernel) {
01057     KMessageBox::error(kmkernel->mainWin(), KIO::buildErrorString(error, errorMsg));
01058   }
01059 
01060 
01061   stage = Quit;
01062   if (error == KIO::ERR_COULD_NOT_LOGIN && !mStorePasswd)
01063     mAskAgain = true;
01064   
01065 
01066 
01067   QTimer::singleShot(0, this, SLOT(slotCancel()));
01068 }
01069 
01070 
01071 void PopAccount::slotGetNextHdr(){
01072   kdDebug(5006) << "slotGetNextHeader" << endl;
01073 
01074   curMsgData.resize(0);
01075   delete curMsgStrm;
01076   curMsgStrm = 0;
01077 
01078   curMsgStrm = new QDataStream( curMsgData, IO_WriteOnly );
01079 }
01080 
01081 void PopAccount::killAllJobs( bool ) {
01082   
01083 }
01084 
01085 } 
01086 #include "popaccount.moc"