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"
30 using 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>
48 using TDEIO::MetaData;
49 
50 #include <tqstylesheet.h>
51 
52 static const unsigned short int pop3DefaultPort = 110;
53 
54 namespace KMail {
55 //-----------------------------------------------------------------------------
56 PopAccount::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 //-----------------------------------------------------------------------------
87 PopAccount::~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 //-----------------------------------------------------------------------------
106 TQString PopAccount::type(void) const
107 {
108  return "pop";
109 }
110 
111 TQString PopAccount::protocol() const {
112  return useSSL() ? POP_SSL_PROTOCOL : POP_PROTOCOL;
113 }
114 
115 unsigned short int PopAccount::defaultPort() const {
116  return pop3DefaultPort;
117 }
118 
119 //-----------------------------------------------------------------------------
120 void 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 //-----------------------------------------------------------------------------
135 void 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 //-----------------------------------------------------------------------------
152 void 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 //-----------------------------------------------------------------------------
222 void 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 //-----------------------------------------------------------------------------
237 void 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 //-----------------------------------------------------------------------------
252 void PopAccount::setUsePipelining(bool b)
253 {
254  mUsePipelining = b;
255 }
256 
257 //-----------------------------------------------------------------------------
258 void PopAccount::setLeaveOnServer(bool b)
259 {
260  mLeaveOnServer = b;
261 }
262 
263 //-----------------------------------------------------------------------------
264 void PopAccount::setLeaveOnServerDays(int days)
265 {
266  mLeaveOnServerDays = days;
267 }
268 
269 //-----------------------------------------------------------------------------
270 void PopAccount::setLeaveOnServerCount(int count)
271 {
272  mLeaveOnServerCount = count;
273 }
274 
275 //-----------------------------------------------------------------------------
276 void PopAccount::setLeaveOnServerSize(int size)
277 {
278  mLeaveOnServerSize = size;
279 }
280 
281 //---------------------------------------------------------------------------
282 void PopAccount::setFilterOnServer(bool b)
283 {
284  mFilterOnServer = b;
285 }
286 
287 //---------------------------------------------------------------------------
288 void PopAccount::setFilterOnServerCheckSize(unsigned int aSize)
289 {
290  mFilterOnServerCheckSize = aSize;
291 }
292 
293 //-----------------------------------------------------------------------------
294 void 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 //-----------------------------------------------------------------------------
306 void PopAccount::slotCancel()
307 {
308  mMsgsPendingDownload.clear();
309  processRemainingQueuedMessages();
310  saveUidList();
311  slotJobFinished();
312 }
313 
314 
315 //-----------------------------------------------------------------------------
316 void 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 //-----------------------------------------------------------------------------
359 void 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 //-----------------------------------------------------------------------------
374 void 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 
431 MetaData 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
451 void 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
483 void 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 //-----------------------------------------------------------------------------
822 void 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 //-----------------------------------------------------------------------------
836 void 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 //-----------------------------------------------------------------------------
861 void 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 //-----------------------------------------------------------------------------
884 void 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 //-----------------------------------------------------------------------------
1022 void 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 //-----------------------------------------------------------------------------
1051 void 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 //-----------------------------------------------------------------------------
1078 void 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 
1088 void 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