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