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