kmail

kmfoldercachedimap.cpp
1 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include <errno.h>
37 
38 #include <tqvaluevector.h>
39 
40 #include "kmkernel.h"
41 #include "kmfoldercachedimap.h"
42 #include "undostack.h"
43 #include "kmfoldermgr.h"
44 #include "kmacctcachedimap.h"
45 #include "accountmanager.h"
47 #include "kmailicalifaceimpl.h"
48 #include "kmfolder.h"
49 #include "kmglobal.h"
50 #include "acljobs.h"
51 #include "broadcaststatus.h"
52 using KPIM::BroadcastStatus;
53 #include "progressmanager.h"
54 
55 using KMail::CachedImapJob;
56 #include "imapaccountbase.h"
57 using KMail::ImapAccountBase;
58 #include "listjob.h"
59 using KMail::ListJob;
60 
61 #include "kmfolderseldlg.h"
62 #include "kmcommands.h"
63 #include "kmmainwidget.h"
64 
65 #include <tdeapplication.h>
66 #include <tdemessagebox.h>
67 #include <tdelocale.h>
68 #include <kdebug.h>
69 #include <tdeconfig.h>
70 #include <tdeio/global.h>
71 #include <tdeio/scheduler.h>
72 #include <tqbuffer.h>
73 #include <tqbuttongroup.h>
74 #include <tqcombobox.h>
75 #include <tqfile.h>
76 #include <tqhbox.h>
77 #include <tqlabel.h>
78 #include <tqlayout.h>
79 #include <tqradiobutton.h>
80 #include <tqvaluelist.h>
81 #include "annotationjobs.h"
82 #include "quotajobs.h"
83 using namespace KMail;
84 #include <globalsettings.h>
85 
86 #define UIDCACHE_VERSION 1
87 #define MAIL_LOSS_DEBUGGING 0
88 
89 static TQString incidencesForToString( KMFolderCachedImap::IncidencesFor r ) {
90  switch (r) {
91  case KMFolderCachedImap::IncForNobody: return "nobody";
92  case KMFolderCachedImap::IncForAdmins: return "admins";
93  case KMFolderCachedImap::IncForReaders: return "readers";
94  }
95  return TQString(); // can't happen
96 }
97 
98 static KMFolderCachedImap::IncidencesFor incidencesForFromString( const TQString& str ) {
99  if ( str == "nobody" ) return KMFolderCachedImap::IncForNobody;
100  if ( str == "admins" ) return KMFolderCachedImap::IncForAdmins;
101  if ( str == "readers" ) return KMFolderCachedImap::IncForReaders;
102  return KMFolderCachedImap::IncForAdmins; // by default
103 }
104 
105 DImapTroubleShootDialog::DImapTroubleShootDialog( TQWidget* parent,
106  const char* name )
107  : KDialogBase( Plain, i18n( "Troubleshooting IMAP Cache" ),
108  Ok | Cancel, Cancel, parent, name, true ),
109  rc( None )
110 {
111  TQFrame* page = plainPage();
112  TQVBoxLayout *topLayout = new TQVBoxLayout( page, 0 );
113  // spell "lose" correctly. but don't cause a fuzzy.
114  TQString txt = i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>"
115  "<p>If you have problems with synchronizing an IMAP "
116  "folder, you should first try rebuilding the index "
117  "file. This will take some time to rebuild, but will "
118  "not cause any problems.</p><p>If that is not enough, "
119  "you can try refreshing the IMAP cache. If you do this, "
120  "you will loose all your local changes for this folder "
121  "and all its subfolders.</p>",
122  "<p><b>Troubleshooting the IMAP cache.</b></p>"
123  "<p>If you have problems with synchronizing an IMAP "
124  "folder, you should first try rebuilding the index "
125  "file. This will take some time to rebuild, but will "
126  "not cause any problems.</p><p>If that is not enough, "
127  "you can try refreshing the IMAP cache. If you do this, "
128  "you will lose all your local changes for this folder "
129  "and all its subfolders.</p>" );
130  topLayout->addWidget( new TQLabel( txt, page ) );
131 
132  mButtonGroup = new TQButtonGroup( 0 );
133 
134  mIndexButton = new TQRadioButton( page );
135  mIndexButton->setText( i18n( "Rebuild &Index" ) );
136  mButtonGroup->insert( mIndexButton );
137  topLayout->addWidget( mIndexButton );
138 
139  TQHBox *hbox = new TQHBox( page );
140  TQLabel *scopeLabel = new TQLabel( i18n( "Scope:" ), hbox );
141  scopeLabel->setEnabled( false );
142  mIndexScope = new TQComboBox( hbox );
143  mIndexScope->insertItem( i18n( "Only current folder" ) );
144  mIndexScope->insertItem( i18n( "Current folder and all subfolders" ) );
145  mIndexScope->insertItem( i18n( "All folders of this account" ) );
146  mIndexScope->setEnabled( false );
147  topLayout->addWidget( hbox );
148 
149  mCacheButton = new TQRadioButton( page );
150  mCacheButton->setText( i18n( "Refresh &Cache" ) );
151  mButtonGroup->insert( mCacheButton );
152  topLayout->addWidget( mCacheButton );
153 
154  enableButtonSeparator( true );
155 
156  connect ( mIndexButton, TQ_SIGNAL(toggled(bool)), mIndexScope, TQ_SLOT(setEnabled(bool)) );
157  connect ( mIndexButton, TQ_SIGNAL(toggled(bool)), scopeLabel, TQ_SLOT(setEnabled(bool)) );
158  connect( mButtonGroup, TQ_SIGNAL( clicked( int ) ), TQ_SLOT( slotChanged() ) );
159  connect( this, TQ_SIGNAL( okClicked () ), this, TQ_SLOT( slotDone() ) );
160  enableButtonOK( false );
161 }
162 
163 int DImapTroubleShootDialog::run()
164 {
165  DImapTroubleShootDialog d;
166  d.exec();
167  return d.rc;
168 }
169 
170 void DImapTroubleShootDialog::slotChanged()
171 {
172  enableButtonOK( mButtonGroup->selected() != 0 );
173 }
174 
175 void DImapTroubleShootDialog::slotDone()
176 {
177  rc = None;
178  if ( mIndexButton->isOn() )
179  rc = mIndexScope->currentItem();
180  else if ( mCacheButton->isOn() )
181  rc = RefreshCache;
182  done( Ok );
183 }
184 
185 KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName )
186  : KMFolderMaildir( folder, aName ),
187  mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ),
188  mSubfolderState( imapNoInformation ),
189  mIncidencesFor( IncForAdmins ),
190  mSharedSeenFlags( false ),
191  mIsSelected( false ),
192  mCheckFlags( true ), mReadOnly( false ), mAccount( NULL ), uidMapDirty( true ),
193  uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ),
194  mFoundAnIMAPDigest( false ),
195  mUserRights( 0 ), mOldUserRights( 0 ), mUserRightsState( KMail::ACLJobs::NotFetchedYet ),
196  mACLListState( KMail::ACLJobs::NotFetchedYet ),
197  mSilentUpload( false ),
198  /*mHoldSyncs( false ),*/
199  mFolderRemoved( false ),
200  mRecurse( true ),
201  mQuotaOnly( false ),
202  mAnnotationFolderTypeChanged( false ),
203  mIncidencesForChanged( false ),
204  mSharedSeenFlagsChanged( false ),
205  mStatusChangedLocally( false ),
206  mPersonalNamespacesCheckDone( true ),
207  mQuotaInfo(), mSomeSubFolderCloseToQuotaChanged( false ), mAlarmsBlocked( false ),
208  mRescueCommandCount( 0 ),
209  mPermanentFlags( 31 ) // assume standard flags by default (see imap4/imapinfo.h for bit fields values)
210 {
211  setUidValidity("");
212  // if we fail to read a uid file but there is one, nuke it
213  if ( readUidCache() == -1 ) {
214  if ( TQFile::exists( uidCacheLocation() ) ) {
215  KMessageBox::error( 0,
216  i18n( "The UID cache file for folder %1 could not be read. There "
217  "could be a problem with file system permission, or it is corrupted."
218  ).arg( folder->prettyURL() ) );
219  // try to unlink it, in case it was corruped. If it couldn't be read
220  // because of permissions, this will fail, which is fine
221  unlink( TQFile::encodeName( uidCacheLocation() ) );
222  }
223  }
224 
225  mProgress = 0;
226 }
227 
228 KMFolderCachedImap::~KMFolderCachedImap()
229 {
230  if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
231  writeConfig();
232 }
233 
234 void KMFolderCachedImap::reallyDoClose( const char* owner )
235 {
236  if( !mFolderRemoved ) {
237  writeUidCache();
238  }
239  KMFolderMaildir::reallyDoClose( owner );
240 }
241 
242 void KMFolderCachedImap::initializeFrom( KMFolderCachedImap* parent )
243 {
244  setAccount( parent->account() );
245  // Now that we have an account, tell it that this folder was created:
246  // if this folder was just removed, then we don't really want to remove it from the server.
247  mAccount->removeDeletedFolder( imapPath() );
248  setUserRights( parent->userRights(), parent->userRightsState() );
249 }
250 
251 void KMFolderCachedImap::readConfig()
252 {
253  TDEConfig* config = KMKernel::config();
254  TDEConfigGroupSaver saver( config, "Folder-" + folder()->idString() );
255  if( mImapPath.isEmpty() ) mImapPath = config->readEntry( "ImapPath" );
256  if( TQString( name() ).upper() == "INBOX" && mImapPath == "/INBOX/" )
257  {
258  folder()->setLabel( i18n( "inbox" ) );
259  // for the icon
260  folder()->setSystemFolder( true );
261  }
262  mNoContent = config->readBoolEntry( "NoContent", false );
263  mReadOnly = config->readBoolEntry( "ReadOnly", false );
264  if ( !config->readEntry( "FolderAttributes" ).isEmpty() )
265  mFolderAttributes = config->readEntry( "FolderAttributes" );
266 
267  if ( mAnnotationFolderType != "FROMSERVER" ) {
268  mAnnotationFolderType = config->readEntry( "Annotation-FolderType" );
269  // if there is an annotation, it has to be XML
270  if ( !mAnnotationFolderType.isEmpty() && !mAnnotationFolderType.startsWith( "mail" ) )
271  kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
272 // kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
273 // << " readConfig: mAnnotationFolderType=" << mAnnotationFolderType << endl;
274  }
275  mIncidencesFor = incidencesForFromString( config->readEntry( "IncidencesFor" ) );
276  mAlarmsBlocked = config->readBoolEntry( "AlarmsBlocked", false );
277 // kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
278 // << " readConfig: mIncidencesFor=" << mIncidencesFor << endl;
279  mSharedSeenFlags = config->readBoolEntry( "SharedSeenFlags", false );
280 
281  mUserRights = config->readNumEntry( "UserRights", 0 );
282  mUserRightsState = static_cast<KMail::ACLJobs::ACLFetchState>(
283  config->readNumEntry( "UserRightsState", KMail::ACLJobs::NotFetchedYet ) );
284  mOldUserRights = mUserRights;
285 
286  int storageQuotaUsage = config->readNumEntry( "StorageQuotaUsage", -1 );
287  int storageQuotaLimit = config->readNumEntry( "StorageQuotaLimit", -1 );
288  TQString storageQuotaRoot = config->readEntry( "StorageQuotaRoot", TQString() );
289  if ( !storageQuotaRoot.isNull() ) { // isEmpty() means we know there is no quota set
290  mQuotaInfo.setName( "STORAGE" );
291  mQuotaInfo.setRoot( storageQuotaRoot );
292 
293  if ( storageQuotaUsage > -1 )
294  mQuotaInfo.setCurrent( storageQuotaUsage );
295  if ( storageQuotaLimit > -1 )
296  mQuotaInfo.setMax( storageQuotaLimit );
297  }
298 
300 
301  mStatusChangedLocally =
302  config->readBoolEntry( "StatusChangedLocally", false );
303  TQStringList uidsChanged = config->readListEntry( "UIDStatusChangedLocally" );
304  for ( TQStringList::iterator it = uidsChanged.begin(); it != uidsChanged.end(); it++ ) {
305  mUIDsOfLocallyChangedStatuses.insert( ( *it ).toUInt() );
306  }
307 
308  mAnnotationFolderTypeChanged = config->readBoolEntry( "AnnotationFolderTypeChanged", false );
309  mIncidencesForChanged = config->readBoolEntry( "IncidencesForChanged", false );
310  mSharedSeenFlagsChanged = config->readBoolEntry( "SharedSeenFlagsChanged", false );
311 
312  if ( mImapPath.isEmpty() ) {
313  mImapPathCreation = config->readEntry("ImapPathCreation");
314  }
315 
316  TQStringList delUids = config->readListEntry( "UIDSDeletedSinceLastSync" );
317 #if MAIL_LOSS_DEBUGGING
318  kdDebug( 5006 ) << "READING IN UIDSDeletedSinceLastSync: " << folder()->prettyURL() << endl << uids << endl;
319 #endif
320  for ( TQStringList::iterator it = delUids.begin(); it != delUids.end(); it++ ) {
321  mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0);
322  }
323 }
324 
325 void KMFolderCachedImap::writeConfig()
326 {
327  // don't re-write the config of a removed folder, this has just been deleted in
328  // the folder manager
329  if ( mFolderRemoved )
330  return;
331 
332  TDEConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
333  configGroup.writeEntry( "ImapPath", mImapPath );
334  configGroup.writeEntry( "NoContent", mNoContent );
335  configGroup.writeEntry( "ReadOnly", mReadOnly );
336  configGroup.writeEntry( "FolderAttributes", mFolderAttributes );
337 
338  // StatusChangedLocally is always false, as we use UIDStatusChangedLocally now
339  configGroup.writeEntry( "StatusChangedLocally", false );
340  TQStringList uidsToWrite;
341  for( std::set<ulong>::iterator it = mUIDsOfLocallyChangedStatuses.begin();
342  it != mUIDsOfLocallyChangedStatuses.end(); it++ ) {
343  uidsToWrite.append( TQString::number( (*it) ) );
344  }
345  configGroup.writeEntry( "UIDStatusChangedLocally", uidsToWrite );
346  if ( !mImapPathCreation.isEmpty() ) {
347  if ( mImapPath.isEmpty() ) {
348  configGroup.writeEntry( "ImapPathCreation", mImapPathCreation );
349  } else {
350  configGroup.deleteEntry( "ImapPathCreation" );
351  }
352  }
353  if ( !mDeletedUIDsSinceLastSync.isEmpty() ) {
354  TQValueList<ulong> uids = mDeletedUIDsSinceLastSync.keys();
355  TQStringList uidstrings;
356  for( TQValueList<ulong>::iterator it = uids.begin(); it != uids.end(); it++ ) {
357  uidstrings.append( TQString::number( (*it) ) );
358  }
359  configGroup.writeEntry( "UIDSDeletedSinceLastSync", uidstrings );
360 #if MAIL_LOSS_DEBUGGING
361  kdDebug( 5006 ) << "WRITING OUT UIDSDeletedSinceLastSync in: " << folder( )->prettyURL( ) << endl << uidstrings << endl;
362 #endif
363  } else {
364  configGroup.deleteEntry( "UIDSDeletedSinceLastSync" );
365  }
366  writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
368 }
369 
370 void KMFolderCachedImap::writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig()
371 {
372  TDEConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
373  if ( !folder()->noContent() )
374  {
375  configGroup.writeEntry( "AnnotationFolderTypeChanged", mAnnotationFolderTypeChanged );
376  configGroup.writeEntry( "Annotation-FolderType", mAnnotationFolderType );
377  configGroup.writeEntry( "IncidencesForChanged", mIncidencesForChanged );
378  configGroup.writeEntry( "IncidencesFor", incidencesForToString( mIncidencesFor ) );
379  configGroup.writeEntry( "AlarmsBlocked", mAlarmsBlocked );
380  configGroup.writeEntry( "SharedSeenFlags", mSharedSeenFlags );
381  configGroup.writeEntry( "SharedSeenFlagsChanged", mSharedSeenFlagsChanged );
382  if ( mUserRightsState != KMail::ACLJobs::FetchFailed ) { // No point in overwriting valid results with invalid ones
383  configGroup.writeEntry( "UserRights", mUserRights );
384  configGroup.writeEntry( "UserRightsState", mUserRightsState );
385  }
386 
387  configGroup.deleteEntry( "StorageQuotaUsage");
388  configGroup.deleteEntry( "StorageQuotaRoot");
389  configGroup.deleteEntry( "StorageQuotaLimit");
390 
391  if ( mQuotaInfo.isValid() ) {
392  if ( mQuotaInfo.current().isValid() ) {
393  configGroup.writeEntry( "StorageQuotaUsage", mQuotaInfo.current().toInt() );
394  }
395  if ( mQuotaInfo.max().isValid() ) {
396  configGroup.writeEntry( "StorageQuotaLimit", mQuotaInfo.max().toInt() );
397  }
398  configGroup.writeEntry( "StorageQuotaRoot", mQuotaInfo.root() );
399  }
400  }
401 }
402 
403 int KMFolderCachedImap::create()
404 {
405  int rc = KMFolderMaildir::create();
406  // FIXME why the below? - till
407  readConfig();
408  mUnreadMsgs = -1;
409  return rc;
410 }
411 
412 void KMFolderCachedImap::remove()
413 {
414  mFolderRemoved = true;
415 
416  TQString part1 = folder()->path() + "/." + dotEscape(name());
417  TQString uidCacheFile = part1 + ".uidcache";
418  // This is the account folder of an account that was just removed
419  // When this happens, be sure to delete all traces of the cache
420  if( TQFile::exists(uidCacheFile) )
421  unlink( TQFile::encodeName( uidCacheFile ) );
422 
424 }
425 
426 TQString KMFolderCachedImap::uidCacheLocation() const
427 {
428  TQString sLocation(folder()->path());
429  if (!sLocation.isEmpty()) sLocation += '/';
430  return sLocation + '.' + dotEscape(fileName()) + ".uidcache";
431 }
432 
433 int KMFolderCachedImap::readUidCache()
434 {
435  TQFile uidcache( uidCacheLocation() );
436  if( uidcache.open( IO_ReadOnly ) ) {
437  char buf[1024];
438  int len = uidcache.readLine( buf, sizeof(buf) );
439  if( len > 0 ) {
440  int cacheVersion;
441  sscanf( buf, "# KMail-UidCache V%d\n", &cacheVersion );
442  if( cacheVersion == UIDCACHE_VERSION ) {
443  len = uidcache.readLine( buf, sizeof(buf) );
444  if( len > 0 ) {
445  setUidValidity( TQString(TQString::fromLocal8Bit(buf)).stripWhiteSpace() );
446  len = uidcache.readLine( buf, sizeof(buf) );
447  if( len > 0 ) {
448 #if MAIL_LOSS_DEBUGGING
449  kdDebug(5006) << "Reading in last uid from cache: " << TQString::fromLocal8Bit(buf).stripWhiteSpace() << " in " << folder()->prettyURL() << endl;
450 #endif
451  // load the last known highest uid from the on disk cache
452  setLastUid( TQString(TQString::fromLocal8Bit(buf)).stripWhiteSpace().toULong() );
453  return 0;
454  }
455  }
456  }
457  }
458  }
459  return -1;
460 }
461 
462 int KMFolderCachedImap::writeUidCache()
463 {
464  if( uidValidity().isEmpty() || uidValidity() == "INVALID" ) {
465  // No info from the server yet, remove the file.
466  if( TQFile::exists( uidCacheLocation() ) )
467  return unlink( TQFile::encodeName( uidCacheLocation() ) );
468  return 0;
469  }
470 #if MAIL_LOSS_DEBUGGING
471  kdDebug(5006) << "Writing out UID cache lastuid: " << lastUid() << " in: " << folder()->prettyURL() << endl;
472 #endif
473  TQFile uidcache( uidCacheLocation() );
474  if( uidcache.open( IO_WriteOnly ) ) {
475  TQTextStream str( &uidcache );
476  str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl;
477  str << uidValidity() << endl;
478  str << lastUid() << endl;
479  uidcache.flush();
480  if ( uidcache.status() == IO_Ok ) {
481  // fsync( uidcache.handle() ); /* this is probably overkill */
482  uidcache.close();
483  if ( uidcache.status() == IO_Ok )
484  return 0;
485  }
486  }
487  KMessageBox::error( 0,
488  i18n( "The UID cache file for folder %1 could not be written. There "
489  "could be a problem with file system permission." ).arg( folder()->prettyURL() ) );
490 
491  return -1;
492 }
493 
494 void KMFolderCachedImap::reloadUidMap()
495 {
496  //kdDebug(5006) << "Reloading Uid Map " << endl;
497  uidMap.clear();
498  open("reloadUdi");
499  for( int i = 0; i < count(); ++i ) {
500  KMMsgBase *msg = getMsgBase( i );
501  if( !msg ) continue;
502  ulong uid = msg->UID();
503  //kdDebug(5006) << "Inserting: " << i << " with uid: " << uid << endl;
504  uidMap.insert( uid, i );
505  }
506  close("reloadUdi");
507  uidMapDirty = false;
508 }
509 
510 KMMessage* KMFolderCachedImap::take(int idx)
511 {
512  uidMapDirty = true;
513  rememberDeletion( idx );
514  return KMFolderMaildir::take(idx);
515 }
516 
517 void KMFolderCachedImap::takeTemporarily( int idx )
518 {
519  KMFolderMaildir::take( idx );
520 }
521 
522 // Add a message without clearing it's X-UID field.
523 int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail,
524  int* index_return )
525 {
526  // Possible optimization: Only dirty if not filtered below
527  ulong uid = msg->UID();
528  if( uid != 0 ) {
529  uidMapDirty = true;
530  }
531 
532  KMFolderOpener openThis(folder(), "KMFolderCachedImap::addMsgInternal");
533  int rc = openThis.openResult();
534  if ( rc ) {
535  kdDebug(5006) << k_funcinfo << "open: " << rc << " of folder: " << label() << endl;
536  return rc;
537  }
538 
539  // Add the message
540  rc = KMFolderMaildir::addMsg(msg, index_return);
541 
542  if( newMail && ( imapPath() == "/INBOX/" ||
543  ( ( mUserRights != ACLJobs::Ok || userRights() & ACLJobs::Administer)
544  && (contentsType() == ContentsTypeMail || GlobalSettings::self()->filterGroupwareFolders()) ) ) )
545  {
546  // This is a new message. Filter it - maybe
547  bool filter = false;
548  if ( GlobalSettings::filterSourceFolders().isEmpty() ) {
549  if ( imapPath() == "/INBOX/" )
550  filter = true;
551  } else {
552  if ( GlobalSettings::filterSourceFolders().contains( folder()->id() ) )
553  filter = true;
554  }
555  if ( filter )
556  mAccount->processNewMsg( msg );
557  }
558 
559  return rc;
560 }
561 
562 /* Reimplemented from KMFolderMaildir */
563 int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return)
564 {
565  if ( !canAddMsgNow( msg, index_return ) ) return 0;
566  // Add it to storage
567  int rc = KMFolderMaildir::addMsgInternal(msg, index_return, true /*stripUID*/);
568  return rc;
569 }
570 
571 void KMFolderCachedImap::rememberDeletion( int idx )
572 {
573  KMMsgBase *msg = getMsgBase( idx );
574  assert(msg);
575  long uid = msg->UID();
576  assert(uid>=0);
577  mDeletedUIDsSinceLastSync.insert(uid, 0);
578  kdDebug(5006) << "Explicit delete of UID " << uid << " at index: " << idx << " in " << folder()->prettyURL() << endl;
579 }
580 
581 /* Reimplemented from KMFolderMaildir */
582 void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet)
583 {
584  if ( contentsType() != ContentsTypeMail ) {
585  kdDebug(5006) << k_funcinfo << "Deleting message with idx " << idx << " in folder " << label() << endl;
586  }
587  uidMapDirty = true;
588  rememberDeletion( idx );
589  // Remove it from disk
590  KMFolderMaildir::removeMsg(idx,imapQuiet);
591 }
592 
593 bool KMFolderCachedImap::canRemoveFolder() const {
594  // If this has subfolders it can't be removed
595  if( folder() && folder()->child() && folder()->child()->count() > 0 )
596  return false;
597 
598 #if 0
599  // No special condition here, so let base class decide
600  return KMFolderMaildir::canRemoveFolder();
601 #endif
602  return true;
603 }
604 
605 /* Reimplemented from KMFolderDir */
606 int KMFolderCachedImap::rename( const TQString& aName,
607  KMFolderDir* /*aParent*/ )
608 {
609  if ( account() == 0 || imapPath().isEmpty() ) {
610  // This can happen when creating a folder and then renaming it without syncing before,
611  // see https://issues.kolab.org/issue3658
612  TQString err = i18n("You must synchronize with the server before renaming IMAP folders.");
613  KMessageBox::error( 0, err );
614  return -1;
615  }
616 
617  TQString oldName = mAccount->renamedFolder( imapPath() );
618  if ( oldName.isEmpty() ) oldName = name();
619  if ( aName == oldName )
620  // Stupid user trying to rename it to it's old name :)
621  return 0;
622 
623  // Make the change appear to the user with setLabel, but we'll do the change
624  // on the server during the next sync. The name() is the name at the time of
625  // the last sync. Only rename if the new one is different. If it's the same,
626  // don't rename, but also make sure the rename is reset, in the case of
627  // A -> B -> A renames.
628  if ( name() != aName )
629  mAccount->addRenamedFolder( imapPath(), folder()->label(), aName );
630  else
631  mAccount->removeRenamedFolder( imapPath() );
632 
633  folder()->setLabel( aName );
634  emit nameChanged(); // for kmailicalifaceimpl
635 
636  return 0;
637 }
638 
639 KMFolder* KMFolderCachedImap::trashFolder() const
640 {
641  TQString trashStr = account()->trash();
642  return kmkernel->dimapFolderMgr()->findIdString( trashStr );
643 }
644 
645 void KMFolderCachedImap::setLastUid( ulong uid )
646 {
647 #if MAIL_LOSS_DEBUGGING
648  kdDebug(5006) << "Setting mLastUid to: " << uid << " in " << folder()->prettyURL() << endl;
649 #endif
650  mLastUid = uid;
651  if( uidWriteTimer == -1 )
652  // Write in one minute
653  uidWriteTimer = startTimer( 60000 );
654 }
655 
656 void KMFolderCachedImap::timerEvent( TQTimerEvent* )
657 {
658  killTimer( uidWriteTimer );
659  uidWriteTimer = -1;
660  if ( writeUidCache() == -1 )
661  unlink( TQFile::encodeName( uidCacheLocation() ) );
662 }
663 
664 ulong KMFolderCachedImap::lastUid()
665 {
666  return mLastUid;
667 }
668 
669 KMMsgBase* KMFolderCachedImap::findByUID( ulong uid )
670 {
671  bool mapReloaded = false;
672  if( uidMapDirty ) {
673  reloadUidMap();
674  mapReloaded = true;
675  }
676 
677  TQMap<ulong,int>::Iterator it = uidMap.find( uid );
678  if( it != uidMap.end() ) {
679  KMMsgBase *msg = getMsgBase( *it );
680 #if MAIL_LOSS_DEBUGGING
681  kdDebug(5006) << "Folder: " << folder()->prettyURL() << endl;
682  kdDebug(5006) << "UID " << uid << " is supposed to be in the map" << endl;
683  kdDebug(5006) << "UID's index is to be " << *it << endl;
684  kdDebug(5006) << "There is a message there? " << (msg != 0) << endl;
685  if ( msg ) {
686  kdDebug(5006) << "Its UID is: " << msg->UID() << endl;
687  }
688 #endif
689 
690  if( msg && msg->UID() == uid )
691  return msg;
692  kdDebug(5006) << "########## Didn't find uid: " << uid << "in cache athough it's supposed to be there!" << endl;
693  } else {
694 #if MAIL_LOSS_DEBUGGING
695  kdDebug(5006) << "Didn't find uid: " << uid << "in cache!" << endl;
696 #endif
697  }
698  // Not found by now
699  // if( mapReloaded )
700  // Not here then
701  return 0;
702  // There could be a problem in the maps. Rebuild them and try again
703  reloadUidMap();
704  it = uidMap.find( uid );
705  if( it != uidMap.end() )
706  // Since the uid map is just rebuilt, no need for the sanity check
707  return getMsgBase( *it );
708 #if MAIL_LOSS_DEBUGGING
709  else
710  kdDebug(5006) << "Reloaded, but stil didn't find uid: " << uid << endl;
711 #endif
712  // Then it's not here
713  return 0;
714 }
715 
716 // This finds and sets the proper account for this folder if it has
717 // not been done
718 KMAcctCachedImap *KMFolderCachedImap::account() const
719 {
720  if( (KMAcctCachedImap *)mAccount == 0 && kmkernel && kmkernel->acctMgr() ) {
721  // Find the account
722  mAccount = static_cast<KMAcctCachedImap *>( kmkernel->acctMgr()->findByName( name() ) );
723  }
724 
725  return mAccount;
726 }
727 
728 void KMFolderCachedImap::slotTroubleshoot()
729 {
730  const int rc = DImapTroubleShootDialog::run();
731 
732  if( rc == DImapTroubleShootDialog::RefreshCache ) {
733  // Refresh cache
734  if( !account() ) {
735  KMessageBox::sorry( 0, i18n("No account setup for this folder.\n"
736  "Please try running a sync before this.") );
737  return;
738  }
739  TQString str = i18n("Are you sure you want to refresh the IMAP cache of "
740  "the folder %1 and all its subfolders?\nThis will "
741  "remove all changes you have done locally to your "
742  "folders.").arg( label() );
743  TQString s1 = i18n("Refresh IMAP Cache");
744  TQString s2 = i18n("&Refresh");
745  if( KMessageBox::warningContinueCancel( 0, str, s1, s2 ) ==
746  KMessageBox::Continue )
747  account()->invalidateIMAPFolders( this );
748  } else {
749  // Rebuild index file
750  switch ( rc ) {
751  case DImapTroubleShootDialog::ReindexAll:
752  {
753  KMFolderCachedImap *rootStorage = dynamic_cast<KMFolderCachedImap*>( account()->rootFolder() );
754  if ( rootStorage )
755  rootStorage->createIndexFromContentsRecursive();
756  break;
757  }
758  case DImapTroubleShootDialog::ReindexCurrent:
759  createIndexFromContents();
760  break;
761  case DImapTroubleShootDialog::ReindexRecursive:
762  createIndexFromContentsRecursive();
763  break;
764  default:
765  return;
766  }
767  KMessageBox::information( 0, i18n( "The index of this folder has been "
768  "recreated." ) );
769  writeIndex();
770  kmkernel->getKMMainWidget()->folderSelected();
771  }
772 }
773 
774 void KMFolderCachedImap::serverSync( bool recurse, bool quotaOnly )
775 {
776  if( mSyncState != SYNC_STATE_INITIAL ) {
777  if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset it to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ), TQString(), i18n("Reset && Sync"), KStdGuiItem::cancel() ) == KMessageBox::Yes ) {
778  mSyncState = SYNC_STATE_INITIAL;
779  } else return;
780  }
781 
782  mRecurse = recurse;
783  mQuotaOnly = quotaOnly;
784  assert( account() );
785 
786  ProgressItem *progressItem = mAccount->mailCheckProgressItem();
787  if ( progressItem ) {
788  progressItem->reset();
789  progressItem->setTotalItems( 100 );
790  }
791  mProgress = 0;
792 
793 #if 0
794  if( mHoldSyncs ) {
795  // All done for this folder.
796  account()->mailCheckProgressItem()->setProgress( 100 );
797  mProgress = 100; // all done
798  newState( mProgress, i18n("Synchronization skipped"));
799  mSyncState = SYNC_STATE_INITIAL;
800  emit folderComplete( this, true );
801  return;
802  }
803 #endif
804  mTentativeHighestUid = 0; // reset, last sync could have been canceled
805 
806  serverSyncInternal();
807 }
808 
809 TQString KMFolderCachedImap::state2String( int state ) const
810 {
811  switch( state ) {
812  case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL";
813  case SYNC_STATE_GET_USERRIGHTS: return "SYNC_STATE_GET_USERRIGHTS";
814  case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES";
815  case SYNC_STATE_UPLOAD_FLAGS: return "SYNC_STATE_UPLOAD_FLAGS";
816  case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
817  case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS";
818  case SYNC_STATE_LIST_NAMESPACES: return "SYNC_STATE_LIST_NAMESPACES";
819  case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2";
820  case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
821  case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES";
822  case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES";
823  case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES";
824  case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES";
825  case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX";
826  case SYNC_STATE_TEST_ANNOTATIONS: return "SYNC_STATE_TEST_ANNOTATIONS";
827  case SYNC_STATE_GET_ANNOTATIONS: return "SYNC_STATE_GET_ANNOTATIONS";
828  case SYNC_STATE_SET_ANNOTATIONS: return "SYNC_STATE_SET_ANNOTATIONS";
829  case SYNC_STATE_GET_ACLS: return "SYNC_STATE_GET_ACLS";
830  case SYNC_STATE_SET_ACLS: return "SYNC_STATE_SET_ACLS";
831  case SYNC_STATE_GET_QUOTA: return "SYNC_STATE_GET_QUOTA";
832  case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS";
833  case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS";
834  case SYNC_STATE_RENAME_FOLDER: return "SYNC_STATE_RENAME_FOLDER";
835  case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
836  case SYNC_STATE_CLOSE: return "SYNC_STATE_CLOSE";
837  case SYNC_STATE_GET_SUBFOLDER_QUOTA: return "SYNC_STATE_GET_SUBFOLDER_QUOTA";
838  default: return "Unknown state";
839  }
840 }
841 
842 /*
843  Progress calculation: each step is assigned a span. Initially the total is 100.
844  But if we skip a step, don't increase the progress.
845  This leaves more room for the step a with variable size (get_messages)
846  connecting 5
847  getuserrights 5
848  rename 5
849  check_uidvalidity 5
850  create_subfolders 5
851  put_messages 10 (but it can take a very long time, with many messages....)
852  upload_flags 5
853  list_subfolders 5
854  list_subfolders2 0 (all local)
855  delete_subfolders 5
856  list_messages 10
857  delete_messages 10
858  expunge_messages 5
859  get_messages variable (remaining-5) i.e. minimum 15.
860  check_annotations 0 (rare)
861  set_annotations 0 (rare)
862  get_annotations 2
863  set_acls 0 (rare)
864  get_acls 3
865 
866  noContent folders have only a few of the above steps
867  (permissions, and all subfolder stuff), so its steps should be given more span
868 
869  */
870 
871 // While the server synchronization is running, mSyncState will hold
872 // the state that should be executed next
873 void KMFolderCachedImap::serverSyncInternal()
874 {
875  // This is used to stop processing when we're about to exit
876  // and the current job wasn't cancellable.
877  // For user-requested abort, we'll use signalAbortRequested instead.
878  if( kmkernel->mailCheckAborted() ) {
879  resetSyncState();
880  emit folderComplete( this, false );
881  return;
882  }
883 
884  //kdDebug(5006) << label() << ": " << state2String( mSyncState ) << endl;
885  switch( mSyncState ) {
886  case SYNC_STATE_INITIAL:
887  {
888  mProgress = 0;
889  foldersForDeletionOnServer.clear();
890  newState( mProgress, i18n("Synchronizing"));
891 
892  open("cachedimap");
893  if ( !noContent() )
894  mAccount->addLastUnreadMsgCount( this, countUnread() );
895 
896  // Connect to the server (i.e. prepare the slave)
897  ImapAccountBase::ConnectionState cs = mAccount->makeConnection();
898  if ( cs == ImapAccountBase::Error ) {
899  // Cancelled by user, or slave can't start
900  // kdDebug(5006) << "makeConnection said Error, aborting." << endl;
901  // We stop here. We're already in SYNC_STATE_INITIAL for the next time.
902  newState( mProgress, i18n( "Error connecting to server %1" ).arg( mAccount->host() ) );
903  close("cachedimap");
904  emit folderComplete(this, false);
905  break;
906  } else if ( cs == ImapAccountBase::Connecting ) {
907  mAccount->setAnnotationCheckPassed( false );
908  // kdDebug(5006) << "makeConnection said Connecting, waiting for signal." << endl;
909  newState( mProgress, i18n("Connecting to %1").arg( mAccount->host() ) );
910  // We'll wait for the connectionResult signal from the account.
911  connect( mAccount, TQ_SIGNAL( connectionResult(int, const TQString&) ),
912  this, TQ_SLOT( slotConnectionResult(int, const TQString&) ) );
913  break;
914  } else {
915  // Connected
916  // kdDebug(5006) << "makeConnection said Connected, proceeding." << endl;
917  mSyncState = SYNC_STATE_GET_USERRIGHTS;
918  // Fall through to next state
919  }
920  }
921 
922 
923  case SYNC_STATE_GET_USERRIGHTS:
924 
925  // Now we have started the sync, emit changed() so that the folder tree can update the status
926  emit syncStateChanged();
927  //kdDebug(5006) << "===== Syncing " << ( mImapPath.isEmpty() ? label() : mImapPath ) << endl;
928 
929  mSyncState = SYNC_STATE_RENAME_FOLDER;
930 
931  if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) {
932  // Check the user's own rights. We do this every time in case they changed.
933  mOldUserRights = mUserRights;
934  newState( mProgress, i18n("Checking permissions"));
935  connect( mAccount, TQ_SIGNAL( receivedUserRights( KMFolder* ) ),
936  this, TQ_SLOT( slotReceivedUserRights( KMFolder* ) ) );
937  mAccount->getUserRights( folder(), imapPath() ); // after connecting, due to the INBOX case
938  break;
939  }
940 
941  else if ( !mQuotaOnly && noContent() && mAccount->hasACLSupport() ) {
942  // This is a no content folder. The server would simply say that mailbox does not exist when
943  // querying the rights for it. So pretend we have no rights.
944  mUserRights = 0;
945  mUserRightsState = KMail::ACLJobs::Ok;
946  writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
947  }
948 
949  case SYNC_STATE_RENAME_FOLDER:
950  {
951  mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
952  // Returns the new name if the folder was renamed, empty otherwise.
953  bool isResourceFolder = kmkernel->iCalIface().isStandardResourceFolder( folder() );
954  TQString newName = mAccount->renamedFolder( imapPath() );
955  if ( !newName.isEmpty() && !folder()->isSystemFolder() && !isResourceFolder ) {
956  newState( mProgress, i18n("Renaming folder") );
957  CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this );
958  connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ), this, TQ_SLOT( slotIncreaseProgress() ) );
959  connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( slotRenameFolderFinished() ) );
960  job->start();
961  break;
962  }
963  }
964 
965  case SYNC_STATE_CHECK_UIDVALIDITY:
966  mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
967  if( !mQuotaOnly && !noContent() ) {
968  checkUidValidity();
969  break;
970  }
971  // Else carry on
972 
973  case SYNC_STATE_CREATE_SUBFOLDERS:
974  mSyncState = SYNC_STATE_PUT_MESSAGES;
975  if ( !mQuotaOnly ) {
976  createNewFolders();
977  break;
978  }
979 
980  case SYNC_STATE_PUT_MESSAGES:
981  mSyncState = SYNC_STATE_UPLOAD_FLAGS;
982  if( !mQuotaOnly && !noContent() ) {
983  uploadNewMessages();
984  break;
985  }
986  // Else carry on
987  case SYNC_STATE_UPLOAD_FLAGS:
988  mSyncState = SYNC_STATE_LIST_NAMESPACES;
989  if( !mQuotaOnly && !noContent() ) {
990  // We haven't downloaded messages yet, so we need to build the map.
991  if( uidMapDirty )
992  reloadUidMap();
993  // Upload flags, unless we know from the ACL that we're not allowed
994  // to do that or they did not change locally
995  if ( mUserRightsState != KMail::ACLJobs::Ok ||
996  ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) {
997  if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) {
998  uploadFlags();
999  break;
1000  } else {
1001  //kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl;
1002  }
1003  } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
1004  if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) {
1005  uploadSeenFlags();
1006  break;
1007  }
1008  }
1009  }
1010  // Else carry on
1011 
1012  case SYNC_STATE_LIST_NAMESPACES:
1013  if ( !mQuotaOnly && this == mAccount->rootFolder() ) {
1014  listNamespaces();
1015  break;
1016  }
1017  mSyncState = SYNC_STATE_LIST_SUBFOLDERS;
1018  // Else carry on
1019 
1020  case SYNC_STATE_LIST_SUBFOLDERS:
1021  newState( mProgress, i18n("Retrieving folderlist"));
1022  mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
1023  if ( !mQuotaOnly ) {
1024  if( !listDirectory() ) {
1025  mSyncState = SYNC_STATE_INITIAL;
1026  KMessageBox::error(0, i18n("Error while retrieving the folderlist"));
1027  }
1028  break;
1029  }
1030 
1031  case SYNC_STATE_LIST_SUBFOLDERS2:
1032  mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
1033  mProgress += 10;
1034  if ( !mQuotaOnly ) {
1035  newState( mProgress, i18n("Retrieving subfolders"));
1036  listDirectory2();
1037  break;
1038  }
1039 
1040  case SYNC_STATE_DELETE_SUBFOLDERS:
1041  mSyncState = SYNC_STATE_LIST_MESSAGES;
1042  if( !mQuotaOnly && !foldersForDeletionOnServer.isEmpty() ) {
1043  newState( mProgress, i18n("Deleting folders from server"));
1044  CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer,
1045  CachedImapJob::tDeleteFolders, this );
1046  connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ), this, TQ_SLOT( slotIncreaseProgress() ) );
1047  connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( slotFolderDeletionOnServerFinished() ) );
1048  job->start();
1049  break;
1050  }
1051  // Not needed, the next step emits newState very quick
1052  //newState( mProgress, i18n("No folders to delete from server"));
1053  // Carry on
1054 
1055  case SYNC_STATE_LIST_MESSAGES:
1056  mSyncState = SYNC_STATE_DELETE_MESSAGES;
1057  if( !mQuotaOnly && !noContent() ) {
1058  newState( mProgress, i18n("Retrieving message list"));
1059  listMessages();
1060  break;
1061  }
1062  // Else carry on
1063 
1064  case SYNC_STATE_DELETE_MESSAGES:
1065  mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
1066  if( !mQuotaOnly && !noContent() ) {
1067  if( deleteMessages() ) {
1068  // Fine, we will continue with the next state
1069  } else {
1070  // No messages to delete, skip to GET_MESSAGES
1071  newState( mProgress, i18n("No messages to delete..."));
1072  mSyncState = SYNC_STATE_GET_MESSAGES;
1073  serverSyncInternal();
1074  }
1075  break;
1076  }
1077  // Else carry on
1078 
1079  case SYNC_STATE_EXPUNGE_MESSAGES:
1080  mSyncState = SYNC_STATE_GET_MESSAGES;
1081  if( !mQuotaOnly && !noContent() ) {
1082  newState( mProgress, i18n("Expunging deleted messages"));
1083  CachedImapJob *job = new CachedImapJob( TQString(),
1084  CachedImapJob::tExpungeFolder, this );
1085  connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ), this, TQ_SLOT( slotIncreaseProgress() ) );
1086  connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( serverSyncInternal() ) );
1087  job->start();
1088  break;
1089  }
1090  // Else carry on
1091 
1092  case SYNC_STATE_GET_MESSAGES:
1093  mSyncState = SYNC_STATE_HANDLE_INBOX;
1094  if( !mQuotaOnly && !noContent() ) {
1095  if( !mMsgsForDownload.isEmpty() ) {
1096  newState( mProgress, i18n("Retrieving one new message","Retrieving %n new messages",mMsgsForDownload.size()));
1097  CachedImapJob *job = new CachedImapJob( mMsgsForDownload,
1098  CachedImapJob::tGetMessage,
1099  this );
1100  connect( job, TQ_SIGNAL( progress(unsigned long, unsigned long) ),
1101  this, TQ_SLOT( slotProgress(unsigned long, unsigned long) ) );
1102  connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( slotUpdateLastUid() ) );
1103  connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( serverSyncInternal() ) );
1104  job->start();
1105  mMsgsForDownload.clear();
1106  break;
1107  } else {
1108  newState( mProgress, i18n("No new messages from server"));
1109  /* There were no messages to download, but it could be that we uploaded some
1110  which we didn't need to download again because we already knew the uid.
1111  Now that we are sure there is nothing to download, and everything that had
1112  to be deleted on the server has been deleted, adjust our local notion of the
1113  highes uid seen thus far. */
1114  slotUpdateLastUid();
1115  if( mLastUid == 0 && uidWriteTimer == -1 ) {
1116  // This is probably a new and empty folder. Write the UID cache
1117  if ( writeUidCache() == -1 ) {
1118  resetSyncState();
1119  emit folderComplete( this, false );
1120  return;
1121  }
1122  }
1123  }
1124  }
1125 
1126  // Else carry on
1127 
1128  case SYNC_STATE_HANDLE_INBOX:
1129  // Wrap up the 'download emails' stage. We always end up at 95 here.
1130  mProgress = 95;
1131  mSyncState = SYNC_STATE_TEST_ANNOTATIONS;
1132 
1133  #define KOLAB_FOLDERTEST "/vendor/kolab/folder-test"
1134  case SYNC_STATE_TEST_ANNOTATIONS:
1135  mSyncState = SYNC_STATE_GET_ANNOTATIONS;
1136  // The first folder with user rights to write annotations
1137  if( !mQuotaOnly && !mAccount->annotationCheckPassed() &&
1138  ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) )
1139  && !imapPath().isEmpty() && imapPath() != "/" ) {
1140  kdDebug(5006) << "Setting test attribute on folder: "<< folder()->prettyURL() << endl;
1141  newState( mProgress, i18n("Checking annotation support"));
1142 
1143  KURL url = mAccount->getUrl();
1144  url.setPath( imapPath() );
1145  KMail::AnnotationList annotations; // to be set
1146 
1147  KMail::AnnotationAttribute attr( KOLAB_FOLDERTEST, "value.shared", "true" );
1148  annotations.append( attr );
1149 
1150  kdDebug(5006) << "Setting test attribute to "<< url << endl;
1151  TDEIO::Job* job = AnnotationJobs::multiSetAnnotation( mAccount->slave(),
1152  url, annotations );
1153  ImapAccountBase::jobData jd( url.url(), folder() );
1154  jd.cancellable = true; // we can always do so later
1155  mAccount->insertJob(job, jd);
1156  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
1157  TQ_SLOT(slotTestAnnotationResult(TDEIO::Job *)));
1158  break;
1159  }
1160 
1161  case SYNC_STATE_GET_ANNOTATIONS: {
1162 #define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type"
1163 #define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for"
1164 #define KOLAB_SHAREDSEEN "/vendor/cmu/cyrus-imapd/sharedseen"
1165 //#define KOLAB_FOLDERTYPE "/comment" //for testing, while cyrus-imap doesn't support /vendor/*
1166  mSyncState = SYNC_STATE_SET_ANNOTATIONS;
1167 
1168  bool needToGetInitialAnnotations = false;
1169  if ( !mQuotaOnly && !noContent() ) {
1170  // for a folder we didn't create ourselves: get annotation from server
1171  if ( mAnnotationFolderType == "FROMSERVER" ) {
1172  needToGetInitialAnnotations = true;
1173  mAnnotationFolderType = TQString();
1174  } else {
1175  updateAnnotationFolderType();
1176  }
1177  }
1178 
1179  // First retrieve the annotation, so that we know we have to set it if it's not set.
1180  // On the other hand, if the user changed the contentstype, there's no need to get first.
1181  if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() &&
1182  ( kmkernel->iCalIface().isEnabled() || needToGetInitialAnnotations ) ) {
1183  TQStringList annotations; // list of annotations to be fetched
1184  if ( !mAnnotationFolderTypeChanged || mAnnotationFolderType.isEmpty() )
1185  annotations << KOLAB_FOLDERTYPE;
1186  if ( !mIncidencesForChanged )
1187  annotations << KOLAB_INCIDENCESFOR;
1188  if ( !mSharedSeenFlagsChanged )
1189  annotations << KOLAB_SHAREDSEEN;
1190  if ( !annotations.isEmpty() ) {
1191  newState( mProgress, i18n("Retrieving annotations"));
1192  KURL url = mAccount->getUrl();
1193  url.setPath( imapPath() );
1195  AnnotationJobs::multiGetAnnotation( mAccount->slave(), url, annotations );
1196  ImapAccountBase::jobData jd( url.url(), folder() );
1197  jd.cancellable = true;
1198  mAccount->insertJob(job, jd);
1199 
1200  connect( job, TQ_SIGNAL(annotationResult(const TQString&, const TQString&, bool)),
1201  TQ_SLOT(slotAnnotationResult(const TQString&, const TQString&, bool)) );
1202  connect( job, TQ_SIGNAL(result(TDEIO::Job *)),
1203  TQ_SLOT(slotGetAnnotationResult(TDEIO::Job *)) );
1204  break;
1205  }
1206  }
1207  } // case
1208  case SYNC_STATE_SET_ANNOTATIONS:
1209 
1210  mSyncState = SYNC_STATE_SET_ACLS;
1211  if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() &&
1212  ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) {
1213  newState( mProgress, i18n("Setting annotations"));
1214  KURL url = mAccount->getUrl();
1215  url.setPath( imapPath() );
1216  KMail::AnnotationList annotations; // to be set
1217  if ( mAnnotationFolderTypeChanged && !mAnnotationFolderType.isEmpty() ) {
1218  KMail::AnnotationAttribute attr( KOLAB_FOLDERTYPE, "value.shared", mAnnotationFolderType );
1219  annotations.append( attr );
1220  kdDebug(5006) << "Setting folder-type annotation for " << label() << " to " << mAnnotationFolderType << endl;
1221  }
1222  if ( mIncidencesForChanged ) {
1223  const TQString val = incidencesForToString( mIncidencesFor );
1224  KMail::AnnotationAttribute attr( KOLAB_INCIDENCESFOR, "value.shared", val );
1225  annotations.append( attr );
1226  kdDebug(5006) << "Setting incidences-for annotation for " << label() << " to " << val << endl;
1227  }
1228  if ( mSharedSeenFlagsChanged ) {
1229  const TQString val = mSharedSeenFlags ? "true" : "false";
1230  KMail::AnnotationAttribute attr( KOLAB_SHAREDSEEN, "value.shared", val );
1231  annotations.append( attr );
1232  kdDebug(5006) << k_funcinfo << "Setting sharedseen annotation for " << label() << " to " << val << endl;
1233  }
1234  if ( !annotations.isEmpty() ) {
1235  TDEIO::Job* job =
1236  AnnotationJobs::multiSetAnnotation( mAccount->slave(), url, annotations );
1237  ImapAccountBase::jobData jd( url.url(), folder() );
1238  jd.cancellable = true; // we can always do so later
1239  mAccount->insertJob(job, jd);
1240 
1241  connect(job, TQ_SIGNAL(annotationChanged( const TQString&, const TQString&, const TQString& ) ),
1242  TQ_SLOT( slotAnnotationChanged( const TQString&, const TQString&, const TQString& ) ));
1243  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
1244  TQ_SLOT(slotSetAnnotationResult(TDEIO::Job *)));
1245  break;
1246  }
1247  }
1248 
1249  case SYNC_STATE_SET_ACLS:
1250  mSyncState = SYNC_STATE_GET_ACLS;
1251 
1252  if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() &&
1253  ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) {
1254  bool hasChangedACLs = false;
1255  ACLList::ConstIterator it = mACLList.begin();
1256  for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) {
1257  hasChangedACLs = (*it).changed;
1258  }
1259  if ( hasChangedACLs ) {
1260  newState( mProgress, i18n("Setting permissions"));
1261  KURL url = mAccount->getUrl();
1262  url.setPath( imapPath() );
1263  TDEIO::Job* job = KMail::ACLJobs::multiSetACL( mAccount->slave(), url, mACLList );
1264  ImapAccountBase::jobData jd( url.url(), folder() );
1265  mAccount->insertJob(job, jd);
1266 
1267  connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
1268  TQ_SLOT(slotMultiSetACLResult(TDEIO::Job *)));
1269  connect(job, TQ_SIGNAL(aclChanged( const TQString&, int )),
1270  TQ_SLOT(slotACLChanged( const TQString&, int )) );
1271  break;
1272  }
1273  }
1274 
1275  case SYNC_STATE_GET_ACLS:
1276  mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
1277 
1278  if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) {
1279  newState( mProgress, i18n( "Retrieving permissions" ) );
1280  mAccount->getACL( folder(), mImapPath );
1281  connect( mAccount, TQ_SIGNAL(receivedACL( KMFolder*, TDEIO::Job*, const KMail::ACLList& )),
1282  this, TQ_SLOT(slotReceivedACL( KMFolder*, TDEIO::Job*, const KMail::ACLList& )) );
1283  break;
1284  }
1285  case SYNC_STATE_FIND_SUBFOLDERS:
1286  {
1287  mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
1288  mSomeSubFolderCloseToQuotaChanged = false;
1289  buildSubFolderList();
1290  }
1291 
1292  // Carry on
1293  case SYNC_STATE_SYNC_SUBFOLDERS:
1294  syncNextSubFolder( false );
1295  break;
1296  case SYNC_STATE_GET_SUBFOLDER_QUOTA:
1297 
1298  // Sync the subfolders again, so that the quota information is updated for all. This state is
1299  // only entered if the close to quota property of one subfolder changed in the previous state.
1300  syncNextSubFolder( true );
1301  break;
1302  case SYNC_STATE_GET_QUOTA:
1303  mSyncState = SYNC_STATE_CLOSE;
1304  if( !noContent() && mAccount->hasQuotaSupport() ) {
1305  mProgress = 98;
1306  newState( mProgress, i18n("Getting quota information"));
1307  KURL url = mAccount->getUrl();
1308  url.setPath( imapPath() );
1309  TDEIO::Job* job = KMail::QuotaJobs::getStorageQuota( mAccount->slave(), url );
1310  ImapAccountBase::jobData jd( url.url(), folder() );
1311  mAccount->insertJob(job, jd);
1312  connect( job, TQ_SIGNAL( storageQuotaResult( const QuotaInfo& ) ),
1313  TQ_SLOT( slotStorageQuotaResult( const QuotaInfo& ) ) );
1314  connect( job, TQ_SIGNAL(result(TDEIO::Job *)),
1315  TQ_SLOT(slotQuotaResult(TDEIO::Job *)) );
1316  break;
1317  }
1318  case SYNC_STATE_CLOSE:
1319  {
1320  mProgress = 100; // all done
1321  newState( mProgress, i18n("Synchronization done"));
1322  KURL url = mAccount->getUrl();
1323  url.setPath( imapPath() );
1324  kmkernel->iCalIface().folderSynced( folder(), url );
1325 
1326  mSyncState = SYNC_STATE_INITIAL;
1327  mAccount->addUnreadMsgCount( this, countUnread() ); // before closing
1328  close( "cachedimap" );
1329  emit syncStateChanged();
1330  emit folderComplete( this, true );
1331  }
1332  break;
1333 
1334  default:
1335  kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
1336  << mSyncState << endl;
1337  }
1338 }
1339 
1340 void KMFolderCachedImap::syncNextSubFolder( bool secondSync )
1341 {
1342  if( mCurrentSubfolder ) {
1343  disconnectSubFolderSignals();
1344  }
1345 
1346  if( mSubfoldersForSync.isEmpty() ) {
1347 
1348  // Sync finished, and a close to quota property of an subfolder changed, therefore go into
1349  // the SYNC_STATE_GET_SUBFOLDER_QUOTA state and sync again
1350  if ( mSomeSubFolderCloseToQuotaChanged && mRecurse && !secondSync ) {
1351  buildSubFolderList();
1352  mSyncState = SYNC_STATE_GET_SUBFOLDER_QUOTA;
1353  serverSyncInternal();
1354  }
1355 
1356  else {
1357 
1358  // Quota checking has to come after syncing subfolder, otherwise the quota information would
1359  // be outdated, since the subfolders can change in size during the syncing.
1360  // https://issues.kolab.org/issue4066
1361  mSyncState = SYNC_STATE_GET_QUOTA;
1362  serverSyncInternal();
1363  }
1364  } else {
1365  mCurrentSubfolder = mSubfoldersForSync.front();
1366  mSubfoldersForSync.pop_front();
1367  if ( mCurrentSubfolder ) {
1368  connect( mCurrentSubfolder, TQ_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
1369  this, TQ_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
1370  connect( mCurrentSubfolder, TQ_SIGNAL( closeToQuotaChanged() ),
1371  this, TQ_SLOT( slotSubFolderCloseToQuotaChanged() ) );
1372 
1373  //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
1374  assert( !mCurrentSubfolder->imapPath().isEmpty() );
1375  mCurrentSubfolder->setAccount( account() );
1376  const bool recurse = mCurrentSubfolder->noChildren() ? false : true;
1377  const bool quotaOnly = secondSync || mQuotaOnly;
1378  mCurrentSubfolder->serverSync( recurse, quotaOnly );
1379  }
1380  else {
1381  // mCurrentSubfolder is empty, probably because it was deleted while syncing. Go on with the
1382  // next subfolder instead.
1383  syncNextSubFolder( secondSync );
1384  }
1385  }
1386 }
1387 
1388 void KMFolderCachedImap::buildSubFolderList()
1389 {
1390  mSubfoldersForSync.clear();
1391  mCurrentSubfolder = 0;
1392  if( folder() && folder()->child() ) {
1393  KMFolderNode *node = folder()->child()->first();
1394  while( node ) {
1395  if( !node->isDir() ) {
1396  KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
1397  const bool folderIsNew = mNewlyCreatedSubfolders.contains( TQGuardedPtr<KMFolderCachedImap>( storage ) );
1398  // Only sync folders that have been accepted by the server
1399  if ( !storage->imapPath().isEmpty()
1400  // and that were not just deleted from it
1401  && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) {
1402  if ( mRecurse || folderIsNew ) {
1403  mSubfoldersForSync << storage;
1404  }
1405  } else {
1406  kdDebug(5006) << "Do not add " << storage->label()
1407  << " to synclist" << endl;
1408  }
1409  }
1410  node = folder()->child()->next();
1411  }
1412  }
1413 
1414  mNewlyCreatedSubfolders.clear();
1415 }
1416 
1417 void KMFolderCachedImap::disconnectSubFolderSignals()
1418 {
1419  disconnect( mCurrentSubfolder, TQ_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
1420  this, TQ_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
1421  disconnect( mCurrentSubfolder, TQ_SIGNAL( closeToQuotaChanged() ),
1422  this, TQ_SLOT( slotSubFolderCloseToQuotaChanged() ) );
1423  mCurrentSubfolder = 0;
1424 }
1425 
1426 /* Connected to the imap account's connectionResult signal.
1427  Emitted when the slave connected or failed to connect.
1428 */
1429 void KMFolderCachedImap::slotConnectionResult( int errorCode, const TQString& errorMsg )
1430 {
1431  disconnect( mAccount, TQ_SIGNAL( connectionResult(int, const TQString&) ),
1432  this, TQ_SLOT( slotConnectionResult(int, const TQString&) ) );
1433  if ( !errorCode ) {
1434  // Success
1435  mSyncState = SYNC_STATE_GET_USERRIGHTS;
1436  mProgress += 5;
1437  serverSyncInternal();
1438  } else {
1439  // Error (error message already shown by the account)
1440  newState( mProgress, TDEIO::buildErrorString( errorCode, errorMsg ));
1441  emit folderComplete(this, false);
1442  }
1443 }
1444 
1445 /* find new messages (messages without a UID) */
1446 TQValueList<unsigned long> KMFolderCachedImap::findNewMessages()
1447 {
1448  TQValueList<unsigned long> result;
1449  for( int i = 0; i < count(); ++i ) {
1450  KMMsgBase *msg = getMsgBase( i );
1451  if( !msg ) continue; /* what goes on if getMsg() returns 0? */
1452  if ( msg->UID() == 0 )
1453  result.append( msg->getMsgSerNum() );
1454  }
1455  return result;
1456 }
1457 
1458 /* Upload new messages to server */
1459 void KMFolderCachedImap::uploadNewMessages()
1460 {
1461  TQValueList<unsigned long> newMsgs = findNewMessages();
1462  if( !newMsgs.isEmpty() ) {
1463  if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) {
1464  newState( mProgress, i18n("Uploading messages to server"));
1465  CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
1466  connect( job, TQ_SIGNAL( progress( unsigned long, unsigned long) ),
1467  this, TQ_SLOT( slotPutProgress(unsigned long, unsigned long) ) );
1468  connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( serverSyncInternal() ) );
1469  job->start();
1470  return;
1471  } else {
1472  KMCommand *command = rescueUnsyncedMessages();
1473  connect( command, TQ_SIGNAL( completed( KMCommand * ) ),
1474  this, TQ_SLOT( serverSyncInternal() ) );
1475  }
1476  } else { // nothing to upload
1477  if ( mUserRights != mOldUserRights && (mOldUserRights & KMail::ACLJobs::Insert)
1478  && !(mUserRights & KMail::ACLJobs::Insert) ) {
1479  // write access revoked
1480  KMessageBox::information( 0, i18n("<p>Your access rights to folder <b>%1</b> have been restricted, "
1481  "it will no longer be possible to add messages to this folder.</p>").arg( folder()->prettyURL() ),
1482  i18n("Acces rights revoked"), "KMailACLRevocationNotification" );
1483  }
1484  }
1485  newState( mProgress, i18n("No messages to upload to server"));
1486  serverSyncInternal();
1487 }
1488 
1489 /* Progress info during uploadNewMessages */
1490 void KMFolderCachedImap::slotPutProgress( unsigned long done, unsigned long total )
1491 {
1492  // (going from mProgress to mProgress+10)
1493  int progressSpan = 10;
1494  newState( mProgress + (progressSpan * done) / total, TQString() );
1495  if ( done == total ) // we're done
1496  mProgress += progressSpan;
1497 }
1498 
1499 /* Upload message flags to server */
1500 void KMFolderCachedImap::uploadFlags()
1501 {
1502  if ( !uidMap.isEmpty() ) {
1503  mStatusFlagsJobs = 0;
1504  newState( mProgress, i18n("Uploading status of messages to server"));
1505 
1506  // FIXME DUPLICATED FROM KMFOLDERIMAP
1507  TQMap< TQString, TQStringList > groups;
1508  //open(); //already done
1509  for( int i = 0; i < count(); ++i ) {
1510  KMMsgBase* msg = getMsgBase( i );
1511  if( !msg || msg->UID() == 0 )
1512  // Either not a valid message or not one that is on the server yet
1513  continue;
1514  if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
1515  && !mStatusChangedLocally ) {
1516  // This message has not had its status changed locally
1517  continue;
1518  }
1519 
1520  TQString flags = KMFolderImap::statusToFlags(msg->status(), mPermanentFlags);
1521  // Collect uids for each typem of flags.
1522  TQString uid;
1523  uid.setNum( msg->UID() );
1524  groups[flags].append(uid);
1525  }
1526  TQMapIterator< TQString, TQStringList > dit;
1527  for( dit = groups.begin(); dit != groups.end(); ++dit ) {
1528  TQCString flags = dit.key().latin1();
1529  TQStringList sets = KMFolderImap::makeSets( (*dit), true );
1530  mStatusFlagsJobs += sets.count(); // ### that's not in kmfolderimap....
1531  // Send off a status setting job for each set.
1532  for( TQStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
1533  TQString imappath = imapPath() + ";UID=" + ( *slit );
1534  mAccount->setImapStatus(folder(), imappath, flags);
1535  }
1536  }
1537  // FIXME END DUPLICATED FROM KMFOLDERIMAP
1538 
1539  if ( mStatusFlagsJobs ) {
1540  connect( mAccount, TQ_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ),
1541  this, TQ_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) );
1542  return;
1543  }
1544  }
1545  newState( mProgress, i18n("No messages to upload to server"));
1546  serverSyncInternal();
1547 }
1548 
1549 void KMFolderCachedImap::uploadSeenFlags()
1550 {
1551  if ( !uidMap.isEmpty() ) {
1552  mStatusFlagsJobs = 0;
1553  newState( mProgress, i18n("Uploading status of messages to server"));
1554 
1555  TQValueList<ulong> seenUids, unseenUids;
1556  for( int i = 0; i < count(); ++i ) {
1557  KMMsgBase* msg = getMsgBase( i );
1558  if( !msg || msg->UID() == 0 )
1559  // Either not a valid message or not one that is on the server yet
1560  continue;
1561 
1562  if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
1563  && !mStatusChangedLocally ) {
1564  // This message has not had its status changed locally
1565  continue;
1566  }
1567 
1568  if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
1569  seenUids.append( msg->UID() );
1570  else
1571  unseenUids.append( msg->UID() );
1572  }
1573  if ( !seenUids.isEmpty() ) {
1574  TQStringList sets = KMFolderImap::makeSets( seenUids, true );
1575  mStatusFlagsJobs += sets.count();
1576  for( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
1577  TQString imappath = imapPath() + ";UID=" + ( *it );
1578  mAccount->setImapSeenStatus( folder(), imappath, true );
1579  }
1580  }
1581  if ( !unseenUids.isEmpty() ) {
1582  TQStringList sets = KMFolderImap::makeSets( unseenUids, true );
1583  mStatusFlagsJobs += sets.count();
1584  for( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
1585  TQString imappath = imapPath() + ";UID=" + ( *it );
1586  mAccount->setImapSeenStatus( folder(), imappath, false );
1587  }
1588  }
1589 
1590  if ( mStatusFlagsJobs ) {
1591  connect( mAccount, TQ_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ),
1592  this, TQ_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) );
1593  return;
1594  }
1595  }
1596  newState( mProgress, i18n("No messages to upload to server"));
1597  serverSyncInternal();
1598 }
1599 
1600 void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const TQString&, bool cont)
1601 {
1602  if ( mSyncState == SYNC_STATE_INITIAL ){
1603  //kdDebug(5006) << "IMAP status changed but reset " << endl;
1604  return; // we were reset
1605  }
1606  //kdDebug(5006) << "IMAP status changed for folder: " << folder->prettyURL() << endl;
1607  if ( folder->storage() == this ) {
1608  --mStatusFlagsJobs;
1609  if ( mStatusFlagsJobs == 0 || !cont ) // done or aborting
1610  disconnect( mAccount, TQ_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ),
1611  this, TQ_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) );
1612  if ( mStatusFlagsJobs == 0 && cont ) {
1613  mProgress += 5;
1614  serverSyncInternal();
1615  //kdDebug(5006) << "Proceeding with mailcheck." << endl;
1616  }
1617  }
1618 }
1619 
1620 // This is not perfect, what if the status didn't really change? Oh well ...
1621 void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle)
1622 {
1623  KMFolderMaildir::setStatus( idx, status, toggle );
1624  const KMMsgBase *msg = getMsgBase( idx );
1625  Q_ASSERT( msg );
1626  if ( msg )
1627  mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
1628 }
1629 
1630 void KMFolderCachedImap::setStatus(TQValueList<int>& ids, KMMsgStatus status, bool toggle)
1631 {
1632  KMFolderMaildir::setStatus(ids, status, toggle);
1633  for (TQValueList<int>::iterator it = ids.begin(); it != ids.end(); it++ ) {
1634  const KMMsgBase *msg = getMsgBase( *it );
1635  Q_ASSERT( msg );
1636  if ( msg )
1637  mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
1638  }
1639 }
1640 
1641 /* Upload new folders to server */
1642 void KMFolderCachedImap::createNewFolders()
1643 {
1644  TQValueList<KMFolderCachedImap*> newFolders = findNewFolders();
1645  //kdDebug(5006) << label() << " createNewFolders:" << newFolders.count() << " new folders." << endl;
1646  if( !newFolders.isEmpty() ) {
1647  newState( mProgress, i18n("Creating subfolders on server"));
1648  CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this );
1649  connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ), this, TQ_SLOT( slotIncreaseProgress() ) );
1650  connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( serverSyncInternal() ) );
1651  job->start();
1652  } else {
1653  serverSyncInternal();
1654  }
1655 }
1656 
1657 TQValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
1658 {
1659  TQValueList<KMFolderCachedImap*> newFolders;
1660  if( folder() && folder()->child() ) {
1661  KMFolderNode *node = folder()->child()->first();
1662  while( node ) {
1663  if( !node->isDir() ) {
1664  if( static_cast<KMFolder*>(node)->folderType() != KMFolderTypeCachedImap ) {
1665  kdError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
1666  << node->name() << " is not an IMAP folder\n";
1667  node = folder()->child()->next();
1668  assert(0);
1669  }
1670  KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
1671  if( folder->imapPath().isEmpty() ) {
1672  newFolders << folder;
1673  }
1674  }
1675  node = folder()->child()->next();
1676  }
1677  }
1678  return newFolders;
1679 }
1680 
1681 bool KMFolderCachedImap::deleteMessages()
1682 {
1683  /* Delete messages from cache that are gone from the server */
1684  TQPtrList<KMMsgBase> msgsForDeletion;
1685 
1686  // It is not possible to just go over all indices and remove
1687  // them one by one because the index list can get resized under
1688  // us. So use msg pointers instead
1689 
1690  TQStringList uids;
1691  TQMap<ulong,int>::const_iterator it = uidMap.constBegin();
1692  for( ; it != uidMap.end(); it++ ) {
1693  ulong uid ( it.key() );
1694  if( uid!=0 && !uidsOnServer.find( uid ) ) {
1695  uids << TQString::number( uid );
1696  msgsForDeletion.append( getMsgBase( *it ) );
1697  }
1698  }
1699 
1700  if( !msgsForDeletion.isEmpty() ) {
1701  if ( contentsType() != ContentsTypeMail ) {
1702  kdDebug(5006) << k_funcinfo << label() << " Going to locally delete " << msgsForDeletion.count()
1703  << " messages, with the uids " << uids.join( "," ) << endl;
1704  }
1705 #if MAIL_LOSS_DEBUGGING
1706  if ( KMessageBox::warningYesNo(
1707  0, i18n( "<qt><p>Mails on the server in folder <b>%1</b> were deleted. "
1708  "Do you want to delete them locally?<br>UIDs: %2</p></qt>" )
1709  .arg( folder()->prettyURL() ).arg( uids.join(",") ) ) == KMessageBox::Yes )
1710 #endif
1711  removeMsg( msgsForDeletion );
1712  }
1713 
1714  if ( mUserRightsState == KMail::ACLJobs::Ok && !( mUserRights & KMail::ACLJobs::Delete ) )
1715  return false;
1716 
1717  /* Delete messages from the server that we dont have anymore */
1718  if( !uidsForDeletionOnServer.isEmpty() ) {
1719  newState( mProgress, i18n("Deleting removed messages from server"));
1720  TQStringList sets = KMFolderImap::makeSets( uidsForDeletionOnServer, true );
1721  uidsForDeletionOnServer.clear();
1722  kdDebug(5006) << "Deleting " << sets.count() << " sets of messages from server folder " << imapPath() << endl;
1723  CachedImapJob *job = new CachedImapJob( sets, CachedImapJob::tDeleteMessage, this );
1724  connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ),
1725  this, TQ_SLOT( slotDeleteMessagesResult(KMail::FolderJob *) ) );
1726  job->start();
1727  return true;
1728  } else {
1729 
1730  // Nothing to delete on the server, make sure the map is clear again.
1731  // Normally this wouldn't be necessary, but there can be stale maps because of
1732  // https://issues.kolab.org/issue3833.
1733  mDeletedUIDsSinceLastSync.clear();
1734  return false;
1735  }
1736 }
1737 
1738 void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob* job )
1739 {
1740  if ( job->error() ) {
1741  // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages
1742  mSyncState = SYNC_STATE_GET_MESSAGES;
1743  } else {
1744  // deleting on the server went fine, clear the pending deletions cache
1745  mDeletedUIDsSinceLastSync.clear();
1746  }
1747  mProgress += 10;
1748  serverSyncInternal();
1749 }
1750 
1751 void KMFolderCachedImap::checkUidValidity() {
1752  // IMAP root folders don't seem to have a UID validity setting.
1753  // Also, don't try the uid validity on new folders
1754  if( imapPath().isEmpty() || imapPath() == "/" )
1755  // Just proceed
1756  serverSyncInternal();
1757  else {
1758  newState( mProgress, i18n("Checking folder validity"));
1759  CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this );
1760  connect( job, TQ_SIGNAL(permanentFlags(int)), TQ_SLOT(slotPermanentFlags(int)) );
1761  connect( job, TQ_SIGNAL( result( KMail::FolderJob* ) ),
1762  this, TQ_SLOT( slotCheckUidValidityResult( KMail::FolderJob* ) ) );
1763  job->start();
1764  }
1765 }
1766 
1767 void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob* job )
1768 {
1769  if ( job->error() ) { // there was an error and the user chose "continue"
1770  // We can't continue doing anything in the same folder though, it would delete all mails.
1771  // But we can continue to subfolders if any. Well we can also try annotation/acl stuff...
1772  mSyncState = SYNC_STATE_HANDLE_INBOX;
1773  }
1774  mProgress += 5;
1775  serverSyncInternal();
1776 }
1777 
1778 void KMFolderCachedImap::slotPermanentFlags(int flags)
1779 {
1780  mPermanentFlags = flags;
1781 }
1782 
1783 /* This will only list the messages in a folder.
1784  No directory listing done*/
1785 void KMFolderCachedImap::listMessages() {
1786  bool groupwareOnly = GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
1787  && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
1788  && folder()->isSystemFolder()
1789  && mImapPath == "/INBOX/";
1790  // Don't list messages on the root folder, and skip the inbox, if this is
1791  // the inbox of a groupware-only dimap account
1792  if( imapPath() == "/" || groupwareOnly ) {
1793  serverSyncInternal();
1794  return;
1795  }
1796 
1797  if( !mAccount->slave() ) { // sync aborted
1798  resetSyncState();
1799  emit folderComplete( this, false );
1800  return;
1801  }
1802  uidsOnServer.clear();
1803  uidsOnServer.resize( count() * 2 );
1804  uidsForDeletionOnServer.clear();
1805  mMsgsForDownload.clear();
1806  mUidsForDownload.clear();
1807  // listing is only considered successful if saw a syntactically correct imapdigest
1808  mFoundAnIMAPDigest = false;
1809 
1810  CachedImapJob* job = new CachedImapJob( FolderJob::tListMessages, this );
1811  connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ),
1812  this, TQ_SLOT( slotGetLastMessagesResult(KMail::FolderJob *) ) );
1813  job->start();
1814 }
1815 
1816 void KMFolderCachedImap::slotGetLastMessagesResult(KMail::FolderJob *job)
1817 {
1818  getMessagesResult(job, true);
1819 }
1820 
1821 // Connected to the listMessages job in CachedImapJob
1822 void KMFolderCachedImap::slotGetMessagesData(TDEIO::Job * job, const TQByteArray & data)
1823 {
1824  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
1825  if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
1826  kdDebug(5006) << "could not find job!?!?!" << endl;
1827  // be sure to reset the sync state, if the listing was partial we would
1828  // otherwise delete not-listed mail locally, and on the next sync on the server
1829  // as well
1830  mSyncState = SYNC_STATE_HANDLE_INBOX;
1831  serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
1832  return;
1833  }
1834  (*it).cdata += TQCString(data, data.size() + 1);
1835  int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
1836  if (pos > 0) {
1837  int a = (*it).cdata.find("\r\nX-uidValidity:");
1838  if (a != -1) {
1839  int b = (*it).cdata.find("\r\n", a + 17);
1840  setUidValidity((*it).cdata.mid(a + 17, b - a - 17));
1841  }
1842  a = (*it).cdata.find("\r\nX-Access:");
1843  // Only trust X-Access (i.e. the imap select info) if we don't know mUserRights.
1844  // The latter is more accurate (checked on every sync) whereas X-Access is only
1845  // updated when selecting the folder again, which might not happen if using
1846  // RMB / Check Mail in this folder. We don't need two (potentially conflicting)
1847  // sources for the readonly setting, in any case.
1848  if (a != -1 && mUserRightsState != KMail::ACLJobs::Ok ) {
1849  int b = (*it).cdata.find("\r\n", a + 12);
1850  const TQString access = (*it).cdata.mid(a + 12, b - a - 12);
1851  setReadOnly( access == "Read only" );
1852  }
1853  (*it).cdata.remove(0, pos);
1854  mFoundAnIMAPDigest = true;
1855  }
1856  pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
1857  // Start with something largish when rebuilding the cache
1858  if ( uidsOnServer.size() == 0 )
1859  uidsOnServer.resize( KMail::nextPrime( 2000 ) );
1860  const int v = 42;
1861  while (pos >= 0) {
1862  /*
1863  KMMessage msg;
1864  msg.fromString((*it).cdata.mid(16, pos - 16));
1865  const int flags = msg.headerField("X-Flags").toInt();
1866  const ulong size = msg.headerField("X-Length").toULong();
1867  const ulong uid = msg.UID();
1868  */
1869  // The below is optimized for speed, not prettiness. The commented out chunk
1870  // above was the solution copied from kmfolderimap, and it's 15-20% slower.
1871  const TQCString& entry( (*it).cdata );
1872  const int indexOfUID = entry.find("X-UID", 16);
1873  const int startOfUIDValue = indexOfUID + 7;
1874  const int indexOfLength = entry.find("X-Length", startOfUIDValue ); // we know length comes after UID
1875  const int startOfLengthValue = indexOfLength + 10;
1876  const int indexOfFlags = entry.find("X-Flags", startOfLengthValue ); // we know flags comes last
1877  const int startOfFlagsValue = indexOfFlags + 9;
1878 
1879  const int flags = entry.mid( startOfFlagsValue, entry.find( '\r', startOfFlagsValue ) - startOfFlagsValue ).toInt();
1880  const ulong size = entry.mid( startOfLengthValue, entry.find( '\r', startOfLengthValue ) - startOfLengthValue ).toULong();
1881  const ulong uid = entry.mid( startOfUIDValue, entry.find( '\r', startOfUIDValue ) - startOfUIDValue ).toULong();
1882 
1883  const bool deleted = ( flags & 8 );
1884  if ( !deleted ) {
1885  if( uid != 0 ) {
1886  if ( uidsOnServer.count() == uidsOnServer.size() ) {
1887  uidsOnServer.resize( KMail::nextPrime( uidsOnServer.size() * 2 ) );
1888  //kdDebug( 5006 ) << "Resizing to: " << uidsOnServer.size() << endl;
1889  }
1890  uidsOnServer.insert( uid, &v );
1891  }
1892  bool redownload = false;
1893  if ( uid <= lastUid() ) {
1894  /*
1895  * If this message UID is not present locally, then it must
1896  * have been deleted by the user, so we delete it on the
1897  * server also. If we don't have delete permissions on the server,
1898  * re-download the message, it must have vanished by some error, or
1899  * while we still thought we were allowed to delete (ACL change).
1900  *
1901  * This relies heavily on lastUid() being correct at all times.
1902  */
1903  // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<label()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl;
1904  KMMsgBase *existingMessage = findByUID(uid);
1905  if( !existingMessage ) {
1906 #if MAIL_LOSS_DEBUGGING
1907  kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should delete it" << endl;
1908 #endif
1909  // double check we deleted it since the last sync
1910  if ( mDeletedUIDsSinceLastSync.contains(uid) ) {
1911  if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & KMail::ACLJobs::Delete ) ) {
1912 #if MAIL_LOSS_DEBUGGING
1913  kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
1914 #endif
1915  uidsForDeletionOnServer << uid;
1916  } else {
1917  redownload = true;
1918  }
1919  } else {
1920  kdDebug(5006) << "WARNING: ####### " << endl;
1921  kdDebug(5006) << "Message locally missing but not deleted in folder: " << folder()->prettyURL() << endl;
1922  kdDebug(5006) << "The missing UID: " << uid << ". It will be redownloaded " << endl;
1923  redownload = true;
1924  }
1925 
1926  } else {
1927  // if this is a read only folder, ignore status updates from the server
1928  // since we can't write our status back our local version is what has to
1929  // be considered correct.
1930  if ( !mReadOnly || !GlobalSettings::allowLocalFlags() ) {
1931  /* The message is OK, update flags */
1932  KMFolderImap::flagsToStatus( existingMessage, flags, false, mReadOnly ? INT_MAX : mPermanentFlags );
1933  } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
1934  KMFolderImap::seenFlagToStatus( existingMessage, flags );
1935  }
1936  }
1937  // kdDebug(5006) << "message with uid " << uid << " found in the local cache. " << endl;
1938  }
1939  if ( uid > lastUid() || redownload ) {
1940 #if MAIL_LOSS_DEBUGGING
1941  kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should download it" << endl;
1942 #endif
1943  // The message is new since the last sync, but we might have just uploaded it, in which case
1944  // the uid map already contains it.
1945  if ( !uidMap.contains( uid ) ) {
1946  mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size);
1947  if( imapPath() == "/INBOX/" )
1948  mUidsForDownload << uid;
1949  }
1950  // Remember the highest uid and once the download is completed, update mLastUid
1951  if ( uid > mTentativeHighestUid ) {
1952 #if MAIL_LOSS_DEBUGGING
1953  kdDebug(5006) << "Setting the tentative highest UID to: " << uid << endl;
1954 #endif
1955  mTentativeHighestUid = uid;
1956  }
1957  }
1958  }
1959  (*it).cdata.remove(0, pos);
1960  (*it).done++;
1961  pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
1962  }
1963 }
1964 
1965 void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet )
1966 {
1967  mProgress += 10;
1968  if ( !job->error() && !mFoundAnIMAPDigest ) {
1969  kdWarning(5006) << "######## Folderlisting did not complete, but there was no error! "
1970  "Aborting sync of folder: " << folder()->prettyURL() << endl;
1971 #if MAIL_LOSS_DEBUGGING
1972  kmkernel->emergencyExit( i18n("Folder listing failed in interesting ways." ) );
1973 #endif
1974  }
1975  if( job->error() ) { // error listing messages but the user chose to continue
1976  mContentState = imapNoInformation;
1977  mSyncState = SYNC_STATE_HANDLE_INBOX; // be sure not to continue in this folder
1978  } else {
1979  if( lastSet ) { // always true here (this comes from online-imap...)
1980  mContentState = imapFinished;
1981  mUIDsOfLocallyChangedStatuses.clear(); // we are up to date again
1982  mStatusChangedLocally = false;
1983  }
1984  }
1985  serverSyncInternal();
1986 }
1987 
1988 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
1989 {
1990  int progressSpan = 100 - 5 - mProgress;
1991  int additionalProgress = ( total == 0 ) ?
1992  progressSpan :
1993  ( progressSpan * done ) / total;
1994 
1995  // Progress info while retrieving new emails
1996  // (going from mProgress to mProgress+progressSpan)
1997  newState( mProgress + additionalProgress, TQString() );
1998 }
1999 
2000 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
2001 {
2002  assert( aAccount->isA("KMAcctCachedImap") );
2003  mAccount = aAccount;
2004  if( imapPath()=="/" ) aAccount->setFolder( folder() );
2005 
2006  // Folder was renamed in a previous session, and the user didn't sync yet
2007  TQString newName = mAccount->renamedFolder( imapPath() );
2008  if ( !newName.isEmpty() )
2009  folder()->setLabel( newName );
2010 
2011  if( !folder() || !folder()->child() || !folder()->child()->count() ) return;
2012  for( KMFolderNode* node = folder()->child()->first(); node;
2013  node = folder()->child()->next() )
2014  if (!node->isDir())
2015  static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
2016 }
2017 
2018 void KMFolderCachedImap::listNamespaces()
2019 {
2020  ImapAccountBase::ListType type = ImapAccountBase::List;
2021  if ( mAccount->onlySubscribedFolders() )
2022  type = ImapAccountBase::ListSubscribed;
2023 
2024  kdDebug(5006) << "listNamespaces " << mNamespacesToList << endl;
2025  if ( mNamespacesToList.isEmpty() ) {
2026  mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
2027  mPersonalNamespacesCheckDone = true;
2028 
2029  TQStringList ns = mAccount->namespaces()[ImapAccountBase::OtherUsersNS];
2030  ns += mAccount->namespaces()[ImapAccountBase::SharedNS];
2031  mNamespacesToCheck = ns.count();
2032  for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
2033  {
2034  if ( (*it).isEmpty() ) {
2035  // ignore empty listings as they have been listed before
2036  --mNamespacesToCheck;
2037  continue;
2038  }
2039  KMail::ListJob* job = new KMail::ListJob( mAccount, type, this, mAccount->addPathToNamespace( *it ) );
2040  job->setHonorLocalSubscription( true );
2041  connect( job, TQ_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
2042  const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
2043  this, TQ_SLOT(slotCheckNamespace(const TQStringList&, const TQStringList&,
2044  const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
2045  job->start();
2046  }
2047  if ( mNamespacesToCheck == 0 ) {
2048  serverSyncInternal();
2049  }
2050  return;
2051  }
2052  mPersonalNamespacesCheckDone = false;
2053 
2054  TQString ns = mNamespacesToList.front();
2055  mNamespacesToList.pop_front();
2056 
2057  mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
2058  newState( mProgress, i18n("Retrieving folders for namespace %1").arg(ns));
2059  KMail::ListJob* job = new KMail::ListJob( mAccount, type, this,
2060  mAccount->addPathToNamespace( ns ) );
2061  job->setNamespace( ns );
2062  job->setHonorLocalSubscription( true );
2063  connect( job, TQ_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
2064  const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
2065  this, TQ_SLOT(slotListResult(const TQStringList&, const TQStringList&,
2066  const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
2067  job->start();
2068 }
2069 
2070 void KMFolderCachedImap::slotCheckNamespace( const TQStringList& subfolderNames,
2071  const TQStringList& subfolderPaths,
2072  const TQStringList& subfolderMimeTypes,
2073  const TQStringList& subfolderAttributes,
2074  const ImapAccountBase::jobData& jobData )
2075 {
2076  Q_UNUSED( subfolderPaths );
2077  Q_UNUSED( subfolderMimeTypes );
2078  Q_UNUSED( subfolderAttributes );
2079  --mNamespacesToCheck;
2080  kdDebug(5006) << "slotCheckNamespace " << subfolderNames << ",remain=" <<
2081  mNamespacesToCheck << endl;
2082 
2083  // get a correct foldername:
2084  // strip / and make sure it does not contain the delimiter
2085  TQString name = jobData.path.mid( 1, jobData.path.length()-2 );
2086  name.remove( mAccount->delimiterForNamespace( name ) );
2087  if ( name.isEmpty() ) {
2088  // should not happen
2089  kdWarning(5006) << "slotCheckNamespace: ignoring empty folder!" << endl;
2090  return;
2091  }
2092 
2093  folder()->createChildFolder();
2094  KMFolderNode *node = 0;
2095  for ( node = folder()->child()->first(); node;
2096  node = folder()->child()->next())
2097  {
2098  if ( !node->isDir() && node->name() == name )
2099  break;
2100  }
2101  if ( !subfolderNames.isEmpty() ) {
2102  if ( node ) {
2103  // folder exists so we have nothing to do - it will be listed later
2104  kdDebug(5006) << "found namespace folder " << name << endl;
2105  } else
2106  {
2107  // create folder
2108  kdDebug(5006) << "create namespace folder " << name << endl;
2109  KMFolder* newFolder = folder()->child()->createFolder( name, false,
2110  KMFolderTypeCachedImap );
2111  if ( newFolder ) {
2112  KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>( newFolder->storage() );
2113  f->setImapPath( mAccount->addPathToNamespace( name ) );
2114  f->setNoContent( true );
2115  f->setAccount( mAccount );
2116  f->close("cachedimap");
2117  kmkernel->dimapFolderMgr()->contentsChanged();
2118  }
2119  }
2120  } else {
2121  if ( node ) {
2122  kdDebug(5006) << "delete namespace folder " << name << endl;
2123  KMFolder* fld = static_cast<KMFolder*>(node);
2124  kmkernel->dimapFolderMgr()->remove( fld );
2125  }
2126  }
2127 
2128  if ( mNamespacesToCheck == 0 ) {
2129  // all namespaces are done so continue with the next step
2130  serverSyncInternal();
2131  }
2132 }
2133 
2134 // This lists the subfolders on the server
2135 // and (in slotListResult) takes care of folders that have been removed on the server
2136 bool KMFolderCachedImap::listDirectory()
2137 {
2138  if( !mAccount->slave() ) { // sync aborted
2139  resetSyncState();
2140  emit folderComplete( this, false );
2141  return false;
2142  }
2143  mSubfolderState = imapInProgress;
2144 
2145  // get the folders
2146  ImapAccountBase::ListType type = ImapAccountBase::List;
2147  if ( mAccount->onlySubscribedFolders() )
2148  type = ImapAccountBase::ListSubscribed;
2149  KMail::ListJob* job = new KMail::ListJob( mAccount, type, this );
2150  job->setHonorLocalSubscription( true );
2151  connect( job, TQ_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
2152  const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
2153  this, TQ_SLOT(slotListResult(const TQStringList&, const TQStringList&,
2154  const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
2155  job->start();
2156 
2157  return true;
2158 }
2159 
2160 void KMFolderCachedImap::slotListResult( const TQStringList& folderNames,
2161  const TQStringList& folderPaths,
2162  const TQStringList& folderMimeTypes,
2163  const TQStringList& folderAttributes,
2164  const ImapAccountBase::jobData& jobData )
2165 {
2166  Q_UNUSED( jobData );
2167  //kdDebug(5006) << label() << ": folderNames=" << folderNames << " folderPaths="
2168  //<< folderPaths << " mimeTypes=" << folderMimeTypes << endl;
2169  mSubfolderNames = folderNames;
2170  mSubfolderPaths = folderPaths;
2171  mSubfolderMimeTypes = folderMimeTypes;
2172  mSubfolderState = imapFinished;
2173  mSubfolderAttributes = folderAttributes;
2174  //kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl;
2175 
2176  folder()->createChildFolder();
2177  KMFolderNode *node = folder()->child()->first();
2178  bool root = ( this == mAccount->rootFolder() );
2179 
2180  TQPtrList<KMFolder> toRemove;
2181  bool emptyList = ( root && mSubfolderNames.empty() );
2182  if ( !emptyList ) {
2183  while (node) {
2184  if (!node->isDir() ) {
2185  KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
2186 
2187  if ( mSubfolderNames.findIndex(node->name()) == -1 ) {
2188  TQString name = node->name();
2189  // as more than one namespace can be listed in the root folder we need to make sure
2190  // that the folder is within the current namespace
2191  bool isInNamespace = ( jobData.curNamespace.isEmpty() ||
2192  jobData.curNamespace == mAccount->namespaceForFolder( f ) );
2193  // ignore some cases
2194  bool ignore = root && ( f->imapPath() == "/INBOX/" ||
2195  mAccount->isNamespaceFolder( name ) || !isInNamespace );
2196 
2197  // This subfolder isn't present on the server
2198  if( !f->imapPath().isEmpty() && !ignore ) {
2199  // The folder has an imap path set, so it has been
2200  // on the server before. Delete it locally.
2201  toRemove.append( f->folder() );
2202  kdDebug(5006) << node->name() << " isn't on the server. It has an imapPath -> delete it locally" << endl;
2203  }
2204  } else { // folder both local and on server
2205  //kdDebug(5006) << node->name() << " is on the server." << endl;
2206 
2210  int index = mSubfolderNames.findIndex( node->name() );
2211  f->mFolderAttributes = folderAttributes[ index ];
2212  }
2213  } else {
2214  //kdDebug(5006) << "skipping dir node:" << node->name() << endl;
2215  }
2216  node = folder()->child()->next();
2217  }
2218  }
2219 
2220  for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) {
2221  rescueUnsyncedMessagesAndDeleteFolder( doomed );
2222  }
2223 
2224  mProgress += 5;
2225 
2226  // just in case there is nothing to rescue
2227  slotRescueDone( 0 );
2228 }
2229 
2230 // This synchronizes the local folders as needed (creation/deletion). No network communication here.
2231 void KMFolderCachedImap::listDirectory2()
2232 {
2233  TQString path = folder()->path();
2234  kmkernel->dimapFolderMgr()->quiet(true);
2235 
2236  bool root = ( this == mAccount->rootFolder() );
2237  if ( root && !mAccount->hasInbox() )
2238  {
2239  KMFolderCachedImap *f = 0;
2240  KMFolderNode *node;
2241  // create the INBOX
2242  for (node = folder()->child()->first(); node; node = folder()->child()->next())
2243  if (!node->isDir() && node->name() == "INBOX") break;
2244  if (node) {
2245  f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
2246  } else {
2247  KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap);
2248  if ( newFolder ) {
2249  f = static_cast<KMFolderCachedImap*>(newFolder->storage());
2250  }
2251  }
2252  if ( f ) {
2253  f->setAccount( mAccount );
2254  f->setImapPath( "/INBOX/" );
2255  f->folder()->setLabel( i18n("inbox") );
2256  }
2257  if (!node) {
2258  if ( f )
2259  f->close("cachedimap");
2260  kmkernel->dimapFolderMgr()->contentsChanged();
2261  }
2262  // so we have an INBOX
2263  mAccount->setHasInbox( true );
2264  }
2265 
2266  if ( root && !mSubfolderNames.isEmpty() ) {
2267  KMFolderCachedImap* parent =
2268  findParent( mSubfolderPaths.first(), mSubfolderNames.first() );
2269  if ( parent ) {
2270  kdDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to "
2271  << parent->label() << endl;
2272  mSubfolderNames.clear();
2273  }
2274  }
2275 
2276  // Find all subfolders present on server but not on disk
2277  TQValueVector<int> foldersNewOnServer;
2278  for (uint i = 0; i < mSubfolderNames.count(); i++) {
2279 
2280  // Find the subdir, if already present
2281  KMFolderCachedImap *f = 0;
2282  KMFolderNode *node = 0;
2283  for (node = folder()->child()->first(); node;
2284  node = folder()->child()->next())
2285  if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
2286 
2287  if (!node) {
2288  // This folder is not present here
2289  // Either it's new on the server, or we just deleted it.
2290  TQString subfolderPath = mSubfolderPaths[i];
2291  // The code used to look at the uidcache to know if it was "just deleted".
2292  // But this breaks with noContent folders and with shared folders.
2293  // So instead we keep a list in the account.
2294  bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath );
2295  // That list is saved/restored across sessions, but to avoid any mistake,
2296  // ask for confirmation if the folder was deleted in a previous session
2297  // (could be that the folder was deleted & recreated meanwhile from another client...)
2298  if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) {
2299  locallyDeleted = KMessageBox::warningYesNo(
2300  0, i18n( "<qt><p>It seems that the folder <b>%1</b> was deleted. Do you want to delete it from the server?</p></qt>" ).arg( mSubfolderNames[i] ), TQString(), KStdGuiItem::del(), KStdGuiItem::cancel() ) == KMessageBox::Yes;
2301  }
2302 
2303  if ( locallyDeleted ) {
2304  kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl;
2305  foldersForDeletionOnServer += mAccount->deletedFolderPaths( subfolderPath ); // grab all subsubfolders too
2306  } else {
2307  kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl;
2308  foldersNewOnServer.append( i );
2309  }
2310  } else { // Folder found locally
2311  if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap )
2312  f = dynamic_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
2313  if( f ) {
2314  // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath()
2315  // << "\nSetting imapPath " << mSubfolderPaths[i] << endl;
2316  // Write folder settings
2317  f->setAccount(mAccount);
2318  f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
2319  f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest");
2320  f->setImapPath(mSubfolderPaths[i]);
2321  }
2322  }
2323  }
2324 
2325  /* In case we are ignoring non-groupware folders, and this is the groupware
2326  * main account, find out the contents types of folders that have newly
2327  * appeared on the server. Otherwise just create them and finish listing.
2328  * If a folder is already known to be locally unsubscribed, it won't be
2329  * listed at all, on this level, so these are only folders that we are
2330  * seeing for the first time. */
2331 
2332  /* Note: We ask the globalsettings, and not the current state of the
2333  * kmkernel->iCalIface().isEnabled(), since that is false during the
2334  * very first sync, where we already want to filter. */
2335  if ( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
2336  && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
2337  && mAccount->hasAnnotationSupport()
2338  && GlobalSettings::self()->theIMAPResourceEnabled()
2339  && !foldersNewOnServer.isEmpty() ) {
2340 
2341  TQStringList paths;
2342  for ( uint i = 0; i < foldersNewOnServer.count(); ++i )
2343  paths << mSubfolderPaths[ foldersNewOnServer[i] ];
2344 
2346  AnnotationJobs::multiUrlGetAnnotation( mAccount->slave(), mAccount->getUrl(), paths, KOLAB_FOLDERTYPE );
2347  ImapAccountBase::jobData jd( TQString(), folder() );
2348  jd.cancellable = true;
2349  mAccount->insertJob(job, jd);
2350  connect( job, TQ_SIGNAL(result(TDEIO::Job *)),
2351  TQ_SLOT(slotMultiUrlGetAnnotationResult(TDEIO::Job *)) );
2352 
2353  } else {
2354  createFoldersNewOnServerAndFinishListing( foldersNewOnServer );
2355  }
2356 }
2357 
2358 void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const TQValueVector<int> foldersNewOnServer )
2359 {
2360  for ( uint i = 0; i < foldersNewOnServer.count(); ++i ) {
2361  int idx = foldersNewOnServer[i];
2362  KMFolder* newFolder = folder()->child()->createFolder( mSubfolderNames[idx], false, KMFolderTypeCachedImap);
2363  if (newFolder) {
2364  KMFolderCachedImap *f = dynamic_cast<KMFolderCachedImap*>(newFolder->storage());
2365  kdDebug(5006) << " ####### Locally creating folder " << mSubfolderNames[idx] <<endl;
2366  f->close("cachedimap");
2367  f->setAccount(mAccount);
2368  f->mAnnotationFolderType = "FROMSERVER";
2369  f->setNoContent(mSubfolderMimeTypes[idx] == "inode/directory");
2370  f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest");
2371  f->setImapPath(mSubfolderPaths[idx]);
2372  f->mFolderAttributes = mSubfolderAttributes[idx];
2373  mNewlyCreatedSubfolders.append( TQGuardedPtr<KMFolderCachedImap>( f ) );
2374  kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <<endl;
2375  //kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl;
2376  kmkernel->dimapFolderMgr()->contentsChanged();
2377  } else {
2378  kdDebug(5006) << "can't create folder " << mSubfolderNames[idx] <<endl;
2379  }
2380  }
2381 
2382  kmkernel->dimapFolderMgr()->quiet(false);
2383  emit listComplete(this);
2384  if ( !mPersonalNamespacesCheckDone ) {
2385  // we're not done with the namespaces
2386  mSyncState = SYNC_STATE_LIST_NAMESPACES;
2387  }
2388  serverSyncInternal();
2389 }
2390 
2391 //-----------------------------------------------------------------------------
2392 KMFolderCachedImap* KMFolderCachedImap::findParent( const TQString& path,
2393  const TQString& name )
2394 {
2395  TQString parent = path.left( path.length() - name.length() - 2 );
2396  if ( parent.length() > 1 )
2397  {
2398  // extract name of the parent
2399  parent = parent.right( parent.length() - 1 );
2400  if ( parent != label() )
2401  {
2402  KMFolderNode *node = folder()->child()->first();
2403  // look for a better parent
2404  while ( node )
2405  {
2406  if ( node->name() == parent )
2407  {
2408  KMFolder* fld = static_cast<KMFolder*>(node);
2409  KMFolderCachedImap* imapFld =
2410  static_cast<KMFolderCachedImap*>( fld->storage() );
2411  return imapFld;
2412  }
2413  node = folder()->child()->next();
2414  }
2415  }
2416  }
2417  return 0;
2418 }
2419 
2420 void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success)
2421 {
2422  Q_UNUSED(sub);
2423  //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl;
2424  if ( success ) {
2425  serverSyncInternal();
2426  }
2427  else
2428  {
2429  // success == false means the sync was aborted.
2430  if ( mCurrentSubfolder ) {
2431  Q_ASSERT( sub == mCurrentSubfolder );
2432  disconnectSubFolderSignals();
2433  }
2434 
2435  // Next step would be to check quota limits and then to close the folder, but don't bother with
2436  // both and close the folder right here, since we aborted.
2437  mSubfoldersForSync.clear();
2438  mSyncState = SYNC_STATE_INITIAL;
2439  close("cachedimap");
2440  emit syncStateChanged();
2441  emit folderComplete( this, false );
2442  }
2443 }
2444 
2445 void KMFolderCachedImap::slotSubFolderCloseToQuotaChanged()
2446 {
2447  if ( !mQuotaOnly ) {
2448  mSomeSubFolderCloseToQuotaChanged = true;
2449  }
2450 }
2451 
2452 void KMFolderCachedImap::slotSimpleData(TDEIO::Job * job, const TQByteArray & data)
2453 {
2454  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2455  if (it == mAccount->jobsEnd()) return;
2456  TQBuffer buff((*it).data);
2457  buff.open(IO_WriteOnly | IO_Append);
2458  buff.writeBlock(data.data(), data.size());
2459  buff.close();
2460 }
2461 
2462 FolderJob*
2463 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
2464  TQString, const AttachmentStrategy* ) const
2465 {
2466  TQPtrList<KMMessage> msgList;
2467  msgList.append( msg );
2468  CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
2469  job->setParentFolder( this );
2470  return job;
2471 }
2472 
2473 FolderJob*
2474 KMFolderCachedImap::doCreateJob( TQPtrList<KMMessage>& msgList, const TQString& sets,
2475  FolderJob::JobType jt, KMFolder *folder ) const
2476 {
2477  //FIXME: how to handle sets here?
2478  Q_UNUSED( sets );
2479  CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
2480  job->setParentFolder( this );
2481  return job;
2482 }
2483 
2484 void
2485 KMFolderCachedImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState state )
2486 {
2487  mUserRights = userRights;
2488  mUserRightsState = state;
2489  writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2490 }
2491 
2492 void
2493 KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder )
2494 {
2495  if ( folder->storage() == this ) {
2496  disconnect( mAccount, TQ_SIGNAL( receivedUserRights( KMFolder* ) ),
2497  this, TQ_SLOT( slotReceivedUserRights( KMFolder* ) ) );
2498  if ( mUserRightsState == KMail::ACLJobs::Ok ) {
2499  setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 );
2500  }
2501  mProgress += 5;
2502  serverSyncInternal();
2503  }
2504 }
2505 
2506 void
2507 KMFolderCachedImap::setReadOnly( bool readOnly )
2508 {
2509  if ( readOnly != mReadOnly ) {
2510  mReadOnly = readOnly;
2511  emit readOnlyChanged( folder() );
2512  }
2513 }
2514 
2515 void
2516 KMFolderCachedImap::slotReceivedACL( KMFolder* folder, TDEIO::Job* job, const KMail::ACLList& aclList )
2517 {
2518  if ( folder->storage() == this ) {
2519  disconnect( mAccount, TQ_SIGNAL(receivedACL( KMFolder*, TDEIO::Job*, const KMail::ACLList& )),
2520  this, TQ_SLOT(slotReceivedACL( KMFolder*, TDEIO::Job*, const KMail::ACLList& )) );
2521  mACLListState = job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok;
2522  mACLList = aclList;
2523  serverSyncInternal();
2524  }
2525 }
2526 
2527 void
2528 KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info )
2529 {
2530  setQuotaInfo( info );
2531 }
2532 
2533 void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info )
2534 {
2535  if ( info != mQuotaInfo ) {
2536  const bool wasCloseToQuota = isCloseToQuota();
2537  mQuotaInfo = info;
2538  writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2539  if ( wasCloseToQuota != isCloseToQuota() ) {
2540  emit closeToQuotaChanged();
2541  }
2542  emit folderSizeChanged();
2543  }
2544 }
2545 
2546 void
2547 KMFolderCachedImap::setACLList( const ACLList& arr )
2548 {
2549  mACLList = arr;
2550  mACLListState = KMail::ACLJobs::Ok;
2551 }
2552 
2553 void
2554 KMFolderCachedImap::slotMultiSetACLResult(TDEIO::Job *job)
2555 {
2556  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2557  if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2558  if ( (*it).parent != folder() ) return; // Shouldn't happen
2559 
2560  if ( job->error() )
2561  // Display error but don't abort the sync just for this
2562  // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel
2563  job->showErrorDialog();
2564  else
2565  kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
2566 
2567  if (mAccount->slave()) mAccount->removeJob(job);
2568  serverSyncInternal();
2569 }
2570 
2571 void
2572 KMFolderCachedImap::slotACLChanged( const TQString& userId, int permissions )
2573 {
2574  // The job indicates success in changing the permissions for this user
2575  // -> we note that it's been done.
2576  for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) {
2577  if ( (*it).userId == userId && (*it).permissions == permissions ) {
2578  if ( permissions == -1 ) // deleted
2579  mACLList.erase( it );
2580  else // added/modified
2581  (*it).changed = false;
2582  return;
2583  }
2584  }
2585 }
2586 
2587 // called by KMAcctCachedImap::killAllJobs
2588 void KMFolderCachedImap::resetSyncState()
2589 {
2590  if ( mSyncState == SYNC_STATE_INITIAL ) return;
2591  mSubfoldersForSync.clear();
2592  mNewlyCreatedSubfolders.clear();
2593  mSyncState = SYNC_STATE_INITIAL;
2594  close("cachedimap");
2595  // Don't use newState here, it would revert to mProgress (which is < current value when listing messages)
2596  KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
2597  TQString str = i18n("Aborted");
2598  if (progressItem)
2599  progressItem->setStatus( str );
2600  emit statusMsg( str );
2601  emit syncStateChanged();
2602 }
2603 
2604 void KMFolderCachedImap::slotIncreaseProgress()
2605 {
2606  mProgress += 5;
2607 }
2608 
2609 void KMFolderCachedImap::newState( int progress, const TQString& syncStatus )
2610 {
2611  //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl;
2612  KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
2613  if( progressItem )
2614  progressItem->setCompletedItems( progress );
2615  if ( !syncStatus.isEmpty() ) {
2616  TQString str;
2617  // For a subfolder, show the label. But for the main folder, it's already shown.
2618  if ( mAccount->imapFolder() == this )
2619  str = syncStatus;
2620  else
2621  str = TQString( "%1: %2" ).arg( label() ).arg( syncStatus );
2622  if( progressItem )
2623  progressItem->setStatus( str );
2624  emit statusMsg( str );
2625  }
2626  if( progressItem )
2627  progressItem->updateProgress();
2628 }
2629 
2630 void KMFolderCachedImap::setSubfolderState( imapState state )
2631 {
2632  mSubfolderState = state;
2633  if ( state == imapNoInformation && folder()->child() )
2634  {
2635  // pass through to childs
2636  KMFolderNode* node;
2637  TQPtrListIterator<KMFolderNode> it( *folder()->child() );
2638  for ( ; (node = it.current()); )
2639  {
2640  ++it;
2641  if (node->isDir()) continue;
2642  KMFolder *folder = static_cast<KMFolder*>(node);
2643  static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state );
2644  }
2645  }
2646 }
2647 
2648 void KMFolderCachedImap::setImapPath(const TQString &path)
2649 {
2650  mImapPath = path;
2651 }
2652 
2653 static bool isFolderTypeKnownToUs( const TQString &type )
2654 {
2655  for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
2656  FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
2657  if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) )
2658  return true;
2659  }
2660  return false;
2661 }
2662 
2663 // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
2664 // It is updated from the folder contents type and whether it's a standard resource folder.
2665 // This happens during the syncing phase and during initFolder for a new folder.
2666 // Don't do it earlier, e.g. from setContentsType:
2667 // on startup, it's too early there to know if this is a standard resource folder.
2668 void KMFolderCachedImap::updateAnnotationFolderType()
2669 {
2670  TQString oldType = mAnnotationFolderType;
2671  TQString oldSubType;
2672  int dot = oldType.find( '.' );
2673  if ( dot != -1 ) {
2674  oldType.truncate( dot );
2675  oldSubType = mAnnotationFolderType.mid( dot + 1 );
2676  }
2677 
2678  TQString newType, newSubType;
2679  // We want to store an annotation on the folder only if using the kolab storage.
2680  if ( kmkernel->iCalIface().storageFormat( folder() ) == KMailICalIfaceImpl::StorageXML ) {
2681  newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType );
2682  if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) )
2683  newSubType = "default";
2684  else if ( oldSubType != "default" )
2685  newSubType = oldSubType; // preserve unknown subtypes, like drafts etc.
2686  }
2687 
2688  // We do not want to overwrite custom folder types (which we treat as mail folders).
2689  // So only overwrite custom folder types if the user changed the folder type himself to something
2690  // other than mail.
2691  const bool changingTypeAllowed = isFolderTypeKnownToUs( oldType ) ||
2692  ( mContentsType != ContentsTypeMail );
2693 
2694  //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl;
2695  if ( ( newType != oldType || newSubType != oldSubType ) && changingTypeAllowed ) {
2696  mAnnotationFolderType = newType + ( newSubType.isEmpty() ? TQString() : "."+newSubType );
2697  mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync
2698  kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl;
2699  }
2700  // Ensure that further readConfig()s don't lose mAnnotationFolderType
2701  writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2702 }
2703 
2704 void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor )
2705 {
2706  if ( mIncidencesFor != incfor ) {
2707  mIncidencesFor = incfor;
2708  mIncidencesForChanged = true;
2709  }
2710 }
2711 
2712 void KMFolderCachedImap::setSharedSeenFlags(bool b)
2713 {
2714  if ( mSharedSeenFlags != b ) {
2715  mSharedSeenFlags = b;
2716  mSharedSeenFlagsChanged = true;
2717  }
2718 }
2719 
2720 void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQString& value, bool found)
2721 {
2722  if ( entry == KOLAB_FOLDERTYPE ) {
2723  // There are four cases.
2724  // 1) no content-type on server -> set it
2725  // 2) different content-type on server, locally changed -> set it (we don't even come here)
2726  // 3) different (known) content-type on server, no local change -> get it
2727  // 4) different unknown content-type on server, probably some older version -> set it
2728  if ( found ) {
2729  TQString type = value;
2730  TQString subtype;
2731  int dot = value.find( '.' );
2732  if ( dot != -1 ) {
2733  type.truncate( dot );
2734  subtype = value.mid( dot + 1 );
2735  }
2736  bool foundKnownType = false;
2737  for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
2738  FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
2739  if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) {
2740  // Case 3: known content-type on server, get it
2741  //kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: found known type of annotation" << endl;
2742  if ( contentsType != ContentsTypeMail )
2743  kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
2744  mAnnotationFolderType = value;
2745  if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent()
2746  && GlobalSettings::self()->theIMAPResourceEnabled()
2747  && subtype == "default" ) {
2748  // Truncate subtype if this folder can't be a default resource folder for us,
2749  // although it apparently is for someone else.
2750  mAnnotationFolderType = type;
2751  kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: parent folder is " << folder()->parent()->owner()->idString() << " => truncating annotation to " << value << endl;
2752  }
2753  setContentsType( contentsType );
2754  mAnnotationFolderTypeChanged = false; // we changed it, not the user
2755  foundKnownType = true;
2756 
2757  // Users don't read events/contacts/etc. in kmail, so mark them all as read.
2758  // This is done in cachedimapjob when getting new messages, but do it here too,
2759  // for the initial set of messages when we didn't know this was a resource folder yet,
2760  // for old folders, etc.
2761  if ( contentsType != ContentsTypeMail )
2762  markUnreadAsRead();
2763 
2764  break;
2765  }
2766  }
2767  if ( !foundKnownType ) {
2768  //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, leaving it untouched" << endl;
2769 
2770  // Case 4: Server has strange content-type. We must not overwrite it, see https://issues.kolab.org/issue2069.
2771  // Treat the content-type as mail until we change it ourselves.
2772  mAnnotationFolderTypeChanged = false;
2773  mAnnotationFolderType = value;
2774  setContentsType( ContentsTypeMail );
2775  }
2776 
2777  // Ensure that further readConfig()s don't lose mAnnotationFolderType
2778  writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2779  // TODO handle subtype (inbox, drafts, sentitems, junkemail)
2780  }
2781  else if ( !mReadOnly ) {
2782  // Case 1: server doesn't have content-type, set it
2783  //kdDebug(5006) << "slotGetAnnotationResult: no annotation found, will need to set it" << endl;
2784  mAnnotationFolderTypeChanged = true;
2785  }
2786  } else if ( entry == KOLAB_INCIDENCESFOR ) {
2787  if ( found ) {
2788  mIncidencesFor = incidencesForFromString( value );
2789  Q_ASSERT( mIncidencesForChanged == false );
2790  }
2791  } else if ( entry == KOLAB_SHAREDSEEN ) {
2792  if ( found ) {
2793  mSharedSeenFlags = value == "true";
2794  }
2795  }
2796 }
2797 
2798 void KMFolderCachedImap::slotGetAnnotationResult( TDEIO::Job* job )
2799 {
2800  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2801  Q_ASSERT( it != mAccount->jobsEnd() );
2802  if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2803  Q_ASSERT( (*it).parent == folder() );
2804  if ( (*it).parent != folder() ) return; // Shouldn't happen
2805 
2807  if ( annjob->error() ) {
2808  if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) {
2809  // that's when the imap server doesn't support annotations
2810  if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
2811  && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
2812  KMessageBox::error( 0, i18n( "The IMAP server %1 does not have support for IMAP annotations. The XML storage cannot be used on this server; please re-configure KMail differently." ).arg( mAccount->host() ) );
2813  mAccount->setHasNoAnnotationSupport();
2814  }
2815  else
2816  kdWarning(5006) << "slotGetAnnotationResult: " << job->errorString() << endl;
2817  }
2818 
2819  if (mAccount->slave()) mAccount->removeJob(job);
2820  mProgress += 2;
2821  serverSyncInternal();
2822 }
2823 
2824 void KMFolderCachedImap::slotMultiUrlGetAnnotationResult( TDEIO::Job* job )
2825 {
2826  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2827  Q_ASSERT( it != mAccount->jobsEnd() );
2828  if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2829  Q_ASSERT( (*it).parent == folder() );
2830  if ( (*it).parent != folder() ) return; // Shouldn't happen
2831 
2832  TQValueVector<int> folders;
2834  = static_cast<AnnotationJobs::MultiUrlGetAnnotationJob *>( job );
2835  if ( annjob->error() ) {
2836  if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) {
2837  // that's when the imap server doesn't support annotations
2838  if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
2839  && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
2840  KMessageBox::error( 0, i18n( "The IMAP server %1 doesn't have support for imap annotations. The XML storage cannot be used on this server, please re-configure KMail differently" ).arg( mAccount->host() ) );
2841  mAccount->setHasNoAnnotationSupport();
2842  }
2843  else
2844  kdWarning(5006) << "slotGetMultiUrlAnnotationResult: " << job->errorString() << endl;
2845  } else {
2846  // we got the annotation allright, let's filter out the ones with the wrong type
2847  TQMap<TQString, TQString> annotations = annjob->annotations();
2848  TQMap<TQString, TQString>::Iterator it = annotations.begin();
2849  for ( ; it != annotations.end(); ++it ) {
2850  const TQString folderPath = it.key();
2851  const TQString annotation = it.data();
2852  kdDebug(5006) << k_funcinfo << "Folder: " << folderPath << " has type: " << annotation << endl;
2853  // we're only interested in the main type
2854  TQString type(annotation);
2855  int dot = annotation.find( '.' );
2856  if ( dot != -1 ) type.truncate( dot );
2857  type = type.simplifyWhiteSpace();
2858 
2859  const int idx = mSubfolderPaths.findIndex( folderPath );
2860  const bool isNoContent = mSubfolderMimeTypes[idx] == "inode/directory";
2861  if ( ( isNoContent && type.isEmpty() )
2862  || ( !type.isEmpty() && type != KMailICalIfaceImpl::annotationForContentsType( ContentsTypeMail ) ) ) {
2863  folders.append( idx );
2864  kdDebug(5006) << k_funcinfo << " subscribing to: " << folderPath << endl;
2865  } else {
2866  kdDebug(5006) << k_funcinfo << " automatically unsubscribing from: " << folderPath << endl;
2867  mAccount->changeLocalSubscription( folderPath, false );
2868  }
2869  }
2870  }
2871 
2872  if (mAccount->slave()) mAccount->removeJob(job);
2873  createFoldersNewOnServerAndFinishListing( folders );
2874 }
2875 
2876 void KMFolderCachedImap::slotQuotaResult( TDEIO::Job* job )
2877 {
2878  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2879  Q_ASSERT( it != mAccount->jobsEnd() );
2880  if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2881  Q_ASSERT( (*it).parent == folder() );
2882  if ( (*it).parent != folder() ) return; // Shouldn't happen
2883 
2884  QuotaJobs::GetStorageQuotaJob* quotajob = static_cast<QuotaJobs::GetStorageQuotaJob *>( job );
2885  QuotaInfo empty;
2886  if ( quotajob->error() ) {
2887  if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) {
2888  // that's when the imap server doesn't support quota
2889  mAccount->setHasNoQuotaSupport();
2890  setQuotaInfo( empty );
2891  }
2892  else
2893  kdWarning(5006) << "slotGetQuotaResult: " << job->errorString() << endl;
2894  }
2895 
2896  if (mAccount->slave()) mAccount->removeJob(job);
2897  mProgress += 2;
2898  serverSyncInternal();
2899 }
2900 
2901 void
2902 KMFolderCachedImap::slotAnnotationChanged( const TQString& entry, const TQString& attribute, const TQString& value )
2903 {
2904  Q_UNUSED( attribute );
2905  Q_UNUSED( value );
2906  //kdDebug(5006) << k_funcinfo << entry << " " << attribute << " " << value << endl;
2907  if ( entry == KOLAB_FOLDERTYPE )
2908  mAnnotationFolderTypeChanged = false;
2909  else if ( entry == KOLAB_INCIDENCESFOR ) {
2910  mIncidencesForChanged = false;
2911  // The incidences-for changed, we must trigger the freebusy creation.
2912  // HACK: in theory we would need a new enum value for this.
2913  kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
2914  } else if ( entry == KOLAB_SHAREDSEEN ) {
2915  mSharedSeenFlagsChanged = false;
2916  }
2917 }
2918 
2919 void KMFolderCachedImap::slotTestAnnotationResult(TDEIO::Job *job)
2920 {
2921  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2922  Q_ASSERT( it != mAccount->jobsEnd() );
2923  if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2924  Q_ASSERT( (*it).parent == folder() );
2925  if ( (*it).parent != folder() ) return; // Shouldn't happen
2926 
2927  mAccount->setAnnotationCheckPassed( true );
2928  if ( job->error() ) {
2929  kdDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl;
2930  mAccount->setHasNoAnnotationSupport( );
2931  } else {
2932  kdDebug(5006) << "Test Annotation was passed OK" << endl;
2933  }
2934  if (mAccount->slave()) mAccount->removeJob(job);
2935  serverSyncInternal();
2936 }
2937 
2938 void
2939 KMFolderCachedImap::slotSetAnnotationResult(TDEIO::Job *job)
2940 {
2941  KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2942  if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2943  if ( (*it).parent != folder() ) return; // Shouldn't happen
2944 
2945  bool cont = true;
2946  if ( job->error() ) {
2947  // Don't show error if the server doesn't support ANNOTATEMORE and this folder only contains mail
2948  if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION && contentsType() == ContentsTypeMail ) {
2949  if (mAccount->slave()) mAccount->removeJob(job);
2950  } else {
2951  cont = mAccount->handleJobError( job, i18n( "Error while setting annotation: " ) + '\n' );
2952  }
2953  } else {
2954  if (mAccount->slave()) mAccount->removeJob(job);
2955  }
2956  if ( cont )
2957  serverSyncInternal();
2958 }
2959 
2960 void KMFolderCachedImap::slotUpdateLastUid()
2961 {
2962  if( mTentativeHighestUid != 0 ) {
2963 
2964  // Sanity checking:
2965  // By now all new mails should be downloaded, which means
2966  // that iteration over the folder should yield only UIDs
2967  // lower or equal to what we think the highes ist, and the
2968  // highest one as well. If not, our notion of the highest
2969  // uid we've seen thus far is wrong, which is dangerous, so
2970  // don't update the mLastUid, then.
2971  // Not entirely true though, mails might have been moved out
2972  // of the folder already by filters, thus giving us a higher tentative
2973  // uid than we actually observe here.
2974  bool sane = count() == 0;
2975 
2976  for (int i=0;i<count(); i++ ) {
2977  ulong uid = getMsgBase(i)->UID();
2978  if ( uid > mTentativeHighestUid && uid > lastUid() ) {
2979  kdWarning(5006) << "DANGER: Either the server listed a wrong highest uid, "
2980  "or we parsed it wrong. Send email to adam@kde.org, please, and include this log." << endl;
2981  kdWarning(5006) << "uid: " << uid << " mTentativeHighestUid: " << mTentativeHighestUid << endl;
2982  assert( false );
2983  break;
2984  } else {
2985  sane = true;
2986  }
2987  }
2988  if (sane) {
2989 #if MAIL_LOSS_DEBUGGING
2990  kdDebug(5006) << "Tentative highest UID test was sane, writing out: " << mTentativeHighestUid << endl;
2991 #endif
2992  setLastUid( mTentativeHighestUid );
2993  }
2994  }
2995  mTentativeHighestUid = 0;
2996 }
2997 
2998 bool KMFolderCachedImap::isMoveable() const
2999 {
3000  return ( hasChildren() == HasNoChildren &&
3001  !folder()->isSystemFolder() ) ? true : false;
3002 }
3003 
3004 void KMFolderCachedImap::slotFolderDeletionOnServerFinished()
3005 {
3006  for ( TQStringList::const_iterator it = foldersForDeletionOnServer.constBegin();
3007  it != foldersForDeletionOnServer.constEnd(); ++it ) {
3008  KURL url( mAccount->getUrl() );
3009  url.setPath( *it );
3010  kmkernel->iCalIface().folderDeletedOnServer( url );
3011  }
3012  serverSyncInternal();
3013 }
3014 
3015 int KMFolderCachedImap::createIndexFromContentsRecursive()
3016 {
3017  if ( !folder() || !folder()->child() )
3018  return 0;
3019 
3020  KMFolderNode *node = 0;
3021  for( TQPtrListIterator<KMFolderNode> it( *folder()->child() ); (node = it.current()); ++it ) {
3022  if( !node->isDir() ) {
3023  KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
3024  kdDebug() << k_funcinfo << "Re-indexing: " << storage->folder()->label() << endl;
3025  int rv = storage->createIndexFromContentsRecursive();
3026  if ( rv > 0 )
3027  return rv;
3028  }
3029  }
3030 
3031  return createIndexFromContents();
3032 }
3033 
3034 void KMFolderCachedImap::setAlarmsBlocked( bool blocked )
3035 {
3036  mAlarmsBlocked = blocked;
3037 }
3038 
3039 bool KMFolderCachedImap::alarmsBlocked() const
3040 {
3041  return mAlarmsBlocked;
3042 }
3043 
3044 bool KMFolderCachedImap::isCloseToQuota() const
3045 {
3046  bool closeToQuota = false;
3047  if ( mQuotaInfo.isValid() && mQuotaInfo.max().toInt() > 0 ) {
3048  const int ratio = mQuotaInfo.current().toInt() * 100 / mQuotaInfo.max().toInt();
3049  //kdDebug(5006) << "Quota ratio: " << ratio << "% " << mQuotaInfo.toString() << endl;
3050  closeToQuota = ( ratio > 0 && ratio >= GlobalSettings::closeToQuotaThreshold() );
3051  }
3052  //kdDebug(5006) << "Folder: " << folder()->prettyURL() << " is over quota: " << closeToQuota << endl;
3053  return closeToQuota;
3054 }
3055 
3056 KMCommand* KMFolderCachedImap::rescueUnsyncedMessages()
3057 {
3058  TQValueList<unsigned long> newMsgs = findNewMessages();
3059  kdDebug() << k_funcinfo << newMsgs << " of " << count() << endl;
3060  if ( newMsgs.isEmpty() )
3061  return 0;
3062  KMFolder *dest = 0;
3063  bool manualMove = true;
3064  while ( GlobalSettings::autoLostFoundMove() ) {
3065  // find the inbox of this account
3066  KMFolder *inboxFolder = kmkernel->findFolderById( TQString(".%1.directory/INBOX").arg( account()->id() ) );
3067  if ( !inboxFolder ) {
3068  kdWarning(5006) << k_funcinfo << "inbox not found!" << endl;
3069  break;
3070  }
3071  KMFolderDir *inboxDir = inboxFolder->child();
3072  if ( !inboxDir && !inboxFolder->storage() )
3073  break;
3074  assert( inboxFolder->storage()->folderType() == KMFolderTypeCachedImap );
3075 
3076  // create lost+found folder if needed
3077  KMFolderNode *node;
3078  KMFolder *lfFolder = 0;
3079  if ( !(node = inboxDir->hasNamedFolder( i18n("lost+found") )) ) {
3080  kdDebug(5006) << k_funcinfo << "creating lost+found folder" << endl;
3081  KMFolder* folder = kmkernel->dimapFolderMgr()->createFolder(
3082  i18n("lost+found"), false, KMFolderTypeCachedImap, inboxDir );
3083  if ( !folder || !folder->storage() )
3084  break;
3085  static_cast<KMFolderCachedImap*>( folder->storage() )->initializeFrom(
3086  static_cast<KMFolderCachedImap*>( inboxFolder->storage() ) );
3087  folder->storage()->setContentsType( KMail::ContentsTypeMail );
3088  folder->storage()->writeConfig();
3089  lfFolder = folder;
3090  } else {
3091  kdDebug(5006) << k_funcinfo << "found lost+found folder" << endl;
3092  lfFolder = dynamic_cast<KMFolder*>( node );
3093  }
3094  if ( !lfFolder || !lfFolder->createChildFolder() || !lfFolder->storage() )
3095  break;
3096 
3097  // create subfolder for this incident
3098  TQDate today = TQDate::currentDate();
3099  TQString baseName = folder()->label() + "-" + TQString::number( today.year() )
3100  + (today.month() < 10 ? "0" : "" ) + TQString::number( today.month() )
3101  + (today.day() < 10 ? "0" : "" ) + TQString::number( today.day() );
3102  TQString name = baseName;
3103  int suffix = 0;
3104  while ( (node = lfFolder->child()->hasNamedFolder( name )) ) {
3105  ++suffix;
3106  name = baseName + '-' + TQString::number( suffix );
3107  }
3108  kdDebug(5006) << k_funcinfo << "creating lost+found folder " << name << endl;
3109  dest = kmkernel->dimapFolderMgr()->createFolder( name, false, KMFolderTypeCachedImap, lfFolder->child() );
3110  if ( !dest || !dest->storage() )
3111  break;
3112  static_cast<KMFolderCachedImap*>( dest->storage() )->initializeFrom(
3113  static_cast<KMFolderCachedImap*>( lfFolder->storage() ) );
3114  dest->storage()->setContentsType( contentsType() );
3115  dest->storage()->writeConfig();
3116 
3117  KMessageBox::sorry( 0, i18n("<p>There are new messages in folder <b>%1</b>, which "
3118  "have not been uploaded to the server yet, but the folder has been deleted "
3119  "on the server or you do not "
3120  "have sufficient access rights on the folder to upload them.</p>"
3121  "<p>All affected messages will therefore be moved to <b>%2</b> "
3122  "to avoid data loss.</p>").arg( folder()->prettyURL() ).arg( dest->prettyURL() ),
3123  i18n("Insufficient access rights") );
3124  manualMove = false;
3125  break;
3126  }
3127 
3128  if ( manualMove ) {
3129  const TQString msg ( i18n( "<p>There are new messages in this folder (%1), which "
3130  "have not been uploaded to the server yet, but the folder has been deleted "
3131  "on the server or you do not "
3132  "have sufficient access rights on the folder now to upload them. "
3133  "Please contact your administrator to allow upload of new messages "
3134  "to you, or move them out of this folder.</p> "
3135  "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyURL() ) );
3136  if ( KMessageBox::warningYesNo( 0, msg, TQString(), i18n("Move"), i18n("Do Not Move") ) == KMessageBox::Yes ) {
3137  KMail::KMFolderSelDlg dlg( kmkernel->getKMMainWidget(),
3138  i18n("Move Messages to Folder"), true );
3139  if ( dlg.exec() ) {
3140  dest = dlg.folder();
3141  }
3142  }
3143  }
3144  if ( dest ) {
3145  TQPtrList<KMMsgBase> msgs;
3146  for( int i = 0; i < count(); ++i ) {
3147  KMMsgBase *msg = getMsgBase( i );
3148  if( !msg ) continue; /* what goes on if getMsg() returns 0? */
3149  if ( msg->UID() == 0 )
3150  msgs.append( msg );
3151  }
3152  KMCommand *command = new KMMoveCommand( dest, msgs );
3153  command->start();
3154  return command;
3155  }
3156  return 0;
3157 }
3158 
3159 void KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root )
3160 {
3161  kdDebug() << k_funcinfo << folder << " " << root << endl;
3162  if ( root )
3163  mToBeDeletedAfterRescue.append( folder );
3164  folder->open("cachedimap");
3165  KMFolderCachedImap* storage = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
3166  if ( storage ) {
3167  KMCommand *command = storage->rescueUnsyncedMessages();
3168  if ( command ) {
3169  connect( command, TQ_SIGNAL(completed(KMCommand*)),
3170  TQ_SLOT(slotRescueDone(KMCommand*)) );
3171  ++mRescueCommandCount;
3172  } else {
3173  // nothing to rescue, close folder
3174  // (we don't need to close it in the other case, it will be deleted anyway)
3175  folder->close("cachedimap");
3176  }
3177  }
3178  if ( folder->child() ) {
3179  KMFolderNode *node = folder->child()->first();
3180  while (node) {
3181  if (!node->isDir() ) {
3182  KMFolder *subFolder = static_cast<KMFolder*>( node );
3183  rescueUnsyncedMessagesAndDeleteFolder( subFolder, false );
3184  }
3185  node = folder->child()->next();
3186  }
3187  }
3188 }
3189 
3190 void KMFolderCachedImap::slotRescueDone(KMCommand * command)
3191 {
3192  // FIXME: check command result
3193  if ( command )
3194  --mRescueCommandCount;
3195  if ( mRescueCommandCount > 0 )
3196  return;
3197  for ( TQValueList<KMFolder*>::ConstIterator it = mToBeDeletedAfterRescue.constBegin();
3198  it != mToBeDeletedAfterRescue.constEnd(); ++it ) {
3199  kmkernel->dimapFolderMgr()->remove( *it );
3200  }
3201  mToBeDeletedAfterRescue.clear();
3202  serverSyncInternal();
3203 }
3204 
3205 void KMFolderCachedImap::slotRenameFolderFinished()
3206 {
3207  // The syncing code assumes the folder was opened by us, and later closes it. So better
3208  // make sure the reference count is correct, since the folder was force-closed by the rename.
3209  // Otherwise bad things can happen, see https://issues.kolab.org/issue3853.
3210  open( "cachedimap" );
3211  serverSyncInternal();
3212 }
3213 
3214 bool KMFolderCachedImap::canDeleteMessages() const
3215 {
3216  if ( isReadOnly() )
3217  return false;
3218  if ( mUserRightsState == KMail::ACLJobs::Ok && !(userRights() & ACLJobs::Delete) )
3219  return false;
3220  return true;
3221 }
3222 
3223 bool KMFolderCachedImap::mailCheckInProgress() const
3224 {
3225  return mSyncState != SYNC_STATE_INITIAL;
3226 }
3227 
3228 #include "kmfoldercachedimap.moc"
virtual void setContentsType(KMail::FolderContentsType type, bool quiet=false)
Set the type of contents held in this folder (mail, calendar, etc.)
virtual void readConfig()
Read the config file.
virtual void setStatus(int idx, KMMsgStatus status, bool toggle=false)
Set the status of the message at index idx to status.
virtual KMFolderType folderType() const
Returns the type of this folder.
Definition: folderstorage.h:96
virtual void remove()
Removes the folder physically from disk and empties the contents of the folder in memory.
virtual void writeConfig()
Write the config file.
virtual int addMsg(TQPtrList< KMMessage > &, TQValueList< int > &index_return)
Adds the given messages to the folder.
KMail list that manages the contents of one directory that may contain folders and/or other directori...
Definition: kmfolderdir.h:16
virtual KMFolder * createFolder(const TQString &folderName, bool sysFldr=false, KMFolderType folderType=KMFolderTypeMbox)
Create a mail folder in this directory with given name.
Definition: kmfolderdir.cpp:95
virtual KMFolderNode * hasNamedFolder(const TQString &name)
Returns folder with given name or zero if it does not exist.
TQString label() const
Returns the label of the folder for visualization.
RAII for KMFolder::open() / close().
Definition: kmfolder.h:688
Mail folder.
Definition: kmfolder.h:69
TQString idString() const
Returns a string that can be used to identify this folder.
Definition: kmfolder.cpp:705
virtual TQString prettyURL() const
URL of the node for visualization purposes.
Definition: kmfolder.cpp:593
virtual TQString label() const
Returns the label of the folder for visualization.
Definition: kmfolder.cpp:581
KMFolderDir * child() const
Returns the folder directory associated with this node or 0 if no such directory exists.
Definition: kmfolder.h:157
void close(const char *owner, bool force=false)
Close folder.
Definition: kmfolder.cpp:489
KMFolderDir * createChildFolder()
Create a child folder directory and associates it with this folder.
Definition: kmfolder.cpp:264
void setNoContent(bool aNoContent)
Specify, that the folder can't contain mails.
Definition: kmfolder.cpp:306
int open(const char *owner)
Open folder for access.
Definition: kmfolder.cpp:479
This is a Mime Message.
Definition: kmmessage.h:68
ulong UID() const
Get/set UID.
Definition: kmmessage.cpp:2225
The account manager is responsible for creating accounts of various types via the factory method crea...
Generic folder list job for (d)imap accounts.
Definition: listjob.h:55
void setHonorLocalSubscription(bool value)
Set whether the listing should include only folders that the account is subscribed to locally.
Definition: listjob.cpp:245
void setNamespace(const TQString &ns)
Set the namespace for this listing.
Definition: listjob.h:105
@ NotFetchedYet
The user rights/ACL have not been fetched from the server yet, we don't know them.
Definition: acljobs.h:65
@ 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
MultiSetACLJob * multiSetACL(TDEIO::Slave *slave, const KURL &url, const ACLList &acl)
Set and delete a list of permissions for different users on a given url.
Definition: acljobs.cpp:258
GetStorageQuotaJob * getStorageQuota(TDEIO::Slave *slave, const KURL &url)
Get the storage quota for a mailbox, if there is one.
Definition: quotajobs.cpp:92
folderdiaquotatab.h
Definition: aboutdata.cpp:40
One entry in the annotation list: attribute name and attribute value.
Information about a message to be downloaded (from the 'IMAP envelope')
Definition: cachedimapjob.h:66