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         const bool folderIsNew = mNewlyCreatedSubfolders.contains( QGuardedPtr<KMFolderCachedImap>( storage ) );
01379         // Only sync folders that have been accepted by the server
01380         if ( !storage->imapPath().isEmpty()
01381              // and that were not just deleted from it
01382              && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) {
01383           if ( mRecurse || folderIsNew ) {
01384             mSubfoldersForSync << storage;
01385           }
01386         } else {
01387           kdDebug(5006) << "Do not add " << storage->label()
01388                         << " to synclist" << endl;
01389         }
01390       }
01391       node = folder()->child()->next();
01392     }
01393   }
01394 
01395   mNewlyCreatedSubfolders.clear();
01396 }
01397 
01398 void KMFolderCachedImap::disconnectSubFolderSignals()
01399 {
01400   disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
01401               this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
01402   disconnect( mCurrentSubfolder, SIGNAL( closeToQuotaChanged() ),
01403               this, SLOT( slotSubFolderCloseToQuotaChanged() ) );
01404   mCurrentSubfolder = 0;
01405 }
01406 
01407 /* Connected to the imap account's connectionResult signal.
01408    Emitted when the slave connected or failed to connect.
01409 */
01410 void KMFolderCachedImap::slotConnectionResult( int errorCode, const QString& errorMsg )
01411 {
01412   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01413               this, SLOT( slotConnectionResult(int, const QString&) ) );
01414   if ( !errorCode ) {
01415     // Success
01416     mSyncState = SYNC_STATE_GET_USERRIGHTS;
01417     mProgress += 5;
01418     serverSyncInternal();
01419   } else {
01420     // Error (error message already shown by the account)
01421     newState( mProgress, KIO::buildErrorString( errorCode, errorMsg ));
01422     emit folderComplete(this, false);
01423   }
01424 }
01425 
01426 /* find new messages (messages without a UID) */
01427 QValueList<unsigned long> KMFolderCachedImap::findNewMessages()
01428 {
01429   QValueList<unsigned long> result;
01430   for( int i = 0; i < count(); ++i ) {
01431     KMMsgBase *msg = getMsgBase( i );
01432     if( !msg ) continue; /* what goes on if getMsg() returns 0? */
01433     if ( msg->UID() == 0 )
01434       result.append( msg->getMsgSerNum() );
01435   }
01436   return result;
01437 }
01438 
01439 /* Upload new messages to server */
01440 void KMFolderCachedImap::uploadNewMessages()
01441 {
01442   QValueList<unsigned long> newMsgs = findNewMessages();
01443   if( !newMsgs.isEmpty() ) {
01444     if ( mUserRights <= 0 || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) {
01445       newState( mProgress, i18n("Uploading messages to server"));
01446       CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
01447       connect( job, SIGNAL( progress( unsigned long, unsigned long) ),
01448                this, SLOT( slotPutProgress(unsigned long, unsigned long) ) );
01449       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01450       job->start();
01451       return;
01452     } else {
01453       KMCommand *command = rescueUnsyncedMessages();
01454       connect( command, SIGNAL( completed( KMCommand * ) ),
01455                this, SLOT( serverSyncInternal() ) );
01456     }
01457   } else { // nothing to upload
01458     if ( mUserRights != mOldUserRights && (mOldUserRights & KMail::ACLJobs::Insert)
01459          && !(mUserRights & KMail::ACLJobs::Insert) ) {
01460       // write access revoked
01461       KMessageBox::information( 0, i18n("<p>Your access rights to folder <b>%1</b> have been restricted, "
01462           "it will no longer be possible to add messages to this folder.</p>").arg( folder()->prettyURL() ),
01463           i18n("Acces rights revoked"), "KMailACLRevocationNotification" );
01464     }
01465   }
01466   newState( mProgress, i18n("No messages to upload to server"));
01467   serverSyncInternal();
01468 }
01469 
01470 /* Progress info during uploadNewMessages */
01471 void KMFolderCachedImap::slotPutProgress( unsigned long done, unsigned long total )
01472 {
01473   // (going from mProgress to mProgress+10)
01474   int progressSpan = 10;
01475   newState( mProgress + (progressSpan * done) / total, QString::null );
01476   if ( done == total ) // we're done
01477     mProgress += progressSpan;
01478 }
01479 
01480 /* Upload message flags to server */
01481 void KMFolderCachedImap::uploadFlags()
01482 {
01483   if ( !uidMap.isEmpty() ) {
01484     mStatusFlagsJobs = 0;
01485     newState( mProgress, i18n("Uploading status of messages to server"));
01486 
01487     // FIXME DUPLICATED FROM KMFOLDERIMAP
01488     QMap< QString, QStringList > groups;
01489     //open(); //already done
01490     for( int i = 0; i < count(); ++i ) {
01491       KMMsgBase* msg = getMsgBase( i );
01492       if( !msg || msg->UID() == 0 )
01493         // Either not a valid message or not one that is on the server yet
01494         continue;
01495       if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
01496            && !mStatusChangedLocally ) {
01497         // This message has not had its status changed locally
01498         continue;
01499       }
01500 
01501       QString flags = KMFolderImap::statusToFlags(msg->status(), mPermanentFlags);
01502       // Collect uids for each typem of flags.
01503       QString uid;
01504       uid.setNum( msg->UID() );
01505       groups[flags].append(uid);
01506     }
01507     QMapIterator< QString, QStringList > dit;
01508     for( dit = groups.begin(); dit != groups.end(); ++dit ) {
01509       QCString flags = dit.key().latin1();
01510       QStringList sets = KMFolderImap::makeSets( (*dit), true );
01511       mStatusFlagsJobs += sets.count(); // ### that's not in kmfolderimap....
01512       // Send off a status setting job for each set.
01513       for( QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01514         QString imappath = imapPath() + ";UID=" + ( *slit );
01515         mAccount->setImapStatus(folder(), imappath, flags);
01516       }
01517     }
01518     // FIXME END DUPLICATED FROM KMFOLDERIMAP
01519 
01520     if ( mStatusFlagsJobs ) {
01521       connect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01522                this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01523       return;
01524     }
01525   }
01526   newState( mProgress, i18n("No messages to upload to server"));
01527   serverSyncInternal();
01528 }
01529 
01530 void KMFolderCachedImap::uploadSeenFlags()
01531 {
01532   if ( !uidMap.isEmpty() ) {
01533     mStatusFlagsJobs = 0;
01534     newState( mProgress, i18n("Uploading status of messages to server"));
01535 
01536     QValueList<ulong> seenUids, unseenUids;
01537     for( int i = 0; i < count(); ++i ) {
01538       KMMsgBase* msg = getMsgBase( i );
01539       if( !msg || msg->UID() == 0 )
01540         // Either not a valid message or not one that is on the server yet
01541         continue;
01542 
01543       if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
01544            && !mStatusChangedLocally ) {
01545         // This message has not had its status changed locally
01546         continue;
01547       }
01548 
01549       if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
01550         seenUids.append( msg->UID() );
01551       else
01552         unseenUids.append( msg->UID() );
01553     }
01554     if ( !seenUids.isEmpty() ) {
01555       QStringList sets = KMFolderImap::makeSets( seenUids, true );
01556       mStatusFlagsJobs += sets.count();
01557       for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
01558         QString imappath = imapPath() + ";UID=" + ( *it );
01559         mAccount->setImapSeenStatus( folder(), imappath, true );
01560       }
01561     }
01562     if ( !unseenUids.isEmpty() ) {
01563       QStringList sets = KMFolderImap::makeSets( unseenUids, true );
01564       mStatusFlagsJobs += sets.count();
01565       for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
01566         QString imappath = imapPath() + ";UID=" + ( *it );
01567         mAccount->setImapSeenStatus( folder(), imappath, false );
01568       }
01569     }
01570 
01571     if ( mStatusFlagsJobs ) {
01572       connect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01573                this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01574       return;
01575     }
01576   }
01577   newState( mProgress, i18n("No messages to upload to server"));
01578   serverSyncInternal();
01579 }
01580 
01581 void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const QString&, bool cont)
01582 {
01583   if ( mSyncState == SYNC_STATE_INITIAL ){
01584       //kdDebug(5006) << "IMAP status changed but reset " << endl;
01585       return; // we were reset
01586   }
01587   //kdDebug(5006) << "IMAP status changed for folder: " << folder->prettyURL() << endl;
01588   if ( folder->storage() == this ) {
01589     --mStatusFlagsJobs;
01590     if ( mStatusFlagsJobs == 0 || !cont ) // done or aborting
01591       disconnect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01592                   this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01593     if ( mStatusFlagsJobs == 0 && cont ) {
01594       mProgress += 5;
01595       serverSyncInternal();
01596       //kdDebug(5006) << "Proceeding with mailcheck." << endl;
01597     }
01598   }
01599 }
01600 
01601 // This is not perfect, what if the status didn't really change? Oh well ...
01602 void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle)
01603 {
01604   KMFolderMaildir::setStatus( idx, status, toggle );
01605   const KMMsgBase *msg = getMsgBase( idx );
01606   Q_ASSERT( msg );
01607   if ( msg )
01608     mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
01609 }
01610 
01611 void KMFolderCachedImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01612 {
01613   KMFolderMaildir::setStatus(ids, status, toggle);
01614   for (QValueList<int>::iterator it = ids.begin(); it != ids.end(); it++ ) {
01615     const KMMsgBase *msg = getMsgBase( *it );
01616     Q_ASSERT( msg );
01617     if ( msg )
01618       mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
01619   }
01620 }
01621 
01622 /* Upload new folders to server */
01623 void KMFolderCachedImap::createNewFolders()
01624 {
01625   QValueList<KMFolderCachedImap*> newFolders = findNewFolders();
01626   //kdDebug(5006) << label() << " createNewFolders:" << newFolders.count() << " new folders." << endl;
01627   if( !newFolders.isEmpty() ) {
01628     newState( mProgress, i18n("Creating subfolders on server"));
01629     CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this );
01630     connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
01631     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01632     job->start();
01633   } else {
01634     serverSyncInternal();
01635   }
01636 }
01637 
01638 QValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
01639 {
01640   QValueList<KMFolderCachedImap*> newFolders;
01641   if( folder() && folder()->child() ) {
01642     KMFolderNode *node = folder()->child()->first();
01643     while( node ) {
01644       if( !node->isDir() ) {
01645         if( static_cast<KMFolder*>(node)->folderType() != KMFolderTypeCachedImap ) {
01646           kdError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
01647                         << node->name() << " is not an IMAP folder\n";
01648           node = folder()->child()->next();
01649           assert(0);
01650         }
01651         KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01652         if( folder->imapPath().isEmpty() ) {
01653           newFolders << folder;
01654         }
01655       }
01656       node = folder()->child()->next();
01657     }
01658   }
01659   return newFolders;
01660 }
01661 
01662 bool KMFolderCachedImap::deleteMessages()
01663 {
01664   /* Delete messages from cache that are gone from the server */
01665   QPtrList<KMMessage> msgsForDeletion;
01666 
01667   // It is not possible to just go over all indices and remove
01668   // them one by one because the index list can get resized under
01669   // us. So use msg pointers instead
01670 
01671   QStringList uids;
01672   QMap<ulong,int>::const_iterator it = uidMap.constBegin();
01673   for( ; it != uidMap.end(); it++ ) {
01674     ulong uid ( it.key() );
01675     if( uid!=0 && !uidsOnServer.find( uid ) ) {
01676       uids << QString::number( uid );
01677       msgsForDeletion.append( getMsg( *it ) );
01678     }
01679   }
01680 
01681   if( !msgsForDeletion.isEmpty() ) {
01682 #if MAIL_LOSS_DEBUGGING
01683       if ( KMessageBox::warningYesNo(
01684              0, i18n( "<qt><p>Mails on the server in folder <b>%1</b> were deleted. "
01685                  "Do you want to delete them locally?<br>UIDs: %2</p></qt>" )
01686              .arg( folder()->prettyURL() ).arg( uids.join(",") ) ) == KMessageBox::Yes )
01687 #endif
01688         removeMsg( msgsForDeletion );
01689   }
01690 
01691   if ( mUserRights > 0 && !( mUserRights & KMail::ACLJobs::Delete ) )
01692     return false;
01693 
01694   /* Delete messages from the server that we dont have anymore */
01695   if( !uidsForDeletionOnServer.isEmpty() ) {
01696     newState( mProgress, i18n("Deleting removed messages from server"));
01697     QStringList sets = KMFolderImap::makeSets( uidsForDeletionOnServer, true );
01698     uidsForDeletionOnServer.clear();
01699     kdDebug(5006) << "Deleting " << sets.count() << " sets of messages from server folder " << imapPath() << endl;
01700     CachedImapJob *job = new CachedImapJob( sets, CachedImapJob::tDeleteMessage, this );
01701     connect( job, SIGNAL( result(KMail::FolderJob *) ),
01702              this, SLOT( slotDeleteMessagesResult(KMail::FolderJob *) ) );
01703     job->start();
01704     return true;
01705   } else {
01706 
01707     // Nothing to delete on the server, make sure the map is clear again.
01708     // Normally this wouldn't be necessary, but there can be stale maps because of
01709     // https://issues.kolab.org/issue3833.
01710     mDeletedUIDsSinceLastSync.clear();
01711     return false;
01712   }
01713 }
01714 
01715 void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob* job )
01716 {
01717   if ( job->error() ) {
01718     // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages
01719     mSyncState = SYNC_STATE_GET_MESSAGES;
01720   } else {
01721     // deleting on the server went fine, clear the pending deletions cache
01722     mDeletedUIDsSinceLastSync.clear();
01723   }
01724   mProgress += 10;
01725   serverSyncInternal();
01726 }
01727 
01728 void KMFolderCachedImap::checkUidValidity() {
01729   // IMAP root folders don't seem to have a UID validity setting.
01730   // Also, don't try the uid validity on new folders
01731   if( imapPath().isEmpty() || imapPath() == "/" )
01732     // Just proceed
01733     serverSyncInternal();
01734   else {
01735     newState( mProgress, i18n("Checking folder validity"));
01736     CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this );
01737     connect( job, SIGNAL(permanentFlags(int)), SLOT(slotPermanentFlags(int)) );
01738     connect( job, SIGNAL( result( KMail::FolderJob* ) ),
01739              this, SLOT( slotCheckUidValidityResult( KMail::FolderJob* ) ) );
01740     job->start();
01741   }
01742 }
01743 
01744 void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob* job )
01745 {
01746   if ( job->error() ) { // there was an error and the user chose "continue"
01747     // We can't continue doing anything in the same folder though, it would delete all mails.
01748     // But we can continue to subfolders if any. Well we can also try annotation/acl stuff...
01749     mSyncState = SYNC_STATE_HANDLE_INBOX;
01750   }
01751   mProgress += 5;
01752   serverSyncInternal();
01753 }
01754 
01755 void KMFolderCachedImap::slotPermanentFlags(int flags)
01756 {
01757   mPermanentFlags = flags;
01758 }
01759 
01760 /* This will only list the messages in a folder.
01761    No directory listing done*/
01762 void KMFolderCachedImap::listMessages() {
01763   bool groupwareOnly = GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
01764                && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
01765                && folder()->isSystemFolder()
01766                && mImapPath == "/INBOX/";
01767   // Don't list messages on the root folder, and skip the inbox, if this is
01768   // the inbox of a groupware-only dimap account
01769   if( imapPath() == "/" || groupwareOnly ) {
01770     serverSyncInternal();
01771     return;
01772   }
01773 
01774   if( !mAccount->slave() ) { // sync aborted
01775     resetSyncState();
01776     emit folderComplete( this, false );
01777     return;
01778   }
01779   uidsOnServer.clear();
01780   uidsOnServer.resize( count() * 2 );
01781   uidsForDeletionOnServer.clear();
01782   mMsgsForDownload.clear();
01783   mUidsForDownload.clear();
01784   // listing is only considered successful if saw a syntactically correct imapdigest
01785   mFoundAnIMAPDigest = false;
01786 
01787   CachedImapJob* job = new CachedImapJob( FolderJob::tListMessages, this );
01788   connect( job, SIGNAL( result(KMail::FolderJob *) ),
01789            this, SLOT( slotGetLastMessagesResult(KMail::FolderJob *) ) );
01790   job->start();
01791 }
01792 
01793 void KMFolderCachedImap::slotGetLastMessagesResult(KMail::FolderJob *job)
01794 {
01795   getMessagesResult(job, true);
01796 }
01797 
01798 // Connected to the listMessages job in CachedImapJob
01799 void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
01800 {
01801   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01802   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
01803     kdDebug(5006) << "could not find job!?!?!" << endl;
01804     // be sure to reset the sync state, if the listing was partial we would
01805     // otherwise delete not-listed mail locally, and on the next sync on the server
01806     // as well
01807     mSyncState = SYNC_STATE_HANDLE_INBOX;
01808     serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
01809     return;
01810   }
01811   (*it).cdata += QCString(data, data.size() + 1);
01812   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
01813   if (pos > 0) {
01814     int a = (*it).cdata.find("\r\nX-uidValidity:");
01815     if (a != -1) {
01816       int b = (*it).cdata.find("\r\n", a + 17);
01817       setUidValidity((*it).cdata.mid(a + 17, b - a - 17));
01818     }
01819     a = (*it).cdata.find("\r\nX-Access:");
01820     // Only trust X-Access (i.e. the imap select info) if we don't know mUserRights.
01821     // The latter is more accurate (checked on every sync) whereas X-Access is only
01822     // updated when selecting the folder again, which might not happen if using
01823     // RMB / Check Mail in this folder. We don't need two (potentially conflicting)
01824     // sources for the readonly setting, in any case.
01825     if (a != -1 && mUserRights == -1 ) {
01826       int b = (*it).cdata.find("\r\n", a + 12);
01827       const QString access = (*it).cdata.mid(a + 12, b - a - 12);
01828       setReadOnly( access == "Read only" );
01829     }
01830     (*it).cdata.remove(0, pos);
01831     mFoundAnIMAPDigest = true;
01832   }
01833   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01834   // Start with something largish when rebuilding the cache
01835   if ( uidsOnServer.size() == 0 )
01836     uidsOnServer.resize( KMail::nextPrime( 2000 ) );
01837   const int v = 42;
01838   while (pos >= 0) {
01839       /*
01840     KMMessage msg;
01841     msg.fromString((*it).cdata.mid(16, pos - 16));
01842     const int flags = msg.headerField("X-Flags").toInt();
01843     const ulong size = msg.headerField("X-Length").toULong();
01844     const ulong uid = msg.UID();
01845        */
01846     // The below is optimized for speed, not prettiness. The commented out chunk
01847     // above was the solution copied from kmfolderimap, and it's 15-20% slower.
01848     const QCString& entry( (*it).cdata );
01849     const int indexOfUID = entry.find("X-UID", 16);
01850     const int startOfUIDValue = indexOfUID  + 7;
01851     const int indexOfLength = entry.find("X-Length", startOfUIDValue ); // we know length comes after UID
01852     const int startOfLengthValue = indexOfLength + 10;
01853     const int indexOfFlags = entry.find("X-Flags", startOfLengthValue ); // we know flags comes last
01854     const int startOfFlagsValue = indexOfFlags + 9;
01855 
01856     const int flags = entry.mid( startOfFlagsValue, entry.find( '\r', startOfFlagsValue ) - startOfFlagsValue ).toInt();
01857     const ulong size = entry.mid( startOfLengthValue, entry.find( '\r', startOfLengthValue ) - startOfLengthValue ).toULong();
01858     const ulong uid = entry.mid( startOfUIDValue, entry.find( '\r', startOfUIDValue ) - startOfUIDValue ).toULong();
01859 
01860     const bool deleted = ( flags & 8 );
01861     if ( !deleted ) {
01862       if( uid != 0 ) {
01863         if ( uidsOnServer.count() == uidsOnServer.size() ) {
01864           uidsOnServer.resize( KMail::nextPrime( uidsOnServer.size() * 2 ) );
01865           //kdDebug( 5006 ) << "Resizing to: " << uidsOnServer.size() << endl;
01866         }
01867         uidsOnServer.insert( uid, &v );
01868       }
01869       bool redownload = false;
01870       if (  uid <= lastUid() ) {
01871        /*
01872         * If this message UID is not present locally, then it must
01873         * have been deleted by the user, so we delete it on the
01874         * server also. If we don't have delete permissions on the server,
01875         * re-download the message, it must have vanished by some error, or
01876         * while we still thought we were allowed to delete (ACL change).
01877         *
01878         * This relies heavily on lastUid() being correct at all times.
01879         */
01880         // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<label()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl;
01881         KMMsgBase *existingMessage = findByUID(uid);
01882         if( !existingMessage ) {
01883 #if MAIL_LOSS_DEBUGGING
01884            kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should delete it" << endl;
01885 #endif
01886           // double check we deleted it since the last sync
01887            if ( mDeletedUIDsSinceLastSync.contains(uid) ) {
01888                if ( mUserRights <= 0 || ( mUserRights & KMail::ACLJobs::Delete ) ) {
01889 #if MAIL_LOSS_DEBUGGING
01890                    kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
01891 #endif
01892                    uidsForDeletionOnServer << uid;
01893                } else {
01894                    redownload = true;
01895                }
01896            } else {
01897                kdDebug(5006) << "WARNING: ####### " << endl;
01898                kdDebug(5006) << "Message locally missing but not deleted in folder: " << folder()->prettyURL() << endl;
01899                kdDebug(5006) << "The missing UID: " << uid << ". It will be redownloaded " << endl;
01900                redownload = true;
01901            }
01902 
01903         } else {
01904           // if this is a read only folder, ignore status updates from the server
01905           // since we can't write our status back our local version is what has to
01906           // be considered correct.
01907           if ( !mReadOnly || !GlobalSettings::allowLocalFlags() ) {
01908             /* The message is OK, update flags */
01909             KMFolderImap::flagsToStatus( existingMessage, flags,  false, mReadOnly ? INT_MAX : mPermanentFlags );
01910           } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
01911             KMFolderImap::seenFlagToStatus( existingMessage, flags );
01912           }
01913         }
01914         // kdDebug(5006) << "message with uid " << uid << " found in the local cache. " << endl;
01915       }
01916       if ( uid > lastUid() || redownload ) {
01917 #if MAIL_LOSS_DEBUGGING
01918         kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should download it" << endl;
01919 #endif
01920         // The message is new since the last sync, but we might have just uploaded it, in which case
01921         // the uid map already contains it.
01922         if ( !uidMap.contains( uid ) ) {
01923           mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size);
01924           if( imapPath() == "/INBOX/" )
01925             mUidsForDownload << uid;
01926         }
01927         // Remember the highest uid and once the download is completed, update mLastUid
01928         if ( uid > mTentativeHighestUid ) {
01929 #if MAIL_LOSS_DEBUGGING
01930           kdDebug(5006) << "Setting the tentative highest UID to: " << uid << endl;
01931 #endif
01932           mTentativeHighestUid = uid;
01933         }
01934       }
01935     }
01936     (*it).cdata.remove(0, pos);
01937     (*it).done++;
01938     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01939   }
01940 }
01941 
01942 void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet )
01943 {
01944   mProgress += 10;
01945   if ( !job->error() && !mFoundAnIMAPDigest ) {
01946       kdWarning(5006) << "######## Folderlisting did not complete, but there was no error! "
01947           "Aborting sync of folder: " << folder()->prettyURL() << endl;
01948 #if MAIL_LOSS_DEBUGGING
01949       kmkernel->emergencyExit( i18n("Folder listing failed in interesting ways." ) );
01950 #endif
01951   }
01952   if( job->error() ) { // error listing messages but the user chose to continue
01953     mContentState = imapNoInformation;
01954     mSyncState = SYNC_STATE_HANDLE_INBOX; // be sure not to continue in this folder
01955   } else {
01956     if( lastSet ) { // always true here (this comes from online-imap...)
01957       mContentState = imapFinished;
01958       mUIDsOfLocallyChangedStatuses.clear(); // we are up to date again
01959       mStatusChangedLocally = false;
01960     }
01961   }
01962   serverSyncInternal();
01963 }
01964 
01965 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
01966 {
01967   int progressSpan = 100 - 5 - mProgress;
01968   int additionalProgress = ( total == 0 ) ?
01969                            progressSpan :
01970                            ( progressSpan * done ) / total;
01971 
01972   // Progress info while retrieving new emails
01973   // (going from mProgress to mProgress+progressSpan)
01974   newState( mProgress + additionalProgress, QString::null );
01975 }
01976 
01977 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
01978 {
01979   assert( aAccount->isA("KMAcctCachedImap") );
01980   mAccount = aAccount;
01981   if( imapPath()=="/" ) aAccount->setFolder( folder() );
01982 
01983   // Folder was renamed in a previous session, and the user didn't sync yet
01984   QString newName = mAccount->renamedFolder( imapPath() );
01985   if ( !newName.isEmpty() )
01986     folder()->setLabel( newName );
01987 
01988   if( !folder() || !folder()->child() || !folder()->child()->count() ) return;
01989   for( KMFolderNode* node = folder()->child()->first(); node;
01990        node = folder()->child()->next() )
01991     if (!node->isDir())
01992       static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
01993 }
01994 
01995 void KMFolderCachedImap::listNamespaces()
01996 {
01997   ImapAccountBase::ListType type = ImapAccountBase::List;
01998   if ( mAccount->onlySubscribedFolders() )
01999     type = ImapAccountBase::ListSubscribed;
02000 
02001   kdDebug(5006) << "listNamespaces " << mNamespacesToList << endl;
02002   if ( mNamespacesToList.isEmpty() ) {
02003     mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
02004     mPersonalNamespacesCheckDone = true;
02005 
02006     QStringList ns = mAccount->namespaces()[ImapAccountBase::OtherUsersNS];
02007     ns += mAccount->namespaces()[ImapAccountBase::SharedNS];
02008     mNamespacesToCheck = ns.count();
02009     for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
02010     {
02011       if ( (*it).isEmpty() ) {
02012         // ignore empty listings as they have been listed before
02013         --mNamespacesToCheck;
02014         continue;
02015       }
02016       KMail::ListJob* job = new KMail::ListJob( mAccount, type, this, mAccount->addPathToNamespace( *it ) );
02017       job->setHonorLocalSubscription( true );
02018       connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
02019               const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
02020           this, SLOT(slotCheckNamespace(const QStringList&, const QStringList&,
02021               const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
02022       job->start();
02023     }
02024     if ( mNamespacesToCheck == 0 ) {
02025       serverSyncInternal();
02026     }
02027     return;
02028   }
02029   mPersonalNamespacesCheckDone = false;
02030 
02031   QString ns = mNamespacesToList.front();
02032   mNamespacesToList.pop_front();
02033 
02034   mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
02035   newState( mProgress, i18n("Retrieving folders for namespace %1").arg(ns));
02036   KMail::ListJob* job = new KMail::ListJob( mAccount, type, this,
02037       mAccount->addPathToNamespace( ns ) );
02038   job->setNamespace( ns );
02039   job->setHonorLocalSubscription( true );
02040   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
02041           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
02042       this, SLOT(slotListResult(const QStringList&, const QStringList&,
02043           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
02044   job->start();
02045 }
02046 
02047 void KMFolderCachedImap::slotCheckNamespace( const QStringList& subfolderNames,
02048                                              const QStringList& subfolderPaths,
02049                                              const QStringList& subfolderMimeTypes,
02050                                              const QStringList& subfolderAttributes,
02051                                              const ImapAccountBase::jobData& jobData )
02052 {
02053   Q_UNUSED( subfolderPaths );
02054   Q_UNUSED( subfolderMimeTypes );
02055   Q_UNUSED( subfolderAttributes );
02056   --mNamespacesToCheck;
02057   kdDebug(5006) << "slotCheckNamespace " << subfolderNames << ",remain=" <<
02058    mNamespacesToCheck << endl;
02059 
02060   // get a correct foldername:
02061   // strip / and make sure it does not contain the delimiter
02062   QString name = jobData.path.mid( 1, jobData.path.length()-2 );
02063   name.remove( mAccount->delimiterForNamespace( name ) );
02064   if ( name.isEmpty() ) {
02065     // should not happen
02066     kdWarning(5006) << "slotCheckNamespace: ignoring empty folder!" << endl;
02067     return;
02068   }
02069 
02070   folder()->createChildFolder();
02071   KMFolderNode *node = 0;
02072   for ( node = folder()->child()->first(); node;
02073         node = folder()->child()->next())
02074   {
02075     if ( !node->isDir() && node->name() == name )
02076       break;
02077   }
02078   if ( !subfolderNames.isEmpty() ) {
02079     if ( node ) {
02080       // folder exists so we have nothing to do - it will be listed later
02081       kdDebug(5006) << "found namespace folder " << name << endl;
02082     } else
02083     {
02084       // create folder
02085       kdDebug(5006) << "create namespace folder " << name << endl;
02086       KMFolder* newFolder = folder()->child()->createFolder( name, false,
02087           KMFolderTypeCachedImap );
02088       if ( newFolder ) {
02089         KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>( newFolder->storage() );
02090         f->setImapPath( mAccount->addPathToNamespace( name ) );
02091         f->setNoContent( true );
02092         f->setAccount( mAccount );
02093         f->close("cachedimap");
02094         kmkernel->dimapFolderMgr()->contentsChanged();
02095       }
02096     }
02097   } else {
02098     if ( node ) {
02099       kdDebug(5006) << "delete namespace folder " << name << endl;
02100       KMFolder* fld = static_cast<KMFolder*>(node);
02101       kmkernel->dimapFolderMgr()->remove( fld );
02102     }
02103   }
02104 
02105   if ( mNamespacesToCheck == 0 ) {
02106     // all namespaces are done so continue with the next step
02107     serverSyncInternal();
02108   }
02109 }
02110 
02111 // This lists the subfolders on the server
02112 // and (in slotListResult) takes care of folders that have been removed on the server
02113 bool KMFolderCachedImap::listDirectory()
02114 {
02115   if( !mAccount->slave() ) { // sync aborted
02116     resetSyncState();
02117     emit folderComplete( this, false );
02118     return false;
02119   }
02120   mSubfolderState = imapInProgress;
02121 
02122   // get the folders
02123   ImapAccountBase::ListType type = ImapAccountBase::List;
02124   if ( mAccount->onlySubscribedFolders() )
02125     type = ImapAccountBase::ListSubscribed;
02126   KMail::ListJob* job = new KMail::ListJob( mAccount, type, this );
02127   job->setHonorLocalSubscription( true );
02128   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
02129           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
02130       this, SLOT(slotListResult(const QStringList&, const QStringList&,
02131           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
02132   job->start();
02133 
02134   return true;
02135 }
02136 
02137 void KMFolderCachedImap::slotListResult( const QStringList& folderNames,
02138                                          const QStringList& folderPaths,
02139                                          const QStringList& folderMimeTypes,
02140                                          const QStringList& folderAttributes,
02141                                          const ImapAccountBase::jobData& jobData )
02142 {
02143   Q_UNUSED( jobData );
02144   //kdDebug(5006) << label() << ": folderNames=" << folderNames << " folderPaths="
02145   //<< folderPaths << " mimeTypes=" << folderMimeTypes << endl;
02146   mSubfolderNames = folderNames;
02147   mSubfolderPaths = folderPaths;
02148   mSubfolderMimeTypes = folderMimeTypes;
02149   mSubfolderState = imapFinished;
02150   mSubfolderAttributes = folderAttributes;
02151   //kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl;
02152 
02153   folder()->createChildFolder();
02154   KMFolderNode *node = folder()->child()->first();
02155   bool root = ( this == mAccount->rootFolder() );
02156 
02157   QPtrList<KMFolder> toRemove;
02158   bool emptyList = ( root && mSubfolderNames.empty() );
02159   if ( !emptyList ) {
02160     while (node) {
02161       if (!node->isDir() ) {
02162         KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02163 
02164         if ( mSubfolderNames.findIndex(node->name()) == -1 ) {
02165           QString name = node->name();
02166           // as more than one namespace can be listed in the root folder we need to make sure
02167           // that the folder is within the current namespace
02168           bool isInNamespace = ( jobData.curNamespace.isEmpty() ||
02169               jobData.curNamespace == mAccount->namespaceForFolder( f ) );
02170           // ignore some cases
02171           bool ignore = root && ( f->imapPath() == "/INBOX/" ||
02172               mAccount->isNamespaceFolder( name ) || !isInNamespace );
02173 
02174           // This subfolder isn't present on the server
02175           if( !f->imapPath().isEmpty() && !ignore  ) {
02176             // The folder has an imap path set, so it has been
02177             // on the server before. Delete it locally.
02178             toRemove.append( f->folder() );
02179             kdDebug(5006) << node->name() << " isn't on the server. It has an imapPath -> delete it locally" << endl;
02180           }
02181         } else { // folder both local and on server
02182           //kdDebug(5006) << node->name() << " is on the server." << endl;
02183 
02187           int index = mSubfolderNames.findIndex( node->name() );
02188           f->mFolderAttributes = folderAttributes[ index ];
02189         }
02190       } else {
02191         //kdDebug(5006) << "skipping dir node:" << node->name() << endl;
02192       }
02193       node = folder()->child()->next();
02194     }
02195   }
02196 
02197   for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) {
02198     rescueUnsyncedMessagesAndDeleteFolder( doomed );
02199   }
02200 
02201   mProgress += 5;
02202 
02203   // just in case there is nothing to rescue
02204   slotRescueDone( 0 );
02205 }
02206 
02207 // This synchronizes the local folders as needed (creation/deletion). No network communication here.
02208 void KMFolderCachedImap::listDirectory2()
02209 {
02210   QString path = folder()->path();
02211   kmkernel->dimapFolderMgr()->quiet(true);
02212 
02213   bool root = ( this == mAccount->rootFolder() );
02214   if ( root && !mAccount->hasInbox() )
02215   {
02216     KMFolderCachedImap *f = 0;
02217     KMFolderNode *node;
02218     // create the INBOX
02219     for (node = folder()->child()->first(); node; node = folder()->child()->next())
02220       if (!node->isDir() && node->name() == "INBOX") break;
02221     if (node) {
02222       f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02223     } else {
02224       KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap);
02225       if ( newFolder ) {
02226         f = static_cast<KMFolderCachedImap*>(newFolder->storage());
02227       }
02228     }
02229     if ( f ) {
02230       f->setAccount( mAccount );
02231       f->setImapPath( "/INBOX/" );
02232       f->folder()->setLabel( i18n("inbox") );
02233     }
02234     if (!node) {
02235       if ( f )
02236         f->close("cachedimap");
02237       kmkernel->dimapFolderMgr()->contentsChanged();
02238     }
02239     // so we have an INBOX
02240     mAccount->setHasInbox( true );
02241   }
02242 
02243   if ( root && !mSubfolderNames.isEmpty() ) {
02244     KMFolderCachedImap* parent =
02245       findParent( mSubfolderPaths.first(), mSubfolderNames.first() );
02246     if ( parent ) {
02247       kdDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to "
02248         << parent->label() << endl;
02249       mSubfolderNames.clear();
02250     }
02251   }
02252 
02253   // Find all subfolders present on server but not on disk
02254   QValueVector<int> foldersNewOnServer;
02255   for (uint i = 0; i < mSubfolderNames.count(); i++) {
02256 
02257     // Find the subdir, if already present
02258     KMFolderCachedImap *f = 0;
02259     KMFolderNode *node = 0;
02260     for (node = folder()->child()->first(); node;
02261          node = folder()->child()->next())
02262       if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
02263 
02264     if (!node) {
02265       // This folder is not present here
02266       // Either it's new on the server, or we just deleted it.
02267       QString subfolderPath = mSubfolderPaths[i];
02268       // The code used to look at the uidcache to know if it was "just deleted".
02269       // But this breaks with noContent folders and with shared folders.
02270       // So instead we keep a list in the account.
02271       bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath );
02272       // That list is saved/restored across sessions, but to avoid any mistake,
02273       // ask for confirmation if the folder was deleted in a previous session
02274       // (could be that the folder was deleted & recreated meanwhile from another client...)
02275       if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) {
02276            locallyDeleted = KMessageBox::warningYesNo(
02277              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;
02278       }
02279 
02280       if ( locallyDeleted ) {
02281         kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl;
02282         foldersForDeletionOnServer += mAccount->deletedFolderPaths( subfolderPath ); // grab all subsubfolders too
02283       } else {
02284         kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl;
02285         foldersNewOnServer.append( i );
02286       }
02287     } else { // Folder found locally
02288       if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap )
02289         f = dynamic_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02290       if( f ) {
02291         // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath()
02292         //               << "\nSetting imapPath " << mSubfolderPaths[i] << endl;
02293         // Write folder settings
02294         f->setAccount(mAccount);
02295         f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
02296         f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest");
02297         f->setImapPath(mSubfolderPaths[i]);
02298       }
02299     }
02300   }
02301 
02302   /* In case we are ignoring non-groupware folders, and this is the groupware
02303    * main account, find out the contents types of folders that have newly
02304    * appeared on the server. Otherwise just create them and finish listing.
02305    * If a folder is already known to be locally unsubscribed, it won't be
02306    * listed at all, on this level, so these are only folders that we are
02307    * seeing for the first time. */
02308 
02309   /*  Note: We ask the globalsettings, and not the current state of the
02310    *  kmkernel->iCalIface().isEnabled(), since that is false during the
02311    *  very first sync, where we already want to filter. */
02312   if ( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
02313      && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
02314      && mAccount->hasAnnotationSupport()
02315      && GlobalSettings::self()->theIMAPResourceEnabled()
02316      && !foldersNewOnServer.isEmpty() ) {
02317 
02318     QStringList paths;
02319     for ( uint i = 0; i < foldersNewOnServer.count(); ++i )
02320       paths << mSubfolderPaths[ foldersNewOnServer[i] ];
02321 
02322     AnnotationJobs::MultiUrlGetAnnotationJob* job =
02323       AnnotationJobs::multiUrlGetAnnotation( mAccount->slave(), mAccount->getUrl(), paths, KOLAB_FOLDERTYPE );
02324     ImapAccountBase::jobData jd( QString::null, folder() );
02325     jd.cancellable = true;
02326     mAccount->insertJob(job, jd);
02327     connect( job, SIGNAL(result(KIO::Job *)),
02328         SLOT(slotMultiUrlGetAnnotationResult(KIO::Job *)) );
02329 
02330   } else {
02331     createFoldersNewOnServerAndFinishListing( foldersNewOnServer );
02332   }
02333 }
02334 
02335 void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const QValueVector<int> foldersNewOnServer )
02336 {
02337   for ( uint i = 0; i < foldersNewOnServer.count(); ++i ) {
02338     int idx = foldersNewOnServer[i];
02339     KMFolder* newFolder = folder()->child()->createFolder( mSubfolderNames[idx], false, KMFolderTypeCachedImap);
02340     if (newFolder) {
02341       KMFolderCachedImap *f = dynamic_cast<KMFolderCachedImap*>(newFolder->storage());
02342       kdDebug(5006) << " ####### Locally creating folder " << mSubfolderNames[idx] <<endl;
02343       f->close("cachedimap");
02344       f->setAccount(mAccount);
02345       f->mAnnotationFolderType = "FROMSERVER";
02346       f->setNoContent(mSubfolderMimeTypes[idx] == "inode/directory");
02347       f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest");
02348       f->setImapPath(mSubfolderPaths[idx]);
02349       f->mFolderAttributes = mSubfolderAttributes[idx];
02350       mNewlyCreatedSubfolders.append( QGuardedPtr<KMFolderCachedImap>( f ) );
02351       kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <<endl;
02352       //kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl;
02353       kmkernel->dimapFolderMgr()->contentsChanged();
02354     } else {
02355       kdDebug(5006) << "can't create folder " << mSubfolderNames[idx] <<endl;
02356     }
02357   }
02358 
02359   kmkernel->dimapFolderMgr()->quiet(false);
02360   emit listComplete(this);
02361   if ( !mPersonalNamespacesCheckDone ) {
02362     // we're not done with the namespaces
02363     mSyncState = SYNC_STATE_LIST_NAMESPACES;
02364   }
02365   serverSyncInternal();
02366 }
02367 
02368 //-----------------------------------------------------------------------------
02369 KMFolderCachedImap* KMFolderCachedImap::findParent( const QString& path,
02370                                                     const QString& name )
02371 {
02372   QString parent = path.left( path.length() - name.length() - 2 );
02373   if ( parent.length() > 1 )
02374   {
02375     // extract name of the parent
02376     parent = parent.right( parent.length() - 1 );
02377     if ( parent != label() )
02378     {
02379       KMFolderNode *node = folder()->child()->first();
02380       // look for a better parent
02381       while ( node )
02382       {
02383         if ( node->name() == parent )
02384         {
02385           KMFolder* fld = static_cast<KMFolder*>(node);
02386           KMFolderCachedImap* imapFld =
02387             static_cast<KMFolderCachedImap*>( fld->storage() );
02388           return imapFld;
02389         }
02390         node = folder()->child()->next();
02391       }
02392     }
02393   }
02394   return 0;
02395 }
02396 
02397 void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success)
02398 {
02399   Q_UNUSED(sub);
02400   //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl;
02401   if ( success ) {
02402     serverSyncInternal();
02403   }
02404   else
02405   {
02406     // success == false means the sync was aborted.
02407     if ( mCurrentSubfolder ) {
02408       Q_ASSERT( sub == mCurrentSubfolder );
02409       disconnectSubFolderSignals();
02410     }
02411 
02412     // Next step would be to check quota limits and then to close the folder, but don't bother with
02413     // both and close the folder right here, since we aborted.
02414     mSubfoldersForSync.clear();
02415     mSyncState = SYNC_STATE_INITIAL;
02416     close("cachedimap");
02417     emit syncStateChanged();
02418     emit folderComplete( this, false );
02419   }
02420 }
02421 
02422 void KMFolderCachedImap::slotSubFolderCloseToQuotaChanged()
02423 {
02424   if ( !mQuotaOnly ) {
02425     mSomeSubFolderCloseToQuotaChanged = true;
02426   }
02427 }
02428 
02429 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
02430 {
02431   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02432   if (it == mAccount->jobsEnd()) return;
02433   QBuffer buff((*it).data);
02434   buff.open(IO_WriteOnly | IO_Append);
02435   buff.writeBlock(data.data(), data.size());
02436   buff.close();
02437 }
02438 
02439 FolderJob*
02440 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
02441                                  QString, const AttachmentStrategy* ) const
02442 {
02443   QPtrList<KMMessage> msgList;
02444   msgList.append( msg );
02445   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
02446   job->setParentFolder( this );
02447   return job;
02448 }
02449 
02450 FolderJob*
02451 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
02452                                  FolderJob::JobType jt, KMFolder *folder ) const
02453 {
02454   //FIXME: how to handle sets here?
02455   Q_UNUSED( sets );
02456   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
02457   job->setParentFolder( this );
02458   return job;
02459 }
02460 
02461 void
02462 KMFolderCachedImap::setUserRights( unsigned int userRights )
02463 {
02464   mUserRights = userRights;
02465   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02466 }
02467 
02468 void
02469 KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder )
02470 {
02471   if ( folder->storage() == this ) {
02472     disconnect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
02473                 this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
02474     if ( mUserRights == 0 ) // didn't work
02475       mUserRights = -1; // error code (used in folderdia)
02476     else
02477       setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 );
02478     mProgress += 5;
02479     serverSyncInternal();
02480   }
02481 }
02482 
02483 void
02484 KMFolderCachedImap::setReadOnly( bool readOnly )
02485 {
02486   if ( readOnly != mReadOnly ) {
02487     mReadOnly = readOnly;
02488     emit readOnlyChanged( folder() );
02489   }
02490 }
02491 
02492 void
02493 KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job*, const KMail::ACLList& aclList )
02494 {
02495   if ( folder->storage() == this ) {
02496     disconnect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
02497                 this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
02498     mACLList = aclList;
02499     serverSyncInternal();
02500   }
02501 }
02502 
02503 void
02504 KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info )
02505 {
02506   setQuotaInfo( info );
02507 }
02508 
02509 void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info )
02510 {
02511     if ( info != mQuotaInfo ) {
02512       const bool wasCloseToQuota = isCloseToQuota();
02513       mQuotaInfo = info;
02514       writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02515       if ( wasCloseToQuota != isCloseToQuota() ) {
02516         emit closeToQuotaChanged();
02517       }
02518       emit folderSizeChanged();
02519     }
02520 }
02521 
02522 void
02523 KMFolderCachedImap::setACLList( const ACLList& arr )
02524 {
02525   mACLList = arr;
02526 }
02527 
02528 void
02529 KMFolderCachedImap::slotMultiSetACLResult(KIO::Job *job)
02530 {
02531   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02532   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02533   if ( (*it).parent != folder() ) return; // Shouldn't happen
02534 
02535   if ( job->error() )
02536     // Display error but don't abort the sync just for this
02537     // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel
02538     job->showErrorDialog();
02539   else
02540     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02541 
02542   if (mAccount->slave()) mAccount->removeJob(job);
02543   serverSyncInternal();
02544 }
02545 
02546 void
02547 KMFolderCachedImap::slotACLChanged( const QString& userId, int permissions )
02548 {
02549   // The job indicates success in changing the permissions for this user
02550   // -> we note that it's been done.
02551   for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) {
02552     if ( (*it).userId == userId && (*it).permissions == permissions ) {
02553       if ( permissions == -1 ) // deleted
02554         mACLList.erase( it );
02555       else // added/modified
02556         (*it).changed = false;
02557       return;
02558     }
02559   }
02560 }
02561 
02562 // called by KMAcctCachedImap::killAllJobs
02563 void KMFolderCachedImap::resetSyncState()
02564 {
02565   if ( mSyncState == SYNC_STATE_INITIAL ) return;
02566   mSubfoldersForSync.clear();
02567   mNewlyCreatedSubfolders.clear();
02568   mSyncState = SYNC_STATE_INITIAL;
02569   close("cachedimap");
02570   // Don't use newState here, it would revert to mProgress (which is < current value when listing messages)
02571   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02572   QString str = i18n("Aborted");
02573   if (progressItem)
02574      progressItem->setStatus( str );
02575   emit statusMsg( str );
02576   emit syncStateChanged();
02577 }
02578 
02579 void KMFolderCachedImap::slotIncreaseProgress()
02580 {
02581   mProgress += 5;
02582 }
02583 
02584 void KMFolderCachedImap::newState( int progress, const QString& syncStatus )
02585 {
02586   //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl;
02587   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02588   if( progressItem )
02589     progressItem->setCompletedItems( progress );
02590   if ( !syncStatus.isEmpty() ) {
02591     QString str;
02592     // For a subfolder, show the label. But for the main folder, it's already shown.
02593     if ( mAccount->imapFolder() == this )
02594       str = syncStatus;
02595     else
02596       str = QString( "%1: %2" ).arg( label() ).arg( syncStatus );
02597     if( progressItem )
02598       progressItem->setStatus( str );
02599     emit statusMsg( str );
02600   }
02601   if( progressItem )
02602     progressItem->updateProgress();
02603 }
02604 
02605 void KMFolderCachedImap::setSubfolderState( imapState state )
02606 {
02607   mSubfolderState = state;
02608   if ( state == imapNoInformation && folder()->child() )
02609   {
02610     // pass through to childs
02611     KMFolderNode* node;
02612     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02613     for ( ; (node = it.current()); )
02614     {
02615       ++it;
02616       if (node->isDir()) continue;
02617       KMFolder *folder = static_cast<KMFolder*>(node);
02618       static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state );
02619     }
02620   }
02621 }
02622 
02623 void KMFolderCachedImap::setImapPath(const QString &path)
02624 {
02625   mImapPath = path;
02626 }
02627 
02628 static bool isFolderTypeKnownToUs( const QString &type )
02629 {
02630   for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
02631     FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
02632     if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) )
02633       return true;
02634   }
02635   return false;
02636 }
02637 
02638 // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
02639 // It is updated from the folder contents type and whether it's a standard resource folder.
02640 // This happens during the syncing phase and during initFolder for a new folder.
02641 // Don't do it earlier, e.g. from setContentsType:
02642 // on startup, it's too early there to know if this is a standard resource folder.
02643 void KMFolderCachedImap::updateAnnotationFolderType()
02644 {
02645   QString oldType = mAnnotationFolderType;
02646   QString oldSubType;
02647   int dot = oldType.find( '.' );
02648   if ( dot != -1 ) {
02649     oldType.truncate( dot );
02650     oldSubType = mAnnotationFolderType.mid( dot + 1 );
02651   }
02652 
02653   QString newType, newSubType;
02654   // We want to store an annotation on the folder only if using the kolab storage.
02655   if ( kmkernel->iCalIface().storageFormat( folder() ) == KMailICalIfaceImpl::StorageXML ) {
02656     newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType );
02657     if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) )
02658       newSubType = "default";
02659     else if ( oldSubType != "default" )
02660       newSubType = oldSubType; // preserve unknown subtypes, like drafts etc.
02661   }
02662 
02663   // We do not want to overwrite custom folder types (which we treat as mail folders).
02664   // So only overwrite custom folder types if the user changed the folder type himself to something
02665   // other than mail.
02666   const bool changingTypeAllowed = isFolderTypeKnownToUs( oldType ) ||
02667                                    ( mContentsType != ContentsTypeMail );
02668 
02669   //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl;
02670   if ( ( newType != oldType || newSubType != oldSubType ) && changingTypeAllowed ) {
02671     mAnnotationFolderType = newType + ( newSubType.isEmpty() ? QString::null : "."+newSubType );
02672     mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync
02673     kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl;
02674   }
02675   // Ensure that further readConfig()s don't lose mAnnotationFolderType
02676   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02677 }
02678 
02679 void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor )
02680 {
02681   if ( mIncidencesFor != incfor ) {
02682     mIncidencesFor = incfor;
02683     mIncidencesForChanged = true;
02684   }
02685 }
02686 
02687 void KMFolderCachedImap::setSharedSeenFlags(bool b)
02688 {
02689   if ( mSharedSeenFlags != b ) {
02690     mSharedSeenFlags = b;
02691     mSharedSeenFlagsChanged = true;
02692   }
02693 }
02694 
02695 void KMFolderCachedImap::slotAnnotationResult(const QString& entry, const QString& value, bool found)
02696 {
02697   if ( entry == KOLAB_FOLDERTYPE ) {
02698     // There are four cases.
02699     // 1) no content-type on server -> set it
02700     // 2) different content-type on server, locally changed -> set it (we don't even come here)
02701     // 3) different (known) content-type on server, no local change -> get it
02702     // 4) different unknown content-type on server, probably some older version -> set it
02703     if ( found ) {
02704       QString type = value;
02705       QString subtype;
02706       int dot = value.find( '.' );
02707       if ( dot != -1 ) {
02708         type.truncate( dot );
02709         subtype = value.mid( dot + 1 );
02710       }
02711       bool foundKnownType = false;
02712       for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
02713         FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
02714         if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) {
02715           // Case 3: known content-type on server, get it
02716           //kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: found known type of annotation" << endl;
02717           if ( contentsType != ContentsTypeMail )
02718             kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
02719           mAnnotationFolderType = value;
02720           if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent()
02721                && GlobalSettings::self()->theIMAPResourceEnabled()
02722                && subtype == "default" ) {
02723             // Truncate subtype if this folder can't be a default resource folder for us,
02724             // although it apparently is for someone else.
02725             mAnnotationFolderType = type;
02726             kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: parent folder is " << folder()->parent()->owner()->idString() << " => truncating annotation to " << value << endl;
02727           }
02728           setContentsType( contentsType );
02729           mAnnotationFolderTypeChanged = false; // we changed it, not the user
02730           foundKnownType = true;
02731 
02732           // Users don't read events/contacts/etc. in kmail, so mark them all as read.
02733           // This is done in cachedimapjob when getting new messages, but do it here too,
02734           // for the initial set of messages when we didn't know this was a resource folder yet,
02735           // for old folders, etc.
02736           if ( contentsType != ContentsTypeMail )
02737             markUnreadAsRead();
02738 
02739           break;
02740         }
02741       }
02742       if ( !foundKnownType ) {
02743         //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, leaving it untouched" << endl;
02744 
02745         // Case 4: Server has strange content-type. We must not overwrite it, see https://issues.kolab.org/issue2069.
02746         //         Treat the content-type as mail until we change it ourselves.
02747         mAnnotationFolderTypeChanged = false;
02748         mAnnotationFolderType = value;
02749         setContentsType( ContentsTypeMail );
02750       }
02751 
02752       // Ensure that further readConfig()s don't lose mAnnotationFolderType
02753       writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02754       // TODO handle subtype (inbox, drafts, sentitems, junkemail)
02755     }
02756     else if ( !mReadOnly ) {
02757       // Case 1: server doesn't have content-type, set it
02758       //kdDebug(5006) << "slotGetAnnotationResult: no annotation found, will need to set it" << endl;
02759       mAnnotationFolderTypeChanged = true;
02760     }
02761   } else if ( entry == KOLAB_INCIDENCESFOR ) {
02762     if ( found ) {
02763       mIncidencesFor = incidencesForFromString( value );
02764       Q_ASSERT( mIncidencesForChanged == false );
02765     }
02766   } else if ( entry == KOLAB_SHAREDSEEN ) {
02767     if ( found ) {
02768       mSharedSeenFlags = value == "true";
02769     }
02770   }
02771 }
02772 
02773 void KMFolderCachedImap::slotGetAnnotationResult( KIO::Job* job )
02774 {
02775   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02776   Q_ASSERT( it != mAccount->jobsEnd() );
02777   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02778   Q_ASSERT( (*it).parent == folder() );
02779   if ( (*it).parent != folder() ) return; // Shouldn't happen
02780 
02781   AnnotationJobs::GetAnnotationJob* annjob = static_cast<AnnotationJobs::GetAnnotationJob *>( job );
02782   if ( annjob->error() ) {
02783     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02784       // that's when the imap server doesn't support annotations
02785       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02786            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02787     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() ) );
02788       mAccount->setHasNoAnnotationSupport();
02789     }
02790     else
02791       kdWarning(5006) << "slotGetAnnotationResult: " << job->errorString() << endl;
02792   }
02793 
02794   if (mAccount->slave()) mAccount->removeJob(job);
02795   mProgress += 2;
02796   serverSyncInternal();
02797 }
02798 
02799 void KMFolderCachedImap::slotMultiUrlGetAnnotationResult( KIO::Job* job )
02800 {
02801   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02802   Q_ASSERT( it != mAccount->jobsEnd() );
02803   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02804   Q_ASSERT( (*it).parent == folder() );
02805   if ( (*it).parent != folder() ) return; // Shouldn't happen
02806 
02807   QValueVector<int> folders;
02808   AnnotationJobs::MultiUrlGetAnnotationJob* annjob
02809     = static_cast<AnnotationJobs::MultiUrlGetAnnotationJob *>( job );
02810   if ( annjob->error() ) {
02811     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02812       // that's when the imap server doesn't support annotations
02813       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02814            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02815         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() ) );
02816       mAccount->setHasNoAnnotationSupport();
02817     }
02818     else
02819       kdWarning(5006) << "slotGetMultiUrlAnnotationResult: " << job->errorString() << endl;
02820   } else {
02821     // we got the annotation allright, let's filter out the ones with the wrong type
02822     QMap<QString, QString> annotations = annjob->annotations();
02823     QMap<QString, QString>::Iterator it = annotations.begin();
02824     for ( ; it != annotations.end(); ++it ) {
02825       const QString folderPath = it.key();
02826       const QString annotation = it.data();
02827       kdDebug(5006) << k_funcinfo << "Folder: " << folderPath << " has type: " << annotation << endl;
02828       // we're only interested in the main type
02829       QString type(annotation);
02830       int dot = annotation.find( '.' );
02831       if ( dot != -1 ) type.truncate( dot );
02832       type = type.simplifyWhiteSpace();
02833 
02834       const int idx = mSubfolderPaths.findIndex( folderPath );
02835       const bool isNoContent =  mSubfolderMimeTypes[idx] == "inode/directory";
02836       if ( ( isNoContent && type.isEmpty() )
02837         || ( !type.isEmpty() && type != KMailICalIfaceImpl::annotationForContentsType( ContentsTypeMail ) ) ) {
02838         folders.append( idx );
02839         kdDebug(5006) << k_funcinfo << " subscribing to: " << folderPath << endl;
02840       } else {
02841         kdDebug(5006) << k_funcinfo << " automatically unsubscribing from: " << folderPath << endl;
02842         mAccount->changeLocalSubscription( folderPath, false );
02843       }
02844     }
02845   }
02846 
02847   if (mAccount->slave()) mAccount->removeJob(job);
02848   createFoldersNewOnServerAndFinishListing( folders );
02849 }
02850 
02851 void KMFolderCachedImap::slotQuotaResult( KIO::Job* job )
02852 {
02853   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02854   Q_ASSERT( it != mAccount->jobsEnd() );
02855   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02856   Q_ASSERT( (*it).parent == folder() );
02857   if ( (*it).parent != folder() ) return; // Shouldn't happen
02858 
02859   QuotaJobs::GetStorageQuotaJob* quotajob = static_cast<QuotaJobs::GetStorageQuotaJob *>( job );
02860   QuotaInfo empty;
02861   if ( quotajob->error() ) {
02862     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02863       // that's when the imap server doesn't support quota
02864       mAccount->setHasNoQuotaSupport();
02865       setQuotaInfo( empty );
02866     }
02867     else
02868       kdWarning(5006) << "slotGetQuotaResult: " << job->errorString() << endl;
02869   }
02870 
02871   if (mAccount->slave()) mAccount->removeJob(job);
02872   mProgress += 2;
02873   serverSyncInternal();
02874 }
02875 
02876 void
02877 KMFolderCachedImap::slotAnnotationChanged( const QString& entry, const QString& attribute, const QString& value )
02878 {
02879   Q_UNUSED( attribute );
02880   Q_UNUSED( value );
02881   //kdDebug(5006) << k_funcinfo << entry << " " << attribute << " " << value << endl;
02882   if ( entry == KOLAB_FOLDERTYPE )
02883     mAnnotationFolderTypeChanged = false;
02884   else if ( entry == KOLAB_INCIDENCESFOR ) {
02885     mIncidencesForChanged = false;
02886     // The incidences-for changed, we must trigger the freebusy creation.
02887     // HACK: in theory we would need a new enum value for this.
02888     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02889   } else if ( entry == KOLAB_SHAREDSEEN ) {
02890     mSharedSeenFlagsChanged = false;
02891   }
02892 }
02893 
02894 void KMFolderCachedImap::slotTestAnnotationResult(KIO::Job *job)
02895 {
02896   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02897   Q_ASSERT( it != mAccount->jobsEnd() );
02898   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02899   Q_ASSERT( (*it).parent == folder() );
02900   if ( (*it).parent != folder() ) return; // Shouldn't happen
02901 
02902   mAccount->setAnnotationCheckPassed( true );
02903   if ( job->error() ) {
02904     kdDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl;
02905     mAccount->setHasNoAnnotationSupport( );
02906   } else {
02907     kdDebug(5006) << "Test Annotation was passed   OK" << endl;
02908   }
02909   if (mAccount->slave()) mAccount->removeJob(job);
02910   serverSyncInternal();
02911 }
02912 
02913 void
02914 KMFolderCachedImap::slotSetAnnotationResult(KIO::Job *job)
02915 {
02916   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02917   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02918   if ( (*it).parent != folder() ) return; // Shouldn't happen
02919 
02920   bool cont = true;
02921   if ( job->error() ) {
02922     // Don't show error if the server doesn't support ANNOTATEMORE and this folder only contains mail
02923     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION && contentsType() == ContentsTypeMail ) {
02924       if (mAccount->slave()) mAccount->removeJob(job);
02925     } else {
02926       cont = mAccount->handleJobError( job, i18n( "Error while setting annotation: " ) + '\n' );
02927     }
02928   } else {
02929     if (mAccount->slave()) mAccount->removeJob(job);
02930   }
02931   if ( cont )
02932     serverSyncInternal();
02933 }
02934 
02935 void KMFolderCachedImap::slotUpdateLastUid()
02936 {
02937   if( mTentativeHighestUid != 0 ) {
02938 
02939       // Sanity checking:
02940       // By now all new mails should be downloaded, which means
02941       // that iteration over the folder should yield only UIDs
02942       // lower or equal to what we think the highes ist, and the
02943       // highest one as well. If not, our notion of the highest
02944       // uid we've seen thus far is wrong, which is dangerous, so
02945       // don't update the mLastUid, then.
02946       // Not entirely true though, mails might have been moved out
02947       // of the folder already by filters, thus giving us a higher tentative
02948       // uid than we actually observe here.
02949       bool sane = count() == 0;
02950 
02951       for (int i=0;i<count(); i++ ) {
02952           ulong uid = getMsgBase(i)->UID();
02953           if ( uid > mTentativeHighestUid && uid > lastUid() ) {
02954               kdWarning(5006) << "DANGER: Either the server listed a wrong highest uid, "
02955                   "or we parsed it wrong. Send email to adam@kde.org, please, and include this log." << endl;
02956               kdWarning(5006) << "uid: " << uid << " mTentativeHighestUid: " << mTentativeHighestUid << endl;
02957               assert( false );
02958               break;
02959           } else {
02960               sane = true;
02961           }
02962       }
02963       if (sane) {
02964 #if MAIL_LOSS_DEBUGGING
02965           kdDebug(5006) << "Tentative highest UID test was sane, writing out: " << mTentativeHighestUid << endl;
02966 #endif
02967           setLastUid( mTentativeHighestUid );
02968       }
02969   }
02970   mTentativeHighestUid = 0;
02971 }
02972 
02973 bool KMFolderCachedImap::isMoveable() const
02974 {
02975   return ( hasChildren() == HasNoChildren &&
02976       !folder()->isSystemFolder() ) ? true : false;
02977 }
02978 
02979 void KMFolderCachedImap::slotFolderDeletionOnServerFinished()
02980 {
02981   for ( QStringList::const_iterator it = foldersForDeletionOnServer.constBegin();
02982       it != foldersForDeletionOnServer.constEnd(); ++it ) {
02983     KURL url( mAccount->getUrl() );
02984     url.setPath( *it );
02985     kmkernel->iCalIface().folderDeletedOnServer( url );
02986   }
02987   serverSyncInternal();
02988 }
02989 
02990 int KMFolderCachedImap::createIndexFromContentsRecursive()
02991 {
02992   if ( !folder() || !folder()->child() )
02993     return 0;
02994 
02995   KMFolderNode *node = 0;
02996   for( QPtrListIterator<KMFolderNode> it( *folder()->child() ); (node = it.current()); ++it ) {
02997     if( !node->isDir() ) {
02998       KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02999       kdDebug() << k_funcinfo << "Re-indexing: " << storage->folder()->label() << endl;
03000       int rv = storage->createIndexFromContentsRecursive();
03001       if ( rv > 0 )
03002         return rv;
03003     }
03004   }
03005 
03006   return createIndexFromContents();
03007 }
03008 
03009 void KMFolderCachedImap::setAlarmsBlocked( bool blocked )
03010 {
03011   mAlarmsBlocked = blocked;
03012 }
03013 
03014 bool KMFolderCachedImap::alarmsBlocked() const
03015 {
03016   return mAlarmsBlocked;
03017 }
03018 
03019 bool KMFolderCachedImap::isCloseToQuota() const
03020 {
03021   bool closeToQuota = false;
03022   if ( mQuotaInfo.isValid() && mQuotaInfo.max().toInt() > 0 ) {
03023     const int ratio = mQuotaInfo.current().toInt() * 100  / mQuotaInfo.max().toInt();
03024     //kdDebug(5006) << "Quota ratio: " << ratio << "% " << mQuotaInfo.toString() << endl;
03025     closeToQuota = ( ratio > 0 && ratio >= GlobalSettings::closeToQuotaThreshold() );
03026   }
03027   //kdDebug(5006) << "Folder: " << folder()->prettyURL() << " is over quota: " << closeToQuota << endl;
03028   return closeToQuota;
03029 }
03030 
03031 KMCommand* KMFolderCachedImap::rescueUnsyncedMessages()
03032 {
03033   QValueList<unsigned long> newMsgs = findNewMessages();
03034   kdDebug() << k_funcinfo << newMsgs << " of " << count() << endl;
03035   if ( newMsgs.isEmpty() )
03036     return 0;
03037   KMFolder *dest = 0;
03038   bool manualMove = true;
03039   while ( GlobalSettings::autoLostFoundMove() ) {
03040     // find the inbox of this account
03041     KMFolder *inboxFolder = kmkernel->findFolderById( QString(".%1.directory/INBOX").arg( account()->id() ) );
03042     if ( !inboxFolder ) {
03043       kdWarning(5006) << k_funcinfo << "inbox not found!" << endl;
03044       break;
03045     }
03046     KMFolderDir *inboxDir = inboxFolder->child();
03047     if ( !inboxDir && !inboxFolder->storage() )
03048       break;
03049     assert( inboxFolder->storage()->folderType() == KMFolderTypeCachedImap );
03050 
03051     // create lost+found folder if needed
03052     KMFolderNode *node;
03053     KMFolder *lfFolder = 0;
03054     if ( !(node = inboxDir->hasNamedFolder( i18n("lost+found") )) ) {
03055       kdDebug(5006) << k_funcinfo << "creating lost+found folder" << endl;
03056       KMFolder* folder = kmkernel->dimapFolderMgr()->createFolder(
03057           i18n("lost+found"), false, KMFolderTypeCachedImap, inboxDir );
03058       if ( !folder || !folder->storage() )
03059         break;
03060       static_cast<KMFolderCachedImap*>( folder->storage() )->initializeFrom(
03061         static_cast<KMFolderCachedImap*>( inboxFolder->storage() ) );
03062       folder->storage()->setContentsType( KMail::ContentsTypeMail );
03063       folder->storage()->writeConfig();
03064       lfFolder = folder;
03065     } else {
03066       kdDebug(5006) << k_funcinfo << "found lost+found folder" << endl;
03067       lfFolder = dynamic_cast<KMFolder*>( node );
03068     }
03069     if ( !lfFolder || !lfFolder->createChildFolder() || !lfFolder->storage() )
03070       break;
03071 
03072     // create subfolder for this incident
03073     QDate today = QDate::currentDate();
03074     QString baseName = folder()->label() + "-" + QString::number( today.year() )
03075         + (today.month() < 10 ? "0" : "" ) + QString::number( today.month() )
03076         + (today.day() < 10 ? "0" : "" ) + QString::number( today.day() );
03077     QString name = baseName;
03078     int suffix = 0;
03079     while ( (node = lfFolder->child()->hasNamedFolder( name )) ) {
03080       ++suffix;
03081       name = baseName + '-' + QString::number( suffix );
03082     }
03083     kdDebug(5006) << k_funcinfo << "creating lost+found folder " << name << endl;
03084     dest = kmkernel->dimapFolderMgr()->createFolder( name, false, KMFolderTypeCachedImap, lfFolder->child() );
03085     if ( !dest || !dest->storage() )
03086         break;
03087     static_cast<KMFolderCachedImap*>( dest->storage() )->initializeFrom(
03088       static_cast<KMFolderCachedImap*>( lfFolder->storage() ) );
03089     dest->storage()->setContentsType( contentsType() );
03090     dest->storage()->writeConfig();
03091 
03092     KMessageBox::sorry( 0, i18n("<p>There are new messages in folder <b>%1</b>, which "
03093           "have not been uploaded to the server yet, but the folder has been deleted "
03094           "on the server or you do not "
03095           "have sufficient access rights on the folder to upload them.</p>"
03096           "<p>All affected messages will therefore be moved to <b>%2</b> "
03097           "to avoid data loss.</p>").arg( folder()->prettyURL() ).arg( dest->prettyURL() ),
03098           i18n("Insufficient access rights") );
03099     manualMove = false;
03100     break;
03101   }
03102 
03103   if ( manualMove ) {
03104     const QString msg ( i18n( "<p>There are new messages in this folder (%1), which "
03105           "have not been uploaded to the server yet, but the folder has been deleted "
03106           "on the server or you do not "
03107           "have sufficient access rights on the folder now to upload them. "
03108           "Please contact your administrator to allow upload of new messages "
03109           "to you, or move them out of this folder.</p> "
03110           "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyURL() ) );
03111     if ( KMessageBox::warningYesNo( 0, msg, QString::null, i18n("Move"), i18n("Do Not Move") ) == KMessageBox::Yes ) {
03112       KMail::KMFolderSelDlg dlg( kmkernel->getKMMainWidget(),
03113           i18n("Move Messages to Folder"), true );
03114       if ( dlg.exec() ) {
03115         dest = dlg.folder();
03116       }
03117     }
03118   }
03119   if ( dest ) {
03120     QPtrList<KMMsgBase> msgs;
03121     for( int i = 0; i < count(); ++i ) {
03122       KMMsgBase *msg = getMsgBase( i );
03123       if( !msg ) continue; /* what goes on if getMsg() returns 0? */
03124       if ( msg->UID() == 0 )
03125         msgs.append( msg );
03126     }
03127     KMCommand *command = new KMMoveCommand( dest, msgs );
03128     command->start();
03129     return command;
03130   }
03131   return 0;
03132 }
03133 
03134 void KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root )
03135 {
03136   kdDebug() << k_funcinfo << folder << " " << root << endl;
03137   if ( root )
03138     mToBeDeletedAfterRescue.append( folder );
03139   folder->open("cachedimap");
03140   KMFolderCachedImap* storage = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
03141   if ( storage ) {
03142     KMCommand *command = storage->rescueUnsyncedMessages();
03143     if ( command ) {
03144       connect( command, SIGNAL(completed(KMCommand*)),
03145                SLOT(slotRescueDone(KMCommand*)) );
03146       ++mRescueCommandCount;
03147     } else {
03148       // nothing to rescue, close folder
03149       // (we don't need to close it in the other case, it will be deleted anyway)
03150       folder->close("cachedimap");
03151     }
03152   }
03153   if ( folder->child() ) {
03154     KMFolderNode *node = folder->child()->first();
03155     while (node) {
03156       if (!node->isDir() ) {
03157         KMFolder *subFolder = static_cast<KMFolder*>( node );
03158         rescueUnsyncedMessagesAndDeleteFolder( subFolder, false );
03159       }
03160       node = folder->child()->next();
03161     }
03162   }
03163 }
03164 
03165 void KMFolderCachedImap::slotRescueDone(KMCommand * command)
03166 {
03167   // FIXME: check command result
03168   if ( command )
03169     --mRescueCommandCount;
03170   if ( mRescueCommandCount > 0 )
03171     return;
03172   for ( QValueList<KMFolder*>::ConstIterator it = mToBeDeletedAfterRescue.constBegin();
03173         it != mToBeDeletedAfterRescue.constEnd(); ++it ) {
03174     kmkernel->dimapFolderMgr()->remove( *it );
03175   }
03176   mToBeDeletedAfterRescue.clear();
03177   serverSyncInternal();
03178 }
03179 
03180 void KMFolderCachedImap::slotRenameFolderFinished()
03181 {
03182   // The syncing code assumes the folder was opened by us, and later closes it. So better
03183   // make sure the reference count is correct, since the folder was force-closed by the rename.
03184   // Otherwise bad things can happen, see https://issues.kolab.org/issue3853.
03185   open( "cachedimap" );
03186   serverSyncInternal();
03187 }
03188 
03189 bool KMFolderCachedImap::canDeleteMessages() const
03190 {
03191   if ( isReadOnly() )
03192     return false;
03193   if ( userRights() > 0 && !(userRights() & ACLJobs::Delete) )
03194     return false;
03195   return true;
03196 }
03197 
03198 bool KMFolderCachedImap::mailCheckInProgress() const
03199 {
03200   return mSyncState != SYNC_STATE_INITIAL;
03201 }
03202 
03203 #include "kmfoldercachedimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys