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