kmail

popaccount.cpp
1/*
2 This file is part of KMail, the KDE mail client.
3 Copyright (c) 2000 Don Sanders <sanders@kde.org>
4
5 Based on popaccount by:
6 Stefan Taferner <taferner@kde.org>
7 Markus Wuebben <markus.wuebben@kde.org>
8
9 KMail is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License, version 2, as
11 published by the Free Software Foundation.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21*/
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include "popaccount.h"
28
29#include "broadcaststatus.h"
30using KPIM::BroadcastStatus;
31#include "progressmanager.h"
32#include "kmfoldermgr.h"
33#include "kmfiltermgr.h"
34#include "kmpopfiltercnfrmdlg.h"
35#include "protocols.h"
36#include "kmglobal.h"
37#include "util.h"
38#include "accountmanager.h"
39
40#include <kdebug.h>
41#include <tdestandarddirs.h>
42#include <tdelocale.h>
43#include <tdemessagebox.h>
44#include <tdemainwindow.h>
45#include <tdeio/scheduler.h>
46#include <tdeio/passdlg.h>
47#include <tdeconfig.h>
48using TDEIO::MetaData;
49
50#include <tqstylesheet.h>
51
52static const unsigned short int pop3DefaultPort = 110;
53
54namespace KMail {
55//-----------------------------------------------------------------------------
56PopAccount::PopAccount(AccountManager* aOwner, const TQString& aAccountName, uint id)
57 : NetworkAccount(aOwner, aAccountName, id),
58 headerIt(headersOnServer),
59 processMsgsTimer( 0, "processMsgsTimer" )
60{
61 init();
62 job = 0;
63 mSlave = 0;
64 mPort = defaultPort();
65 stage = Idle;
66 indexOfCurrentMsg = -1;
67 curMsgStrm = 0;
68 processingDelay = 2*100;
69 mProcessing = false;
70 dataCounter = 0;
71 mUidsOfSeenMsgsDict.setAutoDelete( false );
72 mUidsOfNextSeenMsgsDict.setAutoDelete( false );
73
74 headersOnServer.setAutoDelete(true);
75 connect(&processMsgsTimer,TQ_SIGNAL(timeout()),TQ_SLOT(slotProcessPendingMsgs()));
76 TDEIO::Scheduler::connect(
77 TQ_SIGNAL(slaveError(TDEIO::Slave *, int, const TQString &)),
78 this, TQ_SLOT(slotSlaveError(TDEIO::Slave *, int, const TQString &)));
79
80 mHeaderDeleteUids.clear();
81 mHeaderDownUids.clear();
82 mHeaderLaterUids.clear();
83}
84
85
86//-----------------------------------------------------------------------------
87PopAccount::~PopAccount()
88{
89 if (job) {
90 job->kill();
91 mMsgsPendingDownload.clear();
92 processRemainingQueuedMessages();
93 saveUidList();
94 }
95}
96
97
98//-----------------------------------------------------------------------------
99TQString PopAccount::type(void) const
100{
101 return "pop";
102}
103
104TQString PopAccount::protocol() const {
105 return useSSL() ? POP_SSL_PROTOCOL : POP_PROTOCOL;
106}
107
108unsigned short int PopAccount::defaultPort() const {
109 return pop3DefaultPort;
110}
111
112//-----------------------------------------------------------------------------
113void PopAccount::init(void)
114{
115 NetworkAccount::init();
116
117 mUsePipelining = false;
118 mLeaveOnServer = false;
119 mLeaveOnServerDays = -1;
120 mLeaveOnServerCount = -1;
121 mLeaveOnServerSize = -1;
122 mFilterOnServer = false;
123 //tz todo
124 mFilterOnServerCheckSize = 50000;
125}
126
127//-----------------------------------------------------------------------------
128void PopAccount::pseudoAssign( const KMAccount * a ) {
129 slotAbortRequested();
130 NetworkAccount::pseudoAssign( a );
131
132 const PopAccount * p = dynamic_cast<const PopAccount*>( a );
133 if ( !p ) return;
134
135 setUsePipelining( p->usePipelining() );
136 setLeaveOnServer( p->leaveOnServer() );
137 setLeaveOnServerDays( p->leaveOnServerDays() );
138 setLeaveOnServerCount( p->leaveOnServerCount() );
139 setLeaveOnServerSize( p->leaveOnServerSize() );
140 setFilterOnServer( p->filterOnServer() );
141 setFilterOnServerCheckSize( p->filterOnServerCheckSize() );
142}
143
144//-----------------------------------------------------------------------------
145void PopAccount::processNewMail(bool _interactive)
146{
147 if (stage == Idle) {
148
149 if ( (mAskAgain || passwd().isEmpty() || mLogin.isEmpty()) &&
150 mAuth != "GSSAPI" ) {
151 TQString passwd = NetworkAccount::passwd();
152 bool b = storePasswd();
153 if (TDEIO::PasswordDialog::getNameAndPassword(mLogin, passwd, &b,
154 i18n("You need to supply a username and a password to access this "
155 "mailbox."), false, TQString(), mName, i18n("Account:"))
156 != TQDialog::Accepted)
157 {
158 checkDone( false, CheckAborted );
159 return;
160 } else {
161 setPasswd( passwd, b );
162 if ( b ) {
163 kmkernel->acctMgr()->writeConfig( true );
164 }
165 mAskAgain = false;
166 }
167 }
168
169 TQString seenUidList = locateLocal( "data", "kmail/" + mLogin + ":" + "@" +
170 mHost + ":" + TQString("%1").arg(mPort) );
171 TDEConfig config( seenUidList );
172 TQStringList uidsOfSeenMsgs = config.readListEntry( "seenUidList" );
173 TQValueList<int> timeOfSeenMsgs = config.readIntListEntry( "seenUidTimeList" );
174 mUidsOfSeenMsgsDict.clear();
175 mUidsOfSeenMsgsDict.resize( KMail::nextPrime( ( uidsOfSeenMsgs.count() * 11 ) / 10 ) );
176 int idx = 1;
177 for ( TQStringList::ConstIterator it = uidsOfSeenMsgs.begin();
178 it != uidsOfSeenMsgs.end(); ++it, idx++ ) {
179 // we use mUidsOfSeenMsgsDict to just provide fast random access to the
180 // keys, so we can store the index(+1) that corresponds to the index of
181 // mTimeOfSeenMsgsVector for use in PopAccount::slotData()
182 mUidsOfSeenMsgsDict.insert( *it, (const int *)idx );
183 }
184 mTimeOfSeenMsgsVector.clear();
185 mTimeOfSeenMsgsVector.reserve( timeOfSeenMsgs.size() );
186 for ( TQValueList<int>::ConstIterator it = timeOfSeenMsgs.begin();
187 it != timeOfSeenMsgs.end(); ++it) {
188 mTimeOfSeenMsgsVector.append( *it );
189 }
190 // If the counts differ then the config file has presumably been tampered
191 // with and so to avoid possible unwanted message deletion we'll treat
192 // them all as newly seen by clearing the seen times vector
193 if ( mTimeOfSeenMsgsVector.count() != mUidsOfSeenMsgsDict.count() )
194 mTimeOfSeenMsgsVector.clear();
195 TQStringList downloadLater = config.readListEntry( "downloadLater" );
196 for ( TQStringList::Iterator it = downloadLater.begin(); it != downloadLater.end(); ++it ) {
197 mHeaderLaterUids.insert( *it, true );
198 }
199 mUidsOfNextSeenMsgsDict.clear();
200 mTimeOfNextSeenMsgsMap.clear();
201 mSizeOfNextSeenMsgsDict.clear();
202
203 interactive = _interactive;
204 mUidlFinished = false;
205 startJob();
206 }
207 else {
208 checkDone( false, CheckIgnored );
209 return;
210 }
211}
212
213
214//-----------------------------------------------------------------------------
215void PopAccount::readConfig(TDEConfig& config)
216{
217 NetworkAccount::readConfig(config);
218
219 mUsePipelining = config.readNumEntry("pipelining", false);
220 mLeaveOnServer = config.readNumEntry("leave-on-server", false);
221 mLeaveOnServerDays = config.readNumEntry("leave-on-server-days", -1);
222 mLeaveOnServerCount = config.readNumEntry("leave-on-server-count", -1);
223 mLeaveOnServerSize = config.readNumEntry("leave-on-server-size", -1);
224 mFilterOnServer = config.readNumEntry("filter-on-server", false);
225 mFilterOnServerCheckSize = config.readUnsignedNumEntry("filter-os-check-size", 50000);
226}
227
228
229//-----------------------------------------------------------------------------
230void PopAccount::writeConfig(TDEConfig& config)
231{
232 NetworkAccount::writeConfig(config);
233
234 config.writeEntry("pipelining", mUsePipelining);
235 config.writeEntry("leave-on-server", mLeaveOnServer);
236 config.writeEntry("leave-on-server-days", mLeaveOnServerDays);
237 config.writeEntry("leave-on-server-count", mLeaveOnServerCount);
238 config.writeEntry("leave-on-server-size", mLeaveOnServerSize);
239 config.writeEntry("filter-on-server", mFilterOnServer);
240 config.writeEntry("filter-os-check-size", mFilterOnServerCheckSize);
241}
242
243
244//-----------------------------------------------------------------------------
245void PopAccount::setUsePipelining(bool b)
246{
247 mUsePipelining = b;
248}
249
250//-----------------------------------------------------------------------------
251void PopAccount::setLeaveOnServer(bool b)
252{
253 mLeaveOnServer = b;
254}
255
256//-----------------------------------------------------------------------------
257void PopAccount::setLeaveOnServerDays(int days)
258{
259 mLeaveOnServerDays = days;
260}
261
262//-----------------------------------------------------------------------------
263void PopAccount::setLeaveOnServerCount(int count)
264{
265 mLeaveOnServerCount = count;
266}
267
268//-----------------------------------------------------------------------------
269void PopAccount::setLeaveOnServerSize(int size)
270{
271 mLeaveOnServerSize = size;
272}
273
274//---------------------------------------------------------------------------
275void PopAccount::setFilterOnServer(bool b)
276{
277 mFilterOnServer = b;
278}
279
280//---------------------------------------------------------------------------
281void PopAccount::setFilterOnServerCheckSize(unsigned int aSize)
282{
283 mFilterOnServerCheckSize = aSize;
284}
285
286//-----------------------------------------------------------------------------
287void PopAccount::connectJob() {
288 TDEIO::Scheduler::assignJobToSlave(mSlave, job);
289 connect(job, TQ_SIGNAL( data( TDEIO::Job*, const TQByteArray &)),
290 TQ_SLOT( slotData( TDEIO::Job*, const TQByteArray &)));
291 connect(job, TQ_SIGNAL( result( TDEIO::Job * ) ),
292 TQ_SLOT( slotResult( TDEIO::Job * ) ) );
293 connect(job, TQ_SIGNAL(infoMessage( TDEIO::Job*, const TQString & )),
294 TQ_SLOT( slotMsgRetrieved(TDEIO::Job*, const TQString &)));
295}
296
297
298//-----------------------------------------------------------------------------
299void PopAccount::slotCancel()
300{
301 mMsgsPendingDownload.clear();
302 processRemainingQueuedMessages();
303 saveUidList();
304 slotJobFinished();
305}
306
307
308//-----------------------------------------------------------------------------
309void PopAccount::slotProcessPendingMsgs()
310{
311 if (mProcessing) // not reentrant
312 return;
313 mProcessing = true;
314
315 bool addedOk;
316 TQValueList<KMMessage*>::Iterator cur = msgsAwaitingProcessing.begin();
317 TQStringList::Iterator curId = msgIdsAwaitingProcessing.begin();
318 TQStringList::Iterator curUid = msgUidsAwaitingProcessing.begin();
319
320 while (cur != msgsAwaitingProcessing.end()) {
321 // note we can actually end up processing events in processNewMsg
322 // this happens when send receipts is turned on
323 // hence the check for re-entry at the start of this method.
324 // -sanders Update processNewMsg should no longer process events
325
326 addedOk = processNewMsg(*cur); //added ok? Error displayed if not.
327
328 if (!addedOk) {
329 mMsgsPendingDownload.clear();
330 msgIdsAwaitingProcessing.clear();
331 msgUidsAwaitingProcessing.clear();
332 break;
333 }
334 else {
335 idsOfMsgsToDelete.append( *curId );
336 mUidsOfNextSeenMsgsDict.insert( *curUid, (const int *)1 );
337 mTimeOfNextSeenMsgsMap.insert( *curUid, time(0) );
338 }
339 ++cur;
340 ++curId;
341 ++curUid;
342 }
343
344 msgsAwaitingProcessing.clear();
345 msgIdsAwaitingProcessing.clear();
346 msgUidsAwaitingProcessing.clear();
347 mProcessing = false;
348}
349
350
351//-----------------------------------------------------------------------------
352void PopAccount::slotAbortRequested()
353{
354 if (stage == Idle) return;
355 if ( mMailCheckProgressItem )
356 disconnect( mMailCheckProgressItem, TQ_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
357 this, TQ_SLOT( slotAbortRequested() ) );
358 stage = Quit;
359 if (job) job->kill();
360 job = 0;
361 mSlave = 0;
362 slotCancel();
363}
364
365
366//-----------------------------------------------------------------------------
367void PopAccount::startJob()
368{
369 // Run the precommand
370 if (!runPrecommand(precommand()))
371 {
372 KMessageBox::sorry(0,
373 i18n("Could not execute precommand: %1").arg(precommand()),
374 i18n("KMail Error Message"));
375 checkDone( false, CheckError );
376 return;
377 }
378 // end precommand code
379
380 KURL url = getUrl();
381
382 if ( !url.isValid() ) {
383 KMessageBox::error(0, i18n("Source URL is malformed"),
384 i18n("Kioslave Error Message") );
385 return;
386 }
387
388 mMsgsPendingDownload.clear();
389 idsOfMsgs.clear();
390 mUidForIdMap.clear();
391 idsOfMsgsToDelete.clear();
392 idsOfForcedDeletes.clear();
393
394 //delete any headers if there are some this have to be done because of check again
395 headersOnServer.clear();
396 headers = false;
397 indexOfCurrentMsg = -1;
398
399 Q_ASSERT( !mMailCheckProgressItem );
400 TQString escapedName = TQStyleSheet::escape( mName );
401 mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem(
402 "MailCheck" + mName,
403 escapedName,
404 i18n("Preparing transmission from \"%1\"...").arg( escapedName ),
405 true, // can be canceled
406 useSSL() || useTLS() );
407 connect( mMailCheckProgressItem, TQ_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
408 this, TQ_SLOT( slotAbortRequested() ) );
409
410 numBytes = 0;
411 numBytesRead = 0;
412 stage = List;
413 mSlave = TDEIO::Scheduler::getConnectedSlave( url, slaveConfig() );
414 if (!mSlave)
415 {
416 slotSlaveError(0, TDEIO::ERR_CANNOT_LAUNCH_PROCESS, url.protocol());
417 return;
418 }
419 url.setPath(TQString("/index"));
420 job = TDEIO::get( url, false, false );
421 connectJob();
422}
423
424MetaData PopAccount::slaveConfig() const {
425 MetaData m = NetworkAccount::slaveConfig();
426
427 m.insert("progress", "off");
428 m.insert("pipelining", (mUsePipelining) ? "on" : "off");
429 if (mAuth == "PLAIN" || mAuth == "LOGIN" || mAuth == "CRAM-MD5" ||
430 mAuth == "DIGEST-MD5" || mAuth == "NTLM" || mAuth == "GSSAPI") {
431 m.insert("auth", "SASL");
432 m.insert("sasl", mAuth);
433 } else if ( mAuth == "*" )
434 m.insert("auth", "USER");
435 else
436 m.insert("auth", mAuth);
437
438 return m;
439}
440
441//-----------------------------------------------------------------------------
442// one message is finished
443// add data to a KMMessage
444void PopAccount::slotMsgRetrieved(TDEIO::Job*, const TQString & infoMsg)
445{
446 if (infoMsg != "message complete") return;
447 KMMessage *msg = new KMMessage;
448 msg->setComplete(true);
449 // Make sure to use LF as line ending to make the processing easier
450 // when piping through external programs
451 uint newSize = Util::crlf2lf( curMsgData.data(), curMsgData.size() );
452 curMsgData.resize( newSize );
453 msg->fromByteArray( curMsgData , true );
454 if (stage == Head)
455 {
456 int size = mMsgsPendingDownload[ headerIt.current()->id() ];
457 kdDebug(5006) << "Size of Message: " << size << endl;
458 msg->setMsgLength( size );
459 headerIt.current()->setHeader(msg);
460 ++headerIt;
461 slotGetNextHdr();
462 } else {
463 //kdDebug(5006) << kfuncinfo << "stage == Retr" << endl;
464 //kdDebug(5006) << "curMsgData.size() = " << curMsgData.size() << endl;
465 msg->setMsgLength( curMsgData.size() );
466 msgsAwaitingProcessing.append(msg);
467 msgIdsAwaitingProcessing.append(idsOfMsgs[indexOfCurrentMsg]);
468 msgUidsAwaitingProcessing.append( mUidForIdMap[idsOfMsgs[indexOfCurrentMsg]] );
469 slotGetNextMsg();
470 }
471}
472
473
474//-----------------------------------------------------------------------------
475// finit state machine to cycle trow the stages
476void PopAccount::slotJobFinished() {
477 TQStringList emptyList;
478 if (stage == List) {
479 kdDebug(5006) << k_funcinfo << "stage == List" << endl;
480 // set the initial size of mUidsOfNextSeenMsgsDict to the number of
481 // messages on the server + 10%
482 mUidsOfNextSeenMsgsDict.resize( KMail::nextPrime( ( idsOfMsgs.count() * 11 ) / 10 ) );
483 KURL url = getUrl();
484 url.setPath(TQString("/uidl"));
485 job = TDEIO::get( url, false, false );
486 connectJob();
487 stage = Uidl;
488 }
489 else if (stage == Uidl) {
490 kdDebug(5006) << k_funcinfo << "stage == Uidl" << endl;
491 mUidlFinished = true;
492
493 if ( mLeaveOnServer && mUidForIdMap.isEmpty() &&
494 mUidsOfNextSeenMsgsDict.isEmpty() && !idsOfMsgs.isEmpty() ) {
495 KMessageBox::sorry(0, i18n("Your POP3 server (Account: %1) does not support "
496 "the UIDL command: this command is required to determine, in a reliable way, "
497 "which of the mails on the server KMail has already seen before;\n"
498 "the feature to leave the mails on the server will therefore not "
499 "work properly.").arg(NetworkAccount::name()) );
500 // An attempt to work around buggy pop servers, these seem to be popular.
501 mUidsOfNextSeenMsgsDict = mUidsOfSeenMsgsDict;
502 }
503
504 //check if filter on server
505 if (mFilterOnServer == true) {
506 TQMap<TQString, int>::Iterator hids;
507 for ( hids = mMsgsPendingDownload.begin();
508 hids != mMsgsPendingDownload.end(); hids++ ) {
509 kdDebug(5006) << "Length: " << hids.data() << endl;
510 //check for mails bigger mFilterOnServerCheckSize
511 if ( (unsigned int)hids.data() >= mFilterOnServerCheckSize ) {
512 kdDebug(5006) << "bigger than " << mFilterOnServerCheckSize << endl;
513 headersOnServer.append(new KMPopHeaders( hids.key(),
514 mUidForIdMap[hids.key()],
515 Later));//TODO
516 //set Action if already known
517 if( mHeaderDeleteUids.contains( headersOnServer.current()->uid() ) ) {
518 headersOnServer.current()->setAction(Delete);
519 }
520 else if( mHeaderDownUids.contains( headersOnServer.current()->uid() ) ) {
521 headersOnServer.current()->setAction(Down);
522 }
523 else if( mHeaderLaterUids.contains( headersOnServer.current()->uid() ) ) {
524 headersOnServer.current()->setAction(Later);
525 }
526 }
527 }
528 // delete the uids so that you don't get them twice in the list
529 mHeaderDeleteUids.clear();
530 mHeaderDownUids.clear();
531 mHeaderLaterUids.clear();
532 }
533 // kdDebug(5006) << "Num of Msgs to Filter: " << headersOnServer.count() << endl;
534 // if there are mails which should be checkedc download the headers
535 if ((headersOnServer.count() > 0) && (mFilterOnServer == true)) {
536 headerIt.toFirst();
537 KURL url = getUrl();
538 TQString headerIds;
539 while (headerIt.current())
540 {
541 headerIds += headerIt.current()->id();
542 if (!headerIt.atLast()) headerIds += ",";
543 ++headerIt;
544 }
545 headerIt.toFirst();
546 url.setPath(TQString("/headers/") + headerIds);
547 job = TDEIO::get( url, false, false );
548 connectJob();
549 slotGetNextHdr();
550 stage = Head;
551 }
552 else {
553 stage = Retr;
554 numMsgs = mMsgsPendingDownload.count();
555 numBytesToRead = 0;
556 TQMap<TQString, int>::Iterator len;
557 for ( len = mMsgsPendingDownload.begin();
558 len != mMsgsPendingDownload.end(); len++ )
559 numBytesToRead += len.data();
560 idsOfMsgs = TQStringList( mMsgsPendingDownload.keys() );
561 KURL url = getUrl();
562 url.setPath( "/download/" + idsOfMsgs.join(",") );
563 job = TDEIO::get( url, false, false );
564 connectJob();
565 slotGetNextMsg();
566 processMsgsTimer.start(processingDelay);
567 }
568 }
569 else if (stage == Head) {
570 kdDebug(5006) << k_funcinfo << "stage == Head" << endl;
571
572 // All headers have been downloaded, check which mail you want to get
573 // data is in list headersOnServer
574
575 // check if headers apply to a filter
576 // if set the action of the filter
577 KMPopFilterAction action;
578 bool dlgPopup = false;
579 for (headersOnServer.first(); headersOnServer.current(); headersOnServer.next()) {
580 action = (KMPopFilterAction)kmkernel->popFilterMgr()->process(headersOnServer.current()->header());
581 //debug todo
582 switch ( action ) {
583 case NoAction:
584 kdDebug(5006) << "PopFilterAction = NoAction" << endl;
585 break;
586 case Later:
587 kdDebug(5006) << "PopFilterAction = Later" << endl;
588 break;
589 case Delete:
590 kdDebug(5006) << "PopFilterAction = Delete" << endl;
591 break;
592 case Down:
593 kdDebug(5006) << "PopFilterAction = Down" << endl;
594 break;
595 default:
596 kdDebug(5006) << "PopFilterAction = default oops!" << endl;
597 break;
598 }
599 switch ( action ) {
600 case NoAction:
601 //kdDebug(5006) << "PopFilterAction = NoAction" << endl;
602 dlgPopup = true;
603 break;
604 case Later:
605 if (kmkernel->popFilterMgr()->showLaterMsgs())
606 dlgPopup = true;
607 // fall through
608 default:
609 headersOnServer.current()->setAction(action);
610 headersOnServer.current()->setRuleMatched(true);
611 break;
612 }
613 }
614
615 // if there are some messages which are not coverd by a filter
616 // show the dialog
617 headers = true;
618 if (dlgPopup) {
619 KMPopFilterCnfrmDlg dlg(&headersOnServer, this->name(), kmkernel->popFilterMgr()->showLaterMsgs());
620 dlg.exec();
621 }
622
623 for (headersOnServer.first(); headersOnServer.current(); headersOnServer.next()) {
624 if (headersOnServer.current()->action() == Delete ||
625 headersOnServer.current()->action() == Later) {
626 //remove entries from the lists when the mails should not be downloaded
627 //(deleted or downloaded later)
628 if ( mMsgsPendingDownload.contains( headersOnServer.current()->id() ) ) {
629 mMsgsPendingDownload.remove( headersOnServer.current()->id() );
630 }
631 if (headersOnServer.current()->action() == Delete) {
632 mHeaderDeleteUids.insert(headersOnServer.current()->uid(), true);
633 mUidsOfNextSeenMsgsDict.insert( headersOnServer.current()->uid(),
634 (const int *)1 );
635 idsOfMsgsToDelete.append(headersOnServer.current()->id());
636 mTimeOfNextSeenMsgsMap.insert( headersOnServer.current()->uid(),
637 time(0) );
638 }
639 else {
640 mHeaderLaterUids.insert(headersOnServer.current()->uid(), true);
641 }
642 }
643 else if (headersOnServer.current()->action() == Down) {
644 mHeaderDownUids.insert(headersOnServer.current()->uid(), true);
645 }
646 }
647
648 headersOnServer.clear();
649 stage = Retr;
650 numMsgs = mMsgsPendingDownload.count();
651 numBytesToRead = 0;
652 TQMap<TQString, int>::Iterator len;
653 for (len = mMsgsPendingDownload.begin();
654 len != mMsgsPendingDownload.end(); len++)
655 numBytesToRead += len.data();
656 idsOfMsgs = TQStringList( mMsgsPendingDownload.keys() );
657 KURL url = getUrl();
658 url.setPath( "/download/" + idsOfMsgs.join(",") );
659 job = TDEIO::get( url, false, false );
660 connectJob();
661 slotGetNextMsg();
662 processMsgsTimer.start(processingDelay);
663 }
664 else if (stage == Retr) {
665 if ( mMailCheckProgressItem )
666 mMailCheckProgressItem->setProgress( 100 );
667 processRemainingQueuedMessages();
668
669 mHeaderDeleteUids.clear();
670 mHeaderDownUids.clear();
671 mHeaderLaterUids.clear();
672
673 kmkernel->folderMgr()->syncAllFolders();
674
675 KURL url = getUrl();
676 TQMap< TQPair<time_t, TQString>, int > idsToSave;
677 idsToSave.clear();
678 // Check if we want to keep any messages
679 if ( mLeaveOnServer && !idsOfMsgsToDelete.isEmpty() ) {
680 // Keep all messages on server
681 if ( mLeaveOnServerDays == -1 && mLeaveOnServerCount <= 0 &&
682 mLeaveOnServerSize <= 0)
683 idsOfMsgsToDelete.clear();
684 // Delete old messages
685 else if ( mLeaveOnServerDays > 0 && !mTimeOfNextSeenMsgsMap.isEmpty() ) {
686 time_t timeLimit = time(0) - (86400 * mLeaveOnServerDays);
687 kdDebug() << "timeLimit is " << timeLimit << endl;
688 TQStringList::Iterator cur = idsOfMsgsToDelete.begin();
689 for ( ; cur != idsOfMsgsToDelete.end(); ++cur) {
690 time_t msgTime = mTimeOfNextSeenMsgsMap[mUidForIdMap[*cur]];
691 kdDebug() << "id: " << *cur << " msgTime: " << msgTime << endl;
692 if (msgTime >= timeLimit ||
693 !mTimeOfNextSeenMsgsMap[mUidForIdMap[*cur]]) {
694 kdDebug() << "Saving msg id " << *cur << endl;
695 TQPair<time_t, TQString> msg(msgTime, *cur);
696 idsToSave.insert( msg, 1 );
697 }
698 }
699 }
700 // Delete more old messages if there are more than mLeaveOnServerCount
701 if ( mLeaveOnServerCount > 0 ) {
702 int numToDelete = idsToSave.count() - mLeaveOnServerCount;
703 kdDebug() << "numToDelete is " << numToDelete << endl;
704 if ( numToDelete > 0 && (unsigned)numToDelete < idsToSave.count() ) {
705 TQMap< TQPair<time_t, TQString>, int >::Iterator cur = idsToSave.begin();
706 for ( int deleted = 0; deleted < numToDelete && cur != idsToSave.end()
707 ; deleted++, cur++ ) {
708 kdDebug() << "deleting msg id " << cur.key().second << endl;
709 idsToSave.remove( cur );
710 }
711 }
712 else if ( numToDelete > 0 && (unsigned)numToDelete >= idsToSave.count() )
713 idsToSave.clear();
714 }
715 // Delete more old messages until we're under mLeaveOnServerSize MBs
716 if ( mLeaveOnServerSize > 0 ) {
717 double sizeOnServer = 0;
718 TQMap< TQPair<time_t, TQString>, int >::Iterator cur = idsToSave.begin();
719 for ( ; cur != idsToSave.end(); cur++ ) {
720 sizeOnServer +=
721 *mSizeOfNextSeenMsgsDict[ mUidForIdMap[ cur.key().second ] ];
722 }
723 kdDebug() << "sizeOnServer is " << sizeOnServer/(1024*1024) << "MB" << endl;
724 long limitInBytes = mLeaveOnServerSize * ( 1024 * 1024 );
725 for ( cur = idsToSave.begin(); cur != idsToSave.end()
726 && sizeOnServer > limitInBytes; cur++ ) {
727 sizeOnServer -=
728 *mSizeOfNextSeenMsgsDict[ mUidForIdMap[ cur.key().second ] ];
729 idsToSave.remove( cur );
730 }
731 }
732 // Save msgs from deletion
733 TQMap< TQPair<time_t, TQString>, int >::Iterator it = idsToSave.begin();
734 kdDebug() << "Going to save " << idsToSave.count() << endl;
735 for ( ; it != idsToSave.end(); ++it ) {
736 kdDebug() << "saving msg id " << it.key().second << endl;
737 idsOfMsgsToDelete.remove( it.key().second );
738 }
739 }
740
741 if ( !idsOfForcedDeletes.isEmpty() ) {
742 idsOfMsgsToDelete += idsOfForcedDeletes;
743 idsOfForcedDeletes.clear();
744 }
745
746 // If there are messages to delete then delete them
747 if ( !idsOfMsgsToDelete.isEmpty() ) {
748 stage = Dele;
749 if ( mMailCheckProgressItem )
750 mMailCheckProgressItem->setStatus(
751 i18n( "Fetched 1 message from %1. Deleting messages from server...",
752 "Fetched %n messages from %1. Deleting messages from server...",
753 numMsgs )
754 .arg( mHost ) );
755 url.setPath("/remove/" + idsOfMsgsToDelete.join(","));
756 kdDebug(5006) << "url: " << url.prettyURL() << endl;
757 } else {
758 stage = Quit;
759 if ( mMailCheckProgressItem )
760 mMailCheckProgressItem->setStatus(
761 i18n( "Fetched 1 message from %1. Terminating transmission...",
762 "Fetched %n messages from %1. Terminating transmission...",
763 numMsgs )
764 .arg( mHost ) );
765 url.setPath(TQString("/commit"));
766 kdDebug(5006) << "url: " << url.prettyURL() << endl;
767 }
768 job = TDEIO::get( url, false, false );
769 connectJob();
770 }
771 else if (stage == Dele) {
772 kdDebug(5006) << k_funcinfo << "stage == Dele" << endl;
773 // remove the uids of all messages which have been deleted
774 for ( TQStringList::ConstIterator it = idsOfMsgsToDelete.begin();
775 it != idsOfMsgsToDelete.end(); ++it ) {
776 mUidsOfNextSeenMsgsDict.remove( mUidForIdMap[*it] );
777 }
778 idsOfMsgsToDelete.clear();
779 if ( mMailCheckProgressItem )
780 mMailCheckProgressItem->setStatus(
781 i18n( "Fetched 1 message from %1. Terminating transmission...",
782 "Fetched %n messages from %1. Terminating transmission...",
783 numMsgs )
784 .arg( mHost ) );
785 KURL url = getUrl();
786 url.setPath(TQString("/commit"));
787 job = TDEIO::get( url, false, false );
788 stage = Quit;
789 connectJob();
790 }
791 else if (stage == Quit) {
792 kdDebug(5006) << k_funcinfo << "stage == Quit" << endl;
793 saveUidList();
794 job = 0;
795 if (mSlave) TDEIO::Scheduler::disconnectSlave(mSlave);
796 mSlave = 0;
797 stage = Idle;
798 if( mMailCheckProgressItem ) { // do this only once...
799 bool canceled = !kmkernel || kmkernel->mailCheckAborted() || mMailCheckProgressItem->canceled();
800 int numMessages = canceled ? indexOfCurrentMsg : idsOfMsgs.count();
801 BroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
802 this->name(), numMessages, numBytes, numBytesRead, numBytesToRead, mLeaveOnServer, mMailCheckProgressItem );
803 // set mMailCheckProgressItem = 0 before calling setComplete() to prevent
804 // a race condition
805 ProgressItem *savedMailCheckProgressItem = mMailCheckProgressItem;
806 mMailCheckProgressItem = 0;
807 savedMailCheckProgressItem->setComplete(); // that will delete it
808 checkDone( ( numMessages > 0 ), canceled ? CheckAborted : CheckOK );
809 }
810 }
811}
812
813
814//-----------------------------------------------------------------------------
815void PopAccount::processRemainingQueuedMessages()
816{
817 kdDebug(5006) << k_funcinfo << endl;
818 slotProcessPendingMsgs(); // Force processing of any messages still in the queue
819 processMsgsTimer.stop();
820
821 stage = Quit;
822 if ( kmkernel && kmkernel->folderMgr() ) {
823 kmkernel->folderMgr()->syncAllFolders();
824 }
825}
826
827
828//-----------------------------------------------------------------------------
829void PopAccount::saveUidList()
830{
831 kdDebug(5006) << k_funcinfo << endl;
832 // Don't update the seen uid list unless we successfully got
833 // a new list from the server
834 if (!mUidlFinished) return;
835
836 TQStringList uidsOfNextSeenMsgs;
837 TQValueList<int> seenUidTimeList;
838 TQDictIterator<int> it( mUidsOfNextSeenMsgsDict );
839 for( ; it.current(); ++it ) {
840 uidsOfNextSeenMsgs.append( it.currentKey() );
841 seenUidTimeList.append( mTimeOfNextSeenMsgsMap[it.currentKey()] );
842 }
843 TQString seenUidList = locateLocal( "data", "kmail/" + mLogin + ":" + "@" +
844 mHost + ":" + TQString("%1").arg(mPort) );
845 TDEConfig config( seenUidList );
846 config.writeEntry( "seenUidList", uidsOfNextSeenMsgs );
847 config.writeEntry( "seenUidTimeList", seenUidTimeList );
848 config.writeEntry( "downloadLater", TQStringList( mHeaderLaterUids.keys() ) );
849 config.sync();
850}
851
852
853//-----------------------------------------------------------------------------
854void PopAccount::slotGetNextMsg()
855{
856 TQMap<TQString, int>::Iterator next = mMsgsPendingDownload.begin();
857
858 curMsgData.resize(0);
859 numMsgBytesRead = 0;
860 curMsgLen = 0;
861 delete curMsgStrm;
862 curMsgStrm = 0;
863
864 if ( next != mMsgsPendingDownload.end() ) {
865 // get the next message
866 int nextLen = next.data();
867 curMsgStrm = new TQDataStream( curMsgData, IO_WriteOnly );
868 curMsgLen = nextLen;
869 ++indexOfCurrentMsg;
870 kdDebug(5006) << TQString("Length of message about to get %1").arg( nextLen ) << endl;
871 mMsgsPendingDownload.remove( next.key() );
872 }
873}
874
875
876//-----------------------------------------------------------------------------
877void PopAccount::slotData( TDEIO::Job* job, const TQByteArray &data)
878{
879 if (data.size() == 0) {
880 kdDebug(5006) << "Data: <End>" << endl;
881 if ((stage == Retr) && (numMsgBytesRead < curMsgLen))
882 numBytesRead += curMsgLen - numMsgBytesRead;
883 else if (stage == Head){
884 kdDebug(5006) << "Head: <End>" << endl;
885 }
886 return;
887 }
888
889 int oldNumMsgBytesRead = numMsgBytesRead;
890 if (stage == Retr) {
891 headers = false;
892 curMsgStrm->writeRawBytes( data.data(), data.size() );
893 numMsgBytesRead += data.size();
894 if (numMsgBytesRead > curMsgLen)
895 numMsgBytesRead = curMsgLen;
896 numBytesRead += numMsgBytesRead - oldNumMsgBytesRead;
897 dataCounter++;
898 if ( mMailCheckProgressItem &&
899 ( dataCounter % 5 == 0 ||
900 ( indexOfCurrentMsg + 1 == numMsgs && numMsgBytesRead == curMsgLen ) ) )
901 {
902 TQString msg;
903 if (numBytes != numBytesToRead && mLeaveOnServer)
904 {
905 msg = i18n("Fetching message %1 of %2 (%3 of %4 KB) for %5@%6 "
906 "(%7 KB remain on the server).")
907 .arg(indexOfCurrentMsg+1).arg(numMsgs).arg(numBytesRead/1024)
908 .arg(numBytesToRead/1024).arg(mLogin).arg(mHost).arg(numBytes/1024);
909 }
910 else
911 {
912 msg = i18n("Fetching message %1 of %2 (%3 of %4 KB) for %5@%6.")
913 .arg(indexOfCurrentMsg+1).arg(numMsgs).arg(numBytesRead/1024)
914 .arg(numBytesToRead/1024).arg(mLogin).arg(mHost);
915 }
916 mMailCheckProgressItem->setStatus( msg );
917 mMailCheckProgressItem->setProgress(
918 (numBytesToRead <= 100) ? 50 // We never know what the server tells us
919 // This way of dividing is required for > 21MB of mail
920 : (numBytesRead / (numBytesToRead / 100)) );
921 }
922 return;
923 }
924
925 if (stage == Head) {
926 curMsgStrm->writeRawBytes( data.data(), data.size() );
927 return;
928 }
929
930 // otherwise stage is List Or Uidl
931 TQString qdata = data;
932 qdata = qdata.simplifyWhiteSpace(); // Workaround for Maillennium POP3/UNIBOX
933 int spc = qdata.find( ' ' );
934 if ( stage == List ) {
935 if ( spc > 0 ) {
936 TQString length = qdata.mid(spc+1);
937 if (length.find(' ') != -1) length.truncate(length.find(' '));
938 int len = length.toInt();
939 numBytes += len;
940 TQString id = qdata.left(spc);
941 idsOfMsgs.append( id );
942 mMsgsPendingDownload.insert( id, len );
943 }
944 else {
945 stage = Idle;
946 if ( job ) job->kill();
947 job = 0;
948 mSlave = 0;
949 KMessageBox::error( 0, i18n( "Unable to complete LIST operation." ),
950 i18n( "Invalid Response From Server") );
951 return;
952 }
953 }
954 else { // stage == Uidl
955 Q_ASSERT ( stage == Uidl);
956
957 TQString id;
958 TQString uid;
959
960 if ( spc <= 0 ) {
961 // an invalid uidl line. we might just need to skip it, but
962 // some servers generate invalid uids with valid ids. in that
963 // case we will just make up a uid - which will cause us to
964 // not cache the document, but we will be able to interoperate
965
966 int testid = atoi ( qdata.ascii() );
967 if ( testid < 1 ) {
968 // we'll just have to skip this
969 kdDebug(5006) << "PopAccount::slotData skipping UIDL entry due to parse error "
970 << endl << qdata.ascii() << endl;
971 return;
972 }
973 id.setNum (testid, 10);
974
975 TQString datestring, serialstring;
976
977 serialstring.setNum ( ++dataCounter, 10 );
978 datestring.setNum ( time(NULL),10 );
979 uid = TQString( "uidlgen" ) + datestring + TQString( "." ) + serialstring;
980 kdDebug(5006) << "PopAccount::slotData message " << id.ascii()
981 << "%d has bad UIDL, cannot keep a copy on server" << endl;
982 idsOfForcedDeletes.append( id );
983 }
984 else {
985 id = qdata.left( spc );
986 uid = qdata.mid( spc + 1 );
987 }
988
989 int *size = new int; //malloc(size_of(int));
990 *size = mMsgsPendingDownload[id];
991 mSizeOfNextSeenMsgsDict.insert( uid, size );
992 if ( mUidsOfSeenMsgsDict.find( uid ) != 0 ) {
993 if ( mMsgsPendingDownload.contains( id ) ) {
994 mMsgsPendingDownload.remove( id );
995 }
996 else
997 kdDebug(5006) << "PopAccount::slotData synchronization failure." << endl;
998 idsOfMsgsToDelete.append( id );
999 mUidsOfNextSeenMsgsDict.insert( uid, (const int *)1 );
1000 if ( mTimeOfSeenMsgsVector.empty() ) {
1001 mTimeOfNextSeenMsgsMap.insert( uid, time(0) );
1002 }
1003 else {
1004 // cast the int* with a long to can convert it to a int, BTW
1005 // works with g++-4.0 and amd64
1006 mTimeOfNextSeenMsgsMap.insert( uid, mTimeOfSeenMsgsVector[(int)( long )
1007 mUidsOfSeenMsgsDict[uid] - 1] );
1008 }
1009 }
1010 mUidForIdMap.insert( id, uid );
1011 }
1012}
1013
1014//-----------------------------------------------------------------------------
1015void PopAccount::slotResult( TDEIO::Job* )
1016{
1017 if (!job) return;
1018 if ( job->error() )
1019 {
1020 if (interactive) {
1021 if (headers) { // nothing to be done for headers
1022 idsOfMsgs.clear();
1023 }
1024 if (stage == Head && job->error() == TDEIO::ERR_COULD_NOT_READ)
1025 {
1026 KMessageBox::error(0, i18n("Your server does not support the "
1027 "TOP command. Therefore it is not possible to fetch the headers "
1028 "of large emails first, before downloading them."));
1029 slotCancel();
1030 return;
1031 }
1032 // force the dialog to be shown next time the account is checked
1033 if (!mStorePasswd) mPasswd = "";
1034 job->showErrorDialog();
1035 }
1036 slotCancel();
1037 }
1038 else
1039 slotJobFinished();
1040}
1041
1042
1043//-----------------------------------------------------------------------------
1044void PopAccount::slotSlaveError(TDEIO::Slave *aSlave, int error,
1045 const TQString &errorMsg)
1046{
1047 if (aSlave != mSlave) return;
1048 if (error == TDEIO::ERR_SLAVE_DIED) mSlave = 0;
1049
1050 // explicitely disconnect the slave if the connection went down
1051 if ( error == TDEIO::ERR_CONNECTION_BROKEN && mSlave ) {
1052 TDEIO::Scheduler::disconnectSlave( mSlave );
1053 mSlave = 0;
1054 }
1055
1056 if (interactive && kmkernel) {
1057 KMessageBox::error(kmkernel->mainWin(), TDEIO::buildErrorString(error, errorMsg));
1058 }
1059
1060
1061 stage = Quit;
1062 if (error == TDEIO::ERR_COULD_NOT_LOGIN && !mStorePasswd)
1063 mAskAgain = true;
1064 /* We need a timer, otherwise slotSlaveError of the next account is also
1065 executed, if it reuses the slave, because the slave member variable
1066 is changed too early */
1067 TQTimer::singleShot(0, this, TQ_SLOT(slotCancel()));
1068}
1069
1070//-----------------------------------------------------------------------------
1071void PopAccount::slotGetNextHdr(){
1072 kdDebug(5006) << "slotGetNextHeader" << endl;
1073
1074 curMsgData.resize(0);
1075 delete curMsgStrm;
1076 curMsgStrm = 0;
1077
1078 curMsgStrm = new TQDataStream( curMsgData, IO_WriteOnly );
1079}
1080
1081void PopAccount::killAllJobs( bool ) {
1082 // must reimpl., but we don't use it yet
1083}
1084
1085} // namespace KMail
1086#include "popaccount.moc"
This is a Mime Message.
Definition: kmmessage.h:68
void setComplete(bool v)
Set if the message is a complete message.
Definition: kmmessage.h:869
The account manager is responsible for creating accounts of various types via the factory method crea...
KMail account for pop mail account.
Definition: popaccount.h:27
bool filterOnServer(void) const
Shall messages be filter on the server (TRUE) or not (FALSE).
Definition: popaccount.h:78
bool usePipelining(void) const
Sending of several commands at once.
Definition: popaccount.h:46
int leaveOnServerDays(void) const
If value is positive, leave mail on the server for so many days.
Definition: popaccount.h:59
int leaveOnServerSize(void) const
If value is positive, leave so many MBs on the server.
Definition: popaccount.h:71
bool leaveOnServer(void) const
Shall messages be left on the server upon retreival (TRUE) or deleted (FALSE).
Definition: popaccount.h:53
int leaveOnServerCount(void) const
If value is positive, leave so many messages on the server.
Definition: popaccount.h:65
unsigned int filterOnServerCheckSize(void) const
Size of messages which should be check on the pop server before download.
Definition: popaccount.h:85
folderdiaquotatab.h
Definition: aboutdata.cpp:40