• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

kmail

popaccount.cpp

Go to the documentation of this file.
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 
00024 #include "popaccount.h"
00025 
00026 #include "broadcaststatus.h"
00027 using KPIM::BroadcastStatus;
00028 #include "progressmanager.h"
00029 #include "kmfoldermgr.h"
00030 #include "kmfiltermgr.h"
00031 #include "kmpopfiltercnfrmdlg.h"
00032 #include "protocols.h"
00033 #include "kmglobal.h"
00034 #include "util.h"
00035 #include "accountmanager.h"
00036 
00037 #include <kdebug.h>
00038 #include <kstandarddirs.h>
00039 #include <klocale.h>
00040 #include <kmessagebox.h>
00041 #include <kmainwindow.h>
00042 #include <kio/scheduler.h>
00043 #include <kio/passworddialog.h>
00044 #include <kconfig.h>
00045 #include <kconfiggroup.h>
00046 #include <kio/jobuidelegate.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     mPopFilterConfirmationDialog( 0 ),
00056     mHeaderIndex( 0 )
00057 {
00058   init();
00059   job = 0;
00060   mSlave = 0;
00061   mPort = defaultPort();
00062   stage = Idle;
00063   indexOfCurrentMsg = -1;
00064   curMsgStrm = 0;
00065   processingDelay = 2*100;
00066   mProcessing = false;
00067   dataCounter = 0;
00068 
00069   connect(&processMsgsTimer,SIGNAL(timeout()),SLOT(slotProcessPendingMsgs()));
00070   KIO::Scheduler::connect(
00071     SIGNAL(slaveError(KIO::Slave *, int, const QString &)),
00072     this, SLOT(slotSlaveError(KIO::Slave *, int, const QString &)));
00073 
00074   mHeaderDeleteUids.clear();
00075   mHeaderDownUids.clear();
00076   mHeaderLaterUids.clear();
00077 }
00078 
00079 
00080 //-----------------------------------------------------------------------------
00081 PopAccount::~PopAccount()
00082 {
00083   if (job) {
00084     job->kill();
00085     mMsgsPendingDownload.clear();
00086     processRemainingQueuedMessages();
00087     saveUidList();
00088   }
00089 }
00090 
00091 //-----------------------------------------------------------------------------
00092 QString PopAccount::protocol() const {
00093   return useSSL() ? POP_SSL_PROTOCOL : POP_PROTOCOL;
00094 }
00095 
00096 unsigned short int PopAccount::defaultPort() const {
00097   return pop3DefaultPort;
00098 }
00099 
00100 //-----------------------------------------------------------------------------
00101 void PopAccount::init(void)
00102 {
00103   NetworkAccount::init();
00104 
00105   mUsePipelining = false;
00106   mLeaveOnServer = false;
00107   mLeaveOnServerDays = -1;
00108   mLeaveOnServerCount = -1;
00109   mLeaveOnServerSize = -1;
00110   mFilterOnServer = false;
00111   //tz todo
00112   mFilterOnServerCheckSize = 50000;
00113 }
00114 
00115 //-----------------------------------------------------------------------------
00116 void PopAccount::cancelMailCheck()
00117 {
00118   slotAbortRequested();
00119 }
00120 
00121 //-----------------------------------------------------------------------------
00122 void PopAccount::pseudoAssign( const KMAccount * a ) {
00123   slotAbortRequested();
00124   NetworkAccount::pseudoAssign( a );
00125 
00126   const PopAccount * p = dynamic_cast<const PopAccount*>( a );
00127   if ( !p ) return;
00128 
00129   setUsePipelining( p->usePipelining() );
00130   setLeaveOnServer( p->leaveOnServer() );
00131   setLeaveOnServerDays( p->leaveOnServerDays() );
00132   setLeaveOnServerCount( p->leaveOnServerCount() );
00133   setLeaveOnServerSize( p->leaveOnServerSize() );
00134   setFilterOnServer( p->filterOnServer() );
00135   setFilterOnServerCheckSize( p->filterOnServerCheckSize() );
00136 }
00137 
00138 //-----------------------------------------------------------------------------
00139 void PopAccount::processNewMail(bool _interactive)
00140 {
00141   if (stage == Idle) {
00142 
00143     if ( (mAskAgain || passwd().isEmpty() || mLogin.isEmpty()) &&
00144       mAuth != "GSSAPI" ) {
00145       QString passwd = NetworkAccount::passwd();
00146       bool b = storePasswd();
00147       if (KIO::PasswordDialog::getNameAndPassword(mLogin, passwd, &b,
00148         i18n("You need to supply a username and a password to access this "
00149         "mailbox."), false, QString(), mName, i18n("Account:"))
00150         != QDialog::Accepted)
00151       {
00152         checkDone( false, CheckAborted );
00153         return;
00154       } else {
00155         setPasswd( passwd, b );
00156         if ( b ) {
00157           kmkernel->acctMgr()->writeConfig( true );
00158         }
00159         mAskAgain = false;
00160       }
00161     }
00162 
00163     QString seenUidList = KStandardDirs::locateLocal( "data", "kmail/" + mLogin + ':' + '@' +
00164                                        mHost + ':' + QString("%1").arg(mPort) );
00165     KConfig config( seenUidList );
00166     KConfigGroup group( &config, "<default>" );
00167     QStringList uidsOfSeenMsgs = group.readEntry( "seenUidList", QStringList() );
00168     mUidsOfSeenMsgsDict.clear();
00169     mUidsOfSeenMsgsDict.reserve( KMail::nextPrime( ( uidsOfSeenMsgs.count() * 11 ) / 10 ) );
00170     for ( int i = 0; i < uidsOfSeenMsgs.count(); ++i ) {
00171       // we use mUidsOfSeenMsgsDict to just provide fast random access to the
00172       // keys, so we can store the index that corresponds to the index of
00173       // mTimeOfSeenMsgsVector for use in PopAccount::slotData()
00174       mUidsOfSeenMsgsDict.insert( uidsOfSeenMsgs[i].toLatin1(), i );
00175     }
00176     QList<int> timeOfSeenMsgs = group.readEntry( "seenUidTimeList",QList<int>() );
00177     // If the counts differ then the config file has presumably been tampered
00178     // with and so to avoid possible unwanted message deletion we'll treat
00179     // them all as newly seen by clearing the seen times vector
00180     if ( timeOfSeenMsgs.count() == mUidsOfSeenMsgsDict.count() )
00181       mTimeOfSeenMsgsVector = timeOfSeenMsgs.toVector();
00182     else
00183       mTimeOfSeenMsgsVector.clear();
00184     QStringList downloadLater = group.readEntry( "downloadLater", QStringList() );
00185     for ( int i = 0; i < downloadLater.count(); ++i ) {
00186       mHeaderLaterUids.insert( downloadLater[i].toLatin1() );
00187     }
00188     mUidsOfNextSeenMsgsDict.clear();
00189     mTimeOfNextSeenMsgsMap.clear();
00190     mSizeOfNextSeenMsgsDict.clear();
00191 
00192     interactive = _interactive;
00193     mUidlFinished = false;
00194     startJob();
00195   }
00196   else {
00197     checkDone( false, CheckIgnored );
00198     return;
00199   }
00200 }
00201 
00202 
00203 //-----------------------------------------------------------------------------
00204 void PopAccount::readConfig(KConfigGroup& config)
00205 {
00206   NetworkAccount::readConfig(config);
00207 
00208   mUsePipelining = config.readEntry("pipelining", false );
00209   mLeaveOnServer = config.readEntry("leave-on-server", false );
00210   mLeaveOnServerDays = config.readEntry("leave-on-server-days", -1 );
00211   mLeaveOnServerCount = config.readEntry("leave-on-server-count", -1 );
00212   mLeaveOnServerSize = config.readEntry("leave-on-server-size", -1 );
00213   mFilterOnServer = config.readEntry("filter-on-server", false );
00214   mFilterOnServerCheckSize = config.readEntry("filter-os-check-size", (uint) 50000 );
00215 }
00216 
00217 
00218 //-----------------------------------------------------------------------------
00219 void PopAccount::writeConfig(KConfigGroup& config)
00220 {
00221   NetworkAccount::writeConfig(config);
00222 
00223   config.writeEntry("pipelining", mUsePipelining);
00224   config.writeEntry("leave-on-server", mLeaveOnServer);
00225   config.writeEntry("leave-on-server-days", mLeaveOnServerDays);
00226   config.writeEntry("leave-on-server-count", mLeaveOnServerCount);
00227   config.writeEntry("leave-on-server-size", mLeaveOnServerSize);
00228   config.writeEntry("filter-on-server", mFilterOnServer);
00229   config.writeEntry("filter-os-check-size", mFilterOnServerCheckSize);
00230 }
00231 
00232 
00233 //-----------------------------------------------------------------------------
00234 void PopAccount::setUsePipelining(bool b)
00235 {
00236   mUsePipelining = b;
00237 }
00238 
00239 //-----------------------------------------------------------------------------
00240 void PopAccount::setLeaveOnServer(bool b)
00241 {
00242   mLeaveOnServer = b;
00243 }
00244 
00245 //-----------------------------------------------------------------------------
00246 void PopAccount::setLeaveOnServerDays(int days)
00247 {
00248   mLeaveOnServerDays = days;
00249 }
00250 
00251 //-----------------------------------------------------------------------------
00252 void PopAccount::setLeaveOnServerCount(int count)
00253 {
00254   mLeaveOnServerCount = count;
00255 }
00256 
00257 //-----------------------------------------------------------------------------
00258 void PopAccount::setLeaveOnServerSize(int size)
00259 {
00260   mLeaveOnServerSize = size;
00261 }
00262 
00263 //---------------------------------------------------------------------------
00264 void PopAccount::setFilterOnServer(bool b)
00265 {
00266   mFilterOnServer = b;
00267 }
00268 
00269 //---------------------------------------------------------------------------
00270 void PopAccount::setFilterOnServerCheckSize(unsigned int aSize)
00271 {
00272   mFilterOnServerCheckSize = aSize;
00273 }
00274 
00275 //-----------------------------------------------------------------------------
00276 void PopAccount::connectJob() {
00277   KIO::Scheduler::assignJobToSlave(mSlave, job);
00278   connect(job, SIGNAL( data( KIO::Job*, const QByteArray &)),
00279          SLOT( slotData( KIO::Job*, const QByteArray &)));
00280   connect(job, SIGNAL( result( KJob * ) ),
00281          SLOT( slotResult( KJob * ) ) );
00282   connect(job, SIGNAL(infoMessage( KJob*, const QString &, const QString & )),
00283          SLOT( slotMsgRetrieved(KJob*, const QString &, const QString &)));
00284 }
00285 
00286 
00287 //-----------------------------------------------------------------------------
00288 void PopAccount::slotCancel()
00289 {
00290   mMsgsPendingDownload.clear();
00291   processRemainingQueuedMessages();
00292   saveUidList();
00293   slotJobFinished();
00294 
00295   // Close the pop filter confirmation dialog. Otherwise, KMail crashes because
00296   // slotJobFinished(), which creates that dialog, will try to continue downloading
00297   // when the user closes the dialog.
00298   if ( mPopFilterConfirmationDialog ) {
00299 
00300     // Disconnect the signal, as we are already called from slotAbortRequested()
00301     disconnect( mPopFilterConfirmationDialog, SIGNAL( rejected() ),
00302                 this, SLOT( slotAbortRequested() ) );
00303     mPopFilterConfirmationDialog->reject();
00304   }
00305 }
00306 
00307 
00308 //-----------------------------------------------------------------------------
00309 void PopAccount::slotProcessPendingMsgs()
00310 {
00311   if (mProcessing) // not reentrant
00312     return;
00313   mProcessing = true;
00314 
00315   while ( !msgsAwaitingProcessing.isEmpty() ) {
00316     // note we can actually end up processing events in processNewMsg
00317     // this happens when send receipts is turned on
00318     // hence the check for re-entry at the start of this method.
00319     // -sanders Update processNewMsg should no longer process events
00320 
00321     KMMessage *msg = msgsAwaitingProcessing.dequeue();
00322     const bool addedOk = processNewMsg( msg ); //added ok? Error displayed if not.
00323 
00324     if ( !addedOk ) {
00325       mMsgsPendingDownload.clear();
00326       break;
00327     }
00328 
00329     const QByteArray curId = msgIdsAwaitingProcessing.dequeue();
00330     const QByteArray curUid = msgUidsAwaitingProcessing.dequeue();
00331     idsOfMsgsToDelete.insert( curId );
00332     mUidsOfNextSeenMsgsDict.insert( curUid, 1 );
00333     mTimeOfNextSeenMsgsMap.insert( curUid, time(0) );
00334   }
00335 
00336   msgsAwaitingProcessing.clear();
00337   msgIdsAwaitingProcessing.clear();
00338   msgUidsAwaitingProcessing.clear();
00339   mProcessing = false;
00340 }
00341 
00342 
00343 //-----------------------------------------------------------------------------
00344 void PopAccount::slotAbortRequested()
00345 {
00346   kDebug(5006);
00347   if (stage == Idle)
00348     return;
00349   disconnect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
00350               this, SLOT( slotAbortRequested() ) );
00351   stage = Quit;
00352   if (job)
00353     job->kill();
00354   job = 0;
00355   mSlave = 0;
00356   slotCancel();
00357 }
00358 
00359 
00360 //-----------------------------------------------------------------------------
00361 void PopAccount::startJob()
00362 {
00363   // Run the precommand
00364   if ( !runPrecommand(precommand() ) ) {
00365     checkDone( false, CheckError );
00366     return;
00367   }
00368   // end precommand code
00369 
00370   KUrl url = getUrl();
00371 
00372   if ( !url.isValid() ) {
00373     KMessageBox::error(0, i18n("Source URL is malformed"),
00374                           i18n("Kioslave Error Message") );
00375     return;
00376   }
00377 
00378   mMsgsPendingDownload.clear();
00379   idsOfMsgs.clear();
00380   mUidForIdMap.clear();
00381   idsOfMsgsToDelete.clear();
00382   idsOfForcedDeletes.clear();
00383   //delete any headers if there are some this have to be done because of check again
00384   qDeleteAll( mHeadersOnServer );
00385   mHeadersOnServer.clear();
00386   headers = false;
00387   indexOfCurrentMsg = -1;
00388 
00389   Q_ASSERT( !mMailCheckProgressItem );
00390   mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem(
00391     "MailCheck" + mName,
00392     mName,
00393     i18n("Preparing transmission from \"%1\"...", mName),
00394     true, // can be canceled
00395     useSSL() || useTLS() );
00396   connect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
00397            this, SLOT( slotAbortRequested() ) );
00398 
00399   numBytes = 0;
00400   numBytesRead = 0;
00401   stage = List;
00402   mSlave = KIO::Scheduler::getConnectedSlave( url, slaveConfig() );
00403   if (!mSlave)
00404   {
00405     slotSlaveError(0, KIO::ERR_CANNOT_LAUNCH_PROCESS, url.protocol());
00406     return;
00407   }
00408   url.setPath( "/index" );
00409   job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
00410   connectJob();
00411 }
00412 
00413 MetaData PopAccount::slaveConfig() const {
00414   MetaData m = NetworkAccount::slaveConfig();
00415 
00416   m.insert("progress", "off");
00417   m.insert("pipelining", (mUsePipelining) ? "on" : "off");
00418   if (mAuth == "PLAIN" || mAuth == "LOGIN" || mAuth == "CRAM-MD5" ||
00419       mAuth == "DIGEST-MD5" || mAuth == "NTLM" || mAuth == "GSSAPI") {
00420     m.insert("auth", "SASL");
00421     m.insert("sasl", mAuth);
00422   } else if ( mAuth == "*" )
00423     m.insert("auth", "USER");
00424   else
00425     m.insert("auth", mAuth);
00426 
00427   return m;
00428 }
00429 
00430 //-----------------------------------------------------------------------------
00431 // one message is finished
00432 // add data to a KMMessage
00433 void PopAccount::slotMsgRetrieved(KJob*, const QString & infoMsg, const QString &)
00434 {
00435   if (infoMsg != "message complete") return;
00436   KMMessage *msg = new KMMessage;
00437   msg->setComplete(true);
00438   // Make sure to use LF as line ending to make the processing easier
00439   // when piping through external programs
00440   uint newSize = Util::crlf2lf( curMsgData.data(), curMsgData.size() );
00441   curMsgData.resize( newSize );
00442   msg->fromString( curMsgData, true );
00443   if ( stage == Head ) {
00444     KMPopHeaders *header = mHeadersOnServer[ mHeaderIndex ];
00445     int size = mMsgsPendingDownload[ header->id() ];
00446     kDebug(5006) <<"Size of Message:" << size;
00447     msg->setMsgLength( size );
00448     header->setHeader( msg );
00449     ++mHeaderIndex;
00450     slotGetNextHdr();
00451   } else {
00452     //kDebug(5006) << kfuncinfo <<"stage == Retr";
00453     //kDebug(5006) <<"curMsgData.size() =" << curMsgData.size();
00454     msg->setMsgLength( curMsgData.size() );
00455     msgsAwaitingProcessing.enqueue( msg );
00456     msgIdsAwaitingProcessing.enqueue( idsOfMsgs[indexOfCurrentMsg] );
00457     msgUidsAwaitingProcessing.enqueue( mUidForIdMap[ idsOfMsgs[indexOfCurrentMsg] ] );
00458     slotGetNextMsg();
00459   }
00460 }
00461 
00462 
00463 //-----------------------------------------------------------------------------
00464 // finit state machine to cycle trow the stages
00465 void PopAccount::slotJobFinished() {
00466   QStringList emptyList;
00467   if (stage == List) {
00468     kDebug(5006) <<"stage == List";
00469     // set the initial size of mUidsOfNextSeenMsgsDict to the number of
00470     // messages on the server + 10%
00471     mUidsOfNextSeenMsgsDict.reserve( KMail::nextPrime( ( idsOfMsgs.count() * 11 ) / 10 ) );
00472     KUrl url = getUrl();
00473     url.setPath( "/uidl" );
00474     job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
00475     connectJob();
00476     stage = Uidl;
00477   }
00478   else if (stage == Uidl) {
00479     kDebug(5006) <<"stage == Uidl";
00480     mUidlFinished = true;
00481 
00482     if ( mLeaveOnServer && mUidForIdMap.isEmpty() &&
00483         mUidsOfNextSeenMsgsDict.isEmpty() && !idsOfMsgs.isEmpty() ) {
00484       KMessageBox::sorry(0, i18n("Your POP3 server (Account: %1) does not support "
00485       "the UIDL command: this command is required to determine, in a reliable way, "
00486       "which of the mails on the server KMail has already seen before;\n"
00487       "the feature to leave the mails on the server will therefore not "
00488       "work properly.", NetworkAccount::name()) );
00489       // An attempt to work around buggy pop servers, these seem to be popular.
00490       mUidsOfNextSeenMsgsDict = mUidsOfSeenMsgsDict;
00491     }
00492 
00493     //check if filter on server
00494     if (mFilterOnServer == true) {
00495       for ( QMap<QByteArray, int>::const_iterator hids = mMsgsPendingDownload.begin();
00496             hids != mMsgsPendingDownload.end(); ++hids ) {
00497           kDebug(5006) <<"Length:" << hids.value();
00498           //check for mails bigger mFilterOnServerCheckSize
00499           if ( (unsigned int)hids.value() >= mFilterOnServerCheckSize ) {
00500             kDebug(5006) <<"bigger than" << mFilterOnServerCheckSize;
00501             const QByteArray uid = mUidForIdMap[ hids.key() ];
00502             KMPopHeaders *header = new KMPopHeaders( hids.key(), uid, Later );
00503             //set Action if already known
00504             if ( mHeaderDeleteUids.contains( uid ) ) {
00505               header->setAction(Delete);
00506             }
00507             else if ( mHeaderDownUids.contains( uid ) ) {
00508               header->setAction(Down);
00509             }
00510             else if ( mHeaderLaterUids.contains( uid ) ) {
00511               header->setAction(Later);
00512             }
00513             mHeadersOnServer.append( header );
00514           }
00515       }
00516       // delete the uids so that you don't get them twice in the list
00517       mHeaderDeleteUids.clear();
00518       mHeaderDownUids.clear();
00519       mHeaderLaterUids.clear();
00520     }
00521     // kDebug(5006) <<"Num of Msgs to Filter:" << mHeadersOnServer.count();
00522     // if there are mails which should be checkedc download the headers
00523     if ( ( mHeadersOnServer.count() > 0 ) && ( mFilterOnServer == true ) ) {
00524       KUrl url = getUrl();
00525       QByteArray headerIds = mHeadersOnServer[0]->id();
00526       for ( int i = 1; i < mHeadersOnServer.count(); ++i ) {
00527         headerIds += ',';
00528         headerIds += mHeadersOnServer[i]->id();
00529       }
00530       mHeaderIndex = 0;
00531       url.setPath( "/headers/" + headerIds );
00532       job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
00533       connectJob();
00534       slotGetNextHdr();
00535       stage = Head;
00536     }
00537     else {
00538       stage = Retr;
00539       numMsgs = mMsgsPendingDownload.count();
00540       numBytesToRead = 0;
00541       idsOfMsgs.clear();
00542       QByteArray ids;
00543       if ( numMsgs > 0 ) {
00544         for ( QMap<QByteArray, int>::const_iterator it = mMsgsPendingDownload.begin();
00545               it != mMsgsPendingDownload.end(); ++it ) {
00546           numBytesToRead += it.value();
00547           ids += it.key() + ',';
00548           idsOfMsgs.append( it.key() );
00549         }
00550         ids.chop( 1 ); // cut off the trailing ','
00551       }
00552       KUrl url = getUrl();
00553       url.setPath( "/download/" + ids );
00554       job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
00555       connectJob();
00556       slotGetNextMsg();
00557       processMsgsTimer.start(processingDelay);
00558     }
00559   }
00560   else if (stage == Head) {
00561     kDebug(5006) <<"stage == Head";
00562 
00563     // All headers have been downloaded, check which mail you want to get
00564     // data is in list mHeadersOnServer
00565 
00566     // check if headers apply to a filter
00567     // if set the action of the filter
00568     KMPopFilterAction action;
00569     bool dlgPopup = false;
00570     for ( int i = 0; i < mHeadersOnServer.count(); ++i ) {
00571       KMPopHeaders *header = mHeadersOnServer[i];
00572       action = (KMPopFilterAction)kmkernel->popFilterMgr()->process( header->header() );
00573       //debug todo
00574       switch ( action ) {
00575         case NoAction:
00576           kDebug(5006) <<"PopFilterAction = NoAction";
00577           break;
00578         case Later:
00579           kDebug(5006) <<"PopFilterAction = Later";
00580           break;
00581         case Delete:
00582           kDebug(5006) <<"PopFilterAction = Delete";
00583           break;
00584         case Down:
00585           kDebug(5006) <<"PopFilterAction = Down";
00586           break;
00587         default:
00588           kDebug(5006) <<"PopFilterAction = default oops!";
00589           break;
00590       }
00591       switch ( action ) {
00592         case NoAction:
00593           //kDebug(5006) <<"PopFilterAction = NoAction";
00594           dlgPopup = true;
00595           break;
00596         case Later:
00597           if (kmkernel->popFilterMgr()->showLaterMsgs())
00598             dlgPopup = true;
00599           // fall through
00600         default:
00601           header->setAction( action );
00602           header->setRuleMatched( true );
00603           break;
00604       }
00605     }
00606 
00607     // if there are some messages which are not coverd by a filter
00608     // show the dialog
00609     headers = true;
00610     if ( dlgPopup ) {
00611       mPopFilterConfirmationDialog =
00612           new KMPopFilterCnfrmDlg( mHeadersOnServer, this->name(),
00613                                    kmkernel->popFilterMgr()->showLaterMsgs() );
00614       connect( mPopFilterConfirmationDialog, SIGNAL( rejected() ),
00615                this, SLOT( slotAbortRequested() ) );
00616       mPopFilterConfirmationDialog->exec();
00617     }
00618 
00619     // If the dialog was accepted or never shown (because all pop filters already
00620     // set the actions), mark the messages for deletion, download or for keeping
00621     // them. Then advance to the next stage, the download stage.
00622     if ( !dlgPopup ||
00623          mPopFilterConfirmationDialog->result() == QDialog::Accepted ) {
00624 
00625       for ( int i = 0; i < mHeadersOnServer.count(); ++i ) {
00626         const KMPopHeaders *header = mHeadersOnServer[i];
00627         if ( header->action() == Delete || header->action() == Later) {
00628           //remove entries from the lists when the mails should not be downloaded
00629           //(deleted or downloaded later)
00630           mMsgsPendingDownload.remove( header->id() );
00631           if ( header->action() == Delete ) {
00632             mHeaderDeleteUids.insert( header->uid() );
00633             mUidsOfNextSeenMsgsDict.insert( header->uid(), 1 );
00634             idsOfMsgsToDelete.insert( header->id() );
00635             mTimeOfNextSeenMsgsMap.insert( header->uid(), time(0) );
00636           }
00637           else {
00638             mHeaderLaterUids.insert( header->uid() );
00639           }
00640         }
00641         else if ( header->action() == Down ) {
00642           mHeaderDownUids.insert( header->uid() );
00643         }
00644       }
00645 
00646       qDeleteAll( mHeadersOnServer );
00647       mHeadersOnServer.clear();
00648       stage = Retr;
00649       numMsgs = mMsgsPendingDownload.count();
00650       numBytesToRead = 0;
00651       idsOfMsgs.clear();
00652       QByteArray ids;
00653       if ( numMsgs > 0 ) {
00654         for ( QMap<QByteArray, int>::const_iterator it = mMsgsPendingDownload.begin();
00655               it != mMsgsPendingDownload.end(); ++it ) {
00656           numBytesToRead += it.value();
00657           ids += it.key() + ',';
00658           idsOfMsgs.append( it.key() );
00659         }
00660         ids.chop( 1 ); // cut off the trailing ','
00661       }
00662       KUrl url = getUrl();
00663       url.setPath( "/download/" + ids );
00664       job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
00665       connectJob();
00666       slotGetNextMsg();
00667       processMsgsTimer.start(processingDelay);
00668     }
00669     delete mPopFilterConfirmationDialog;
00670     mPopFilterConfirmationDialog = 0;
00671   }
00672   else if (stage == Retr) {
00673     mMailCheckProgressItem->setProgress( 100 );
00674     processRemainingQueuedMessages();
00675 
00676     mHeaderDeleteUids.clear();
00677     mHeaderDownUids.clear();
00678     mHeaderLaterUids.clear();
00679 
00680     kmkernel->folderMgr()->syncAllFolders();
00681 
00682     KUrl url = getUrl();
00683 
00684     // Check if we want to keep any messages.
00685     //
00686     // The default is to delete all messages which have been sucessfully downloaded
00687     // or which we have seen before (which are remembered in the config file).
00688     // This excludes only messages which we have not seen before and at
00689     // the same time failed to download correctly, or messages which the pop
00690     // filter manager decided to leave on the server (the "download later" option)
00691     // The messages which we want to delete are contained in idsOfMsgsToDelete.
00692     //
00693     // In the code below, we check if any "leave on server" rules apply and remove
00694     // the messages which should be left on the server from idsOfMsgsToDelete.
00695     // This is done by storing the messages to leave on the server in idsToSave,
00696     // which is later subtracted from idsOfMsgsToDelete.
00697 
00698     // Start with an empty list of messages to keep
00699     QList< QPair<time_t, QByteArray> > idsToSave;
00700 
00701     if ( mLeaveOnServer && !idsOfMsgsToDelete.isEmpty() ) {
00702 
00703       // If the time-limited leave rule is checked, add the newer messages to
00704       // the list of messages to keep
00705       if ( mLeaveOnServerDays > 0 && !mTimeOfNextSeenMsgsMap.isEmpty() ) {
00706         time_t timeLimit = time(0) - (86400 * mLeaveOnServerDays);
00707         for ( QSet<QByteArray>::const_iterator it = idsOfMsgsToDelete.begin();
00708               it != idsOfMsgsToDelete.end(); ++it ) {
00709           time_t msgTime = mTimeOfNextSeenMsgsMap[ mUidForIdMap[*it] ];
00710           if ( msgTime >= timeLimit || msgTime == 0 ) {
00711             QPair<time_t, QByteArray> pair( msgTime, *it );
00712             idsToSave.append( pair );
00713           }
00714         }
00715       }
00716 
00717       // Otherwise, add all messages to the list of messages to keep - this may
00718       // be reduced in the following number-limited leave rule and size-limited
00719       // leave rule checks
00720       else {
00721         foreach ( const QByteArray id, idsOfMsgsToDelete ) {
00722           time_t msgTime = mTimeOfNextSeenMsgsMap[ mUidForIdMap[id] ];
00723           QPair<time_t, QByteArray> pair( msgTime, id );
00724           idsToSave.append( pair );
00725         }
00726       }
00727 
00728       // sort the idsToSave list so that in the following we remove the oldest messages
00729       qSort( idsToSave );
00730 
00731       // Delete more old messages if there are more than mLeaveOnServerCount
00732       if ( mLeaveOnServerCount > 0 ) {
00733         int numToDelete = idsToSave.count() - mLeaveOnServerCount;
00734         if ( numToDelete > 0 && numToDelete < idsToSave.count() ) {
00735           // get rid of the first numToDelete messages
00736           #ifdef DEBUG
00737           for ( int i = 0; i < numToDelete; ++i )
00738             kDebug(5006) <<"deleting msg id" << idsToSave[i].second;
00739           #endif
00740           idsToSave = idsToSave.mid( numToDelete );
00741         }
00742         else if ( numToDelete >= idsToSave.count() )
00743           idsToSave.clear();
00744       }
00745 
00746       // Delete more old messages until we're under mLeaveOnServerSize MBs
00747       if ( mLeaveOnServerSize > 0 ) {
00748         const long limitInBytes = mLeaveOnServerSize * ( 1024 * 1024 );
00749         long sizeOnServer = 0;
00750         int firstMsgToKeep = idsToSave.count() - 1;
00751         for ( ; firstMsgToKeep >= 0 && sizeOnServer <= limitInBytes; --firstMsgToKeep ) {
00752           sizeOnServer +=
00753             mSizeOfNextSeenMsgsDict[ mUidForIdMap[ idsToSave[firstMsgToKeep].second ] ];
00754         }
00755         if ( sizeOnServer > limitInBytes )
00756           firstMsgToKeep++;
00757         #ifdef DEBUG
00758         for ( int i = 0; i < firstMsgToKeep; ++i )
00759           kDebug(5006) <<"deleting msg id" << idsToSave[i].second;
00760         #endif
00761         if ( firstMsgToKeep > 0 )
00762           idsToSave = idsToSave.mid( firstMsgToKeep );
00763       }
00764       // Save msgs from deletion
00765       kDebug(5006) << "Going to save" << idsToSave.count();
00766       for ( int i = 0; i < idsToSave.count(); ++i ) {
00767         kDebug(5006) << "keeping msg id" << idsToSave[i].second;
00768         idsOfMsgsToDelete.remove( idsToSave[i].second );
00769       }
00770     }
00771 
00772     if ( !idsOfForcedDeletes.isEmpty() ) {
00773       idsOfMsgsToDelete += idsOfForcedDeletes;
00774       idsOfForcedDeletes.clear();
00775     }
00776 
00777     // If there are messages to delete then delete them
00778     if ( !idsOfMsgsToDelete.isEmpty() ) {
00779       stage = Dele;
00780       mMailCheckProgressItem->setStatus(
00781         i18np( "Fetched 1 message from %2. Deleting messages from server...",
00782               "Fetched %1 messages from %2. Deleting messages from server...",
00783               numMsgs,
00784           mHost ) );
00785       QSet<QByteArray>::const_iterator it = idsOfMsgsToDelete.begin();
00786       QByteArray ids = *it;
00787       ++it;
00788       for ( ; it != idsOfMsgsToDelete.end(); ++it ) {
00789         ids += ',';
00790         ids += *it;
00791       }
00792       url.setPath( "/remove/" + ids );
00793     } else {
00794       stage = Quit;
00795       mMailCheckProgressItem->setStatus(
00796         i18np( "Fetched 1 message from %2. Terminating transmission...",
00797               "Fetched %1 messages from %2. Terminating transmission...",
00798               numMsgs,
00799           mHost ) );
00800       url.setPath( "/commit" );
00801     }
00802     job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
00803     connectJob();
00804   }
00805   else if (stage == Dele) {
00806     kDebug(5006) <<"stage == Dele";
00807     // remove the uids of all messages which have been deleted
00808     for ( QSet<QByteArray>::const_iterator it = idsOfMsgsToDelete.begin();
00809           it != idsOfMsgsToDelete.end(); ++it ) {
00810       mUidsOfNextSeenMsgsDict.remove( mUidForIdMap[*it] );
00811     }
00812     idsOfMsgsToDelete.clear();
00813     mMailCheckProgressItem->setStatus(
00814       i18np( "Fetched 1 message from %2. Terminating transmission...",
00815             "Fetched %1 messages from %2. Terminating transmission...",
00816             numMsgs,
00817         mHost ) );
00818     KUrl url = getUrl();
00819     url.setPath( "/commit" );
00820     job = KIO::get( url, KIO::NoReload, KIO::HideProgressInfo );
00821     stage = Quit;
00822     connectJob();
00823   }
00824   else if (stage == Quit) {
00825     kDebug(5006) <<"stage == Quit";
00826     saveUidList();
00827     job = 0;
00828     if ( mSlave )
00829       KIO::Scheduler::disconnectSlave(mSlave);
00830     mSlave = 0;
00831     stage = Idle;
00832     if ( mMailCheckProgressItem ) { // do this only once...
00833       bool canceled = !kmkernel || kmkernel->mailCheckAborted() ||
00834                       mMailCheckProgressItem->canceled();
00835       int numMessages = canceled ? indexOfCurrentMsg : idsOfMsgs.count();
00836       BroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
00837                        this->name(), numMessages, numBytes, numBytesRead,
00838                        numBytesToRead, mLeaveOnServer, mMailCheckProgressItem );
00839       mMailCheckProgressItem->setComplete();
00840       mMailCheckProgressItem = 0;
00841       checkDone( ( numMessages > 0 ), canceled ? CheckAborted : CheckOK );
00842     }
00843   }
00844 }
00845 
00846 
00847 //-----------------------------------------------------------------------------
00848 void PopAccount::processRemainingQueuedMessages()
00849 {
00850   kDebug(5006) ;
00851   slotProcessPendingMsgs(); // Force processing of any messages still in the queue
00852   processMsgsTimer.stop();
00853 
00854   stage = Quit;
00855   if ( kmkernel && kmkernel->folderMgr() ) {
00856     kmkernel->folderMgr()->syncAllFolders();
00857   }
00858 }
00859 
00860 
00861 //-----------------------------------------------------------------------------
00862 void PopAccount::saveUidList()
00863 {
00864   kDebug(5006) ;
00865   // Don't update the seen uid list unless we successfully got
00866   // a new list from the server
00867   if (!mUidlFinished) return;
00868 
00869   QStringList uidsOfNextSeenMsgs;
00870   QList<int> seenUidTimeList;
00871   for ( QHash<QByteArray,int>::const_iterator it = mUidsOfNextSeenMsgsDict.begin();
00872        it != mUidsOfNextSeenMsgsDict.end(); ++it ) {
00873     uidsOfNextSeenMsgs.append( it.key() );
00874     seenUidTimeList.append( mTimeOfNextSeenMsgsMap[ it.key() ] );
00875   }
00876   QString seenUidList = KStandardDirs::locateLocal( "data", "kmail/" + mLogin + ':' + '@' +
00877                                       mHost + ':' + QString::number( mPort ) );
00878   KConfig config( seenUidList );
00879   KConfigGroup group( &config, "<default>" );
00880   group.writeEntry( "seenUidList", uidsOfNextSeenMsgs );
00881   group.writeEntry( "seenUidTimeList", seenUidTimeList );
00882   QByteArray laterList;
00883   laterList.reserve( mHeaderLaterUids.count() * 5 ); // what's the average size of a uid?
00884   foreach( const QByteArray& uid, mHeaderLaterUids.values() ) {
00885       if ( !laterList.isEmpty() )
00886           laterList += ',';
00887       laterList.append( uid );
00888   }
00889   group.writeEntry( "downloadLater", laterList.constData() );
00890   config.sync();
00891 }
00892 
00893 
00894 //-----------------------------------------------------------------------------
00895 void PopAccount::slotGetNextMsg()
00896 {
00897   curMsgData.resize(0);
00898   numMsgBytesRead = 0;
00899   curMsgLen = 0;
00900   delete curMsgStrm;
00901   curMsgStrm = 0;
00902 
00903   if ( !mMsgsPendingDownload.isEmpty() ) {
00904     // get the next message
00905     QMap<QByteArray, int>::iterator next = mMsgsPendingDownload.begin();
00906     curMsgStrm = new QDataStream( &curMsgData, QIODevice::WriteOnly );
00907     curMsgLen = next.value();
00908     ++indexOfCurrentMsg;
00909     kDebug(5006) << QString("Length of message about to get %1").arg( curMsgLen );
00910     mMsgsPendingDownload.erase( next );
00911   }
00912 }
00913 
00914 
00915 //-----------------------------------------------------------------------------
00916 void PopAccount::slotData( KIO::Job* job, const QByteArray &data)
00917 {
00918   Q_UNUSED( job );
00919 
00920   if (data.size() == 0) {
00921     kDebug(5006) <<"Data: <End>";
00922     if ((stage == Retr) && (numMsgBytesRead < curMsgLen))
00923       numBytesRead += curMsgLen - numMsgBytesRead;
00924     else if (stage == Head){
00925       kDebug(5006) <<"Head: <End>";
00926     }
00927     return;
00928   }
00929 
00930   int oldNumMsgBytesRead = numMsgBytesRead;
00931   if (stage == Retr) {
00932     headers = false;
00933     curMsgStrm->writeRawData( data.data(), data.size() );
00934     numMsgBytesRead += data.size();
00935     if (numMsgBytesRead > curMsgLen)
00936       numMsgBytesRead = curMsgLen;
00937     numBytesRead += numMsgBytesRead - oldNumMsgBytesRead;
00938     dataCounter++;
00939     if (dataCounter % 5 == 0)
00940     {
00941       QString msg;
00942       if (numBytes != numBytesToRead && mLeaveOnServer)
00943       {
00944         msg = ki18n("Fetching message %1 of %2 (%3 of %4 KB) for %5@%6 "
00945                     "(%7 KB remain on the server).")
00946            .subs( indexOfCurrentMsg+1 ).subs( numMsgs )
00947            .subs( numBytesRead/1024 ).subs( numBytesToRead/1024 )
00948            .subs( mLogin ).subs( mHost ).subs( numBytes/1024 )
00949            .toString();
00950       }
00951       else
00952       {
00953         msg = ki18n("Fetching message %1 of %2 (%3 of %4 KB) for %5@%6.")
00954            .subs( indexOfCurrentMsg+1 ).subs( numMsgs ).subs( numBytesRead/1024 )
00955            .subs( numBytesToRead/1024 ).subs( mLogin ).subs( mHost )
00956            .toString();
00957       }
00958       mMailCheckProgressItem->setStatus( msg );
00959       mMailCheckProgressItem->setProgress(
00960         (numBytesToRead <= 100) ? 50  // We never know what the server tells us
00961         // This way of dividing is required for > 21MB of mail
00962         : (numBytesRead / (numBytesToRead / 100)) );
00963     }
00964     return