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