kmail

imapaccountbase.cpp
1 /*
2  * imapaccountbase.cpp
3  *
4  * Copyright (c) 2000-2002 Michael Haeckel <haeckel@kde.org>
5  * Copyright (c) 2002 Marc Mutz <mutz@kde.org>
6  *
7  * This file is based on work on pop3 and imap account implementations
8  * by Don Sanders <sanders@kde.org> and Michael Haeckel <haeckel@kde.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; version 2 of the License
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include "imapaccountbase.h"
29 using KMail::SieveConfig;
30 
31 #include "accountmanager.h"
33 #include "kmfolder.h"
34 #include "broadcaststatus.h"
35 using KPIM::BroadcastStatus;
36 #include "kmmainwin.h"
37 #include "kmfolderimap.h"
38 #include "kmmainwidget.h"
39 #include "kmmainwin.h"
40 #include "kmmsgpart.h"
41 #include "acljobs.h"
42 #include "kmfoldercachedimap.h"
43 #include "bodyvisitor.h"
44 using KMail::BodyVisitor;
45 #include "imapjob.h"
46 using KMail::ImapJob;
47 #include "protocols.h"
48 #include "progressmanager.h"
49 using KPIM::ProgressManager;
50 #include "kmfoldermgr.h"
51 #include "listjob.h"
52 
53 #include <tdeapplication.h>
54 #include <kdebug.h>
55 #include <tdeconfig.h>
56 #include <tdelocale.h>
57 #include <tdemessagebox.h>
58 using TDEIO::MetaData;
59 #include <tdeio/passdlg.h>
60 using TDEIO::PasswordDialog;
61 #include <tdeio/scheduler.h>
62 #include <tdeio/slave.h>
63 #include <mimelib/bodypart.h>
64 #include <mimelib/body.h>
65 #include <mimelib/headers.h>
66 #include <mimelib/message.h>
67 //using TDEIO::Scheduler; // use FQN below
68 
69 #include <tqregexp.h>
70 #include <tqstylesheet.h>
71 
72 namespace KMail {
73 
74  static const unsigned short int imapDefaultPort = 143;
75 
76  //
77  //
78  // Ctor and Dtor
79  //
80  //
81 
82  ImapAccountBase::ImapAccountBase( AccountManager * parent, const TQString & name, uint id )
83  : NetworkAccount( parent, name, id ),
84  mIdleTimer( 0, "mIdleTimer" ),
85  mNoopTimer( 0, "mNoopTimer" ),
86  mTotal( 0 ),
87  mCountUnread( 0 ),
88  mCountLastUnread( 0 ),
89  mAutoExpunge( true ),
90  mHiddenFolders( false ),
91  mOnlySubscribedFolders( false ),
92  mOnlyLocallySubscribedFolders( false ),
93  mLoadOnDemand( true ),
94  mListOnlyOpenFolders( false ),
95  mProgressEnabled( false ),
96  mErrorDialogIsActive( false ),
97  mPasswordDialogIsActive( false ),
98  mACLSupport( true ),
99  mAnnotationSupport( true ),
100  mQuotaSupport( true ),
101  mSlaveConnected( false ),
102  mSlaveConnectionError( false ),
103  mCheckingSingleFolder( false ),
104  mListDirProgressItem( 0 )
105  {
106  mPort = imapDefaultPort;
107  mBodyPartList.setAutoDelete(true);
108  TDEIO::Scheduler::connect(TQ_SIGNAL(slaveError(TDEIO::Slave *, int, const TQString &)),
109  this, TQ_SLOT(slotSchedulerSlaveError(TDEIO::Slave *, int, const TQString &)));
110  TDEIO::Scheduler::connect(TQ_SIGNAL(slaveConnected(TDEIO::Slave *)),
111  this, TQ_SLOT(slotSchedulerSlaveConnected(TDEIO::Slave *)));
112  connect(&mNoopTimer, TQ_SIGNAL(timeout()), TQ_SLOT(slotNoopTimeout()));
113  connect(&mIdleTimer, TQ_SIGNAL(timeout()), TQ_SLOT(slotIdleTimeout()));
114  }
115 
116  ImapAccountBase::~ImapAccountBase() {
117  kdWarning( mSlave, 5006 )
118  << "slave should have been destroyed by subclass!" << endl;
119  }
120 
121  void ImapAccountBase::init() {
122  mAutoExpunge = true;
123  mHiddenFolders = false;
124  mOnlySubscribedFolders = false;
125  mOnlyLocallySubscribedFolders = false;
126  mLoadOnDemand = true;
127  mListOnlyOpenFolders = false;
128  mProgressEnabled = false;
129  }
130 
131  void ImapAccountBase::pseudoAssign( const KMAccount * a ) {
132  NetworkAccount::pseudoAssign( a );
133 
134  const ImapAccountBase * i = dynamic_cast<const ImapAccountBase*>( a );
135  if ( !i ) return;
136 
137  setAutoExpunge( i->autoExpunge() );
138  setHiddenFolders( i->hiddenFolders() );
139  setOnlySubscribedFolders( i->onlySubscribedFolders() );
140  setOnlyLocallySubscribedFolders( i->onlyLocallySubscribedFolders() );
141  setLoadOnDemand( i->loadOnDemand() );
142  setListOnlyOpenFolders( i->listOnlyOpenFolders() );
143  setNamespaces( i->namespaces() );
144  setNamespaceToDelimiter( i->namespaceToDelimiter() );
145  localBlacklistFromStringList( i->locallyBlacklistedFolders() );
146  }
147 
148  unsigned short int ImapAccountBase::defaultPort() const {
149  return imapDefaultPort;
150  }
151 
152  TQString ImapAccountBase::protocol() const {
153  return useSSL() ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL;
154  }
155 
156  //
157  //
158  // Getters and Setters
159  //
160  //
161 
162  void ImapAccountBase::setAutoExpunge( bool expunge ) {
163  mAutoExpunge = expunge;
164  }
165 
166  void ImapAccountBase::setHiddenFolders( bool show ) {
167  mHiddenFolders = show;
168  }
169 
170  void ImapAccountBase::setOnlySubscribedFolders( bool show ) {
171  mOnlySubscribedFolders = show;
172  }
173 
174  void ImapAccountBase::setOnlyLocallySubscribedFolders( bool show ) {
175  mOnlyLocallySubscribedFolders = show;
176  }
177 
178  void ImapAccountBase::setLoadOnDemand( bool load ) {
179  mLoadOnDemand = load;
180  }
181 
182  void ImapAccountBase::setListOnlyOpenFolders( bool only ) {
183  mListOnlyOpenFolders = only;
184  }
185 
186  //
187  //
188  // read/write config
189  //
190  //
191 
192  void ImapAccountBase::readConfig( /*const*/ TDEConfig/*Base*/ & config ) {
193  NetworkAccount::readConfig( config );
194 
195  setAutoExpunge( config.readBoolEntry( "auto-expunge", false ) );
196  setHiddenFolders( config.readBoolEntry( "hidden-folders", false ) );
197  setOnlySubscribedFolders( config.readBoolEntry( "subscribed-folders", false ) );
198  setOnlyLocallySubscribedFolders( config.readBoolEntry( "locally-subscribed-folders", false ) );
199  setLoadOnDemand( config.readBoolEntry( "loadondemand", false ) );
200  setListOnlyOpenFolders( config.readBoolEntry( "listOnlyOpenFolders", false ) );
201  mCapabilities = config.readListEntry( "capabilities", TQStringList() );
202  // read namespaces
203  nsMap map;
204  TQStringList list = config.readListEntry( TQString::number( PersonalNS ) );
205  if ( !list.isEmpty() )
206  map[PersonalNS] = list.gres( "\"", "" );
207  list = config.readListEntry( TQString::number( OtherUsersNS ) );
208  if ( !list.isEmpty() )
209  map[OtherUsersNS] = list.gres( "\"", "" );
210  list = config.readListEntry( TQString::number( SharedNS ) );
211  if ( !list.isEmpty() )
212  map[SharedNS] = list.gres( "\"", "" );
213  setNamespaces( map );
214  // read namespace - delimiter
215  namespaceDelim entries = config.entryMap( config.group() );
216  namespaceDelim namespaceToDelimiter;
217  for ( namespaceDelim::ConstIterator it = entries.begin();
218  it != entries.end(); ++it ) {
219  if ( it.key().startsWith( "Namespace:" ) ) {
220  TQString key = it.key().right( it.key().length() - 10 );
221  namespaceToDelimiter[key] = it.data();
222  }
223  }
224  setNamespaceToDelimiter( namespaceToDelimiter );
225  mOldPrefix = config.readEntry( "prefix" );
226  if ( !mOldPrefix.isEmpty() ) {
227  makeConnection();
228  }
229  localBlacklistFromStringList( config.readListEntry( "locallyUnsubscribedFolders" ) );
230  }
231 
232  void ImapAccountBase::writeConfig( TDEConfig/*Base*/ & config ) /*const*/ {
233  NetworkAccount::writeConfig( config );
234 
235  config.writeEntry( "auto-expunge", autoExpunge() );
236  config.writeEntry( "hidden-folders", hiddenFolders() );
237  config.writeEntry( "subscribed-folders", onlySubscribedFolders() );
238  config.writeEntry( "locally-subscribed-folders", onlyLocallySubscribedFolders() );
239  config.writeEntry( "loadondemand", loadOnDemand() );
240  config.writeEntry( "listOnlyOpenFolders", listOnlyOpenFolders() );
241  config.writeEntry( "capabilities", mCapabilities );
242  TQString data;
243  for ( nsMap::Iterator it = mNamespaces.begin(); it != mNamespaces.end(); ++it ) {
244  if ( !it.data().isEmpty() ) {
245  data = "\"" + it.data().join("\",\"") + "\"";
246  config.writeEntry( TQString::number( it.key() ), data );
247  }
248  }
249  TQString key;
250  for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin();
251  it != mNamespaceToDelimiter.end(); ++it ) {
252  key = "Namespace:" + it.key();
253  config.writeEntry( key, it.data() );
254  }
255  config.writeEntry( "locallyUnsubscribedFolders", locallyBlacklistedFolders() );
256  }
257 
258  //
259  //
260  // Network processing
261  //
262  //
263 
264  MetaData ImapAccountBase::slaveConfig() const {
265  MetaData m = NetworkAccount::slaveConfig();
266 
267  m.insert( "auth", auth() );
268  if ( autoExpunge() )
269  m.insert( "expunge", "auto" );
270 
271  return m;
272  }
273 
274  ImapAccountBase::ConnectionState ImapAccountBase::makeConnection()
275  {
276  if ( mSlave && mSlaveConnected ) {
277  return Connected;
278  }
279  if ( mPasswordDialogIsActive ) return Connecting;
280 
281  if( mAskAgain || ( ( passwd().isEmpty() || login().isEmpty() ) &&
282  auth() != "GSSAPI" ) ) {
283 
284  Q_ASSERT( !mSlave ); // disconnected on 'wrong login' error already, or first try
285  TQString log = login();
286  TQString pass = passwd();
287  // We init "store" to true to indicate that we want to have the
288  // "keep password" checkbox. Then, we set [Passwords]Keep to
289  // storePasswd(), so that the checkbox in the dialog will be
290  // init'ed correctly:
291  TDEConfigGroup passwords( TDEGlobal::config(), "Passwords" );
292  passwords.writeEntry( "Keep", storePasswd() );
293  TQString msg = i18n("You need to supply a username and a password to "
294  "access this mailbox.");
295  mPasswordDialogIsActive = true;
296 
297  PasswordDialog dlg( msg, log, true /* store pw */, true, KMKernel::self()->mainWin() );
298  dlg.setPlainCaption( i18n("Authorization Dialog") );
299  dlg.addCommentLine( i18n("Account:"), name() );
300  int ret = dlg.exec();
301  if (ret != TQDialog::Accepted ) {
302  mPasswordDialogIsActive = false;
303  mAskAgain = false;
304  emit connectionResult( TDEIO::ERR_USER_CANCELED, TQString() );
305  return Error;
306  }
307  mPasswordDialogIsActive = false;
308  // The user has been given the chance to change login and
309  // password, so copy both from the dialog:
310  setPasswd( dlg.password(), dlg.keepPassword() );
311  setLogin( dlg.username() );
312  mAskAgain = false;
313  }
314  // already waiting for a connection?
315  if ( mSlave && !mSlaveConnected ) return Connecting;
316 
317  mSlaveConnected = false;
318  mSlave = TDEIO::Scheduler::getConnectedSlave( getUrl(), slaveConfig() );
319  if ( !mSlave ) {
320  KMessageBox::error(0, i18n("Could not start process for %1.")
321  .arg( getUrl().protocol() ) );
322  return Error;
323  }
324  if ( mSlave->isConnected() ) {
325  slotSchedulerSlaveConnected( mSlave );
326  return Connected;
327  }
328 
329  return Connecting;
330  }
331 
332  bool ImapAccountBase::handleJobError( TDEIO::Job *job, const TQString& context, bool abortSync )
333  {
334  JobIterator it = findJob( job );
335  if ( it != jobsEnd() && (*it).progressItem )
336  {
337  (*it).progressItem->setComplete();
338  (*it).progressItem = 0;
339  }
340  return handleError( job->error(), job->errorText(), job, context, abortSync );
341  }
342 
343  // Called when we're really all done.
344  void ImapAccountBase::postProcessNewMail( bool showStatusMsg ) {
345  setCheckingMail(false);
346  int newMails = 0;
347  if ( mCountUnread > 0 && mCountUnread > mCountLastUnread ) {
348  newMails = mCountUnread - mCountLastUnread;
349  mCountLastUnread = mCountUnread;
350  mCountUnread = 0;
351  checkDone( true, CheckOK );
352  } else {
353  mCountUnread = 0;
354  checkDone( false, CheckOK );
355  }
356  if ( showStatusMsg )
357  BroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
358  name(), newMails);
359  }
360 
361  //-----------------------------------------------------------------------------
362  void ImapAccountBase::changeSubscription( bool subscribe, const TQString& imapPath, bool quiet )
363  {
364  // change the subscription of the folder
365  KURL url = getUrl();
366  url.setPath(imapPath);
367 
368  TQByteArray packedArgs;
369  TQDataStream stream( packedArgs, IO_WriteOnly);
370 
371  if (subscribe)
372  stream << (int) 'u' << url;
373  else
374  stream << (int) 'U' << url;
375 
376  // create the TDEIO-job
377  if ( makeConnection() != Connected )
378  return;// ## doesn't handle Connecting
379  TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false);
380  TDEIO::Scheduler::assignJobToSlave(mSlave, job);
381  jobData jd( url.url(), NULL );
382  // a bit of a hack to save one slot
383  if (subscribe) jd.onlySubscribed = true;
384  else jd.onlySubscribed = false;
385  jd.quiet = quiet;
386  insertJob(job, jd);
387 
388  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
389  TQ_SLOT(slotSubscriptionResult(TDEIO::Job *)));
390  }
391 
392  //-----------------------------------------------------------------------------
393  void ImapAccountBase::slotSubscriptionResult( TDEIO::Job * job )
394  {
395  // result of a subscription-job
396  JobIterator it = findJob( job );
397  if ( it == jobsEnd() ) return;
398  bool onlySubscribed = (*it).onlySubscribed;
399  TQString path = static_cast<TDEIO::SimpleJob*>(job)->url().path();
400  if (job->error())
401  {
402  if ( !(*it).quiet )
403  handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' );
404  emit subscriptionChangeFailed( job->errorString() );
405  // ## emit subscriptionChanged here in case anyone needs it to support continue/cancel
406  }
407  else
408  {
409  emit subscriptionChanged( path, onlySubscribed );
410  if (mSlave) removeJob(job);
411  }
412  }
413 
414  //-----------------------------------------------------------------------------
415  // TODO imapPath can be removed once parent can be a KMFolderImapBase or whatever
416  void ImapAccountBase::getUserRights( KMFolder* parent, const TQString& imapPath )
417  {
418  // There isn't much point in asking the server about a user's rights on his own inbox,
419  // it might not be the effective permissions (at least with Cyrus, one can admin his own inbox,
420  // even after a SETACL that removes the admin permissions. Other imap servers apparently
421  // don't even allow removing one's own admin permission, so this code won't hurt either).
422  if ( imapPath == "/INBOX/" ) {
423  if ( parent->folderType() == KMFolderTypeImap )
424  static_cast<KMFolderImap*>( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok );
425  else if ( parent->folderType() == KMFolderTypeCachedImap )
426  static_cast<KMFolderCachedImap*>( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok );
427  emit receivedUserRights( parent ); // warning, you need to connect first to get that one
428  return;
429  }
430 
431  KURL url = getUrl();
432  url.setPath(imapPath);
433 
434  ACLJobs::GetUserRightsJob* job = ACLJobs::getUserRights( mSlave, url );
435 
436  jobData jd( url.url(), parent );
437  jd.cancellable = true;
438  insertJob(job, jd);
439 
440  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
441  TQ_SLOT(slotGetUserRightsResult(TDEIO::Job *)));
442  }
443 
444  void ImapAccountBase::slotGetUserRightsResult( TDEIO::Job* _job )
445  {
446  ACLJobs::GetUserRightsJob* job = static_cast<ACLJobs::GetUserRightsJob *>( _job );
447  JobIterator it = findJob( job );
448  if ( it == jobsEnd() ) return;
449 
450  KMFolder* folder = (*it).parent;
451  if ( job->error() ) {
452  if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) // that's when the imap server doesn't support ACLs
453  mACLSupport = false;
454  else
455  kdWarning(5006) << "slotGetUserRightsResult: " << job->errorString() << endl;
456  } else {
457 #ifndef NDEBUG
458  //kdDebug(5006) << "User Rights: " << ACLJobs::permissionsToString( job->permissions() ) << endl;
459 #endif
460  }
461  // Store the permissions
462  if ( folder->folderType() == KMFolderTypeImap )
463  static_cast<KMFolderImap*>( folder->storage() )->setUserRights( job->permissions(),
465  else if ( folder->folderType() == KMFolderTypeCachedImap )
466  static_cast<KMFolderCachedImap*>( folder->storage() )->setUserRights( job->permissions(),
468 
469  if (mSlave) removeJob(job);
470  emit receivedUserRights( folder );
471  }
472 
473  //-----------------------------------------------------------------------------
474  void ImapAccountBase::getACL( KMFolder* parent, const TQString& imapPath )
475  {
476  KURL url = getUrl();
477  url.setPath(imapPath);
478 
479  ACLJobs::GetACLJob* job = ACLJobs::getACL( mSlave, url );
480  jobData jd( url.url(), parent );
481  jd.cancellable = true;
482  insertJob(job, jd);
483 
484  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
485  TQ_SLOT(slotGetACLResult(TDEIO::Job *)));
486  }
487 
488  void ImapAccountBase::slotGetACLResult( TDEIO::Job* _job )
489  {
490  ACLJobs::GetACLJob* job = static_cast<ACLJobs::GetACLJob *>( _job );
491  JobIterator it = findJob( job );
492  if ( it == jobsEnd() ) return;
493 
494  KMFolder* folder = (*it).parent;
495  emit receivedACL( folder, job, job->entries() );
496  if (mSlave) removeJob(job);
497  }
498 
499  //-----------------------------------------------------------------------------
500  // Do not remove imapPath, FolderDiaQuotaTab needs to call this with parent==0.
501  void ImapAccountBase::getStorageQuotaInfo( KMFolder* parent, const TQString& imapPath )
502  {
503  if ( !mSlave ) return;
504  KURL url = getUrl();
505  url.setPath(imapPath);
506 
507  QuotaJobs::GetStorageQuotaJob* job = QuotaJobs::getStorageQuota( mSlave, url );
508  jobData jd( url.url(), parent );
509  jd.cancellable = true;
510  insertJob(job, jd);
511 
512  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
513  TQ_SLOT(slotGetStorageQuotaInfoResult(TDEIO::Job *)));
514  }
515 
516  void ImapAccountBase::slotGetStorageQuotaInfoResult( TDEIO::Job* _job )
517  {
518  QuotaJobs::GetStorageQuotaJob* job = static_cast<QuotaJobs::GetStorageQuotaJob *>( _job );
519  JobIterator it = findJob( job );
520  if ( it == jobsEnd() ) return;
521  if ( job->error() && job->error() == TDEIO::ERR_UNSUPPORTED_ACTION )
522  setHasNoQuotaSupport();
523 
524  KMFolder* folder = (*it).parent; // can be 0
525  emit receivedStorageQuotaInfo( folder, job, job->storageQuotaInfo() );
526  if (mSlave) removeJob(job);
527  }
528 
529  void ImapAccountBase::slotNoopTimeout()
530  {
531  if ( mSlave ) {
532  TQByteArray packedArgs;
533  TQDataStream stream( packedArgs, IO_WriteOnly );
534 
535  stream << ( int ) 'N';
536 
537  TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false );
538  TDEIO::Scheduler::assignJobToSlave(mSlave, job);
539  connect( job, TQ_SIGNAL(result( TDEIO::Job * ) ),
540  this, TQ_SLOT( slotSimpleResult( TDEIO::Job * ) ) );
541  } else {
542  /* Stop the timer, we have disconnected. We have to make sure it is
543  started again when a new slave appears. */
544  mNoopTimer.stop();
545  }
546  }
547 
548  void ImapAccountBase::slotIdleTimeout()
549  {
550  if ( mSlave ) {
551  TDEIO::Scheduler::disconnectSlave(mSlave);
552  mSlave = 0;
553  mSlaveConnected = false;
554  /* As for the noop timer, we need to make sure this one is started
555  again when a new slave goes up. */
556  mIdleTimer.stop();
557  }
558  }
559 
560  void ImapAccountBase::slotAbortRequested( KPIM::ProgressItem* item )
561  {
562  if ( item )
563  item->setComplete();
564  killAllJobs();
565  }
566 
567 
568  //-----------------------------------------------------------------------------
569  void ImapAccountBase::slotSchedulerSlaveError(TDEIO::Slave *aSlave, int errorCode,
570  const TQString &errorMsg)
571  {
572  if (aSlave != mSlave) return;
573  handleError( errorCode, errorMsg, 0, TQString(), true );
574  if ( mAskAgain )
575  if ( makeConnection() != ImapAccountBase::Error )
576  return;
577 
578  if ( !mSlaveConnected ) {
579  mSlaveConnectionError = true;
580  resetConnectionList( this );
581  if ( mSlave )
582  {
583  TDEIO::Scheduler::disconnectSlave( slave() );
584  mSlave = 0;
585  }
586  }
587  emit connectionResult( errorCode, errorMsg );
588  }
589 
590  //-----------------------------------------------------------------------------
591  void ImapAccountBase::slotSchedulerSlaveConnected(TDEIO::Slave *aSlave)
592  {
593  if (aSlave != mSlave) return;
594  mSlaveConnected = true;
595  mNoopTimer.start( 60000 ); // make sure we start sending noops
596  emit connectionResult( 0, TQString() ); // success
597 
598  if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) {
599  connect( this, TQ_SIGNAL( namespacesFetched( const ImapAccountBase::nsDelimMap& ) ),
600  this, TQ_SLOT( slotSaveNamespaces( const ImapAccountBase::nsDelimMap& ) ) );
601  getNamespaces();
602  }
603 
604  // get capabilities
605  TQByteArray packedArgs;
606  TQDataStream stream( packedArgs, IO_WriteOnly);
607  stream << (int) 'c';
608  TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false );
609  TDEIO::Scheduler::assignJobToSlave( mSlave, job );
610  connect( job, TQ_SIGNAL(infoMessage(TDEIO::Job*, const TQString&)),
611  TQ_SLOT(slotCapabilitiesResult(TDEIO::Job*, const TQString&)) );
612  }
613 
614  //-----------------------------------------------------------------------------
615  void ImapAccountBase::slotCapabilitiesResult( TDEIO::Job*, const TQString& result )
616  {
617  mCapabilities = TQStringList::split(' ', result.lower() );
618  kdDebug(5006) << "capabilities:" << mCapabilities << endl;
619  }
620 
621  //-----------------------------------------------------------------------------
622  void ImapAccountBase::getNamespaces()
623  {
624  disconnect( this, TQ_SIGNAL( connectionResult(int, const TQString&) ),
625  this, TQ_SLOT( getNamespaces() ) );
626  if ( makeConnection() != Connected || !mSlave ) {
627  kdDebug(5006) << "getNamespaces - wait for connection" << endl;
628  if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) {
629  // when the connection is established slotSchedulerSlaveConnected notifies us
630  } else {
631  // getNamespaces was called by someone else
632  connect( this, TQ_SIGNAL( connectionResult(int, const TQString&) ),
633  this, TQ_SLOT( getNamespaces() ) );
634  }
635  return;
636  }
637 
638  TQByteArray packedArgs;
639  TQDataStream stream( packedArgs, IO_WriteOnly);
640  stream << (int) 'n';
641  jobData jd;
642  jd.total = 1; jd.done = 0; jd.cancellable = true;
643  jd.progressItem = ProgressManager::createProgressItem(
644  ProgressManager::getUniqueID(),
645  i18n("Retrieving Namespaces"),
646  TQString(), true, useSSL() || useTLS() );
647  jd.progressItem->setTotalItems( 1 );
648  connect ( jd.progressItem,
649  TQ_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
650  this,
651  TQ_SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
652  TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false );
653  TDEIO::Scheduler::assignJobToSlave( mSlave, job );
654  insertJob( job, jd );
655  connect( job, TQ_SIGNAL( infoMessage(TDEIO::Job*, const TQString&) ),
656  TQ_SLOT( slotNamespaceResult(TDEIO::Job*, const TQString&) ) );
657  }
658 
659  //-----------------------------------------------------------------------------
660  void ImapAccountBase::slotNamespaceResult( TDEIO::Job* job, const TQString& str )
661  {
662  JobIterator it = findJob( job );
663  if ( it == jobsEnd() ) return;
664 
665  nsDelimMap map;
666  namespaceDelim nsDelim;
667  TQStringList ns = TQStringList::split( ",", str );
668  for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it ) {
669  // split, allow empty parts as we can get empty namespaces
670  TQStringList parts = TQStringList::split( "=", *it, true );
671  imapNamespace section = imapNamespace( parts[0].toInt() );
672  if ( map.contains( section ) ) {
673  nsDelim = map[section];
674  } else {
675  nsDelim.clear();
676  }
677  // map namespace to delimiter
678  nsDelim[parts[1]] = parts[2];
679  map[section] = nsDelim;
680  }
681  removeJob(it);
682 
683  kdDebug(5006) << "namespaces fetched" << endl;
684  emit namespacesFetched( map );
685  }
686 
687  //-----------------------------------------------------------------------------
688  void ImapAccountBase::slotSaveNamespaces( const ImapAccountBase::nsDelimMap& map )
689  {
690  kdDebug(5006) << "slotSaveNamespaces " << name() << endl;
691  // extract the needed information
692  mNamespaces.clear();
693  mNamespaceToDelimiter.clear();
694  for ( uint i = 0; i < 3; ++i ) {
695  imapNamespace section = imapNamespace( i );
696  namespaceDelim ns = map[ section ];
697  namespaceDelim::ConstIterator it;
698  TQStringList list;
699  for ( it = ns.begin(); it != ns.end(); ++it ) {
700  list += it.key();
701  mNamespaceToDelimiter[ it.key() ] = it.data();
702  }
703  if ( !list.isEmpty() ) {
704  mNamespaces[section] = list;
705  }
706  }
707  // see if we need to migrate an old prefix
708  if ( !mOldPrefix.isEmpty() ) {
709  migratePrefix();
710  }
711  emit namespacesFetched();
712  }
713 
714  //-----------------------------------------------------------------------------
715  void ImapAccountBase::migratePrefix()
716  {
717  if ( !mOldPrefix.isEmpty() && mOldPrefix != "/" ) {
718  // strip /
719  if ( mOldPrefix.startsWith("/") ) {
720  mOldPrefix = mOldPrefix.right( mOldPrefix.length()-1 );
721  }
722  if ( mOldPrefix.endsWith("/") ) {
723  mOldPrefix = mOldPrefix.left( mOldPrefix.length()-1 );
724  }
725  TQStringList list = mNamespaces[PersonalNS];
726  bool done = false;
727  for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
728  if ( (*it).startsWith( mOldPrefix ) ) {
729  // should be ok
730  done = true;
731  kdDebug(5006) << "migratePrefix - no migration needed" << endl;
732  break;
733  }
734  }
735  if ( !done ) {
736  TQString msg = i18n("KMail has detected a prefix entry in the "
737  "configuration of the account \"%1\" which is obsolete with the "
738  "support of IMAP namespaces.").arg( name() );
739  if ( list.contains( "" ) ) {
740  // replace empty entry with the old prefix
741  list.remove( "" );
742  list += mOldPrefix;
743  mNamespaces[PersonalNS] = list;
744  if ( mNamespaceToDelimiter.contains( "" ) ) {
745  TQString delim = mNamespaceToDelimiter[""];
746  mNamespaceToDelimiter.remove( "" );
747  mNamespaceToDelimiter[mOldPrefix] = delim;
748  }
749  kdDebug(5006) << "migratePrefix - replaced empty with " << mOldPrefix << endl;
750  msg += i18n("The configuration was automatically migrated but you should check "
751  "your account configuration.");
752  } else if ( list.count() == 1 ) {
753  // only one entry in the personal namespace so replace it
754  TQString old = list.first();
755  list.clear();
756  list += mOldPrefix;
757  mNamespaces[PersonalNS] = list;
758  if ( mNamespaceToDelimiter.contains( old ) ) {
759  TQString delim = mNamespaceToDelimiter[old];
760  mNamespaceToDelimiter.remove( old );
761  mNamespaceToDelimiter[mOldPrefix] = delim;
762  }
763  kdDebug(5006) << "migratePrefix - replaced single with " << mOldPrefix << endl;
764  msg += i18n("The configuration was automatically migrated but you should check "
765  "your account configuration.");
766  } else {
767  kdDebug(5006) << "migratePrefix - migration failed" << endl;
768  msg += i18n("It was not possible to migrate your configuration automatically "
769  "so please check your account configuration.");
770  }
771  KMessageBox::information( kmkernel->getKMMainWidget(), msg );
772  }
773  } else
774  {
775  kdDebug(5006) << "migratePrefix - no migration needed" << endl;
776  }
777  mOldPrefix = "";
778  }
779 
780  //-----------------------------------------------------------------------------
781  TQString ImapAccountBase::namespaceForFolder( FolderStorage* storage )
782  {
783  TQString path;
784  if ( storage->folderType() == KMFolderTypeImap ) {
785  path = static_cast<KMFolderImap*>( storage )->imapPath();
786  } else if ( storage->folderType() == KMFolderTypeCachedImap ) {
787  path = static_cast<KMFolderCachedImap*>( storage )->imapPath();
788  }
789 
790  nsMap::Iterator it;
791  for ( it = mNamespaces.begin(); it != mNamespaces.end(); ++it )
792  {
793  TQStringList::Iterator strit;
794  for ( strit = it.data().begin(); strit != it.data().end(); ++strit )
795  {
796  TQString ns = *strit;
797  if ( ns.endsWith("/") || ns.endsWith(".") ) {
798  // strip delimiter for the comparison
799  ns = ns.left( ns.length()-1 );
800  }
801  // first ignore an empty prefix as it would match always
802  if ( !ns.isEmpty() && path.find( ns ) != -1 ) {
803  return (*strit);
804  }
805  }
806  }
807  return TQString();
808  }
809 
810  //-----------------------------------------------------------------------------
811  TQString ImapAccountBase::delimiterForNamespace( const TQString& prefix )
812  {
813  //kdDebug(5006) << "delimiterForNamespace " << prefix << endl;
814  // try to match exactly
815  if ( mNamespaceToDelimiter.contains(prefix) ) {
816  return mNamespaceToDelimiter[prefix];
817  }
818 
819  // then try if the prefix is part of a namespace
820  // exclude empty namespace
821  for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin();
822  it != mNamespaceToDelimiter.end(); ++it ) {
823  // the namespace definition sometimes contains the delimiter
824  // make sure we also match this version
825  TQString stripped = it.key().left( it.key().length() - 1 );
826  if ( !it.key().isEmpty() &&
827  ( prefix.contains( it.key() ) || prefix.contains( stripped ) ) ) {
828  return it.data();
829  }
830  }
831  // see if we have an empty namespace
832  // this should always be the case
833  if ( mNamespaceToDelimiter.contains( "" ) ) {
834  return mNamespaceToDelimiter[""];
835  }
836  // well, we tried
837  //kdDebug(5006) << "delimiterForNamespace - not found" << endl;
838  return TQString();
839  }
840 
841  //-----------------------------------------------------------------------------
842  TQString ImapAccountBase::delimiterForFolder( FolderStorage* storage )
843  {
844  TQString prefix = namespaceForFolder( storage );
845  TQString delim = delimiterForNamespace( prefix );
846  return delim;
847  }
848 
849  //-----------------------------------------------------------------------------
850  void ImapAccountBase::slotSimpleResult(TDEIO::Job * job)
851  {
852  JobIterator it = findJob( job );
853  bool quiet = false;
854  if (it != mapJobData.end()) {
855  quiet = (*it).quiet;
856  if ( !(job->error() && !quiet) ) // the error handler removes in that case
857  removeJob(it);
858  }
859  if (job->error()) {
860  if (!quiet)
861  handleJobError(job, TQString() );
862  else {
863  if ( job->error() == TDEIO::ERR_CONNECTION_BROKEN && slave() ) {
864  // make sure ERR_CONNECTION_BROKEN is properly handled and the slave
865  // disconnected even when quiet()
866  TDEIO::Scheduler::disconnectSlave( slave() );
867  mSlave = 0;
868  }
869  if (job->error() == TDEIO::ERR_SLAVE_DIED)
870  slaveDied();
871  }
872  }
873  }
874 
875  //-----------------------------------------------------------------------------
876  bool ImapAccountBase::handlePutError( TDEIO::Job* job, jobData& jd, KMFolder* folder )
877  {
878  Q_ASSERT( !jd.msgList.isEmpty() );
879  KMMessage* msg = jd.msgList.first();
880  // Use double-quotes around the subject to keep the sentence readable,
881  // but don't use double quotes around the sender since from() might return a double-quoted name already
882  const TQString subject = msg->subject().isEmpty() ? i18n( "<unknown>" ) : TQString("\"%1\"").arg( msg->subject() );
883  const TQString from = msg->from().isEmpty() ? i18n( "<unknown>" ) : msg->from();
884  TQString myError = "<p><b>" + i18n("Error while uploading message")
885  + "</b></p><p>"
886  + i18n("Could not upload the message dated %1 from <i>%2</i> with subject <i>%3</i> to the server.").arg( msg->dateStr(), TQStyleSheet::escape( from ), TQStyleSheet::escape( subject ) )
887  + "</p><p>"
888  + i18n("The destination folder was: <b>%1</b>.").arg( TQStyleSheet::escape( folder->prettyURL() ) )
889  + "</p><p>"
890  + i18n("The server reported:") + "</p>";
891  return handleJobError( job, myError );
892  }
893 
894  TQString ImapAccountBase::prettifyQuotaError( const TQString& _error, TDEIO::Job * job )
895  {
896  TQString error = _error;
897  if ( error.find( "quota", 0, false ) == -1 ) return error;
898  // this is a quota error, prettify it a bit
899  JobIterator it = findJob( job );
900  TQString quotaAsString( i18n("No detailed quota information available.") );
901  bool readOnly = false;
902  if (it != mapJobData.end()) {
903  const KMFolder * const folder = (*it).parent;
904  if( !folder ) return _error;
905  const KMFolderCachedImap * const imap = dynamic_cast<const KMFolderCachedImap*>( folder->storage() );
906  if ( imap ) {
907  quotaAsString = imap->quotaInfo().toString();
908  }
909  readOnly = folder->isReadOnly();
910  }
911  error = i18n("The folder is too close to its quota limit. (%1)").arg( quotaAsString );
912  if ( readOnly ) {
913  error += i18n("\nSince you do not have write privileges on this folder, "
914  "please ask the owner of the folder to free up some space in it.");
915  }
916  return error;
917  }
918 
919  //-----------------------------------------------------------------------------
920  bool ImapAccountBase::handleError( int errorCode, const TQString &errorMsg, TDEIO::Job* job, const TQString& context, bool abortSync )
921  {
922  // Copy job's data before a possible killAllJobs
923  TQStringList errors;
924  if ( job && job->error() != TDEIO::ERR_SLAVE_DEFINED /*workaround for tdelibs-3.2*/)
925  errors = job->detailedErrorStrings();
926 
927  bool jobsKilled = true;
928  switch( errorCode ) {
929  case TDEIO::ERR_SLAVE_DIED: slaveDied(); killAllJobs( true ); break;
930  case TDEIO::ERR_COULD_NOT_AUTHENTICATE: // bad password
931  mAskAgain = true;
932  // fallthrough intended
933  case TDEIO::ERR_CONNECTION_BROKEN:
934  case TDEIO::ERR_COULD_NOT_CONNECT:
935  case TDEIO::ERR_SERVER_TIMEOUT:
936  // These mean that we'll have to reconnect on the next attempt, so disconnect and set mSlave to 0.
937  killAllJobs( true );
938  break;
939  case TDEIO::ERR_COULD_NOT_LOGIN:
940  case TDEIO::ERR_USER_CANCELED:
941  killAllJobs( false );
942  break;
943  default:
944  if ( abortSync )
945  killAllJobs( false );
946  else
947  jobsKilled = false;
948  break;
949  }
950 
951  // check if we still display an error
952  if ( !mErrorDialogIsActive && errorCode != TDEIO::ERR_USER_CANCELED ) {
953  mErrorDialogIsActive = true;
954  TQString msg = context + '\n' + prettifyQuotaError( TDEIO::buildErrorString( errorCode, errorMsg ), job );
955  TQString caption = i18n("Error");
956 
957  if ( jobsKilled || errorCode == TDEIO::ERR_COULD_NOT_LOGIN ) {
958  if ( errorCode == TDEIO::ERR_SERVER_TIMEOUT || errorCode == TDEIO::ERR_CONNECTION_BROKEN ) {
959  msg = i18n("The connection to the server %1 was unexpectedly closed or timed out. It will be re-established automatically if possible.").
960  arg( name() );
961  KMessageBox::information( kapp->activeWindow(), msg, caption, "kmailConnectionBrokenErrorDialog" );
962  // Show it in the status bar, in case the user has ticked "don't show again"
963  if ( errorCode == TDEIO::ERR_CONNECTION_BROKEN )
964  KPIM::BroadcastStatus::instance()->setStatusMsg(
965  i18n( "The connection to account %1 was broken." ).arg( name() ) );
966  else if ( errorCode == TDEIO::ERR_SERVER_TIMEOUT )
967  KPIM::BroadcastStatus::instance()->setStatusMsg(
968  i18n( "The connection to account %1 timed out." ).arg( name() ) );
969  } else {
970  if ( !errors.isEmpty() )
971  KMessageBox::detailedError( kapp->activeWindow(), msg, errors.join("\n").prepend("<qt>"), caption );
972  else
973  KMessageBox::error( kapp->activeWindow(), msg, caption );
974  }
975  } else { // i.e. we have a chance to continue, ask the user about it
976  if ( errors.count() >= 3 ) { // there is no detailedWarningContinueCancel... (#86517)
977  TQString error = prettifyQuotaError( errors[1], job );
978  msg = TQString( "<qt>") + context + error + '\n' + errors[2];
979  caption = errors[0];
980  }
981  int ret = KMessageBox::warningContinueCancel( kapp->activeWindow(), msg, caption );
982  if ( ret == KMessageBox::Cancel ) {
983  jobsKilled = true;
984  killAllJobs( false );
985  }
986  }
987  mErrorDialogIsActive = false;
988  } else {
989  if ( mErrorDialogIsActive )
990  kdDebug(5006) << "suppressing error:" << errorMsg << endl;
991  }
992  if ( job && !jobsKilled )
993  removeJob( job );
994  return !jobsKilled; // jobsKilled==false -> continue==true
995  }
996 
997  //-----------------------------------------------------------------------------
998  void ImapAccountBase::cancelMailCheck()
999  {
1000  TQMap<TDEIO::Job*, jobData>::Iterator it = mapJobData.begin();
1001  while ( it != mapJobData.end() ) {
1002  kdDebug(5006) << "cancelMailCheck: job is cancellable: " << (*it).cancellable << endl;
1003  if ( (*it).cancellable ) {
1004  it.key()->kill();
1005  TQMap<TDEIO::Job*, jobData>::Iterator rmit = it;
1006  ++it;
1007  mapJobData.remove( rmit );
1008  // We killed a job -> this kills the slave
1009  mSlave = 0;
1010  } else
1011  ++it;
1012  }
1013 
1014  for( TQPtrListIterator<FolderJob> it( mJobList ); it.current(); ++it ) {
1015  if ( it.current()->isCancellable() ) {
1016  FolderJob* job = it.current();
1017  job->setPassiveDestructor( true );
1018  mJobList.remove( job );
1019  delete job;
1020  } else
1021  ++it;
1022  }
1023  }
1024 
1025  //-----------------------------------------------------------------------------
1026  void ImapAccountBase::processNewMailInFolder( KMFolder* folder, FolderListType type /*= Single*/ )
1027  {
1028  if ( mFoldersQueuedForChecking.contains( folder ) )
1029  return;
1030  mFoldersQueuedForChecking.append( folder );
1031  mCheckingSingleFolder = ( type == Single );
1032  if ( checkingMail() )
1033  {
1034  disconnect( this, TQ_SIGNAL( finishedCheck( bool, CheckStatus ) ),
1035  this, TQ_SLOT( slotCheckQueuedFolders() ) );
1036  connect( this, TQ_SIGNAL( finishedCheck( bool, CheckStatus ) ),
1037  this, TQ_SLOT( slotCheckQueuedFolders() ) );
1038  } else {
1039  slotCheckQueuedFolders();
1040  }
1041  }
1042 
1043  //-----------------------------------------------------------------------------
1044  void ImapAccountBase::slotCheckQueuedFolders()
1045  {
1046  disconnect( this, TQ_SIGNAL( finishedCheck( bool, CheckStatus ) ),
1047  this, TQ_SLOT( slotCheckQueuedFolders() ) );
1048 
1049  TQValueList<TQGuardedPtr<KMFolder> > mSaveList = mMailCheckFolders;
1050  mMailCheckFolders = mFoldersQueuedForChecking;
1051  if ( kmkernel->acctMgr() )
1052  kmkernel->acctMgr()->singleCheckMail(this, true);
1053  mMailCheckFolders = mSaveList;
1054  mFoldersQueuedForChecking.clear();
1055  }
1056 
1057  //-----------------------------------------------------------------------------
1058  bool ImapAccountBase::checkingMail( KMFolder *folder )
1059  {
1060  if (checkingMail() && mFoldersQueuedForChecking.contains(folder))
1061  return true;
1062  return false;
1063  }
1064 
1065  //-----------------------------------------------------------------------------
1066  void ImapAccountBase::handleBodyStructure( TQDataStream & stream, KMMessage * msg,
1067  const AttachmentStrategy *as )
1068  {
1069  mBodyPartList.clear();
1070  mCurrentMsg = msg;
1071  // first delete old parts as we construct our own
1072  msg->deleteBodyParts();
1073  // make the parts and fill the mBodyPartList
1074  constructParts( stream, 1, 0, 0, msg->asDwMessage() );
1075  if ( mBodyPartList.count() == 1 ) // we directly set the body later, at partsToLoad below
1076  msg->deleteBodyParts();
1077 
1078  if ( !as )
1079  {
1080  kdWarning(5006) << k_funcinfo << " - found no attachment strategy!" << endl;
1081  return;
1082  }
1083 
1084  // see what parts have to loaded according to attachmentstrategy
1085  BodyVisitor *visitor = BodyVisitorFactory::getVisitor( as );
1086  visitor->visit( mBodyPartList );
1087  TQPtrList<KMMessagePart> parts = visitor->partsToLoad();
1088  delete visitor;
1089  TQPtrListIterator<KMMessagePart> it( parts );
1090  KMMessagePart *part;
1091  int partsToLoad = 0;
1092  // check how many parts we have to load
1093  while ( (part = it.current()) != 0 )
1094  {
1095  ++it;
1096  if ( part->loadPart() )
1097  {
1098  ++partsToLoad;
1099  }
1100  }
1101  // if the only body part is not text, part->loadPart() would return false
1102  // and that part is never loaded, so make sure it loads.
1103  // it seems that TEXT does load the single body part even if it is not text/*
1104  if ( mBodyPartList.count() == 1 && partsToLoad == 0 )
1105  partsToLoad = 1; // this causes the next test to succeed, and loads the whole message
1106 
1107  if ( (mBodyPartList.count() * 0.5) < partsToLoad )
1108  {
1109  // more than 50% of the parts have to be loaded anyway so it is faster
1110  // to load the message completely
1111  kdDebug(5006) << "Falling back to normal mode" << endl;
1112  FolderJob *job = msg->parent()->createJob(
1113  msg, FolderJob::tGetMessage, 0, "TEXT" );
1114  job->start();
1115  return;
1116  }
1117  it.toFirst();
1118  while ( (part = it.current()) != 0 )
1119  {
1120  ++it;
1121  kdDebug(5006) << "ImapAccountBase::handleBodyStructure - load " << part->partSpecifier()
1122  << " (" << part->originalContentTypeStr() << ")" << endl;
1123  if ( part->loadHeaders() )
1124  {
1125  kdDebug(5006) << "load HEADER" << endl;
1126  FolderJob *job = msg->parent()->createJob(
1127  msg, FolderJob::tGetMessage, 0, part->partSpecifier()+".MIME" );
1128  job->start();
1129  }
1130  if ( part->loadPart() )
1131  {
1132  kdDebug(5006) << "load Part" << endl;
1133  FolderJob *job = msg->parent()->createJob(
1134  msg, FolderJob::tGetMessage, 0, part->partSpecifier() );
1135  job->start();
1136  }
1137  }
1138  }
1139 
1140  //-----------------------------------------------------------------------------
1141  void ImapAccountBase::constructParts( TQDataStream & stream, int count, KMMessagePart* parentKMPart,
1142  DwBodyPart * parent, const DwMessage * dwmsg )
1143  {
1144  int children;
1145  for (int i = 0; i < count; i++)
1146  {
1147  stream >> children;
1148  KMMessagePart* part = new KMMessagePart( stream );
1149  part->setParent( parentKMPart );
1150  mBodyPartList.append( part );
1151  kdDebug(5006) << "ImapAccountBase::constructParts - created id " << part->partSpecifier()
1152  << " of type " << part->originalContentTypeStr() << endl;
1153  DwBodyPart *dwpart = mCurrentMsg->createDWBodyPart( part );
1154 
1155  if ( parent )
1156  {
1157  // add to parent body
1158  parent->Body().AddBodyPart( dwpart );
1159  dwpart->Parse();
1160 // kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
1161 // << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl;
1162  } else if ( part->partSpecifier() != "0" &&
1163  !part->partSpecifier().endsWith(".HEADER") )
1164  {
1165  // add to message
1166  dwmsg->Body().AddBodyPart( dwpart );
1167  dwpart->Parse();
1168 // kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
1169 // << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl;
1170  } else
1171  dwpart = 0;
1172 
1173  if ( !parentKMPart )
1174  parentKMPart = part;
1175 
1176  if (children > 0)
1177  {
1178  DwBodyPart* newparent = dwpart;
1179  const DwMessage* newmsg = dwmsg;
1180  if ( part->originalContentTypeStr() == "MESSAGE/RFC822" && dwpart &&
1181  dwpart->Body().Message() )
1182  {
1183  // set the encapsulated message as the new message
1184  newparent = 0;
1185  newmsg = dwpart->Body().Message();
1186  }
1187  KMMessagePart* newParentKMPart = part;
1188  if ( part->partSpecifier().endsWith(".HEADER") ) // we don't want headers as parent
1189  newParentKMPart = parentKMPart;
1190 
1191  constructParts( stream, children, newParentKMPart, newparent, newmsg );
1192  }
1193  }
1194  }
1195 
1196  //-----------------------------------------------------------------------------
1197  void ImapAccountBase::setImapStatus( KMFolder* folder, const TQString& path, const TQCString& flags )
1198  {
1199  // set the status on the server, the uids are integrated in the path
1200  kdDebug(5006) << "setImapStatus path=" << path << " to: " << flags << endl;
1201  KURL url = getUrl();
1202  url.setPath(path);
1203 
1204  TQByteArray packedArgs;
1205  TQDataStream stream( packedArgs, IO_WriteOnly);
1206 
1207  stream << (int) 'S' << url << flags;
1208 
1209  if ( makeConnection() != Connected )
1210  return; // can't happen with dimap
1211 
1212  TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false);
1213  TDEIO::Scheduler::assignJobToSlave(slave(), job);
1214  ImapAccountBase::jobData jd( url.url(), folder );
1215  jd.path = path;
1216  insertJob(job, jd);
1217  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
1218  TQ_SLOT(slotSetStatusResult(TDEIO::Job *)));
1219  }
1220 
1221  void ImapAccountBase::setImapSeenStatus(KMFolder * folder, const TQString & path, bool seen)
1222  {
1223  KURL url = getUrl();
1224  url.setPath(path);
1225 
1226  TQByteArray packedArgs;
1227  TQDataStream stream( packedArgs, IO_WriteOnly);
1228 
1229  stream << (int) 's' << url << seen;
1230 
1231  if ( makeConnection() != Connected )
1232  return; // can't happen with dimap
1233 
1234  TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false);
1235  TDEIO::Scheduler::assignJobToSlave(slave(), job);
1236  ImapAccountBase::jobData jd( url.url(), folder );
1237  jd.path = path;
1238  insertJob(job, jd);
1239  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
1240  TQ_SLOT(slotSetStatusResult(TDEIO::Job *)));
1241  }
1242 
1243  //-----------------------------------------------------------------------------
1244  void ImapAccountBase::slotSetStatusResult(TDEIO::Job * job)
1245  {
1246  ImapAccountBase::JobIterator it = findJob(job);
1247  if ( it == jobsEnd() ) return;
1248  int errorCode = job->error();
1249  KMFolder * const parent = (*it).parent;
1250  const TQString path = (*it).path;
1251  if (errorCode && errorCode != TDEIO::ERR_CANNOT_OPEN_FOR_WRITING)
1252  {
1253  bool cont = handleJobError( job, i18n( "Error while uploading status of messages to server: " ) + '\n' );
1254  emit imapStatusChanged( parent, path, cont );
1255  }
1256  else
1257  {
1258  emit imapStatusChanged( parent, path, true );
1259  removeJob(it);
1260  }
1261  }
1262 
1263  //-----------------------------------------------------------------------------
1264  void ImapAccountBase::setFolder(KMFolder* folder, bool addAccount)
1265  {
1266  if (folder)
1267  {
1268  folder->setSystemLabel(name());
1269  folder->setId(id());
1270  }
1271  NetworkAccount::setFolder(folder, addAccount);
1272  }
1273 
1274  //-----------------------------------------------------------------------------
1275  void ImapAccountBase::removeJob( JobIterator& it )
1276  {
1277  if( (*it).progressItem ) {
1278  (*it).progressItem->setComplete();
1279  (*it).progressItem = 0;
1280  }
1281  mapJobData.remove( it );
1282  }
1283 
1284  //-----------------------------------------------------------------------------
1285  void KMail::ImapAccountBase::removeJob( TDEIO::Job* job )
1286  {
1287  mapJobData.remove( job );
1288  }
1289 
1290  //-----------------------------------------------------------------------------
1291  KPIM::ProgressItem* ImapAccountBase::listDirProgressItem()
1292  {
1293  if ( !mListDirProgressItem )
1294  {
1295  mListDirProgressItem = ProgressManager::createProgressItem(
1296  "ListDir" + name(),
1297  TQStyleSheet::escape( name() ),
1298  i18n("retrieving folders"),
1299  true,
1300  useSSL() || useTLS() );
1301  connect ( mListDirProgressItem,
1302  TQ_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
1303  this,
1304  TQ_SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
1305  // Start with a guessed value of the old folder count plus 5%. As long
1306  // as the list of folders doesn't constantly change, that should be good
1307  // enough.
1308  unsigned int count = folderCount();
1309  mListDirProgressItem->setTotalItems( count + (unsigned int)(count*0.05) );
1310  }
1311  return mListDirProgressItem;
1312  }
1313 
1314  //-----------------------------------------------------------------------------
1315  unsigned int ImapAccountBase::folderCount() const
1316  {
1317  if ( !rootFolder() || !rootFolder()->folder() || !rootFolder()->folder()->child() )
1318  return 0;
1319  return kmkernel->imapFolderMgr()->folderCount( rootFolder()->folder()->child() );
1320  }
1321 
1322  //------------------------------------------------------------------------------
1323  TQString ImapAccountBase::addPathToNamespace( const TQString& prefix )
1324  {
1325  TQString myPrefix = prefix;
1326  if ( !myPrefix.startsWith( "/" ) ) {
1327  myPrefix = "/" + myPrefix;
1328  }
1329  if ( !myPrefix.endsWith( "/" ) ) {
1330  myPrefix += "/";
1331  }
1332 
1333  return myPrefix;
1334  }
1335 
1336  //------------------------------------------------------------------------------
1337  bool ImapAccountBase::isNamespaceFolder( TQString& name )
1338  {
1339  TQStringList ns = mNamespaces[OtherUsersNS];
1340  ns += mNamespaces[SharedNS];
1341  ns += mNamespaces[PersonalNS];
1342  TQString nameWithDelimiter;
1343  for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
1344  {
1345  nameWithDelimiter = name + delimiterForNamespace( *it );
1346  if ( *it == name || *it == nameWithDelimiter )
1347  return true;
1348  }
1349  return false;
1350  }
1351 
1352  //------------------------------------------------------------------------------
1353  ImapAccountBase::nsDelimMap ImapAccountBase::namespacesWithDelimiter()
1354  {
1355  nsDelimMap map;
1356  nsMap::ConstIterator it;
1357  for ( uint i = 0; i < 3; ++i )
1358  {
1359  imapNamespace section = imapNamespace( i );
1360  TQStringList namespaces = mNamespaces[section];
1361  namespaceDelim nsDelim;
1362  TQStringList::Iterator lit;
1363  for ( lit = namespaces.begin(); lit != namespaces.end(); ++lit )
1364  {
1365  nsDelim[*lit] = delimiterForNamespace( *lit );
1366  }
1367  map[section] = nsDelim;
1368  }
1369  return map;
1370  }
1371 
1372  //------------------------------------------------------------------------------
1373  TQString ImapAccountBase::createImapPath( const TQString& parent,
1374  const TQString& folderName )
1375  {
1376  kdDebug(5006) << "createImapPath parent="<<parent<<", folderName="<<folderName<<endl;
1377  TQString newName = parent;
1378  // strip / at the end
1379  if ( newName.endsWith("/") ) {
1380  newName = newName.left( newName.length() - 1 );
1381  }
1382  // add correct delimiter
1383  TQString delim = delimiterForNamespace( newName );
1384  // should not happen...
1385  if ( delim.isEmpty() ) {
1386  delim = "/";
1387  }
1388  if ( !newName.isEmpty() &&
1389  !newName.endsWith( delim ) && !folderName.startsWith( delim ) ) {
1390  newName = newName + delim;
1391  }
1392  newName = newName + folderName;
1393  // add / at the end
1394  if ( !newName.endsWith("/") ) {
1395  newName = newName + "/";
1396  }
1397 
1398  return newName;
1399  }
1400 
1401  //------------------------------------------------------------------------------
1402  TQString ImapAccountBase::createImapPath( FolderStorage* parent,
1403  const TQString& folderName )
1404  {
1405  TQString path;
1406  if ( parent->folderType() == KMFolderTypeImap ) {
1407  path = static_cast<KMFolderImap*>( parent )->imapPath();
1408  } else if ( parent->folderType() == KMFolderTypeCachedImap ) {
1409  path = static_cast<KMFolderCachedImap*>( parent )->imapPath();
1410  } else {
1411  // error
1412  return path;
1413  }
1414 
1415  return createImapPath( path, folderName );
1416  }
1417 
1418 
1419  bool ImapAccountBase::locallySubscribedTo( const TQString& imapPath )
1420  {
1421  return mLocalSubscriptionBlackList.find( imapPath ) == mLocalSubscriptionBlackList.end();
1422  }
1423 
1424  void ImapAccountBase::changeLocalSubscription( const TQString& imapPath, bool subscribe )
1425  {
1426  if ( subscribe ) {
1427  // find in blacklist and remove from it
1428  mLocalSubscriptionBlackList.erase( imapPath );
1429  } else {
1430  // blacklist
1431  mLocalSubscriptionBlackList.insert( imapPath );
1432  }
1433  }
1434 
1435 
1436  TQStringList ImapAccountBase::locallyBlacklistedFolders() const
1437  {
1438  TQStringList list;
1439  std::set<TQString>::const_iterator it = mLocalSubscriptionBlackList.begin();
1440  std::set<TQString>::const_iterator end = mLocalSubscriptionBlackList.end();
1441  for ( ; it != end ; ++it )
1442  list.append( *it );
1443  return list;
1444  }
1445 
1446  void ImapAccountBase::localBlacklistFromStringList( const TQStringList &list )
1447  {
1448  for( TQStringList::ConstIterator it = list.constBegin( ); it != list.constEnd( ); ++it )
1449  mLocalSubscriptionBlackList.insert( *it );
1450  }
1451 
1452 } // namespace KMail
1453 
1454 #include "imapaccountbase.moc"
The FolderStorage class is the bass class for the storage related aspects of a collection of mail (a ...
Definition: folderstorage.h:80
virtual KMFolderType folderType() const
Returns the type of this folder.
Definition: folderstorage.h:96
Mail folder.
Definition: kmfolder.h:69
virtual TQString prettyURL() const
URL of the node for visualization purposes.
Definition: kmfolder.cpp:593
KMFolderType folderType() const
Returns the type of this folder.
Definition: kmfolder.cpp:233
bool isReadOnly() const
Is the folder read-only?
Definition: kmfolder.cpp:561
static KMKernel * self()
normal control stuff
Definition: kmkernel.h:259
This is a Mime Message.
Definition: kmmessage.h:68
TQString from() const
Get or set the 'From' header field.
Definition: kmmessage.cpp:2015
TQString subject() const
Get or set the 'Subject' header field.
Definition: kmmessage.cpp:2049
void deleteBodyParts()
Delete all body parts.
Definition: kmmessage.cpp:3174
TQString dateStr() const
Get or set the 'Date' header field.
Definition: kmmessage.cpp:1797
The account manager is responsible for creating accounts of various types via the factory method crea...
@ Ok
The user rights/ACL have been fetched from the server sucessfully.
Definition: acljobs.h:66
@ FetchFailed
The attempt to fetch the user rights/ACL from the server failed.
Definition: acljobs.h:67
folderdiaquotatab.h
Definition: aboutdata.cpp:40