kmail

kmfoldercachedimap.cpp

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