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