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