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