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