kmail

kmfoldercachedimap.cpp

00001 
00032 #ifdef HAVE_CONFIG_H
00033 #include <config.h>
00034 #endif
00035 
00036 #include <errno.h>
00037 
00038 #include <qvaluevector.h>
00039 
00040 #include "kmkernel.h"
00041 #include "kmfoldercachedimap.h"
00042 #include "undostack.h"
00043 #include "kmfoldermgr.h"
00044 #include "kmacctcachedimap.h"
00045 #include "accountmanager.h"
00046 using KMail::AccountManager;
00047 #include "kmailicalifaceimpl.h"
00048 #include "kmfolder.h"
00049 #include "kmglobal.h"
00050 #include "acljobs.h"
00051 #include "broadcaststatus.h"
00052 using KPIM::BroadcastStatus;
00053 #include "progressmanager.h"
00054 
00055 using KMail::CachedImapJob;
00056 #include "imapaccountbase.h"
00057 using KMail::ImapAccountBase;
00058 #include "listjob.h"
00059 using KMail::ListJob;
00060 
00061 #include "kmfolderseldlg.h"
00062 #include "kmcommands.h"
00063 #include "kmmainwidget.h"
00064 
00065 #include <kapplication.h>
00066 #include <kmessagebox.h>
00067 #include <klocale.h>
00068 #include <kdebug.h>
00069 #include <kconfig.h>
00070 #include <kio/global.h>
00071 #include <kio/scheduler.h>
00072 #include <qbuffer.h>
00073 #include <qbuttongroup.h>
00074 #include <qcombobox.h>
00075 #include <qfile.h>
00076 #include <qhbox.h>
00077 #include <qlabel.h>
00078 #include <qlayout.h>
00079 #include <qradiobutton.h>
00080 #include <qvaluelist.h>
00081 #include "annotationjobs.h"
00082 #include "quotajobs.h"
00083 using namespace KMail;
00084 #include <globalsettings.h>
00085 
00086 #define UIDCACHE_VERSION 1
00087 #define MAIL_LOSS_DEBUGGING 0
00088 
00089 static QString incidencesForToString( KMFolderCachedImap::IncidencesFor r ) {
00090   switch (r) {
00091   case KMFolderCachedImap::IncForNobody: return "nobody";
00092   case KMFolderCachedImap::IncForAdmins: return "admins";
00093   case KMFolderCachedImap::IncForReaders: return "readers";
00094   }
00095   return QString::null; // can't happen
00096 }
00097 
00098 static KMFolderCachedImap::IncidencesFor incidencesForFromString( const QString& str ) {
00099   if ( str == "nobody" ) return KMFolderCachedImap::IncForNobody;
00100   if ( str == "admins" ) return KMFolderCachedImap::IncForAdmins;
00101   if ( str == "readers" ) return KMFolderCachedImap::IncForReaders;
00102   return KMFolderCachedImap::IncForAdmins; // by default
00103 }
00104 
00105 DImapTroubleShootDialog::DImapTroubleShootDialog( QWidget* parent,
00106                                                   const char* name )
00107   : KDialogBase( Plain, i18n( "Troubleshooting IMAP Cache" ),
00108                  Ok | Cancel, Cancel, parent, name, true ),
00109     rc( None )
00110 {
00111   QFrame* page = plainPage();
00112   QVBoxLayout *topLayout = new QVBoxLayout( page, 0 );
00113   // spell "lose" correctly. but don't cause a fuzzy.
00114   QString txt = i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>"
00115                       "<p>If you have problems with synchronizing an IMAP "
00116                       "folder, you should first try rebuilding the index "
00117                       "file. This will take some time to rebuild, but will "
00118                       "not cause any problems.</p><p>If that is not enough, "
00119                       "you can try refreshing the IMAP cache. If you do this, "
00120                       "you will loose all your local changes for this folder "
00121                       "and all its subfolders.</p>",
00122                       "<p><b>Troubleshooting the IMAP cache.</b></p>"
00123                       "<p>If you have problems with synchronizing an IMAP "
00124                       "folder, you should first try rebuilding the index "
00125                       "file. This will take some time to rebuild, but will "
00126                       "not cause any problems.</p><p>If that is not enough, "
00127                       "you can try refreshing the IMAP cache. If you do this, "
00128                       "you will lose all your local changes for this folder "
00129                       "and all its subfolders.</p>" );
00130   topLayout->addWidget( new QLabel( txt, page ) );
00131 
00132   mButtonGroup = new QButtonGroup( 0 );
00133 
00134   mIndexButton = new QRadioButton( page );
00135   mIndexButton->setText( i18n( "Rebuild &Index" ) );
00136   mButtonGroup->insert( mIndexButton );
00137   topLayout->addWidget( mIndexButton );
00138 
00139   QHBox *hbox = new QHBox( page );
00140   QLabel *scopeLabel = new QLabel( i18n( "Scope:" ), hbox );
00141   scopeLabel->setEnabled( false );
00142   mIndexScope = new QComboBox( hbox );
00143   mIndexScope->insertItem( i18n( "Only current folder" ) );
00144   mIndexScope->insertItem( i18n( "Current folder and all subfolders" ) );
00145   mIndexScope->insertItem( i18n( "All folders of this account" ) );
00146   mIndexScope->setEnabled( false );
00147   topLayout->addWidget( hbox );
00148 
00149   mCacheButton = new QRadioButton( page );
00150   mCacheButton->setText( i18n( "Refresh &Cache" ) );
00151   mButtonGroup->insert( mCacheButton );
00152   topLayout->addWidget( mCacheButton );
00153 
00154   enableButtonSeparator( true );
00155 
00156   connect ( mIndexButton, SIGNAL(toggled(bool)), mIndexScope, SLOT(setEnabled(bool)) );
00157   connect ( mIndexButton, SIGNAL(toggled(bool)), scopeLabel, SLOT(setEnabled(bool)) );
00158   connect( mButtonGroup, SIGNAL( clicked( int ) ), SLOT( slotChanged() ) );
00159   connect( this, SIGNAL( okClicked () ), this, SLOT( slotDone() ) );
00160   enableButtonOK( false );
00161 }
00162 
00163 int DImapTroubleShootDialog::run()
00164 {
00165   DImapTroubleShootDialog d;
00166   d.exec();
00167   return d.rc;
00168 }
00169 
00170 void DImapTroubleShootDialog::slotChanged()
00171 {
00172   enableButtonOK( mButtonGroup->selected() != 0 );
00173 }
00174 
00175 void DImapTroubleShootDialog::slotDone()
00176 {
00177   rc = None;
00178   if ( mIndexButton->isOn() )
00179     rc = mIndexScope->currentItem();
00180   else if ( mCacheButton->isOn() )
00181     rc = RefreshCache;
00182   done( Ok );
00183 }
00184 
00185 KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName )
00186   : KMFolderMaildir( folder, aName ),
00187     mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ),
00188     mSubfolderState( imapNoInformation ),
00189     mIncidencesFor( IncForAdmins ),
00190     mSharedSeenFlags( false ),
00191     mIsSelected( false ),
00192     mCheckFlags( true ), mReadOnly( false ), mAccount( NULL ), uidMapDirty( true ),
00193     uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ),
00194     mFoundAnIMAPDigest( false ),
00195     mUserRights( 0 ), mOldUserRights( 0 ), mSilentUpload( false ),
00196     /*mHoldSyncs( false ),*/
00197     mFolderRemoved( false ),
00198     mRecurse( true ),
00199     mQuotaOnly( false ),
00200     mAnnotationFolderTypeChanged( false ),
00201     mIncidencesForChanged( false ),
00202     mSharedSeenFlagsChanged( false ),
00203     mStatusChangedLocally( false ),
00204     mPersonalNamespacesCheckDone( true ),
00205     mQuotaInfo(), mSomeSubFolderCloseToQuotaChanged( false ), mAlarmsBlocked( false ),
00206     mRescueCommandCount( 0 ),
00207     mPermanentFlags( 31 ) // assume standard flags by default (see imap4/imapinfo.h for bit fields values)
00208 {
00209   setUidValidity("");
00210   // if we fail to read a uid file but there is one, nuke it
00211   if ( readUidCache() == -1 ) {
00212     if ( QFile::exists( uidCacheLocation() ) ) {
00213         KMessageBox::error( 0,
00214         i18n( "The UID cache file for folder %1 could not be read. There "
00215               "could be a problem with file system permission, or it is corrupted."
00216               ).arg( folder->prettyURL() ) );
00217         // try to unlink it, in case it was corruped. If it couldn't be read
00218         // because of permissions, this will fail, which is fine
00219         unlink( QFile::encodeName( uidCacheLocation() ) );
00220     }
00221   }
00222 
00223   mProgress = 0;
00224 }
00225 
00226 KMFolderCachedImap::~KMFolderCachedImap()
00227 {
00228   if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
00229   writeConfig();
00230 }
00231 
00232 void KMFolderCachedImap::reallyDoClose( const char* owner )
00233 {
00234   if( !mFolderRemoved ) {
00235     writeUidCache();
00236   }
00237   KMFolderMaildir::reallyDoClose( owner );
00238 }
00239 
00240 void KMFolderCachedImap::initializeFrom( KMFolderCachedImap* parent )
00241 {
00242   setAccount( parent->account() );
00243   // Now that we have an account, tell it that this folder was created:
00244   // if this folder was just removed, then we don't really want to remove it from the server.
00245   mAccount->removeDeletedFolder( imapPath() );
00246   setUserRights( parent->userRights() );
00247 }
00248 
00249 void KMFolderCachedImap::readConfig()
00250 {
00251   KConfig* config = KMKernel::config();
00252   KConfigGroupSaver saver( config, "Folder-" + folder()->idString() );
00253   if( mImapPath.isEmpty() ) mImapPath = config->readEntry( "ImapPath" );
00254   if( QString( name() ).upper() == "INBOX" && mImapPath == "/INBOX/" )
00255   {
00256     folder()->setLabel( i18n( "inbox" ) );
00257     // for the icon
00258     folder()->setSystemFolder( true );
00259   }
00260   mNoContent = config->readBoolEntry( "NoContent", false );
00261   mReadOnly = config->readBoolEntry( "ReadOnly", false );
00262   if ( !config->readEntry( "FolderAttributes" ).isEmpty() )
00263     mFolderAttributes = config->readEntry( "FolderAttributes" );
00264 
00265   if ( mAnnotationFolderType != "FROMSERVER" ) {
00266     mAnnotationFolderType = config->readEntry( "Annotation-FolderType" );
00267     // if there is an annotation, it has to be XML
00268     if ( !mAnnotationFolderType.isEmpty() && !mAnnotationFolderType.startsWith( "mail" ) )
00269       kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
00270 //    kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
00271 //                  << " readConfig: mAnnotationFolderType=" << mAnnotationFolderType << endl;
00272   }
00273   mIncidencesFor = incidencesForFromString( config->readEntry( "IncidencesFor" ) );
00274   mAlarmsBlocked = config->readBoolEntry( "AlarmsBlocked", false );
00275 //  kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
00276 //                << " readConfig: mIncidencesFor=" << mIncidencesFor << endl;
00277   mSharedSeenFlags = config->readBoolEntry( "SharedSeenFlags", false );
00278 
00279   mUserRights = config->readNumEntry( "UserRights", 0 ); // default is we don't know
00280   mOldUserRights = mUserRights;
00281 
00282   int storageQuotaUsage = config->readNumEntry( "StorageQuotaUsage", -1 );
00283   int storageQuotaLimit = config->readNumEntry( "StorageQuotaLimit", -1 );
00284   QString storageQuotaRoot = config->readEntry( "StorageQuotaRoot", QString::null );
00285   if ( !storageQuotaRoot.isNull() ) { // isEmpty() means we know there is no quota set
00286       mQuotaInfo.setName( "STORAGE" );
00287       mQuotaInfo.setRoot( storageQuotaRoot );
00288 
00289       if ( storageQuotaUsage > -1 )
00290         mQuotaInfo.setCurrent( storageQuotaUsage );
00291       if ( storageQuotaLimit > -1 )
00292         mQuotaInfo.setMax( storageQuotaLimit );
00293   }
00294 
00295   KMFolderMaildir::readConfig();
00296 
00297   mStatusChangedLocally =
00298     config->readBoolEntry( "StatusChangedLocally", false );
00299   QStringList uidsChanged = config->readListEntry( "UIDStatusChangedLocally" );
00300   for ( QStringList::iterator it = uidsChanged.begin(); it != uidsChanged.end(); it++ ) {
00301     mUIDsOfLocallyChangedStatuses.insert( ( *it ).toUInt() );
00302   }
00303 
00304   mAnnotationFolderTypeChanged = config->readBoolEntry( "AnnotationFolderTypeChanged", false );
00305   mIncidencesForChanged = config->readBoolEntry( "IncidencesForChanged", false );
00306   mSharedSeenFlagsChanged = config->readBoolEntry( "SharedSeenFlagsChanged", false );
00307 
00308   if ( mImapPath.isEmpty() ) {
00309     mImapPathCreation = config->readEntry("ImapPathCreation");
00310   }
00311 
00312   QStringList delUids = config->readListEntry( "UIDSDeletedSinceLastSync" );
00313 #if MAIL_LOSS_DEBUGGING
00314   kdDebug( 5006 ) << "READING IN UIDSDeletedSinceLastSync: " << folder()->prettyURL() << endl << uids << endl;
00315 #endif
00316   for ( QStringList::iterator it = delUids.begin(); it != delUids.end(); it++ ) {
00317     mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0);
00318   }
00319 }
00320 
00321 void KMFolderCachedImap::writeConfig()
00322 {
00323   // don't re-write the config of a removed folder, this has just been deleted in
00324   // the folder manager
00325   if ( mFolderRemoved )
00326     return;
00327 
00328   KConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
00329   configGroup.writeEntry( "ImapPath", mImapPath );
00330   configGroup.writeEntry( "NoContent", mNoContent );
00331   configGroup.writeEntry( "ReadOnly", mReadOnly );
00332   configGroup.writeEntry( "FolderAttributes", mFolderAttributes );
00333 
00334   // StatusChangedLocally is always false, as we use UIDStatusChangedLocally now
00335   configGroup.writeEntry( "StatusChangedLocally", false );
00336   QStringList uidsToWrite;
00337   for( std::set<ulong>::iterator it = mUIDsOfLocallyChangedStatuses.begin();
00338        it != mUIDsOfLocallyChangedStatuses.end(); it++ ) {
00339     uidsToWrite.append( QString::number( (*it) ) );
00340   }
00341   configGroup.writeEntry( "UIDStatusChangedLocally", uidsToWrite );
00342   if ( !mImapPathCreation.isEmpty() ) {
00343     if ( mImapPath.isEmpty() ) {
00344       configGroup.writeEntry( "ImapPathCreation", mImapPathCreation );
00345     } else {
00346       configGroup.deleteEntry( "ImapPathCreation" );
00347     }
00348   }
00349   if ( !mDeletedUIDsSinceLastSync.isEmpty() ) {
00350       QValueList<ulong> uids = mDeletedUIDsSinceLastSync.keys();
00351       QStringList uidstrings;
00352       for( QValueList<ulong>::iterator it = uids.begin(); it != uids.end(); it++ ) {
00353           uidstrings.append(  QString::number( (*it) ) );
00354       }
00355       configGroup.writeEntry( "UIDSDeletedSinceLastSync", uidstrings );
00356 #if MAIL_LOSS_DEBUGGING
00357       kdDebug( 5006 ) << "WRITING OUT UIDSDeletedSinceLastSync in: " << folder( )->prettyURL( ) << endl << uidstrings << endl;
00358 #endif
00359   } else {
00360     configGroup.deleteEntry( "UIDSDeletedSinceLastSync" );
00361   }
00362   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
00363   KMFolderMaildir::writeConfig();
00364 }
00365 
00366 void KMFolderCachedImap::writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig()
00367 {
00368   KConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
00369   if ( !folder()->noContent() )
00370   {
00371     configGroup.writeEntry( "AnnotationFolderTypeChanged", mAnnotationFolderTypeChanged );
00372     configGroup.writeEntry( "Annotation-FolderType", mAnnotationFolderType );
00373     configGroup.writeEntry( "IncidencesForChanged", mIncidencesForChanged );
00374     configGroup.writeEntry( "IncidencesFor", incidencesForToString( mIncidencesFor ) );
00375     configGroup.writeEntry( "AlarmsBlocked", mAlarmsBlocked );
00376     configGroup.writeEntry( "SharedSeenFlags", mSharedSeenFlags );
00377     configGroup.writeEntry( "SharedSeenFlagsChanged", mSharedSeenFlagsChanged );
00378     configGroup.writeEntry( "UserRights", mUserRights );
00379 
00380     configGroup.deleteEntry( "StorageQuotaUsage");
00381     configGroup.deleteEntry( "StorageQuotaRoot");
00382     configGroup.deleteEntry( "StorageQuotaLimit");
00383 
00384     if ( mQuotaInfo.isValid() ) {
00385       if ( mQuotaInfo.current().isValid() ) {
00386         configGroup.writeEntry( "StorageQuotaUsage", mQuotaInfo.current().toInt() );
00387       }
00388       if ( mQuotaInfo.max().isValid() ) {
00389         configGroup.writeEntry( "StorageQuotaLimit", mQuotaInfo.max().toInt() );
00390       }
00391       configGroup.writeEntry( "StorageQuotaRoot", mQuotaInfo.root() );
00392     }
00393   }
00394 }
00395 
00396 int KMFolderCachedImap::create()
00397 {
00398   int rc = KMFolderMaildir::create();
00399   // FIXME why the below? - till
00400   readConfig();
00401   mUnreadMsgs = -1;
00402   return rc;
00403 }
00404 
00405 void KMFolderCachedImap::remove()
00406 {
00407   mFolderRemoved = true;
00408 
00409   QString part1 = folder()->path() + "/." + dotEscape(name());
00410   QString uidCacheFile = part1 + ".uidcache";
00411   // This is the account folder of an account that was just removed
00412   // When this happens, be sure to delete all traces of the cache
00413   if( QFile::exists(uidCacheFile) )
00414     unlink( QFile::encodeName( uidCacheFile ) );
00415 
00416   FolderStorage::remove();
00417 }
00418 
00419 QString KMFolderCachedImap::uidCacheLocation() const
00420 {
00421   QString sLocation(folder()->path());
00422   if (!sLocation.isEmpty()) sLocation += '/';
00423   return sLocation + '.' + dotEscape(fileName()) + ".uidcache";
00424 }
00425 
00426 int KMFolderCachedImap::readUidCache()
00427 {
00428   QFile uidcache( uidCacheLocation() );
00429   if( uidcache.open( IO_ReadOnly ) ) {
00430     char buf[1024];
00431     int len = uidcache.readLine( buf, sizeof(buf) );
00432     if( len > 0 ) {
00433       int cacheVersion;
00434       sscanf( buf, "# KMail-UidCache V%d\n",  &cacheVersion );
00435       if( cacheVersion == UIDCACHE_VERSION ) {
00436         len = uidcache.readLine( buf, sizeof(buf) );
00437         if( len > 0 ) {
00438           setUidValidity( QString::fromLocal8Bit(buf).stripWhiteSpace() );
00439           len = uidcache.readLine( buf, sizeof(buf) );
00440           if( len > 0 ) {
00441 #if MAIL_LOSS_DEBUGGING
00442             kdDebug(5006) << "Reading in last uid from cache: " << QString::fromLocal8Bit(buf).stripWhiteSpace() << " in " << folder()->prettyURL() << endl;
00443 #endif
00444             // load the last known highest uid from the on disk cache
00445             setLastUid( QString::fromLocal8Bit(buf).stripWhiteSpace().toULong() );
00446             return 0;
00447           }
00448         }
00449       }
00450     }
00451   }
00452   return -1;
00453 }
00454 
00455 int KMFolderCachedImap::writeUidCache()
00456 {
00457   if( uidValidity().isEmpty() || uidValidity() == "INVALID" ) {
00458     // No info from the server yet, remove the file.
00459     if( QFile::exists( uidCacheLocation() ) )
00460       return unlink( QFile::encodeName( uidCacheLocation() ) );
00461     return 0;
00462   }
00463 #if MAIL_LOSS_DEBUGGING
00464   kdDebug(5006) << "Writing out UID cache lastuid: " << lastUid()  << " in: " << folder()->prettyURL() << endl;
00465 #endif
00466   QFile uidcache( uidCacheLocation() );
00467   if( uidcache.open( IO_WriteOnly ) ) {
00468     QTextStream str( &uidcache );
00469     str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl;
00470     str << uidValidity() << endl;
00471     str << lastUid() << endl;
00472     uidcache.flush();
00473     if ( uidcache.status() == IO_Ok ) {
00474       fsync( uidcache.handle() ); /* this is probably overkill */
00475       uidcache.close();
00476       if ( uidcache.status() == IO_Ok )
00477         return 0;
00478     }
00479   }
00480   KMessageBox::error( 0,
00481         i18n( "The UID cache file for folder %1 could not be written. There "
00482               "could be a problem with file system permission." ).arg( folder()->prettyURL() ) );
00483 
00484   return -1;
00485 }
00486 
00487 void KMFolderCachedImap::reloadUidMap()
00488 {
00489   //kdDebug(5006) << "Reloading Uid Map " << endl;
00490   uidMap.clear();
00491   open("reloadUdi");
00492   for( int i = 0; i < count(); ++i ) {
00493     KMMsgBase *msg = getMsgBase( i );
00494     if( !msg ) continue;
00495     ulong uid = msg->UID();
00496     //kdDebug(5006) << "Inserting: " << i << " with uid: " << uid << endl;
00497     uidMap.insert( uid, i );
00498   }
00499   close("reloadUdi");
00500   uidMapDirty = false;
00501 }
00502 
00503 KMMessage* KMFolderCachedImap::take(int idx)
00504 {
00505   uidMapDirty = true;
00506   rememberDeletion( idx );
00507   return KMFolderMaildir::take(idx);
00508 }
00509 
00510 void KMFolderCachedImap::takeTemporarily( int idx )
00511 {
00512   KMFolderMaildir::take( idx );
00513 }
00514 
00515 // Add a message without clearing it's X-UID field.
00516 int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail,
00517                                         int* index_return )
00518 {
00519   // Possible optimization: Only dirty if not filtered below
00520   ulong uid = msg->UID();
00521   if( uid != 0 ) {
00522     uidMapDirty = true;
00523   }
00524 
00525   KMFolderOpener openThis(folder(), "KMFolderCachedImap::addMsgInternal");
00526   int rc = openThis.openResult();
00527   if ( rc ) {
00528     kdDebug(5006) << k_funcinfo << "open: " << rc << " of folder: " << label() << endl;
00529     return rc;
00530   }
00531 
00532   // Add the message
00533   rc = KMFolderMaildir::addMsg(msg, index_return);
00534 
00535   if( newMail && ( imapPath() == "/INBOX/" ||
00536       ( (userRights() <= 0 || userRights() & ACLJobs::Administer)
00537       && (contentsType() == ContentsTypeMail || GlobalSettings::self()->filterGroupwareFolders()) ) ) )
00538   {
00539     // This is a new message. Filter it - maybe
00540     bool filter = false;
00541     if ( GlobalSettings::filterSourceFolders().isEmpty() ) {
00542       if ( imapPath() == "/INBOX/" )
00543         filter = true;
00544     } else {
00545       if ( GlobalSettings::filterSourceFolders().contains( folder()->id() ) )
00546         filter = true;
00547     }
00548     if ( filter )
00549       mAccount->processNewMsg( msg );
00550   }
00551 
00552   return rc;
00553 }
00554 
00555 /* Reimplemented from KMFolderMaildir */
00556 int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return)
00557 {
00558   if ( !canAddMsgNow( msg, index_return ) ) return 0;
00559   // Add it to storage
00560   int rc = KMFolderMaildir::addMsgInternal(msg, index_return, true /*stripUID*/);
00561   return rc;
00562 }
00563 
00564 void KMFolderCachedImap::rememberDeletion( int idx )
00565 {
00566   KMMsgBase *msg = getMsgBase( idx );
00567   assert(msg);
00568   long uid = msg->UID();
00569   assert(uid>=0);
00570   mDeletedUIDsSinceLastSync.insert(uid, 0);
00571   kdDebug(5006) << "Explicit delete of UID " << uid << " at index: " << idx << " in " << folder()->prettyURL() << endl;
00572 }
00573 
00574 /* Reimplemented from KMFolderMaildir */
00575 void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet)
00576 {
00577   uidMapDirty = true;
00578   rememberDeletion( idx );
00579   // Remove it from disk
00580   KMFolderMaildir::removeMsg(idx,imapQuiet);
00581 }
00582 
00583 bool KMFolderCachedImap::canRemoveFolder() const {
00584   // If this has subfolders it can't be removed
00585   if( folder() && folder()->child() && folder()->child()->count() > 0 )
00586     return false;
00587 
00588 #if 0
00589   // No special condition here, so let base class decide
00590   return KMFolderMaildir::canRemoveFolder();
00591 #endif
00592   return true;
00593 }
00594 
00595 /* Reimplemented from KMFolderDir */
00596 int KMFolderCachedImap::rename( const QString& aName,
00597                                 KMFolderDir* /*aParent*/ )
00598 {
00599   if ( account() == 0 || imapPath().isEmpty() ) {
00600     // This can happen when creating a folder and then renaming it without syncing before,
00601     // see https://issues.kolab.org/issue3658
00602     QString err = i18n("You must synchronize with the server before renaming IMAP folders.");
00603     KMessageBox::error( 0, err );
00604     return -1;
00605   }
00606 
00607   QString oldName = mAccount->renamedFolder( imapPath() );
00608   if ( oldName.isEmpty() ) oldName = name();
00609   if ( aName == oldName )
00610     // Stupid user trying to rename it to it's old name :)
00611     return 0;
00612 
00613   // Make the change appear to the user with setLabel, but we'll do the change
00614   // on the server during the next sync. The name() is the name at the time of
00615   // the last sync. Only rename if the new one is different. If it's the same,
00616   // don't rename, but also make sure the rename is reset, in the case of
00617   // A -> B -> A renames.
00618   if ( name() != aName )
00619     mAccount->addRenamedFolder( imapPath(), folder()->label(), aName );
00620   else
00621     mAccount->removeRenamedFolder( imapPath() );
00622 
00623   folder()->setLabel( aName );
00624   emit nameChanged(); // for kmailicalifaceimpl
00625 
00626   return 0;
00627 }
00628 
00629 KMFolder* KMFolderCachedImap::trashFolder() const
00630 {
00631   QString trashStr = account()->trash();
00632   return kmkernel->dimapFolderMgr()->findIdString( trashStr );
00633 }
00634 
00635 void KMFolderCachedImap::setLastUid( ulong uid )
00636 {
00637 #if MAIL_LOSS_DEBUGGING
00638   kdDebug(5006) << "Setting mLastUid to: " << uid  <<  " in " << folder()->prettyURL() << endl;
00639 #endif
00640   mLastUid = uid;
00641   if( uidWriteTimer == -1 )
00642     // Write in one minute
00643     uidWriteTimer = startTimer( 60000 );
00644 }
00645 
00646 void KMFolderCachedImap::timerEvent( QTimerEvent* )
00647 {
00648   killTimer( uidWriteTimer );
00649   uidWriteTimer = -1;
00650   if ( writeUidCache() == -1 )
00651     unlink( QFile::encodeName( uidCacheLocation() ) );
00652 }
00653 
00654 ulong KMFolderCachedImap::lastUid()
00655 {
00656   return mLastUid;
00657 }
00658 
00659 KMMsgBase* KMFolderCachedImap::findByUID( ulong uid )
00660 {
00661   bool mapReloaded = false;
00662   if( uidMapDirty ) {
00663     reloadUidMap();
00664     mapReloaded = true;
00665   }
00666 
00667   QMap<ulong,int>::Iterator it = uidMap.find( uid );
00668   if( it != uidMap.end() ) {
00669     KMMsgBase *msg = getMsgBase( *it );
00670 #if MAIL_LOSS_DEBUGGING
00671     kdDebug(5006) << "Folder: " << folder()->prettyURL() << endl;
00672     kdDebug(5006) << "UID " << uid << " is supposed to be in the map" << endl;
00673     kdDebug(5006) << "UID's index is to be " << *it << endl;
00674     kdDebug(5006) << "There is a message there? " << (msg != 0) << endl;
00675     if ( msg ) {
00676       kdDebug(5006) << "Its UID is: " << msg->UID() << endl;
00677     }
00678 #endif
00679 
00680     if( msg && msg->UID() == uid )
00681       return msg;
00682     kdDebug(5006) << "########## Didn't find uid: " << uid << "in cache athough it's supposed to be there!" << endl;
00683   } else {
00684 #if MAIL_LOSS_DEBUGGING
00685     kdDebug(5006) << "Didn't find uid: " << uid << "in cache!" << endl;
00686 #endif
00687   }
00688   // Not found by now
00689  // if( mapReloaded )
00690     // Not here then
00691     return 0;
00692   // There could be a problem in the maps. Rebuild them and try again
00693   reloadUidMap();
00694   it = uidMap.find( uid );
00695   if( it != uidMap.end() )
00696     // Since the uid map is just rebuilt, no need for the sanity check
00697     return getMsgBase( *it );
00698 #if MAIL_LOSS_DEBUGGING
00699   else
00700     kdDebug(5006) << "Reloaded, but stil didn't find uid: " << uid << endl;
00701 #endif
00702   // Then it's not here
00703   return 0;
00704 }
00705 
00706 // This finds and sets the proper account for this folder if it has
00707 // not been done
00708 KMAcctCachedImap *KMFolderCachedImap::account() const
00709 {
00710   if( (KMAcctCachedImap *)mAccount == 0 && kmkernel && kmkernel->acctMgr() ) {
00711     // Find the account
00712     mAccount = static_cast<KMAcctCachedImap *>( kmkernel->acctMgr()->findByName( name() ) );
00713   }
00714 
00715   return mAccount;
00716 }
00717 
00718 void KMFolderCachedImap::slotTroubleshoot()
00719 {
00720   const int rc = DImapTroubleShootDialog::run();
00721 
00722   if( rc == DImapTroubleShootDialog::RefreshCache ) {
00723     // Refresh cache
00724     if( !account() ) {
00725       KMessageBox::sorry( 0, i18n("No account setup for this folder.\n"
00726                                   "Please try running a sync before this.") );
00727       return;
00728     }
00729     QString str = i18n("Are you sure you want to refresh the IMAP cache of "
00730                        "the folder %1 and all its subfolders?\nThis will "
00731                        "remove all changes you have done locally to your "
00732                        "folders.").arg( label() );
00733     QString s1 = i18n("Refresh IMAP Cache");
00734     QString s2 = i18n("&Refresh");
00735     if( KMessageBox::warningContinueCancel( 0, str, s1, s2 ) ==
00736         KMessageBox::Continue )
00737       account()->invalidateIMAPFolders( this );
00738   } else {
00739     // Rebuild index file
00740     switch ( rc ) {
00741       case DImapTroubleShootDialog::ReindexAll:
00742       {
00743         KMFolderCachedImap *rootStorage = dynamic_cast<KMFolderCachedImap*>( account()->rootFolder() );
00744         if ( rootStorage )
00745           rootStorage->createIndexFromContentsRecursive();
00746         break;
00747       }
00748       case DImapTroubleShootDialog::ReindexCurrent:
00749         createIndexFromContents();
00750         break;
00751       case DImapTroubleShootDialog::ReindexRecursive:
00752         createIndexFromContentsRecursive();
00753         break;
00754       default:
00755         return;
00756     }
00757     KMessageBox::information( 0, i18n( "The index of this folder has been "
00758                                        "recreated." ) );
00759     writeIndex();
00760     kmkernel->getKMMainWidget()->folderSelected();
00761   }
00762 }
00763 
00764 void KMFolderCachedImap::serverSync( bool recurse, bool quotaOnly )
00765 {
00766   if( mSyncState != SYNC_STATE_INITIAL ) {
00767     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 ), QString::null, i18n("Reset && Sync"), KStdGuiItem::cancel() ) == KMessageBox::Yes ) {
00768       mSyncState = SYNC_STATE_INITIAL;
00769     } else return;
00770   }
00771 
00772   mRecurse = recurse;
00773   mQuotaOnly = quotaOnly;
00774   assert( account() );
00775 
00776   ProgressItem *progressItem = mAccount->mailCheckProgressItem();
00777   if ( progressItem ) {
00778     progressItem->reset();
00779     progressItem->setTotalItems( 100 );
00780   }
00781   mProgress = 0;
00782 
00783 #if 0
00784   if( mHoldSyncs ) {
00785     // All done for this folder.
00786     account()->mailCheckProgressItem()->setProgress( 100 );
00787     mProgress = 100; // all done
00788     newState( mProgress, i18n("Synchronization skipped"));
00789     mSyncState = SYNC_STATE_INITIAL;
00790     emit folderComplete( this, true );
00791     return;
00792   }
00793 #endif
00794   mTentativeHighestUid = 0; // reset, last sync could have been canceled
00795 
00796   serverSyncInternal();
00797 }
00798 
00799 QString KMFolderCachedImap::state2String( int state ) const
00800 {
00801   switch( state ) {
00802   case SYNC_STATE_INITIAL:             return "SYNC_STATE_INITIAL";
00803   case SYNC_STATE_GET_USERRIGHTS:      return "SYNC_STATE_GET_USERRIGHTS";
00804   case SYNC_STATE_PUT_MESSAGES:        return "SYNC_STATE_PUT_MESSAGES";
00805   case SYNC_STATE_UPLOAD_FLAGS:        return "SYNC_STATE_UPLOAD_FLAGS";
00806   case SYNC_STATE_CREATE_SUBFOLDERS:   return "SYNC_STATE_CREATE_SUBFOLDERS";
00807   case SYNC_STATE_LIST_SUBFOLDERS:     return "SYNC_STATE_LIST_SUBFOLDERS";
00808   case SYNC_STATE_LIST_NAMESPACES:     return "SYNC_STATE_LIST_NAMESPACES";
00809   case SYNC_STATE_LIST_SUBFOLDERS2:    return "SYNC_STATE_LIST_SUBFOLDERS2";
00810   case SYNC_STATE_DELETE_SUBFOLDERS:   return "SYNC_STATE_DELETE_SUBFOLDERS";
00811   case SYNC_STATE_LIST_MESSAGES:       return "SYNC_STATE_LIST_MESSAGES";
00812   case SYNC_STATE_DELETE_MESSAGES:     return "SYNC_STATE_DELETE_MESSAGES";
00813   case SYNC_STATE_GET_MESSAGES:        return "SYNC_STATE_GET_MESSAGES";
00814   case SYNC_STATE_EXPUNGE_MESSAGES:    return "SYNC_STATE_EXPUNGE_MESSAGES";
00815   case SYNC_STATE_HANDLE_INBOX:        return "SYNC_STATE_HANDLE_INBOX";
00816   case SYNC_STATE_TEST_ANNOTATIONS:    return "SYNC_STATE_TEST_ANNOTATIONS";
00817   case SYNC_STATE_GET_ANNOTATIONS:     return "SYNC_STATE_GET_ANNOTATIONS";
00818   case SYNC_STATE_SET_ANNOTATIONS:     return "SYNC_STATE_SET_ANNOTATIONS";
00819   case SYNC_STATE_GET_ACLS:            return "SYNC_STATE_GET_ACLS";
00820   case SYNC_STATE_SET_ACLS:            return "SYNC_STATE_SET_ACLS";
00821   case SYNC_STATE_GET_QUOTA:           return "SYNC_STATE_GET_QUOTA";
00822   case SYNC_STATE_FIND_SUBFOLDERS:     return "SYNC_STATE_FIND_SUBFOLDERS";
00823   case SYNC_STATE_SYNC_SUBFOLDERS:     return "SYNC_STATE_SYNC_SUBFOLDERS";
00824   case SYNC_STATE_RENAME_FOLDER:       return "SYNC_STATE_RENAME_FOLDER";
00825   case SYNC_STATE_CHECK_UIDVALIDITY:   return "SYNC_STATE_CHECK_UIDVALIDITY";
00826   case SYNC_STATE_CLOSE:               return "SYNC_STATE_CLOSE";
00827   case SYNC_STATE_GET_SUBFOLDER_QUOTA: return "SYNC_STATE_GET_SUBFOLDER_QUOTA";
00828   default:                             return "Unknown state";
00829   }
00830 }
00831 
00832 /*
00833   Progress calculation: each step is assigned a span. Initially the total is 100.
00834   But if we skip a step, don't increase the progress.
00835   This leaves more room for the step a with variable size (get_messages)
00836    connecting 5
00837    getuserrights 5
00838    rename 5
00839    check_uidvalidity 5
00840    create_subfolders 5
00841    put_messages 10 (but it can take a very long time, with many messages....)
00842    upload_flags 5
00843    list_subfolders 5
00844    list_subfolders2 0 (all local)
00845    delete_subfolders 5
00846    list_messages 10
00847    delete_messages 10
00848    expunge_messages 5
00849    get_messages variable (remaining-5) i.e. minimum 15.
00850    check_annotations 0 (rare)
00851    set_annotations 0 (rare)
00852    get_annotations 2
00853    set_acls 0 (rare)
00854    get_acls 3
00855 
00856   noContent folders have only a few of the above steps
00857   (permissions, and all subfolder stuff), so its steps should be given more span
00858 
00859  */
00860 
00861 // While the server synchronization is running, mSyncState will hold
00862 // the state that should be executed next
00863 void KMFolderCachedImap::serverSyncInternal()
00864 {
00865   // This is used to stop processing when we're about to exit
00866   // and the current job wasn't cancellable.
00867   // For user-requested abort, we'll use signalAbortRequested instead.
00868   if( kmkernel->mailCheckAborted() ) {
00869     resetSyncState();
00870     emit folderComplete( this, false );
00871     return;
00872   }
00873 
00874   //kdDebug(5006) << label() << ": " << state2String( mSyncState ) << endl;
00875   switch( mSyncState ) {
00876   case SYNC_STATE_INITIAL:
00877   {
00878     mProgress = 0;
00879     foldersForDeletionOnServer.clear();
00880     newState( mProgress, i18n("Synchronizing"));
00881 
00882     open("cachedimap");
00883     if ( !noContent() )
00884         mAccount->addLastUnreadMsgCount( this, countUnread() );
00885 
00886     // Connect to the server (i.e. prepare the slave)
00887     ImapAccountBase::ConnectionState cs = mAccount->makeConnection();
00888     if ( cs == ImapAccountBase::Error ) {
00889       // Cancelled by user, or slave can't start
00890       // kdDebug(5006) << "makeConnection said Error, aborting." << endl;
00891       // We stop here. We're already in SYNC_STATE_INITIAL for the next time.
00892       newState( mProgress, i18n( "Error connecting to server %1" ).arg( mAccount->host() ) );
00893       close("cachedimap");
00894       emit folderComplete(this, false);
00895       break;
00896     } else if ( cs == ImapAccountBase::Connecting ) {
00897       mAccount->setAnnotationCheckPassed( false );
00898       // kdDebug(5006) << "makeConnection said Connecting, waiting for signal." << endl;
00899       newState( mProgress, i18n("Connecting to %1").arg( mAccount->host() ) );
00900       // We'll wait for the connectionResult signal from the account.
00901       connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
00902                this, SLOT( slotConnectionResult(int, const QString&) ) );
00903       break;
00904     } else {
00905       // Connected
00906       // kdDebug(5006) << "makeConnection said Connected, proceeding." << endl;
00907       mSyncState = SYNC_STATE_GET_USERRIGHTS;
00908       // Fall through to next state
00909     }
00910   }
00911 
00912 
00913   case SYNC_STATE_GET_USERRIGHTS:
00914 
00915     // Now we have started the sync, emit changed() so that the folder tree can update the status
00916     emit changed();
00917     //kdDebug(5006) << "===== Syncing " << ( mImapPath.isEmpty() ? label() : mImapPath ) << endl;
00918 
00919     mSyncState = SYNC_STATE_RENAME_FOLDER;
00920 
00921     if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) {
00922       // Check the user's own rights. We do this every time in case they changed.
00923       mOldUserRights = mUserRights;
00924       newState( mProgress, i18n("Checking permissions"));
00925       connect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
00926                this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
00927       mAccount->getUserRights( folder(), imapPath() ); // after connecting, due to the INBOX case
00928       break;
00929     }
00930 
00931   case SYNC_STATE_RENAME_FOLDER:
00932   {
00933     mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
00934     // Returns the new name if the folder was renamed, empty otherwise.
00935     bool isResourceFolder = kmkernel->iCalIface().isStandardResourceFolder( folder() );
00936     QString newName = mAccount->renamedFolder( imapPath() );
00937     if ( !newName.isEmpty() && !folder()->isSystemFolder() && !isResourceFolder ) {
00938       newState( mProgress, i18n("Renaming folder") );
00939       CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this );
00940       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
00941       connect( job, SIGNAL( finished() ), this, SLOT( slotRenameFolderFinished() ) );
00942       job->start();
00943       break;
00944     }
00945   }
00946 
00947   case SYNC_STATE_CHECK_UIDVALIDITY:
00948     mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
00949     if( !mQuotaOnly && !noContent() ) {
00950       checkUidValidity();
00951       break;
00952     }
00953     // Else carry on
00954 
00955   case SYNC_STATE_CREATE_SUBFOLDERS:
00956     mSyncState = SYNC_STATE_PUT_MESSAGES;
00957     if ( !mQuotaOnly ) {
00958       createNewFolders();
00959       break;
00960     }
00961 
00962   case SYNC_STATE_PUT_MESSAGES:
00963     mSyncState = SYNC_STATE_UPLOAD_FLAGS;
00964     if( !mQuotaOnly && !noContent() ) {
00965       uploadNewMessages();
00966       break;
00967     }
00968     // Else carry on
00969   case SYNC_STATE_UPLOAD_FLAGS:
00970     mSyncState = SYNC_STATE_LIST_NAMESPACES;
00971     if( !mQuotaOnly && !noContent() ) {
00972        // We haven't downloaded messages yet, so we need to build the map.
00973        if( uidMapDirty )
00974          reloadUidMap();
00975        // Upload flags, unless we know from the ACL that we're not allowed
00976        // to do that or they did not change locally
00977        if ( mUserRights <= 0 || ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) {
00978          if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) {
00979            uploadFlags();
00980            break;
00981          } else {
00982            //kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl;
00983          }
00984        } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
00985          if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) {
00986            uploadSeenFlags();
00987            break;
00988          }
00989        }
00990     }
00991     // Else carry on
00992 
00993   case SYNC_STATE_LIST_NAMESPACES:
00994     if ( !mQuotaOnly && this == mAccount->rootFolder() ) {
00995       listNamespaces();
00996       break;
00997     }
00998     mSyncState = SYNC_STATE_LIST_SUBFOLDERS;
00999     // Else carry on
01000 
01001   case SYNC_STATE_LIST_SUBFOLDERS:
01002     newState( mProgress, i18n("Retrieving folderlist"));
01003     mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
01004     if ( !mQuotaOnly ) {
01005       if( !listDirectory() ) {
01006         mSyncState = SYNC_STATE_INITIAL;
01007         KMessageBox::error(0, i18n("Error while retrieving the folderlist"));
01008       }
01009       break;
01010     }
01011 
01012   case SYNC_STATE_LIST_SUBFOLDERS2:
01013     mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
01014     mProgress += 10;
01015     if ( !mQuotaOnly ) {
01016       newState( mProgress, i18n("Retrieving subfolders"));
01017       listDirectory2();
01018       break;
01019     }
01020 
01021   case SYNC_STATE_DELETE_SUBFOLDERS:
01022     mSyncState = SYNC_STATE_LIST_MESSAGES;
01023     if( !mQuotaOnly && !foldersForDeletionOnServer.isEmpty() ) {
01024       newState( mProgress, i18n("Deleting folders from server"));
01025       CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer,
01026                                                   CachedImapJob::tDeleteFolders, this );
01027       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
01028       connect( job, SIGNAL( finished() ), this, SLOT( slotFolderDeletionOnServerFinished() ) );
01029       job->start();
01030       break;
01031     }
01032     // Not needed, the next step emits newState very quick
01033     //newState( mProgress, i18n("No folders to delete from server"));
01034       // Carry on
01035 
01036   case SYNC_STATE_LIST_MESSAGES:
01037     mSyncState = SYNC_STATE_DELETE_MESSAGES;
01038     if( !mQuotaOnly && !noContent() ) {
01039       newState( mProgress, i18n("Retrieving message list"));
01040       listMessages();
01041       break;
01042     }
01043     // Else carry on
01044 
01045   case SYNC_STATE_DELETE_MESSAGES:
01046     mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
01047     if( !mQuotaOnly && !noContent() ) {
01048       if( deleteMessages() ) {
01049         // Fine, we will continue with the next state
01050       } else {
01051         // No messages to delete, skip to GET_MESSAGES
01052         newState( mProgress, i18n("No messages to delete..."));
01053         mSyncState = SYNC_STATE_GET_MESSAGES;
01054         serverSyncInternal();
01055       }
01056       break;
01057     }
01058     // Else carry on
01059 
01060   case SYNC_STATE_EXPUNGE_MESSAGES:
01061     mSyncState = SYNC_STATE_GET_MESSAGES;
01062     if( !mQuotaOnly && !noContent() ) {
01063       newState( mProgress, i18n("Expunging deleted messages"));
01064       CachedImapJob *job = new CachedImapJob( QString::null,
01065                                               CachedImapJob::tExpungeFolder, this );
01066       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
01067       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01068       job->start();
01069       break;
01070     }
01071     // Else carry on
01072 
01073   case SYNC_STATE_GET_MESSAGES:
01074     mSyncState = SYNC_STATE_HANDLE_INBOX;
01075     if( !mQuotaOnly && !noContent() ) {
01076       if( !mMsgsForDownload.isEmpty() ) {
01077         newState( mProgress, i18n("Retrieving new messages"));
01078         CachedImapJob *job = new CachedImapJob( mMsgsForDownload,
01079                                                 CachedImapJob::tGetMessage,
01080                                                 this );
01081         connect( job, SIGNAL( progress(unsigned long, unsigned long) ),
01082                  this, SLOT( slotProgress(unsigned long, unsigned long) ) );
01083         connect( job, SIGNAL( finished() ), this, SLOT( slotUpdateLastUid() ) );
01084         connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01085         job->start();
01086         mMsgsForDownload.clear();
01087         break;
01088       } else {
01089         newState( mProgress, i18n("No new messages from server"));
01090         /* There were no messages to download, but it could be that we uploaded some
01091            which we didn't need to download again because we already knew the uid.
01092            Now that we are sure there is nothing to download, and everything that had
01093            to be deleted on the server has been deleted, adjust our local notion of the
01094            highes uid seen thus far. */
01095         slotUpdateLastUid();
01096         if( mLastUid == 0 && uidWriteTimer == -1 ) {
01097           // This is probably a new and empty folder. Write the UID cache
01098           if ( writeUidCache() == -1 ) {
01099             resetSyncState();
01100             emit folderComplete( this, false );
01101             return;
01102           }
01103         }
01104       }
01105     }
01106 
01107     // Else carry on
01108 
01109   case SYNC_STATE_HANDLE_INBOX:
01110     // Wrap up the 'download emails' stage. We always end up at 95 here.
01111     mProgress = 95;
01112     mSyncState = SYNC_STATE_TEST_ANNOTATIONS;
01113 
01114   #define KOLAB_FOLDERTEST "/vendor/kolab/folder-test"
01115   case SYNC_STATE_TEST_ANNOTATIONS:
01116     mSyncState = SYNC_STATE_GET_ANNOTATIONS;
01117     // The first folder with user rights to write annotations
01118     if( !mQuotaOnly && !mAccount->annotationCheckPassed() &&
01119          ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) )
01120          && !imapPath().isEmpty() && imapPath() != "/" ) {
01121       kdDebug(5006) << "Setting test attribute on folder: "<< folder()->prettyURL() << endl;
01122       newState( mProgress, i18n("Checking annotation support"));
01123 
01124       KURL url = mAccount->getUrl();
01125       url.setPath( imapPath() );
01126       KMail::AnnotationList annotations; // to be set
01127 
01128       KMail::AnnotationAttribute attr( KOLAB_FOLDERTEST, "value.shared", "true" );
01129       annotations.append( attr );
01130 
01131       kdDebug(5006) << "Setting test attribute to "<< url << endl;
01132       KIO::Job* job = AnnotationJobs::multiSetAnnotation( mAccount->slave(),
01133           url, annotations );
01134       ImapAccountBase::jobData jd( url.url(), folder() );
01135       jd.cancellable = true; // we can always do so later
01136       mAccount->insertJob(job, jd);
01137        connect(job, SIGNAL(result(KIO::Job *)),
01138               SLOT(slotTestAnnotationResult(KIO::Job *)));
01139       break;
01140     }
01141 
01142   case SYNC_STATE_GET_ANNOTATIONS: {
01143 #define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type"
01144 #define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for"
01145 #define KOLAB_SHAREDSEEN "/vendor/cmu/cyrus-imapd/sharedseen"
01146 //#define KOLAB_FOLDERTYPE "/comment"  //for testing, while cyrus-imap doesn't support /vendor/*
01147     mSyncState = SYNC_STATE_SET_ANNOTATIONS;
01148 
01149     bool needToGetInitialAnnotations = false;
01150     if ( !mQuotaOnly && !noContent() ) {
01151       // for a folder we didn't create ourselves: get annotation from server
01152       if ( mAnnotationFolderType == "FROMSERVER" ) {
01153         needToGetInitialAnnotations = true;
01154         mAnnotationFolderType = QString::null;
01155       } else {
01156         updateAnnotationFolderType();
01157       }
01158     }
01159 
01160     // First retrieve the annotation, so that we know we have to set it if it's not set.
01161     // On the other hand, if the user changed the contentstype, there's no need to get first.
01162     if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() &&
01163         ( kmkernel->iCalIface().isEnabled() || needToGetInitialAnnotations ) ) {
01164       QStringList annotations; // list of annotations to be fetched
01165       if ( !mAnnotationFolderTypeChanged || mAnnotationFolderType.isEmpty() )
01166         annotations << KOLAB_FOLDERTYPE;
01167       if ( !mIncidencesForChanged )
01168         annotations << KOLAB_INCIDENCESFOR;
01169       if ( !mSharedSeenFlagsChanged )
01170         annotations << KOLAB_SHAREDSEEN;
01171       if ( !annotations.isEmpty() ) {
01172         newState( mProgress, i18n("Retrieving annotations"));
01173         KURL url = mAccount->getUrl();
01174         url.setPath( imapPath() );
01175         AnnotationJobs::MultiGetAnnotationJob* job =
01176           AnnotationJobs::multiGetAnnotation( mAccount->slave(), url, annotations );
01177         ImapAccountBase::jobData jd( url.url(), folder() );
01178         jd.cancellable = true;
01179         mAccount->insertJob(job, jd);
01180 
01181         connect( job, SIGNAL(annotationResult(const QString&, const QString&, bool)),
01182                  SLOT(slotAnnotationResult(const QString&, const QString&, bool)) );
01183         connect( job, SIGNAL(result(KIO::Job *)),
01184                  SLOT(slotGetAnnotationResult(KIO::Job *)) );
01185         break;
01186       }
01187     }
01188   } // case
01189   case SYNC_STATE_SET_ANNOTATIONS:
01190 
01191     mSyncState = SYNC_STATE_SET_ACLS;
01192     if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() &&
01193          ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) {
01194       newState( mProgress, i18n("Setting annotations"));
01195       KURL url = mAccount->getUrl();
01196       url.setPath( imapPath() );
01197       KMail::AnnotationList annotations; // to be set
01198       if ( mAnnotationFolderTypeChanged && !mAnnotationFolderType.isEmpty() ) {
01199         KMail::AnnotationAttribute attr( KOLAB_FOLDERTYPE, "value.shared", mAnnotationFolderType );
01200         annotations.append( attr );
01201         kdDebug(5006) << "Setting folder-type annotation for " << label() << " to " << mAnnotationFolderType << endl;
01202       }
01203       if ( mIncidencesForChanged ) {
01204         const QString val = incidencesForToString( mIncidencesFor );
01205         KMail::AnnotationAttribute attr( KOLAB_INCIDENCESFOR, "value.shared", val );
01206         annotations.append( attr );
01207         kdDebug(5006) << "Setting incidences-for annotation for " << label() << " to " << val << endl;
01208       }
01209       if ( mSharedSeenFlagsChanged ) {
01210         const QString val = mSharedSeenFlags ? "true" : "false";
01211         KMail::AnnotationAttribute attr( KOLAB_SHAREDSEEN, "value.shared", val );
01212         annotations.append( attr );
01213         kdDebug(5006) << k_funcinfo << "Setting sharedseen annotation for " << label() << " to " << val << endl;
01214       }
01215       if ( !annotations.isEmpty() ) {
01216         KIO::Job* job =
01217           AnnotationJobs::multiSetAnnotation( mAccount->slave(), url, annotations );
01218         ImapAccountBase::jobData jd( url.url(), folder() );
01219         jd.cancellable = true; // we can always do so later
01220         mAccount->insertJob(job, jd);
01221 
01222         connect(job, SIGNAL(annotationChanged( const QString&, const QString&, const QString& ) ),
01223                 SLOT( slotAnnotationChanged( const QString&, const QString&, const QString& ) ));
01224         connect(job, SIGNAL(result(KIO::Job *)),
01225                 SLOT(slotSetAnnotationResult(KIO::Job *)));
01226         break;
01227       }
01228     }
01229 
01230   case SYNC_STATE_SET_ACLS:
01231     mSyncState = SYNC_STATE_GET_ACLS;
01232 
01233     if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() &&
01234       ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) {
01235       bool hasChangedACLs = false;
01236       ACLList::ConstIterator it = mACLList.begin();
01237       for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) {
01238         hasChangedACLs = (*it).changed;
01239       }
01240       if ( hasChangedACLs ) {
01241         newState( mProgress, i18n("Setting permissions"));
01242         KURL url = mAccount->getUrl();
01243         url.setPath( imapPath() );
01244         KIO::Job* job = KMail::ACLJobs::multiSetACL( mAccount->slave(), url, mACLList );
01245         ImapAccountBase::jobData jd( url.url(), folder() );
01246         mAccount->insertJob(job, jd);
01247 
01248         connect(job, SIGNAL(result(KIO::Job *)),
01249                 SLOT(slotMultiSetACLResult(KIO::Job *)));
01250         connect(job, SIGNAL(aclChanged( const QString&, int )),
01251                 SLOT(slotACLChanged( const QString&, int )) );
01252         break;
01253       }
01254     }
01255 
01256   case SYNC_STATE_GET_ACLS:
01257     mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
01258 
01259     if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) {
01260       newState( mProgress, i18n( "Retrieving permissions" ) );
01261       mAccount->getACL( folder(), mImapPath );
01262       connect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
01263                this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
01264       break;
01265     }
01266   case SYNC_STATE_FIND_SUBFOLDERS:
01267     {
01268       mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
01269       mSomeSubFolderCloseToQuotaChanged = false;
01270       buildSubFolderList();
01271     }
01272 
01273     // Carry on
01274   case SYNC_STATE_SYNC_SUBFOLDERS:
01275     syncNextSubFolder( false );
01276     break;
01277   case SYNC_STATE_GET_SUBFOLDER_QUOTA:
01278 
01279     // Sync the subfolders again, so that the quota information is updated for all. This state is
01280     // only entered if the close to quota property of one subfolder changed in the previous state.
01281     syncNextSubFolder( true );
01282     break;
01283   case SYNC_STATE_GET_QUOTA:
01284     mSyncState = SYNC_STATE_CLOSE;
01285     if( !noContent() && mAccount->hasQuotaSupport() ) {
01286       mProgress = 98;
01287       newState( mProgress, i18n("Getting quota information"));
01288       KURL url = mAccount->getUrl();
01289       url.setPath( imapPath() );
01290       KIO::Job* job = KMail::QuotaJobs::getStorageQuota( mAccount->slave(), url );
01291       ImapAccountBase::jobData jd( url.url(), folder() );
01292       mAccount->insertJob(job, jd);
01293       connect( job, SIGNAL( storageQuotaResult( const QuotaInfo& ) ),
01294           SLOT( slotStorageQuotaResult( const QuotaInfo& ) ) );
01295       connect( job, SIGNAL(result(KIO::Job *)),
01296           SLOT(slotQuotaResult(KIO::Job *)) );
01297       break;
01298     }
01299   case SYNC_STATE_CLOSE:
01300     {
01301       mProgress = 100; // all done
01302       newState( mProgress, i18n("Synchronization done"));
01303       KURL url = mAccount->getUrl();
01304       url.setPath( imapPath() );
01305       kmkernel->iCalIface().folderSynced( folder(), url );
01306 
01307       mSyncState = SYNC_STATE_INITIAL;
01308       mAccount->addUnreadMsgCount( this, countUnread() ); // before closing
01309       close( "cachedimap" );
01310       emit changed();
01311       emit folderComplete( this, true );
01312     }
01313     break;
01314 
01315   default:
01316     kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
01317                   << mSyncState << endl;
01318   }
01319 }
01320 
01321 void KMFolderCachedImap::syncNextSubFolder( bool secondSync )
01322 {
01323   if( mCurrentSubfolder ) {
01324     disconnectSubFolderSignals();
01325   }
01326 
01327   if( mSubfoldersForSync.isEmpty() ) {
01328 
01329     // Sync finished, and a close to quota property of an subfolder changed, therefore go into
01330     // the SYNC_STATE_GET_SUBFOLDER_QUOTA state and sync again
01331     if ( mSomeSubFolderCloseToQuotaChanged && mRecurse && !secondSync ) {
01332       buildSubFolderList();
01333       mSyncState = SYNC_STATE_GET_SUBFOLDER_QUOTA;
01334       serverSyncInternal();
01335     }
01336 
01337     else {
01338 
01339       // Quota checking has to come after syncing subfolder, otherwise the quota information would
01340       // be outdated, since the subfolders can change in size during the syncing.
01341       // https://issues.kolab.org/issue4066
01342       mSyncState = SYNC_STATE_GET_QUOTA;
01343       serverSyncInternal();
01344     }
01345   } else {
01346     mCurrentSubfolder = mSubfoldersForSync.front();
01347     mSubfoldersForSync.pop_front();
01348     if ( mCurrentSubfolder ) {
01349       connect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
01350                this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
01351       connect( mCurrentSubfolder, SIGNAL( closeToQuotaChanged() ),
01352                this, SLOT( slotSubFolderCloseToQuotaChanged() ) );
01353 
01354       //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
01355       assert( !mCurrentSubfolder->imapPath().isEmpty() );
01356       mCurrentSubfolder->setAccount( account() );
01357       const bool recurse = mCurrentSubfolder->noChildren() ? false : true;
01358       const bool quotaOnly = secondSync || mQuotaOnly;
01359       mCurrentSubfolder->serverSync( recurse, quotaOnly );
01360     }
01361     else {
01362       // mCurrentSubfolder is empty, probably because it was deleted while syncing. Go on with the
01363       // next subfolder instead.
01364       syncNextSubFolder( secondSync );
01365     }
01366   }
01367 }
01368 
01369 void KMFolderCachedImap::buildSubFolderList()
01370 {
01371   mSubfoldersForSync.clear();
01372   mCurrentSubfolder = 0;
01373   if( folder() && folder()->child() ) {
01374     KMFolderNode *node = folder()->child()->first();
01375     while( node ) {
01376       if( !node->isDir() ) {
01377         KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01378         // Only sync folders that have been accepted by the server
01379         if ( !storage->imapPath().isEmpty()
01380              // and that were not just deleted from it
01381              && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) {
01382           mSubfoldersForSync << storage;
01383         } else {
01384           kdDebug(5006) << "Do not add " << storage->label()
01385                         << " to synclist" << endl;
01386         }
01387       }
01388       node = folder()->child()->next();
01389     }
01390   }
01391 
01392   if ( !mRecurse ) // "check mail for this folder" only
01393     mSubfoldersForSync.clear();
01394 }
01395 
01396 void KMFolderCachedImap::disconnectSubFolderSignals()
01397 {
01398   disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
01399               this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
01400   disconnect( mCurrentSubfolder, SIGNAL( closeToQuotaChanged() ),
01401               this, SLOT( slotSubFolderCloseToQuotaChanged() ) );
01402   mCurrentSubfolder = 0;
01403 }
01404 
01405 /* Connected to the imap account's connectionResult signal.
01406    Emitted when the slave connected or failed to connect.
01407 */
01408 void KMFolderCachedImap::slotConnectionResult( int errorCode, const QString& errorMsg )
01409 {
01410   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01411               this, SLOT( slotConnectionResult(int, const QString&) ) );
01412   if ( !errorCode ) {
01413     // Success
01414     mSyncState = SYNC_STATE_GET_USERRIGHTS;
01415     mProgress += 5;
01416     serverSyncInternal();
01417   } else {
01418     // Error (error message already shown by the account)
01419     newState( mProgress, KIO::buildErrorString( errorCode, errorMsg ));
01420     emit folderComplete(this, false);
01421   }
01422 }
01423 
01424 /* find new messages (messages without a UID) */
01425 QValueList<unsigned long> KMFolderCachedImap::findNewMessages()
01426 {
01427   QValueList<unsigned long> result;
01428   for( int i = 0; i < count(); ++i ) {
01429     KMMsgBase *msg = getMsgBase( i );
01430     if( !msg ) continue; /* what goes on if getMsg() returns 0? */
01431     if ( msg->UID() == 0 )
01432       result.append( msg->getMsgSerNum() );
01433   }
01434   return result;
01435 }
01436 
01437 /* Upload new messages to server */
01438 void KMFolderCachedImap::uploadNewMessages()
01439 {
01440   QValueList<unsigned long> newMsgs = findNewMessages();
01441   if( !newMsgs.isEmpty() ) {
01442     if ( mUserRights <= 0 || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) {
01443       newState( mProgress, i18n("Uploading messages to server"));
01444       CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
01445       connect( job, SIGNAL( progress( unsigned long, unsigned long) ),
01446                this, SLOT( slotPutProgress(unsigned long, unsigned long) ) );
01447       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01448       job->start();
01449       return;
01450     } else {
01451       KMCommand *command = rescueUnsyncedMessages();
01452       connect( command, SIGNAL( completed( KMCommand * ) ),
01453                this, SLOT( serverSyncInternal() ) );
01454     }
01455   } else { // nothing to upload
01456     if ( mUserRights != mOldUserRights && (mOldUserRights & KMail::ACLJobs::Insert)
01457          && !(mUserRights & KMail::ACLJobs::Insert) ) {
01458       // write access revoked
01459       KMessageBox::information( 0, i18n("<p>Your access rights to folder <b>%1</b> have been restricted, "
01460           "it will no longer be possible to add messages to this folder.</p>").arg( folder()->prettyURL() ),
01461           i18n("Acces rights revoked"), "KMailACLRevocationNotification" );
01462     }
01463   }
01464   newState( mProgress, i18n("No messages to upload to server"));
01465   serverSyncInternal();
01466 }
01467 
01468 /* Progress info during uploadNewMessages */
01469 void KMFolderCachedImap::slotPutProgress( unsigned long done, unsigned long total )
01470 {
01471   // (going from mProgress to mProgress+10)
01472   int progressSpan = 10;
01473   newState( mProgress + (progressSpan * done) / total, QString::null );
01474   if ( done == total ) // we're done
01475     mProgress += progressSpan;
01476 }
01477 
01478 /* Upload message flags to server */
01479 void KMFolderCachedImap::uploadFlags()
01480 {
01481   if ( !uidMap.isEmpty() ) {
01482     mStatusFlagsJobs = 0;
01483     newState( mProgress, i18n("Uploading status of messages to server"));
01484 
01485     // FIXME DUPLICATED FROM KMFOLDERIMAP
01486     QMap< QString, QStringList > groups;
01487     //open(); //already done
01488     for( int i = 0; i < count(); ++i ) {
01489       KMMsgBase* msg = getMsgBase( i );
01490       if( !msg || msg->UID() == 0 )
01491         // Either not a valid message or not one that is on the server yet
01492         continue;
01493       if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
01494            && !mStatusChangedLocally ) {
01495         // This message has not had its status changed locally
01496         continue;
01497       }
01498 
01499       QString flags = KMFolderImap::statusToFlags(msg->status(), mPermanentFlags);
01500       // Collect uids for each typem of flags.
01501       QString uid;
01502       uid.setNum( msg->UID() );
01503       groups[flags].append(uid);
01504     }
01505     QMapIterator< QString, QStringList > dit;
01506     for( dit = groups.begin(); dit != groups.end(); ++dit ) {
01507       QCString flags = dit.key().latin1();
01508       QStringList sets = KMFolderImap::makeSets( (*dit), true );
01509       mStatusFlagsJobs += sets.count(); // ### that's not in kmfolderimap....
01510       // Send off a status setting job for each set.
01511       for( QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01512         QString imappath = imapPath() + ";UID=" + ( *slit );
01513         mAccount->setImapStatus(folder(), imappath, flags);
01514       }
01515     }
01516     // FIXME END DUPLICATED FROM KMFOLDERIMAP
01517 
01518     if ( mStatusFlagsJobs ) {
01519       connect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01520                this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01521       return;
01522     }
01523   }
01524   newState( mProgress, i18n("No messages to upload to server"));
01525   serverSyncInternal();
01526 }
01527 
01528 void KMFolderCachedImap::uploadSeenFlags()
01529 {
01530   if ( !uidMap.isEmpty() ) {
01531     mStatusFlagsJobs = 0;
01532     newState( mProgress, i18n("Uploading status of messages to server"));
01533 
01534     QValueList<ulong> seenUids, unseenUids;
01535     for( int i = 0; i < count(); ++i ) {
01536       KMMsgBase* msg = getMsgBase( i );
01537       if( !msg || msg->UID() == 0 )
01538         // Either not a valid message or not one that is on the server yet
01539         continue;
01540 
01541       if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
01542            && !mStatusChangedLocally ) {
01543         // This message has not had its status changed locally
01544         continue;
01545       }
01546 
01547       if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
01548         seenUids.append( msg->UID() );
01549       else
01550         unseenUids.append( msg->UID() );
01551     }
01552     if ( !seenUids.isEmpty() ) {
01553       QStringList sets = KMFolderImap::makeSets( seenUids, true );
01554       mStatusFlagsJobs += sets.count();
01555       for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
01556         QString imappath = imapPath() + ";UID=" + ( *it );
01557         mAccount->setImapSeenStatus( folder(), imappath, true );
01558       }
01559     }
01560     if ( !unseenUids.isEmpty() ) {
01561       QStringList sets = KMFolderImap::makeSets( unseenUids, true );
01562       mStatusFlagsJobs += sets.count();
01563       for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
01564         QString imappath = imapPath() + ";UID=" + ( *it );
01565         mAccount->setImapSeenStatus( folder(), imappath, false );
01566       }
01567     }
01568 
01569     if ( mStatusFlagsJobs ) {
01570       connect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01571                this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01572       return;
01573     }
01574   }
01575   newState( mProgress, i18n("No messages to upload to server"));
01576   serverSyncInternal();
01577 }
01578 
01579 void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const QString&, bool cont)
01580 {
01581   if ( mSyncState == SYNC_STATE_INITIAL ){
01582       //kdDebug(5006) << "IMAP status changed but reset " << endl;
01583       return; // we were reset
01584   }
01585   //kdDebug(5006) << "IMAP status changed for folder: " << folder->prettyURL() << endl;
01586   if ( folder->storage() == this ) {
01587     --mStatusFlagsJobs;
01588     if ( mStatusFlagsJobs == 0 || !cont ) // done or aborting
01589       disconnect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01590                   this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01591     if ( mStatusFlagsJobs == 0 && cont ) {
01592       mProgress += 5;
01593       serverSyncInternal();
01594       //kdDebug(5006) << "Proceeding with mailcheck." << endl;
01595     }
01596   }
01597 }
01598 
01599 // This is not perfect, what if the status didn't really change? Oh well ...
01600 void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle)
01601 {
01602   KMFolderMaildir::setStatus( idx, status, toggle );
01603   const KMMsgBase *msg = getMsgBase( idx );
01604   Q_ASSERT( msg );
01605   if ( msg )
01606     mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
01607 }
01608 
01609 void KMFolderCachedImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01610 {
01611   KMFolderMaildir::setStatus(ids, status, toggle);
01612   for (QValueList<int>::iterator it = ids.begin(); it != ids.end(); it++ ) {
01613     const KMMsgBase *msg = getMsgBase( *it );
01614     Q_ASSERT( msg );
01615     if ( msg )
01616       mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
01617   }
01618 }
01619 
01620 /* Upload new folders to server */
01621 void KMFolderCachedImap::createNewFolders()
01622 {
01623   QValueList<KMFolderCachedImap*> newFolders = findNewFolders();
01624   //kdDebug(5006) << label() << " createNewFolders:" << newFolders.count() << " new folders." << endl;
01625   if( !newFolders.isEmpty() ) {
01626     newState( mProgress, i18n("Creating subfolders on server"));
01627     CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this );
01628     connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
01629     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01630     job->start();
01631   } else {
01632     serverSyncInternal();
01633   }
01634 }
01635 
01636 QValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
01637 {
01638   QValueList<KMFolderCachedImap*> newFolders;
01639   if( folder() && folder()->child() ) {
01640     KMFolderNode *node = folder()->child()->first();
01641     while( node ) {
01642       if( !node->isDir() ) {
01643         if( static_cast<KMFolder*>(node)->folderType() != KMFolderTypeCachedImap ) {
01644           kdError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
01645                         << node->name() << " is not an IMAP folder\n";
01646           node = folder()->child()->next();
01647           assert(0);
01648         }
01649         KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01650         if( folder->imapPath().isEmpty() ) {
01651           newFolders << folder;
01652         }
01653       }
01654       node = folder()->child()->next();
01655     }
01656   }
01657   return newFolders;
01658 }
01659 
01660 bool KMFolderCachedImap::deleteMessages()
01661 {
01662   /* Delete messages from cache that are gone from the server */
01663   QPtrList<KMMessage> msgsForDeletion;
01664 
01665   // It is not possible to just go over all indices and remove
01666   // them one by one because the index list can get resized under
01667   // us. So use msg pointers instead
01668 
01669   QStringList uids;
01670   QMap<ulong,int>::const_iterator it = uidMap.constBegin();
01671   for( ; it != uidMap.end(); it++ ) {
01672     ulong uid ( it.key() );
01673     if( uid!=0 && !uidsOnServer.find( uid ) ) {
01674       uids << QString::number( uid );
01675       msgsForDeletion.append( getMsg( *it ) );
01676     }
01677   }
01678 
01679   if( !msgsForDeletion.isEmpty() ) {
01680 #if MAIL_LOSS_DEBUGGING
01681       if ( KMessageBox::warningYesNo(
01682              0, i18n( "<qt><p>Mails on the server in folder <b>%1</b> were deleted. "
01683                  "Do you want to delete them locally?<br>UIDs: %2</p></qt>" )
01684              .arg( folder()->prettyURL() ).arg( uids.join(",") ) ) == KMessageBox::Yes )
01685 #endif
01686         removeMsg( msgsForDeletion );
01687   }
01688 
01689   if ( mUserRights > 0 && !( mUserRights & KMail::ACLJobs::Delete ) )
01690     return false;
01691 
01692   /* Delete messages from the server that we dont have anymore */
01693   if( !uidsForDeletionOnServer.isEmpty() ) {
01694     newState( mProgress, i18n("Deleting removed messages from server"));
01695     QStringList sets = KMFolderImap::makeSets( uidsForDeletionOnServer, true );
01696     uidsForDeletionOnServer.clear();
01697     kdDebug(5006) << "Deleting " << sets.count() << " sets of messages from server folder " << imapPath() << endl;
01698     CachedImapJob *job = new CachedImapJob( sets, CachedImapJob::tDeleteMessage, this );
01699     connect( job, SIGNAL( result(KMail::FolderJob *) ),
01700              this, SLOT( slotDeleteMessagesResult(KMail::FolderJob *) ) );
01701     job->start();
01702     return true;
01703   } else {
01704 
01705     // Nothing to delete on the server, make sure the map is clear again.
01706     // Normally this wouldn't be necessary, but there can be stale maps because of
01707     // https://issues.kolab.org/issue3833.
01708     mDeletedUIDsSinceLastSync.clear();
01709     return false;
01710   }
01711 }
01712 
01713 void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob* job )
01714 {
01715   if ( job->error() ) {
01716     // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages
01717     mSyncState = SYNC_STATE_GET_MESSAGES;
01718   } else {
01719     // deleting on the server went fine, clear the pending deletions cache
01720     mDeletedUIDsSinceLastSync.clear();
01721   }
01722   mProgress += 10;
01723   serverSyncInternal();
01724 }
01725 
01726 void KMFolderCachedImap::checkUidValidity() {
01727   // IMAP root folders don't seem to have a UID validity setting.
01728   // Also, don't try the uid validity on new folders
01729   if( imapPath().isEmpty() || imapPath() == "/" )
01730     // Just proceed
01731     serverSyncInternal();
01732   else {
01733     newState( mProgress, i18n("Checking folder validity"));
01734     CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this );
01735     connect( job, SIGNAL(permanentFlags(int)), SLOT(slotPermanentFlags(int)) );
01736     connect( job, SIGNAL( result( KMail::FolderJob* ) ),
01737              this, SLOT( slotCheckUidValidityResult( KMail::FolderJob* ) ) );
01738     job->start();
01739   }
01740 }
01741 
01742 void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob* job )
01743 {
01744   if ( job->error() ) { // there was an error and the user chose "continue"
01745     // We can't continue doing anything in the same folder though, it would delete all mails.
01746     // But we can continue to subfolders if any. Well we can also try annotation/acl stuff...
01747     mSyncState = SYNC_STATE_HANDLE_INBOX;
01748   }
01749   mProgress += 5;
01750   serverSyncInternal();
01751 }
01752 
01753 void KMFolderCachedImap::slotPermanentFlags(int flags)
01754 {
01755   mPermanentFlags = flags;
01756 }
01757 
01758 /* This will only list the messages in a folder.
01759    No directory listing done*/
01760 void KMFolderCachedImap::listMessages() {
01761   bool groupwareOnly = GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
01762                && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
01763                && folder()->isSystemFolder()
01764                && mImapPath == "/INBOX/";
01765   // Don't list messages on the root folder, and skip the inbox, if this is
01766   // the inbox of a groupware-only dimap account
01767   if( imapPath() == "/" || groupwareOnly ) {
01768     serverSyncInternal();
01769     return;
01770   }
01771 
01772   if( !mAccount->slave() ) { // sync aborted
01773     resetSyncState();
01774     emit folderComplete( this, false );
01775     return;
01776   }
01777   uidsOnServer.clear();
01778   uidsOnServer.resize( count() * 2 );
01779   uidsForDeletionOnServer.clear();
01780   mMsgsForDownload.clear();
01781   mUidsForDownload.clear();
01782   // listing is only considered successful if saw a syntactically correct imapdigest
01783   mFoundAnIMAPDigest = false;
01784 
01785   CachedImapJob* job = new CachedImapJob( FolderJob::tListMessages, this );
01786   connect( job, SIGNAL( result(KMail::FolderJob *) ),
01787            this, SLOT( slotGetLastMessagesResult(KMail::FolderJob *) ) );
01788   job->start();
01789 }
01790 
01791 void KMFolderCachedImap::slotGetLastMessagesResult(KMail::FolderJob *job)
01792 {
01793   getMessagesResult(job, true);
01794 }
01795 
01796 // Connected to the listMessages job in CachedImapJob
01797 void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
01798 {
01799   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01800   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
01801     kdDebug(5006) << "could not find job!?!?!" << endl;
01802     // be sure to reset the sync state, if the listing was partial we would
01803     // otherwise delete not-listed mail locally, and on the next sync on the server
01804     // as well
01805     mSyncState = SYNC_STATE_HANDLE_INBOX;
01806     serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
01807     return;
01808   }
01809   (*it).cdata += QCString(data, data.size() + 1);
01810   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
01811   if (pos > 0) {
01812     int a = (*it).cdata.find("\r\nX-uidValidity:");
01813     if (a != -1) {
01814       int b = (*it).cdata.find("\r\n", a + 17);
01815       setUidValidity((*it).cdata.mid(a + 17, b - a - 17));
01816     }
01817     a = (*it).cdata.find("\r\nX-Access:");
01818     // Only trust X-Access (i.e. the imap select info) if we don't know mUserRights.
01819     // The latter is more accurate (checked on every sync) whereas X-Access is only
01820     // updated when selecting the folder again, which might not happen if using
01821     // RMB / Check Mail in this folder. We don't need two (potentially conflicting)
01822     // sources for the readonly setting, in any case.
01823     if (a != -1 && mUserRights == -1 ) {
01824       int b = (*it).cdata.find("\r\n", a + 12);
01825       const QString access = (*it).cdata.mid(a + 12, b - a - 12);
01826       setReadOnly( access == "Read only" );
01827     }
01828     (*it).cdata.remove(0, pos);
01829     mFoundAnIMAPDigest = true;
01830   }
01831   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01832   // Start with something largish when rebuilding the cache
01833   if ( uidsOnServer.size() == 0 )
01834     uidsOnServer.resize( KMail::nextPrime( 2000 ) );
01835   const int v = 42;
01836   while (pos >= 0) {
01837       /*
01838     KMMessage msg;
01839     msg.fromString((*it).cdata.mid(16, pos - 16));
01840     const int flags = msg.headerField("X-Flags").toInt();
01841     const ulong size = msg.headerField("X-Length").toULong();
01842     const ulong uid = msg.UID();
01843        */
01844     // The below is optimized for speed, not prettiness. The commented out chunk
01845     // above was the solution copied from kmfolderimap, and it's 15-20% slower.
01846     const QCString& entry( (*it).cdata );
01847     const int indexOfUID = entry.find("X-UID", 16);
01848     const int startOfUIDValue = indexOfUID  + 7;
01849     const int indexOfLength = entry.find("X-Length", startOfUIDValue ); // we know length comes after UID
01850     const int startOfLengthValue = indexOfLength + 10;
01851     const int indexOfFlags = entry.find("X-Flags", startOfLengthValue ); // we know flags comes last
01852     const int startOfFlagsValue = indexOfFlags + 9;
01853 
01854     const int flags = entry.mid( startOfFlagsValue, entry.find( '\r', startOfFlagsValue ) - startOfFlagsValue ).toInt();
01855     const ulong size = entry.mid( startOfLengthValue, entry.find( '\r', startOfLengthValue ) - startOfLengthValue ).toULong();
01856     const ulong uid = entry.mid( startOfUIDValue, entry.find( '\r', startOfUIDValue ) - startOfUIDValue ).toULong();
01857 
01858     const bool deleted = ( flags & 8 );
01859     if ( !deleted ) {
01860       if( uid != 0 ) {
01861         if ( uidsOnServer.count() == uidsOnServer.size() ) {
01862           uidsOnServer.resize( KMail::nextPrime( uidsOnServer.size() * 2 ) );
01863           //kdDebug( 5006 ) << "Resizing to: " << uidsOnServer.size() << endl;
01864         }
01865         uidsOnServer.insert( uid, &v );
01866       }
01867       bool redownload = false;
01868       if (  uid <= lastUid() ) {
01869        /*
01870         * If this message UID is not present locally, then it must
01871         * have been deleted by the user, so we delete it on the
01872         * server also. If we don't have delete permissions on the server,
01873         * re-download the message, it must have vanished by some error, or
01874         * while we still thought we were allowed to delete (ACL change).
01875         *
01876         * This relies heavily on lastUid() being correct at all times.
01877         */
01878         // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<label()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl;
01879         KMMsgBase *existingMessage = findByUID(uid);
01880         if( !existingMessage ) {
01881 #if MAIL_LOSS_DEBUGGING
01882            kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should delete it" << endl;
01883 #endif
01884           // double check we deleted it since the last sync
01885            if ( mDeletedUIDsSinceLastSync.contains(uid) ) {
01886                if ( mUserRights <= 0 || ( mUserRights & KMail::ACLJobs::Delete ) ) {
01887 #if MAIL_LOSS_DEBUGGING
01888                    kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
01889 #endif
01890                    uidsForDeletionOnServer << uid;
01891                } else {
01892                    redownload = true;
01893                }
01894            } else {
01895                kdDebug(5006) << "WARNING: ####### " << endl;
01896                kdDebug(5006) << "Message locally missing but not deleted in folder: " << folder()->prettyURL() << endl;
01897                kdDebug(5006) << "The missing UID: " << uid << ". It will be redownloaded " << endl;
01898                redownload = true;
01899            }
01900 
01901         } else {
01902           // if this is a read only folder, ignore status updates from the server
01903           // since we can't write our status back our local version is what has to
01904           // be considered correct.
01905           if ( !mReadOnly || !GlobalSettings::allowLocalFlags() ) {
01906             /* The message is OK, update flags */
01907             KMFolderImap::flagsToStatus( existingMessage, flags,  false, mReadOnly ? INT_MAX : mPermanentFlags );
01908           } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
01909             KMFolderImap::seenFlagToStatus( existingMessage, flags );
01910           }
01911         }
01912         // kdDebug(5006) << "message with uid " << uid << " found in the local cache. " << endl;
01913       }
01914       if ( uid > lastUid() || redownload ) {
01915 #if MAIL_LOSS_DEBUGGING
01916         kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should download it" << endl;
01917 #endif
01918         // The message is new since the last sync, but we might have just uploaded it, in which case
01919         // the uid map already contains it.
01920         if ( !uidMap.contains( uid ) ) {
01921           mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size);
01922           if( imapPath() == "/INBOX/" )
01923             mUidsForDownload << uid;
01924         }
01925         // Remember the highest uid and once the download is completed, update mLastUid
01926         if ( uid > mTentativeHighestUid ) {
01927 #if MAIL_LOSS_DEBUGGING
01928           kdDebug(5006) << "Setting the tentative highest UID to: " << uid << endl;
01929 #endif
01930           mTentativeHighestUid = uid;
01931         }
01932       }
01933     }
01934     (*it).cdata.remove(0, pos);
01935     (*it).done++;
01936     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01937   }
01938 }
01939 
01940 void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet )
01941 {
01942   mProgress += 10;
01943   if ( !job->error() && !mFoundAnIMAPDigest ) {
01944       kdWarning(5006) << "######## Folderlisting did not complete, but there was no error! "
01945           "Aborting sync of folder: " << folder()->prettyURL() << endl;
01946 #if MAIL_LOSS_DEBUGGING
01947       kmkernel->emergencyExit( i18n("Folder listing failed in interesting ways." ) );
01948 #endif
01949   }
01950   if( job->error() ) { // error listing messages but the user chose to continue
01951     mContentState = imapNoInformation;
01952     mSyncState = SYNC_STATE_HANDLE_INBOX; // be sure not to continue in this folder
01953   } else {
01954     if( lastSet ) { // always true here (this comes from online-imap...)
01955       mContentState = imapFinished;
01956       mUIDsOfLocallyChangedStatuses.clear(); // we are up to date again
01957       mStatusChangedLocally = false;
01958     }
01959   }
01960   serverSyncInternal();
01961 }
01962 
01963 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
01964 {
01965   int progressSpan = 100 - 5 - mProgress;
01966   //kdDebug(5006) << "KMFolderCachedImap::slotProgress done=" << done << " total=" << total << "=> mProgress=" << mProgress + ( progressSpan * done ) / total << endl;
01967   // Progress info while retrieving new emails
01968   // (going from mProgress to mProgress+progressSpan)
01969   newState( mProgress + (progressSpan * done) / total, QString::null );
01970 }
01971 
01972 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
01973 {
01974   assert( aAccount->isA("KMAcctCachedImap") );
01975   mAccount = aAccount;
01976   if( imapPath()=="/" ) aAccount->setFolder( folder() );
01977 
01978   // Folder was renamed in a previous session, and the user didn't sync yet
01979   QString newName = mAccount->renamedFolder( imapPath() );
01980   if ( !newName.isEmpty() )
01981     folder()->setLabel( newName );
01982 
01983   if( !folder() || !folder()->child() || !folder()->child()->count() ) return;
01984   for( KMFolderNode* node = folder()->child()->first(); node;
01985        node = folder()->child()->next() )
01986     if (!node->isDir())
01987       static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
01988 }
01989 
01990 void KMFolderCachedImap::listNamespaces()
01991 {
01992   ImapAccountBase::ListType type = ImapAccountBase::List;
01993   if ( mAccount->onlySubscribedFolders() )
01994     type = ImapAccountBase::ListSubscribed;
01995 
01996   kdDebug(5006) << "listNamespaces " << mNamespacesToList << endl;
01997   if ( mNamespacesToList.isEmpty() ) {
01998     mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
01999     mPersonalNamespacesCheckDone = true;
02000 
02001     QStringList ns = mAccount->namespaces()[ImapAccountBase::OtherUsersNS];
02002     ns += mAccount->namespaces()[ImapAccountBase::SharedNS];
02003     mNamespacesToCheck = ns.count();
02004     for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
02005     {
02006       if ( (*it).isEmpty() ) {
02007         // ignore empty listings as they have been listed before
02008         --mNamespacesToCheck;
02009         continue;
02010       }
02011       KMail::ListJob* job = new KMail::ListJob( mAccount, type, this, mAccount->addPathToNamespace( *it ) );
02012       job->setHonorLocalSubscription( true );
02013       connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
02014               const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
02015           this, SLOT(slotCheckNamespace(const QStringList&, const QStringList&,
02016               const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
02017       job->start();
02018     }
02019     if ( mNamespacesToCheck == 0 ) {
02020       serverSyncInternal();
02021     }
02022     return;
02023   }
02024   mPersonalNamespacesCheckDone = false;
02025 
02026   QString ns = mNamespacesToList.front();
02027   mNamespacesToList.pop_front();
02028 
02029   mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
02030   newState( mProgress, i18n("Retrieving folders for namespace %1").arg(ns));
02031   KMail::ListJob* job = new KMail::ListJob( mAccount, type, this,
02032       mAccount->addPathToNamespace( ns ) );
02033   job->setNamespace( ns );
02034   job->setHonorLocalSubscription( true );
02035   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
02036           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
02037       this, SLOT(slotListResult(const QStringList&, const QStringList&,
02038           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
02039   job->start();
02040 }
02041 
02042 void KMFolderCachedImap::slotCheckNamespace( const QStringList& subfolderNames,
02043                                              const QStringList& subfolderPaths,
02044                                              const QStringList& subfolderMimeTypes,
02045                                              const QStringList& subfolderAttributes,
02046                                              const ImapAccountBase::jobData& jobData )
02047 {
02048   Q_UNUSED( subfolderPaths );
02049   Q_UNUSED( subfolderMimeTypes );
02050   Q_UNUSED( subfolderAttributes );
02051   --mNamespacesToCheck;
02052   kdDebug(5006) << "slotCheckNamespace " << subfolderNames << ",remain=" <<
02053    mNamespacesToCheck << endl;
02054 
02055   // get a correct foldername:
02056   // strip / and make sure it does not contain the delimiter
02057   QString name = jobData.path.mid( 1, jobData.path.length()-2 );
02058   name.remove( mAccount->delimiterForNamespace( name ) );
02059   if ( name.isEmpty() ) {
02060     // should not happen
02061     kdWarning(5006) << "slotCheckNamespace: ignoring empty folder!" << endl;
02062     return;
02063   }
02064 
02065   folder()->createChildFolder();
02066   KMFolderNode *node = 0;
02067   for ( node = folder()->child()->first(); node;
02068         node = folder()->child()->next())
02069   {
02070     if ( !node->isDir() && node->name() == name )
02071       break;
02072   }
02073   if ( !subfolderNames.isEmpty() ) {
02074     if ( node ) {
02075       // folder exists so we have nothing to do - it will be listed later
02076       kdDebug(5006) << "found namespace folder " << name << endl;
02077     } else
02078     {
02079       // create folder
02080       kdDebug(5006) << "create namespace folder " << name << endl;
02081       KMFolder* newFolder = folder()->child()->createFolder( name, false,
02082           KMFolderTypeCachedImap );
02083       if ( newFolder ) {
02084         KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>( newFolder->storage() );
02085         f->setImapPath( mAccount->addPathToNamespace( name ) );
02086         f->setNoContent( true );
02087         f->setAccount( mAccount );
02088         f->close("cachedimap");
02089         kmkernel->dimapFolderMgr()->contentsChanged();
02090       }
02091     }
02092   } else {
02093     if ( node ) {
02094       kdDebug(5006) << "delete namespace folder " << name << endl;
02095       KMFolder* fld = static_cast<KMFolder*>(node);
02096       kmkernel->dimapFolderMgr()->remove( fld );
02097     }
02098   }
02099 
02100   if ( mNamespacesToCheck == 0 ) {
02101     // all namespaces are done so continue with the next step
02102     serverSyncInternal();
02103   }
02104 }
02105 
02106 // This lists the subfolders on the server
02107 // and (in slotListResult) takes care of folders that have been removed on the server
02108 bool KMFolderCachedImap::listDirectory()
02109 {
02110   if( !mAccount->slave() ) { // sync aborted
02111     resetSyncState();
02112     emit folderComplete( this, false );
02113     return false;
02114   }
02115   mSubfolderState = imapInProgress;
02116 
02117   // get the folders
02118   ImapAccountBase::ListType type = ImapAccountBase::List;
02119   if ( mAccount->onlySubscribedFolders() )
02120     type = ImapAccountBase::ListSubscribed;
02121   KMail::ListJob* job = new KMail::ListJob( mAccount, type, this );
02122   job->setHonorLocalSubscription( true );
02123   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
02124           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
02125       this, SLOT(slotListResult(const QStringList&, const QStringList&,
02126           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
02127   job->start();
02128 
02129   return true;
02130 }
02131 
02132 void KMFolderCachedImap::slotListResult( const QStringList& folderNames,
02133                                          const QStringList& folderPaths,
02134                                          const QStringList& folderMimeTypes,
02135                                          const QStringList& folderAttributes,
02136                                          const ImapAccountBase::jobData& jobData )
02137 {
02138   Q_UNUSED( jobData );
02139   //kdDebug(5006) << label() << ": folderNames=" << folderNames << " folderPaths="
02140   //<< folderPaths << " mimeTypes=" << folderMimeTypes << endl;
02141   mSubfolderNames = folderNames;
02142   mSubfolderPaths = folderPaths;
02143   mSubfolderMimeTypes = folderMimeTypes;
02144   mSubfolderState = imapFinished;
02145   mSubfolderAttributes = folderAttributes;
02146   //kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl;
02147 
02148   folder()->createChildFolder();
02149   KMFolderNode *node = folder()->child()->first();
02150   bool root = ( this == mAccount->rootFolder() );
02151 
02152   QPtrList<KMFolder> toRemove;
02153   bool emptyList = ( root && mSubfolderNames.empty() );
02154   if ( !emptyList ) {
02155     while (node) {
02156       if (!node->isDir() ) {
02157         KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02158 
02159         if ( mSubfolderNames.findIndex(node->name()) == -1 ) {
02160           QString name = node->name();
02161           // as more than one namespace can be listed in the root folder we need to make sure
02162           // that the folder is within the current namespace
02163           bool isInNamespace = ( jobData.curNamespace.isEmpty() ||
02164               jobData.curNamespace == mAccount->namespaceForFolder( f ) );
02165           // ignore some cases
02166           bool ignore = root && ( f->imapPath() == "/INBOX/" ||
02167               mAccount->isNamespaceFolder( name ) || !isInNamespace );
02168 
02169           // This subfolder isn't present on the server
02170           if( !f->imapPath().isEmpty() && !ignore  ) {
02171             // The folder has an imap path set, so it has been
02172             // on the server before. Delete it locally.
02173             toRemove.append( f->folder() );
02174             kdDebug(5006) << node->name() << " isn't on the server. It has an imapPath -> delete it locally" << endl;
02175           }
02176         } else { // folder both local and on server
02177           //kdDebug(5006) << node->name() << " is on the server." << endl;
02178 
02182           int index = mSubfolderNames.findIndex( node->name() );
02183           f->mFolderAttributes = folderAttributes[ index ];
02184         }
02185       } else {
02186         //kdDebug(5006) << "skipping dir node:" << node->name() << endl;
02187       }
02188       node = folder()->child()->next();
02189     }
02190   }
02191 
02192   for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) {
02193     rescueUnsyncedMessagesAndDeleteFolder( doomed );
02194   }
02195 
02196   mProgress += 5;
02197 
02198   // just in case there is nothing to rescue
02199   slotRescueDone( 0 );
02200 }
02201 
02202 // This synchronizes the local folders as needed (creation/deletion). No network communication here.
02203 void KMFolderCachedImap::listDirectory2()
02204 {
02205   QString path = folder()->path();
02206   kmkernel->dimapFolderMgr()->quiet(true);
02207 
02208   bool root = ( this == mAccount->rootFolder() );
02209   if ( root && !mAccount->hasInbox() )
02210   {
02211     KMFolderCachedImap *f = 0;
02212     KMFolderNode *node;
02213     // create the INBOX
02214     for (node = folder()->child()->first(); node; node = folder()->child()->next())
02215       if (!node->isDir() && node->name() == "INBOX") break;
02216     if (node) {
02217       f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02218     } else {
02219       KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap);
02220       if ( newFolder ) {
02221         f = static_cast<KMFolderCachedImap*>(newFolder->storage());
02222       }
02223     }
02224     if ( f ) {
02225       f->setAccount( mAccount );
02226       f->setImapPath( "/INBOX/" );
02227       f->folder()->setLabel( i18n("inbox") );
02228     }
02229     if (!node) {
02230       if ( f )
02231         f->close("cachedimap");
02232       kmkernel->dimapFolderMgr()->contentsChanged();
02233     }
02234     // so we have an INBOX
02235     mAccount->setHasInbox( true );
02236   }
02237 
02238   if ( root && !mSubfolderNames.isEmpty() ) {
02239     KMFolderCachedImap* parent =
02240       findParent( mSubfolderPaths.first(), mSubfolderNames.first() );
02241     if ( parent ) {
02242       kdDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to "
02243         << parent->label() << endl;
02244       mSubfolderNames.clear();
02245     }
02246   }
02247 
02248   // Find all subfolders present on server but not on disk
02249   QValueVector<int> foldersNewOnServer;
02250   for (uint i = 0; i < mSubfolderNames.count(); i++) {
02251 
02252     // Find the subdir, if already present
02253     KMFolderCachedImap *f = 0;
02254     KMFolderNode *node = 0;
02255     for (node = folder()->child()->first(); node;
02256          node = folder()->child()->next())
02257       if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
02258 
02259     if (!node) {
02260       // This folder is not present here
02261       // Either it's new on the server, or we just deleted it.
02262       QString subfolderPath = mSubfolderPaths[i];
02263       // The code used to look at the uidcache to know if it was "just deleted".
02264       // But this breaks with noContent folders and with shared folders.
02265       // So instead we keep a list in the account.
02266       bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath );
02267       // That list is saved/restored across sessions, but to avoid any mistake,
02268       // ask for confirmation if the folder was deleted in a previous session
02269       // (could be that the folder was deleted & recreated meanwhile from another client...)
02270       if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) {
02271            locallyDeleted = KMessageBox::warningYesNo(
02272              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] ), QString::null, KStdGuiItem::del(), KStdGuiItem::cancel() ) == KMessageBox::Yes;
02273       }
02274 
02275       if ( locallyDeleted ) {
02276         kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl;
02277         foldersForDeletionOnServer += mAccount->deletedFolderPaths( subfolderPath ); // grab all subsubfolders too
02278       } else {
02279         kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl;
02280         foldersNewOnServer.append( i );
02281       }
02282     } else { // Folder found locally
02283       if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap )
02284         f = dynamic_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02285       if( f ) {
02286         // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath()
02287         //               << "\nSetting imapPath " << mSubfolderPaths[i] << endl;
02288         // Write folder settings
02289         f->setAccount(mAccount);
02290         f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
02291         f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest");
02292         f->setImapPath(mSubfolderPaths[i]);
02293       }
02294     }
02295   }
02296 
02297   /* In case we are ignoring non-groupware folders, and this is the groupware
02298    * main account, find out the contents types of folders that have newly
02299    * appeared on the server. Otherwise just create them and finish listing.
02300    * If a folder is already known to be locally unsubscribed, it won't be
02301    * listed at all, on this level, so these are only folders that we are
02302    * seeing for the first time. */
02303 
02304   /*  Note: We ask the globalsettings, and not the current state of the
02305    *  kmkernel->iCalIface().isEnabled(), since that is false during the
02306    *  very first sync, where we already want to filter. */
02307   if ( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
02308      && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
02309      && mAccount->hasAnnotationSupport()
02310      && GlobalSettings::self()->theIMAPResourceEnabled()
02311      && !foldersNewOnServer.isEmpty() ) {
02312 
02313     QStringList paths;
02314     for ( uint i = 0; i < foldersNewOnServer.count(); ++i )
02315       paths << mSubfolderPaths[ foldersNewOnServer[i] ];
02316 
02317     AnnotationJobs::MultiUrlGetAnnotationJob* job =
02318       AnnotationJobs::multiUrlGetAnnotation( mAccount->slave(), mAccount->getUrl(), paths, KOLAB_FOLDERTYPE );
02319     ImapAccountBase::jobData jd( QString::null, folder() );
02320     jd.cancellable = true;
02321     mAccount->insertJob(job, jd);
02322     connect( job, SIGNAL(result(KIO::Job *)),
02323         SLOT(slotMultiUrlGetAnnotationResult(KIO::Job *)) );
02324 
02325   } else {
02326     createFoldersNewOnServerAndFinishListing( foldersNewOnServer );
02327   }
02328 }
02329 
02330 void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const QValueVector<int> foldersNewOnServer )
02331 {
02332   for ( uint i = 0; i < foldersNewOnServer.count(); ++i ) {
02333     int idx = foldersNewOnServer[i];
02334     KMFolder* newFolder = folder()->child()->createFolder( mSubfolderNames[idx], false, KMFolderTypeCachedImap);
02335     if (newFolder) {
02336       KMFolderCachedImap *f = dynamic_cast<KMFolderCachedImap*>(newFolder->storage());
02337       kdDebug(5006) << " ####### Locally creating folder " << mSubfolderNames[idx] <<endl;
02338       f->close("cachedimap");
02339       f->setAccount(mAccount);
02340       f->mAnnotationFolderType = "FROMSERVER";
02341       f->setNoContent(mSubfolderMimeTypes[idx] == "inode/directory");
02342       f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest");
02343       f->setImapPath(mSubfolderPaths[idx]);
02344       f->mFolderAttributes = mSubfolderAttributes[idx];
02345       kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <<endl;
02346       //kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl;
02347       kmkernel->dimapFolderMgr()->contentsChanged();
02348     } else {
02349       kdDebug(5006) << "can't create folder " << mSubfolderNames[idx] <<endl;
02350     }
02351   }
02352 
02353   kmkernel->dimapFolderMgr()->quiet(false);
02354   emit listComplete(this);
02355   if ( !mPersonalNamespacesCheckDone ) {
02356     // we're not done with the namespaces
02357     mSyncState = SYNC_STATE_LIST_NAMESPACES;
02358   }
02359   serverSyncInternal();
02360 }
02361 
02362 //-----------------------------------------------------------------------------
02363 KMFolderCachedImap* KMFolderCachedImap::findParent( const QString& path,
02364                                                     const QString& name )
02365 {
02366   QString parent = path.left( path.length() - name.length() - 2 );
02367   if ( parent.length() > 1 )
02368   {
02369     // extract name of the parent
02370     parent = parent.right( parent.length() - 1 );
02371     if ( parent != label() )
02372     {
02373       KMFolderNode *node = folder()->child()->first();
02374       // look for a better parent
02375       while ( node )
02376       {
02377         if ( node->name() == parent )
02378         {
02379           KMFolder* fld = static_cast<KMFolder*>(node);
02380           KMFolderCachedImap* imapFld =
02381             static_cast<KMFolderCachedImap*>( fld->storage() );
02382           return imapFld;
02383         }
02384         node = folder()->child()->next();
02385       }
02386     }
02387   }
02388   return 0;
02389 }
02390 
02391 void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success)
02392 {
02393   Q_UNUSED(sub);
02394   //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl;
02395   if ( success ) {
02396     serverSyncInternal();
02397   }
02398   else
02399   {
02400     // success == false means the sync was aborted.
02401     if ( mCurrentSubfolder ) {
02402       Q_ASSERT( sub == mCurrentSubfolder );
02403       disconnectSubFolderSignals();
02404     }
02405 
02406     // Next step would be to check quota limits and then to close the folder, but don't bother with
02407     // both and close the folder right here, since we aborted.
02408     mSubfoldersForSync.clear();
02409     mSyncState = SYNC_STATE_INITIAL;
02410     close("cachedimap");
02411     emit changed();
02412     emit folderComplete( this, false );
02413   }
02414 }
02415 
02416 void KMFolderCachedImap::slotSubFolderCloseToQuotaChanged()
02417 {
02418   if ( !mQuotaOnly ) {
02419     mSomeSubFolderCloseToQuotaChanged = true;
02420   }
02421 }
02422 
02423 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
02424 {
02425   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02426   if (it == mAccount->jobsEnd()) return;
02427   QBuffer buff((*it).data);
02428   buff.open(IO_WriteOnly | IO_Append);
02429   buff.writeBlock(data.data(), data.size());
02430   buff.close();
02431 }
02432 
02433 FolderJob*
02434 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
02435                                  QString, const AttachmentStrategy* ) const
02436 {
02437   QPtrList<KMMessage> msgList;
02438   msgList.append( msg );
02439   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
02440   job->setParentFolder( this );
02441   return job;
02442 }
02443 
02444 FolderJob*
02445 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
02446                                  FolderJob::JobType jt, KMFolder *folder ) const
02447 {
02448   //FIXME: how to handle sets here?
02449   Q_UNUSED( sets );
02450   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
02451   job->setParentFolder( this );
02452   return job;
02453 }
02454 
02455 void
02456 KMFolderCachedImap::setUserRights( unsigned int userRights )
02457 {
02458   mUserRights = userRights;
02459   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02460 }
02461 
02462 void
02463 KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder )
02464 {
02465   if ( folder->storage() == this ) {
02466     disconnect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
02467                 this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
02468     if ( mUserRights == 0 ) // didn't work
02469       mUserRights = -1; // error code (used in folderdia)
02470     else
02471       setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 );
02472     mProgress += 5;
02473     serverSyncInternal();
02474   }
02475 }
02476 
02477 void
02478 KMFolderCachedImap::setReadOnly( bool readOnly )
02479 {
02480   if ( readOnly != mReadOnly ) {
02481     mReadOnly = readOnly;
02482     emit readOnlyChanged( folder() );
02483   }
02484 }
02485 
02486 void
02487 KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job*, const KMail::ACLList& aclList )
02488 {
02489   if ( folder->storage() == this ) {
02490     disconnect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
02491                 this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
02492     mACLList = aclList;
02493     serverSyncInternal();
02494   }
02495 }
02496 
02497 void
02498 KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info )
02499 {
02500   setQuotaInfo( info );
02501 }
02502 
02503 void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info )
02504 {
02505     if ( info != mQuotaInfo ) {
02506       const bool wasCloseToQuota = isCloseToQuota();
02507       mQuotaInfo = info;
02508       writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02509       if ( wasCloseToQuota != isCloseToQuota() ) {
02510         emit closeToQuotaChanged();
02511       }
02512       emit folderSizeChanged();
02513     }
02514 }
02515 
02516 void
02517 KMFolderCachedImap::setACLList( const ACLList& arr )
02518 {
02519   mACLList = arr;
02520 }
02521 
02522 void
02523 KMFolderCachedImap::slotMultiSetACLResult(KIO::Job *job)
02524 {
02525   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02526   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02527   if ( (*it).parent != folder() ) return; // Shouldn't happen
02528 
02529   if ( job->error() )
02530     // Display error but don't abort the sync just for this
02531     // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel
02532     job->showErrorDialog();
02533   else
02534     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02535 
02536   if (mAccount->slave()) mAccount->removeJob(job);
02537   serverSyncInternal();
02538 }
02539 
02540 void
02541 KMFolderCachedImap::slotACLChanged( const QString& userId, int permissions )
02542 {
02543   // The job indicates success in changing the permissions for this user
02544   // -> we note that it's been done.
02545   for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) {
02546     if ( (*it).userId == userId && (*it).permissions == permissions ) {
02547       if ( permissions == -1 ) // deleted
02548         mACLList.erase( it );
02549       else // added/modified
02550         (*it).changed = false;
02551       return;
02552     }
02553   }
02554 }
02555 
02556 // called by KMAcctCachedImap::killAllJobs
02557 void KMFolderCachedImap::resetSyncState()
02558 {
02559   if ( mSyncState == SYNC_STATE_INITIAL ) return;
02560   mSubfoldersForSync.clear();
02561   mSyncState = SYNC_STATE_INITIAL;
02562   close("cachedimap");
02563   // Don't use newState here, it would revert to mProgress (which is < current value when listing messages)
02564   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02565   QString str = i18n("Aborted");
02566   if (progressItem)
02567      progressItem->setStatus( str );
02568   emit statusMsg( str );
02569 }
02570 
02571 void KMFolderCachedImap::slotIncreaseProgress()
02572 {
02573   mProgress += 5;
02574 }
02575 
02576 void KMFolderCachedImap::newState( int progress, const QString& syncStatus )
02577 {
02578   //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl;
02579   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02580   if( progressItem )
02581     progressItem->setCompletedItems( progress );
02582   if ( !syncStatus.isEmpty() ) {
02583     QString str;
02584     // For a subfolder, show the label. But for the main folder, it's already shown.
02585     if ( mAccount->imapFolder() == this )
02586       str = syncStatus;
02587     else
02588       str = QString( "%1: %2" ).arg( label() ).arg( syncStatus );
02589     if( progressItem )
02590       progressItem->setStatus( str );
02591     emit statusMsg( str );
02592   }
02593   if( progressItem )
02594     progressItem->updateProgress();
02595 }
02596 
02597 void KMFolderCachedImap::setSubfolderState( imapState state )
02598 {
02599   mSubfolderState = state;
02600   if ( state == imapNoInformation && folder()->child() )
02601   {
02602     // pass through to childs
02603     KMFolderNode* node;
02604     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02605     for ( ; (node = it.current()); )
02606     {
02607       ++it;
02608       if (node->isDir()) continue;
02609       KMFolder *folder = static_cast<KMFolder*>(node);
02610       static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state );
02611     }
02612   }
02613 }
02614 
02615 void KMFolderCachedImap::setImapPath(const QString &path)
02616 {
02617   mImapPath = path;
02618 }
02619 
02620 static bool isFolderTypeKnownToUs( const QString &type )
02621 {
02622   for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
02623     FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
02624     if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) )
02625       return true;
02626   }
02627   return false;
02628 }
02629 
02630 // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
02631 // It is updated from the folder contents type and whether it's a standard resource folder.
02632 // This happens during the syncing phase and during initFolder for a new folder.
02633 // Don't do it earlier, e.g. from setContentsType:
02634 // on startup, it's too early there to know if this is a standard resource folder.
02635 void KMFolderCachedImap::updateAnnotationFolderType()
02636 {
02637   QString oldType = mAnnotationFolderType;
02638   QString oldSubType;
02639   int dot = oldType.find( '.' );
02640   if ( dot != -1 ) {
02641     oldType.truncate( dot );
02642     oldSubType = mAnnotationFolderType.mid( dot + 1 );
02643   }
02644 
02645   QString newType, newSubType;
02646   // We want to store an annotation on the folder only if using the kolab storage.
02647   if ( kmkernel->iCalIface().storageFormat( folder() ) == KMailICalIfaceImpl::StorageXML ) {
02648     newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType );
02649     if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) )
02650       newSubType = "default";
02651     else if ( oldSubType != "default" )
02652       newSubType = oldSubType; // preserve unknown subtypes, like drafts etc.
02653   }
02654 
02655   // We do not want to overwrite custom folder types (which we treat as mail folders).
02656   // So only overwrite custom folder types if the user changed the folder type himself to something
02657   // other than mail.
02658   const bool changingTypeAllowed = isFolderTypeKnownToUs( oldType ) ||
02659                                    ( mContentsType != ContentsTypeMail );
02660 
02661   //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl;
02662   if ( ( newType != oldType || newSubType != oldSubType ) && changingTypeAllowed ) {
02663     mAnnotationFolderType = newType + ( newSubType.isEmpty() ? QString::null : "."+newSubType );
02664     mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync
02665     kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl;
02666   }
02667   // Ensure that further readConfig()s don't lose mAnnotationFolderType
02668   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02669 }
02670 
02671 void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor )
02672 {
02673   if ( mIncidencesFor != incfor ) {
02674     mIncidencesFor = incfor;
02675     mIncidencesForChanged = true;
02676   }
02677 }
02678 
02679 void KMFolderCachedImap::setSharedSeenFlags(bool b)
02680 {
02681   if ( mSharedSeenFlags != b ) {
02682     mSharedSeenFlags = b;
02683     mSharedSeenFlagsChanged = true;
02684   }
02685 }
02686 
02687 void KMFolderCachedImap::slotAnnotationResult(const QString& entry, const QString& value, bool found)
02688 {
02689   if ( entry == KOLAB_FOLDERTYPE ) {
02690     // There are four cases.
02691     // 1) no content-type on server -> set it
02692     // 2) different content-type on server, locally changed -> set it (we don't even come here)
02693     // 3) different (known) content-type on server, no local change -> get it
02694     // 4) different unknown content-type on server, probably some older version -> set it
02695     if ( found ) {
02696       QString type = value;
02697       QString subtype;
02698       int dot = value.find( '.' );
02699       if ( dot != -1 ) {
02700         type.truncate( dot );
02701         subtype = value.mid( dot + 1 );
02702       }
02703       bool foundKnownType = false;
02704       for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
02705         FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
02706         if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) {
02707           // Case 3: known content-type on server, get it
02708           //kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: found known type of annotation" << endl;
02709           if ( contentsType != ContentsTypeMail )
02710             kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
02711           mAnnotationFolderType = value;
02712           if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent()
02713                && GlobalSettings::self()->theIMAPResourceEnabled()
02714                && subtype == "default" ) {
02715             // Truncate subtype if this folder can't be a default resource folder for us,
02716             // although it apparently is for someone else.
02717             mAnnotationFolderType = type;
02718             kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: parent folder is " << folder()->parent()->owner()->idString() << " => truncating annotation to " << value << endl;
02719           }
02720           setContentsType( contentsType );
02721           mAnnotationFolderTypeChanged = false; // we changed it, not the user
02722           foundKnownType = true;
02723 
02724           // Users don't read events/contacts/etc. in kmail, so mark them all as read.
02725           // This is done in cachedimapjob when getting new messages, but do it here too,
02726           // for the initial set of messages when we didn't know this was a resource folder yet,
02727           // for old folders, etc.
02728           if ( contentsType != ContentsTypeMail )
02729             markUnreadAsRead();
02730 
02731           break;
02732         }
02733       }
02734       if ( !foundKnownType ) {
02735         //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, leaving it untouched" << endl;
02736 
02737         // Case 4: Server has strange content-type. We must not overwrite it, see https://issues.kolab.org/issue2069.
02738         //         Treat the content-type as mail until we change it ourselves.
02739         mAnnotationFolderTypeChanged = false;
02740         mAnnotationFolderType = value;
02741         setContentsType( ContentsTypeMail );
02742       }
02743 
02744       // Ensure that further readConfig()s don't lose mAnnotationFolderType
02745       writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02746       // TODO handle subtype (inbox, drafts, sentitems, junkemail)
02747     }
02748     else if ( !mReadOnly ) {
02749       // Case 1: server doesn't have content-type, set it
02750       //kdDebug(5006) << "slotGetAnnotationResult: no annotation found, will need to set it" << endl;
02751       mAnnotationFolderTypeChanged = true;
02752     }
02753   } else if ( entry == KOLAB_INCIDENCESFOR ) {
02754     if ( found ) {
02755       mIncidencesFor = incidencesForFromString( value );
02756       Q_ASSERT( mIncidencesForChanged == false );
02757     }
02758   } else if ( entry == KOLAB_SHAREDSEEN ) {
02759     if ( found ) {
02760       mSharedSeenFlags = value == "true";
02761     }
02762   }
02763 }
02764 
02765 void KMFolderCachedImap::slotGetAnnotationResult( KIO::Job* job )
02766 {
02767   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02768   Q_ASSERT( it != mAccount->jobsEnd() );
02769   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02770   Q_ASSERT( (*it).parent == folder() );
02771   if ( (*it).parent != folder() ) return; // Shouldn't happen
02772 
02773   AnnotationJobs::GetAnnotationJob* annjob = static_cast<AnnotationJobs::GetAnnotationJob *>( job );
02774   if ( annjob->error() ) {
02775     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02776       // that's when the imap server doesn't support annotations
02777       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02778            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02779     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() ) );
02780       mAccount->setHasNoAnnotationSupport();
02781     }
02782     else
02783       kdWarning(5006) << "slotGetAnnotationResult: " << job->errorString() << endl;
02784   }
02785 
02786   if (mAccount->slave()) mAccount->removeJob(job);
02787   mProgress += 2;
02788   serverSyncInternal();
02789 }
02790 
02791 void KMFolderCachedImap::slotMultiUrlGetAnnotationResult( KIO::Job* job )
02792 {
02793   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02794   Q_ASSERT( it != mAccount->jobsEnd() );
02795   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02796   Q_ASSERT( (*it).parent == folder() );
02797   if ( (*it).parent != folder() ) return; // Shouldn't happen
02798 
02799   QValueVector<int> folders;
02800   AnnotationJobs::MultiUrlGetAnnotationJob* annjob
02801     = static_cast<AnnotationJobs::MultiUrlGetAnnotationJob *>( job );
02802   if ( annjob->error() ) {
02803     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02804       // that's when the imap server doesn't support annotations
02805       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02806            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02807         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() ) );
02808       mAccount->setHasNoAnnotationSupport();
02809     }
02810     else
02811       kdWarning(5006) << "slotGetMultiUrlAnnotationResult: " << job->errorString() << endl;
02812   } else {
02813     // we got the annotation allright, let's filter out the ones with the wrong type
02814     QMap<QString, QString> annotations = annjob->annotations();
02815     QMap<QString, QString>::Iterator it = annotations.begin();
02816     for ( ; it != annotations.end(); ++it ) {
02817       const QString folderPath = it.key();
02818       const QString annotation = it.data();
02819       kdDebug(5006) << k_funcinfo << "Folder: " << folderPath << " has type: " << annotation << endl;
02820       // we're only interested in the main type
02821       QString type(annotation);
02822       int dot = annotation.find( '.' );
02823       if ( dot != -1 ) type.truncate( dot );
02824       type = type.simplifyWhiteSpace();
02825 
02826       const int idx = mSubfolderPaths.findIndex( folderPath );
02827       const bool isNoContent =  mSubfolderMimeTypes[idx] == "inode/directory";
02828       if ( ( isNoContent && type.isEmpty() )
02829         || ( !type.isEmpty() && type != KMailICalIfaceImpl::annotationForContentsType( ContentsTypeMail ) ) ) {
02830         folders.append( idx );
02831         kdDebug(5006) << k_funcinfo << " subscribing to: " << folderPath << endl;
02832       } else {
02833         kdDebug(5006) << k_funcinfo << " automatically unsubscribing from: " << folderPath << endl;
02834         mAccount->changeLocalSubscription( folderPath, false );
02835       }
02836     }
02837   }
02838 
02839   if (mAccount->slave()) mAccount->removeJob(job);
02840   createFoldersNewOnServerAndFinishListing( folders );
02841 }
02842 
02843 void KMFolderCachedImap::slotQuotaResult( KIO::Job* job )
02844 {
02845   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02846   Q_ASSERT( it != mAccount->jobsEnd() );
02847   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02848   Q_ASSERT( (*it).parent == folder() );
02849   if ( (*it).parent != folder() ) return; // Shouldn't happen
02850 
02851   QuotaJobs::GetStorageQuotaJob* quotajob = static_cast<QuotaJobs::GetStorageQuotaJob *>( job );
02852   QuotaInfo empty;
02853   if ( quotajob->error() ) {
02854     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02855       // that's when the imap server doesn't support quota
02856       mAccount->setHasNoQuotaSupport();
02857       setQuotaInfo( empty );
02858     }
02859     else
02860       kdWarning(5006) << "slotGetQuotaResult: " << job->errorString() << endl;
02861   }
02862 
02863   if (mAccount->slave()) mAccount->removeJob(job);
02864   mProgress += 2;
02865   serverSyncInternal();
02866 }
02867 
02868 void
02869 KMFolderCachedImap::slotAnnotationChanged( const QString& entry, const QString& attribute, const QString& value )
02870 {
02871   Q_UNUSED( attribute );
02872   Q_UNUSED( value );
02873   //kdDebug(5006) << k_funcinfo << entry << " " << attribute << " " << value << endl;
02874   if ( entry == KOLAB_FOLDERTYPE )
02875     mAnnotationFolderTypeChanged = false;
02876   else if ( entry == KOLAB_INCIDENCESFOR ) {
02877     mIncidencesForChanged = false;
02878     // The incidences-for changed, we must trigger the freebusy creation.
02879     // HACK: in theory we would need a new enum value for this.
02880     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02881   } else if ( entry == KOLAB_SHAREDSEEN ) {
02882     mSharedSeenFlagsChanged = false;
02883   }
02884 }
02885 
02886 void KMFolderCachedImap::slotTestAnnotationResult(KIO::Job *job)
02887 {
02888   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02889   Q_ASSERT( it != mAccount->jobsEnd() );
02890   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02891   Q_ASSERT( (*it).parent == folder() );
02892   if ( (*it).parent != folder() ) return; // Shouldn't happen
02893 
02894   mAccount->setAnnotationCheckPassed( true );
02895   if ( job->error() ) {
02896     kdDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl;
02897     mAccount->setHasNoAnnotationSupport( );
02898   } else {
02899     kdDebug(5006) << "Test Annotation was passed   OK" << endl;
02900   }
02901   if (mAccount->slave()) mAccount->removeJob(job);
02902   serverSyncInternal();
02903 }
02904 
02905 void
02906 KMFolderCachedImap::slotSetAnnotationResult(KIO::Job *job)
02907 {
02908   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02909   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02910   if ( (*it).parent != folder() ) return; // Shouldn't happen
02911 
02912   bool cont = true;
02913   if ( job->error() ) {
02914     // Don't show error if the server doesn't support ANNOTATEMORE and this folder only contains mail
02915     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION && contentsType() == ContentsTypeMail ) {
02916       if (mAccount->slave()) mAccount->removeJob(job);
02917     } else {
02918       cont = mAccount->handleJobError( job, i18n( "Error while setting annotation: " ) + '\n' );
02919     }
02920   } else {
02921     if (mAccount->slave()) mAccount->removeJob(job);
02922   }
02923   if ( cont )
02924     serverSyncInternal();
02925 }
02926 
02927 void KMFolderCachedImap::slotUpdateLastUid()
02928 {
02929   if( mTentativeHighestUid != 0 ) {
02930 
02931       // Sanity checking:
02932       // By now all new mails should be downloaded, which means
02933       // that iteration over the folder should yield only UIDs
02934       // lower or equal to what we think the highes ist, and the
02935       // highest one as well. If not, our notion of the highest
02936       // uid we've seen thus far is wrong, which is dangerous, so
02937       // don't update the mLastUid, then.
02938       // Not entirely true though, mails might have been moved out
02939       // of the folder already by filters, thus giving us a higher tentative
02940       // uid than we actually observe here.
02941       bool sane = count() == 0;
02942 
02943       for (int i=0;i<count(); i++ ) {
02944           ulong uid = getMsgBase(i)->UID();
02945           if ( uid > mTentativeHighestUid && uid > lastUid() ) {
02946               kdWarning(5006) << "DANGER: Either the server listed a wrong highest uid, "
02947                   "or we parsed it wrong. Send email to adam@kde.org, please, and include this log." << endl;
02948               kdWarning(5006) << "uid: " << uid << " mTentativeHighestUid: " << mTentativeHighestUid << endl;
02949               assert( false );
02950               break;
02951           } else {
02952               sane = true;
02953           }
02954       }
02955       if (sane) {
02956 #if MAIL_LOSS_DEBUGGING
02957           kdDebug(5006) << "Tentative highest UID test was sane, writing out: " << mTentativeHighestUid << endl;
02958 #endif
02959           setLastUid( mTentativeHighestUid );
02960       }
02961   }
02962   mTentativeHighestUid = 0;
02963 }
02964 
02965 bool KMFolderCachedImap::isMoveable() const
02966 {
02967   return ( hasChildren() == HasNoChildren &&
02968       !folder()->isSystemFolder() ) ? true : false;
02969 }
02970 
02971 void KMFolderCachedImap::slotFolderDeletionOnServerFinished()
02972 {
02973   for ( QStringList::const_iterator it = foldersForDeletionOnServer.constBegin();
02974       it != foldersForDeletionOnServer.constEnd(); ++it ) {
02975     KURL url( mAccount->getUrl() );
02976     url.setPath( *it );
02977     kmkernel->iCalIface().folderDeletedOnServer( url );
02978   }
02979   serverSyncInternal();
02980 }
02981 
02982 int KMFolderCachedImap::createIndexFromContentsRecursive()
02983 {
02984   if ( !folder() || !folder()->child() )
02985     return 0;
02986 
02987   KMFolderNode *node = 0;
02988   for( QPtrListIterator<KMFolderNode> it( *folder()->child() ); (node = it.current()); ++it ) {
02989     if( !node->isDir() ) {
02990       KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02991       kdDebug() << k_funcinfo << "Re-indexing: " << storage->folder()->label() << endl;
02992       int rv = storage->createIndexFromContentsRecursive();
02993       if ( rv > 0 )
02994         return rv;
02995     }
02996   }
02997 
02998   return createIndexFromContents();
02999 }
03000 
03001 void KMFolderCachedImap::setAlarmsBlocked( bool blocked )
03002 {
03003   mAlarmsBlocked = blocked;
03004 }
03005 
03006 bool KMFolderCachedImap::alarmsBlocked() const
03007 {
03008   return mAlarmsBlocked;
03009 }
03010 
03011 bool KMFolderCachedImap::isCloseToQuota() const
03012 {
03013   bool closeToQuota = false;
03014   if ( mQuotaInfo.isValid() && mQuotaInfo.max().toInt() > 0 ) {
03015     const int ratio = mQuotaInfo.current().toInt() * 100  / mQuotaInfo.max().toInt();
03016     //kdDebug(5006) << "Quota ratio: " << ratio << "% " << mQuotaInfo.toString() << endl;
03017     closeToQuota = ( ratio > 0 && ratio >= GlobalSettings::closeToQuotaThreshold() );
03018   }
03019   //kdDebug(5006) << "Folder: " << folder()->prettyURL() << " is over quota: " << closeToQuota << endl;
03020   return closeToQuota;
03021 }
03022 
03023 KMCommand* KMFolderCachedImap::rescueUnsyncedMessages()
03024 {
03025   QValueList<unsigned long> newMsgs = findNewMessages();
03026   kdDebug() << k_funcinfo << newMsgs << " of " << count() << endl;
03027   if ( newMsgs.isEmpty() )
03028     return 0;
03029   KMFolder *dest = 0;
03030   bool manualMove = true;
03031   while ( GlobalSettings::autoLostFoundMove() ) {
03032     // find the inbox of this account
03033     KMFolder *inboxFolder = kmkernel->findFolderById( QString(".%1.directory/INBOX").arg( account()->id() ) );
03034     if ( !inboxFolder ) {
03035       kdWarning(5006) << k_funcinfo << "inbox not found!" << endl;
03036       break;
03037     }
03038     KMFolderDir *inboxDir = inboxFolder->child();
03039     if ( !inboxDir && !inboxFolder->storage() )
03040       break;
03041     assert( inboxFolder->storage()->folderType() == KMFolderTypeCachedImap );
03042 
03043     // create lost+found folder if needed
03044     KMFolderNode *node;
03045     KMFolder *lfFolder = 0;
03046     if ( !(node = inboxDir->hasNamedFolder( i18n("lost+found") )) ) {
03047       kdDebug(5006) << k_funcinfo << "creating lost+found folder" << endl;
03048       KMFolder* folder = kmkernel->dimapFolderMgr()->createFolder(
03049           i18n("lost+found"), false, KMFolderTypeCachedImap, inboxDir );
03050       if ( !folder || !folder->storage() )
03051         break;
03052       static_cast<KMFolderCachedImap*>( folder->storage() )->initializeFrom(
03053         static_cast<KMFolderCachedImap*>( inboxFolder->storage() ) );
03054       folder->storage()->setContentsType( KMail::ContentsTypeMail );
03055       folder->storage()->writeConfig();
03056       lfFolder = folder;
03057     } else {
03058       kdDebug(5006) << k_funcinfo << "found lost+found folder" << endl;
03059       lfFolder = dynamic_cast<KMFolder*>( node );
03060     }
03061     if ( !lfFolder || !lfFolder->createChildFolder() || !lfFolder->storage() )
03062       break;
03063 
03064     // create subfolder for this incident
03065     QDate today = QDate::currentDate();
03066     QString baseName = folder()->label() + "-" + QString::number( today.year() )
03067         + (today.month() < 10 ? "0" : "" ) + QString::number( today.month() )
03068         + (today.day() < 10 ? "0" : "" ) + QString::number( today.day() );
03069     QString name = baseName;
03070     int suffix = 0;
03071     while ( (node = lfFolder->child()->hasNamedFolder( name )) ) {
03072       ++suffix;
03073       name = baseName + '-' + QString::number( suffix );
03074     }
03075     kdDebug(5006) << k_funcinfo << "creating lost+found folder " << name << endl;
03076     dest = kmkernel->dimapFolderMgr()->createFolder( name, false, KMFolderTypeCachedImap, lfFolder->child() );
03077     if ( !dest || !dest->storage() )
03078         break;
03079     static_cast<KMFolderCachedImap*>( dest->storage() )->initializeFrom(
03080       static_cast<KMFolderCachedImap*>( lfFolder->storage() ) );
03081     dest->storage()->setContentsType( contentsType() );
03082     dest->storage()->writeConfig();
03083 
03084     KMessageBox::sorry( 0, i18n("<p>There are new messages in folder <b>%1</b>, which "
03085           "have not been uploaded to the server yet, but the folder has been deleted "
03086           "on the server or you do not "
03087           "have sufficient access rights on the folder to upload them.</p>"
03088           "<p>All affected messages will therefore be moved to <b>%2</b> "
03089           "to avoid data loss.</p>").arg( folder()->prettyURL() ).arg( dest->prettyURL() ),
03090           i18n("Insufficient access rights") );
03091     manualMove = false;
03092     break;
03093   }
03094 
03095   if ( manualMove ) {
03096     const QString msg ( i18n( "<p>There are new messages in this folder (%1), which "
03097           "have not been uploaded to the server yet, but the folder has been deleted "
03098           "on the server or you do not "
03099           "have sufficient access rights on the folder now to upload them. "
03100           "Please contact your administrator to allow upload of new messages "
03101           "to you, or move them out of this folder.</p> "
03102           "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyURL() ) );
03103     if ( KMessageBox::warningYesNo( 0, msg, QString::null, i18n("Move"), i18n("Do Not Move") ) == KMessageBox::Yes ) {
03104       KMail::KMFolderSelDlg dlg( kmkernel->getKMMainWidget(),
03105           i18n("Move Messages to Folder"), true );
03106       if ( dlg.exec() ) {
03107         dest = dlg.folder();
03108       }
03109     }
03110   }
03111   if ( dest ) {
03112     QPtrList<KMMsgBase> msgs;
03113     for( int i = 0; i < count(); ++i ) {
03114       KMMsgBase *msg = getMsgBase( i );
03115       if( !msg ) continue; /* what goes on if getMsg() returns 0? */
03116       if ( msg->UID() == 0 )
03117         msgs.append( msg );
03118     }
03119     KMCommand *command = new KMMoveCommand( dest, msgs );
03120     command->start();
03121     return command;
03122   }
03123   return 0;
03124 }
03125 
03126 void KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root )
03127 {
03128   kdDebug() << k_funcinfo << folder << " " << root << endl;
03129   if ( root )
03130     mToBeDeletedAfterRescue.append( folder );
03131   folder->open("cachedimap");
03132   KMFolderCachedImap* storage = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
03133   if ( storage ) {
03134     KMCommand *command = storage->rescueUnsyncedMessages();
03135     if ( command ) {
03136       connect( command, SIGNAL(completed(KMCommand*)),
03137                SLOT(slotRescueDone(KMCommand*)) );
03138       ++mRescueCommandCount;
03139     } else {
03140       // nothing to rescue, close folder
03141       // (we don't need to close it in the other case, it will be deleted anyway)
03142       folder->close("cachedimap");
03143     }
03144   }
03145   if ( folder->child() ) {
03146     KMFolderNode *node = folder->child()->first();
03147     while (node) {
03148       if (!node->isDir() ) {
03149         KMFolder *subFolder = static_cast<KMFolder*>( node );
03150         rescueUnsyncedMessagesAndDeleteFolder( subFolder, false );
03151       }
03152       node = folder->child()->next();
03153     }
03154   }
03155 }
03156 
03157 void KMFolderCachedImap::slotRescueDone(KMCommand * command)
03158 {
03159   // FIXME: check command result
03160   if ( command )
03161     --mRescueCommandCount;
03162   if ( mRescueCommandCount > 0 )
03163     return;
03164   for ( QValueList<KMFolder*>::ConstIterator it = mToBeDeletedAfterRescue.constBegin();
03165         it != mToBeDeletedAfterRescue.constEnd(); ++it ) {
03166     kmkernel->dimapFolderMgr()->remove( *it );
03167   }
03168   mToBeDeletedAfterRescue.clear();
03169   serverSyncInternal();
03170 }
03171 
03172 void KMFolderCachedImap::slotRenameFolderFinished()
03173 {
03174   // The syncing code assumes the folder was opened by us, and later closes it. So better
03175   // make sure the reference count is correct, since the folder was force-closed by the rename.
03176   // Otherwise bad things can happen, see https://issues.kolab.org/issue3853.
03177   open( "cachedimap" );
03178   serverSyncInternal();
03179 }
03180 
03181 bool KMFolderCachedImap::canDeleteMessages() const
03182 {
03183   if ( isReadOnly() )
03184     return false;
03185   if ( userRights() > 0 && !(userRights() & ACLJobs::Delete) )
03186     return false;
03187   return true;
03188 }
03189 
03190 bool KMFolderCachedImap::mailCheckInProgress() const
03191 {
03192   return mSyncState != SYNC_STATE_INITIAL;
03193 }
03194 
03195 #include "kmfoldercachedimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys