kmail

kmfoldercachedimap.cpp

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