kmail

kmfoldercachedimap.cpp

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