kmail

popaccount.cpp

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