kmail Library API Documentation

kmfoldercachedimap.cpp

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