kmail

kmfoldercachedimap.cpp

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