kmail

kmfoldercachedimap.cpp

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