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