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