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 ), mReadOnly( false ), 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   ProgressItem *progressItem = mAccount->mailCheckProgressItem();
00543   if ( progressItem ) {
00544     progressItem->reset();
00545     progressItem->setTotalItems( 100 );
00546   }
00547   mProgress = 0;
00548 
00549 #if 0
00550   if( mHoldSyncs ) {
00551     // All done for this folder.
00552     account()->mailCheckProgressItem()->setProgress( 100 );
00553     mProgress = 100; // all done
00554     newState( mProgress, i18n("Synchronization skipped"));
00555     mSyncState = SYNC_STATE_INITIAL;
00556     emit folderComplete( this, true );
00557     return;
00558   }
00559 #endif
00560   mTentativeHighestUid = 0; // reset, last sync could have been canceled
00561 
00562   serverSyncInternal();
00563 }
00564 
00565 QString KMFolderCachedImap::state2String( int state ) const
00566 {
00567   switch( state ) {
00568   case SYNC_STATE_INITIAL:           return "SYNC_STATE_INITIAL";
00569   case SYNC_STATE_GET_USERRIGHTS:    return "SYNC_STATE_GET_USERRIGHTS";
00570   case SYNC_STATE_PUT_MESSAGES:      return "SYNC_STATE_PUT_MESSAGES";
00571   case SYNC_STATE_UPLOAD_FLAGS:      return "SYNC_STATE_UPLOAD_FLAGS";
00572   case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
00573   case SYNC_STATE_LIST_SUBFOLDERS:   return "SYNC_STATE_LIST_SUBFOLDERS";
00574   case SYNC_STATE_LIST_NAMESPACES:   return "SYNC_STATE_LIST_NAMESPACES";
00575   case SYNC_STATE_LIST_SUBFOLDERS2:  return "SYNC_STATE_LIST_SUBFOLDERS2";
00576   case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
00577   case SYNC_STATE_LIST_MESSAGES:     return "SYNC_STATE_LIST_MESSAGES";
00578   case SYNC_STATE_DELETE_MESSAGES:   return "SYNC_STATE_DELETE_MESSAGES";
00579   case SYNC_STATE_GET_MESSAGES:      return "SYNC_STATE_GET_MESSAGES";
00580   case SYNC_STATE_EXPUNGE_MESSAGES:  return "SYNC_STATE_EXPUNGE_MESSAGES";
00581   case SYNC_STATE_HANDLE_INBOX:      return "SYNC_STATE_HANDLE_INBOX";
00582   case SYNC_STATE_TEST_ANNOTATIONS:  return "SYNC_STATE_TEST_ANNOTATIONS";
00583   case SYNC_STATE_GET_ANNOTATIONS:   return "SYNC_STATE_GET_ANNOTATIONS";
00584   case SYNC_STATE_SET_ANNOTATIONS:   return "SYNC_STATE_SET_ANNOTATIONS";
00585   case SYNC_STATE_GET_ACLS:          return "SYNC_STATE_GET_ACLS";
00586   case SYNC_STATE_SET_ACLS:          return "SYNC_STATE_SET_ACLS";
00587   case SYNC_STATE_FIND_SUBFOLDERS:   return "SYNC_STATE_FIND_SUBFOLDERS";
00588   case SYNC_STATE_SYNC_SUBFOLDERS:   return "SYNC_STATE_SYNC_SUBFOLDERS";
00589   case SYNC_STATE_RENAME_FOLDER:     return "SYNC_STATE_RENAME_FOLDER";
00590   case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
00591   default:                           return "Unknown state";
00592   }
00593 }
00594 
00595 /*
00596   Progress calculation: each step is assigned a span. Initially the total is 100.
00597   But if we skip a step, don't increase the progress.
00598   This leaves more room for the step a with variable size (get_messages)
00599    connecting 5
00600    getuserrights 5
00601    rename 5
00602    check_uidvalidity 5
00603    create_subfolders 5
00604    put_messages 10 (but it can take a very long time, with many messages....)
00605    upload_flags 5
00606    list_subfolders 5
00607    list_subfolders2 0 (all local)
00608    delete_subfolders 5
00609    list_messages 10
00610    delete_messages 10
00611    expunge_messages 5
00612    get_messages variable (remaining-5) i.e. minimum 15.
00613    check_annotations 0 (rare)
00614    set_annotations 0 (rare)
00615    get_annotations 2
00616    set_acls 0 (rare)
00617    get_acls 3
00618 
00619   noContent folders have only a few of the above steps
00620   (permissions, and all subfolder stuff), so its steps should be given more span
00621 
00622  */
00623 
00624 // While the server synchronization is running, mSyncState will hold
00625 // the state that should be executed next
00626 void KMFolderCachedImap::serverSyncInternal()
00627 {
00628   // This is used to stop processing when we're about to exit
00629   // and the current job wasn't cancellable.
00630   // For user-requested abort, we'll use signalAbortRequested instead.
00631   if( kmkernel->mailCheckAborted() ) {
00632     resetSyncState();
00633     emit folderComplete( this, false );
00634     return;
00635   }
00636 
00637   //kdDebug(5006) << label() << ": " << state2String( mSyncState ) << endl;
00638   switch( mSyncState ) {
00639   case SYNC_STATE_INITIAL:
00640   {
00641     mProgress = 0;
00642     foldersForDeletionOnServer.clear();
00643     newState( mProgress, i18n("Synchronizing"));
00644 
00645     open();
00646     if ( !noContent() )
00647         mAccount->addLastUnreadMsgCount( this, countUnread() );
00648 
00649     // Connect to the server (i.e. prepare the slave)
00650     ImapAccountBase::ConnectionState cs = mAccount->makeConnection();
00651     if ( cs == ImapAccountBase::Error ) {
00652       // Cancelled by user, or slave can't start
00653       // kdDebug(5006) << "makeConnection said Error, aborting." << endl;
00654       // We stop here. We're already in SYNC_STATE_INITIAL for the next time.
00655       newState( mProgress, i18n( "Error connecting to server %1" ).arg( mAccount->host() ) );
00656       close();
00657       emit folderComplete(this, false);
00658       break;
00659     } else if ( cs == ImapAccountBase::Connecting ) {
00660       mAccount->setAnnotationCheckPassed( false );
00661       // kdDebug(5006) << "makeConnection said Connecting, waiting for signal." << endl;
00662       newState( mProgress, i18n("Connecting to %1").arg( mAccount->host() ) );
00663       // We'll wait for the connectionResult signal from the account.
00664       connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
00665                this, SLOT( slotConnectionResult(int, const QString&) ) );
00666       break;
00667     } else {
00668       // Connected
00669       // kdDebug(5006) << "makeConnection said Connected, proceeding." << endl;
00670       mSyncState = SYNC_STATE_GET_USERRIGHTS;
00671       // Fall through to next state
00672     }
00673   }
00674 
00675 
00676   case SYNC_STATE_GET_USERRIGHTS:
00677     //kdDebug(5006) << "===== Syncing " << ( mImapPath.isEmpty() ? label() : mImapPath ) << endl;
00678 
00679     mSyncState = SYNC_STATE_RENAME_FOLDER;
00680 
00681     if( !noContent() && mAccount->hasACLSupport() ) {
00682       // Check the user's own rights. We do this every time in case they changed.
00683       newState( mProgress, i18n("Checking permissions"));
00684       connect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
00685                this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
00686       mAccount->getUserRights( folder(), imapPath() ); // after connecting, due to the INBOX case
00687       break;
00688     }
00689 
00690   case SYNC_STATE_RENAME_FOLDER:
00691   {
00692     mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
00693     // Returns the new name if the folder was renamed, empty otherwise.
00694     bool isResourceFolder = kmkernel->iCalIface().isStandardResourceFolder( folder() );
00695     QString newName = mAccount->renamedFolder( imapPath() );
00696     if ( !newName.isEmpty() && !folder()->isSystemFolder() && !isResourceFolder ) {
00697       newState( mProgress, i18n("Renaming folder") );
00698       CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this );
00699       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
00700       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00701       job->start();
00702       break;
00703     }
00704   }
00705 
00706   case SYNC_STATE_CHECK_UIDVALIDITY:
00707     mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
00708     if( !noContent() ) {
00709       checkUidValidity();
00710       break;
00711     }
00712     // Else carry on
00713 
00714   case SYNC_STATE_CREATE_SUBFOLDERS:
00715     mSyncState = SYNC_STATE_PUT_MESSAGES;
00716     createNewFolders();
00717     break;
00718 
00719   case SYNC_STATE_PUT_MESSAGES:
00720     mSyncState = SYNC_STATE_UPLOAD_FLAGS;
00721     if( !noContent() ) {
00722       uploadNewMessages();
00723       break;
00724     }
00725     // Else carry on
00726   case SYNC_STATE_UPLOAD_FLAGS:
00727     mSyncState = SYNC_STATE_LIST_NAMESPACES;
00728     if( !noContent() ) {
00729        // We haven't downloaded messages yet, so we need to build the map.
00730        if( uidMapDirty )
00731          reloadUidMap();
00732        // Upload flags, unless we know from the ACL that we're not allowed
00733        // to do that or they did not change locally
00734        if ( mUserRights <= 0 || ( mUserRights & KMail::ACLJobs::WriteFlags ) ) {
00735          if ( mStatusChangedLocally ) {
00736            uploadFlags();
00737            break;
00738          } else {
00739            //kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl;
00740          }
00741        }
00742     }
00743     // Else carry on
00744 
00745   case SYNC_STATE_LIST_NAMESPACES:
00746     if ( this == mAccount->rootFolder() ) {
00747       listNamespaces();
00748       break;
00749     }
00750     mSyncState = SYNC_STATE_LIST_SUBFOLDERS;
00751     // Else carry on
00752 
00753   case SYNC_STATE_LIST_SUBFOLDERS:
00754     newState( mProgress, i18n("Retrieving folderlist"));
00755     mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
00756     if( !listDirectory() ) {
00757       mSyncState = SYNC_STATE_INITIAL;
00758       KMessageBox::error(0, i18n("Error while retrieving the folderlist"));
00759     }
00760     break;
00761 
00762   case SYNC_STATE_LIST_SUBFOLDERS2:
00763     mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
00764     mProgress += 10;
00765     newState( mProgress, i18n("Retrieving subfolders"));
00766     listDirectory2();
00767     break;
00768 
00769   case SYNC_STATE_DELETE_SUBFOLDERS:
00770     mSyncState = SYNC_STATE_LIST_MESSAGES;
00771     if( !foldersForDeletionOnServer.isEmpty() ) {
00772       newState( mProgress, i18n("Deleting folders from server"));
00773       CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer,
00774                                                   CachedImapJob::tDeleteFolders, this );
00775       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
00776       connect( job, SIGNAL( finished() ), this, SLOT( slotFolderDeletionOnServerFinished() ) );
00777       job->start();
00778       break;
00779     }
00780     // Not needed, the next step emits newState very quick
00781     //newState( mProgress, i18n("No folders to delete from server"));
00782       // Carry on
00783 
00784   case SYNC_STATE_LIST_MESSAGES:
00785     mSyncState = SYNC_STATE_DELETE_MESSAGES;
00786     if( !noContent() ) {
00787       newState( mProgress, i18n("Retrieving message list"));
00788       listMessages();
00789       break;
00790     }
00791     // Else carry on
00792 
00793   case SYNC_STATE_DELETE_MESSAGES:
00794     mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
00795     if( !noContent() ) {
00796       if( deleteMessages() ) {
00797         // Fine, we will continue with the next state
00798       } else {
00799         // No messages to delete, skip to GET_MESSAGES
00800         newState( mProgress, i18n("No messages to delete..."));
00801         mSyncState = SYNC_STATE_GET_MESSAGES;
00802         serverSyncInternal();
00803       }
00804       break;
00805     }
00806     // Else carry on
00807 
00808   case SYNC_STATE_EXPUNGE_MESSAGES:
00809     mSyncState = SYNC_STATE_GET_MESSAGES;
00810     if( !noContent() ) {
00811       newState( mProgress, i18n("Expunging deleted messages"));
00812       CachedImapJob *job = new CachedImapJob( QString::null,
00813                                               CachedImapJob::tExpungeFolder, this );
00814       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
00815       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00816       job->start();
00817       break;
00818     }
00819     // Else carry on
00820 
00821   case SYNC_STATE_GET_MESSAGES:
00822     mSyncState = SYNC_STATE_HANDLE_INBOX;
00823     if( !noContent() ) {
00824       if( !mMsgsForDownload.isEmpty() ) {
00825         newState( mProgress, i18n("Retrieving new messages"));
00826         CachedImapJob *job = new CachedImapJob( mMsgsForDownload,
00827                                                 CachedImapJob::tGetMessage,
00828                                                 this );
00829         connect( job, SIGNAL( progress(unsigned long, unsigned long) ),
00830                  this, SLOT( slotProgress(unsigned long, unsigned long) ) );
00831         connect( job, SIGNAL( finished() ), this, SLOT( slotUpdateLastUid() ) );
00832         connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00833         job->start();
00834         mMsgsForDownload.clear();
00835         break;
00836       } else {
00837         newState( mProgress, i18n("No new messages from server"));
00838         /* There were no messages to download, but it could be that we uploaded some
00839            which we didn't need to download again because we already knew the uid.
00840            Now that we are sure there is nothing to download, and everything that had
00841            to be deleted on the server has been deleted, adjust our local notion of the
00842            highes uid seen thus far. */
00843         slotUpdateLastUid();
00844         if( mLastUid == 0 && uidWriteTimer == -1 )
00845           // This is probably a new and empty folder. Write the UID cache
00846           writeUidCache();
00847       }
00848     }
00849 
00850     // Else carry on
00851 
00852   case SYNC_STATE_HANDLE_INBOX:
00853     // Wrap up the 'download emails' stage. We always end up at 95 here.
00854     mProgress = 95;
00855     mSyncState = SYNC_STATE_TEST_ANNOTATIONS;
00856 
00857   #define KOLAB_FOLDERTEST "/vendor/kolab/folder-test"
00858   case SYNC_STATE_TEST_ANNOTATIONS:
00859     mSyncState = SYNC_STATE_GET_ANNOTATIONS;
00860     // The first folder with user rights to write annotations
00861     if( !mAccount->annotationCheckPassed() &&
00862          ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) )
00863          && !imapPath().isEmpty() && imapPath() != "/" ) {
00864       kdDebug(5006) << "Setting test attribute on folder: "<< folder()->prettyURL() << endl;
00865       newState( mProgress, i18n("Checking annotation support"));
00866 
00867       KURL url = mAccount->getUrl();
00868       url.setPath( imapPath() );
00869       KMail::AnnotationList annotations; // to be set
00870 
00871       KMail::AnnotationAttribute attr( KOLAB_FOLDERTEST, "value.shared", "true" );
00872       annotations.append( attr );
00873 
00874       kdDebug(5006) << "Setting test attribute to "<< url << endl;
00875       KIO::Job* job = AnnotationJobs::multiSetAnnotation( mAccount->slave(),
00876           url, annotations );
00877       ImapAccountBase::jobData jd( url.url(), folder() );
00878       jd.cancellable = true; // we can always do so later
00879       mAccount->insertJob(job, jd);
00880        connect(job, SIGNAL(result(KIO::Job *)),
00881               SLOT(slotTestAnnotationResult(KIO::Job *)));
00882       break;
00883     }
00884 
00885   case SYNC_STATE_GET_ANNOTATIONS: {
00886 #define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type"
00887 #define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for"
00888 //#define KOLAB_FOLDERTYPE "/comment"  //for testing, while cyrus-imap doesn't support /vendor/*
00889     mSyncState = SYNC_STATE_SET_ANNOTATIONS;
00890 
00891     bool needToGetInitialAnnotations = false;
00892     if ( !noContent() ) {
00893       // for a folder we didn't create ourselves: get annotation from server
00894       if ( mAnnotationFolderType == "FROMSERVER" ) {
00895         needToGetInitialAnnotations = true;
00896         mAnnotationFolderType = QString::null;
00897       } else {
00898         updateAnnotationFolderType();
00899       }
00900     }
00901 
00902     // First retrieve the annotation, so that we know we have to set it if it's not set.
00903     // On the other hand, if the user changed the contentstype, there's no need to get first.
00904     if ( !noContent() && mAccount->hasAnnotationSupport() &&
00905         ( kmkernel->iCalIface().isEnabled() || needToGetInitialAnnotations ) ) {
00906       QStringList annotations; // list of annotations to be fetched
00907       if ( !mAnnotationFolderTypeChanged || mAnnotationFolderType.isEmpty() )
00908         annotations << KOLAB_FOLDERTYPE;
00909       if ( !mIncidencesForChanged )
00910         annotations << KOLAB_INCIDENCESFOR;
00911       if ( !annotations.isEmpty() ) {
00912         newState( mProgress, i18n("Retrieving annotations"));
00913         KURL url = mAccount->getUrl();
00914         url.setPath( imapPath() );
00915         AnnotationJobs::MultiGetAnnotationJob* job =
00916           AnnotationJobs::multiGetAnnotation( mAccount->slave(), url, annotations );
00917         ImapAccountBase::jobData jd( url.url(), folder() );
00918         jd.cancellable = true;
00919         mAccount->insertJob(job, jd);
00920 
00921         connect( job, SIGNAL(annotationResult(const QString&, const QString&, bool)),
00922                  SLOT(slotAnnotationResult(const QString&, const QString&, bool)) );
00923         connect( job, SIGNAL(result(KIO::Job *)),
00924                  SLOT(slotGetAnnotationResult(KIO::Job *)) );
00925         break;
00926       }
00927     }
00928   } // case
00929   case SYNC_STATE_SET_ANNOTATIONS:
00930 
00931     mSyncState = SYNC_STATE_SET_ACLS;
00932     if ( !noContent() && mAccount->hasAnnotationSupport() &&
00933          ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) {
00934       newState( mProgress, i18n("Setting annotations"));
00935       KURL url = mAccount->getUrl();
00936       url.setPath( imapPath() );
00937       KMail::AnnotationList annotations; // to be set
00938       if ( mAnnotationFolderTypeChanged && !mAnnotationFolderType.isEmpty() ) {
00939         KMail::AnnotationAttribute attr( KOLAB_FOLDERTYPE, "value.shared", mAnnotationFolderType );
00940         annotations.append( attr );
00941         kdDebug(5006) << "Setting folder-type annotation for " << label() << " to " << mAnnotationFolderType << endl;
00942       }
00943       if ( mIncidencesForChanged ) {
00944         const QString val = incidencesForToString( mIncidencesFor );
00945         KMail::AnnotationAttribute attr( KOLAB_INCIDENCESFOR, "value.shared", val );
00946         annotations.append( attr );
00947         kdDebug(5006) << "Setting incidences-for annotation for " << label() << " to " << val << endl;
00948       }
00949       if ( !annotations.isEmpty() ) {
00950         KIO::Job* job =
00951           AnnotationJobs::multiSetAnnotation( mAccount->slave(), url, annotations );
00952         ImapAccountBase::jobData jd( url.url(), folder() );
00953         jd.cancellable = true; // we can always do so later
00954         mAccount->insertJob(job, jd);
00955 
00956         connect(job, SIGNAL(annotationChanged( const QString&, const QString&, const QString& ) ),
00957                 SLOT( slotAnnotationChanged( const QString&, const QString&, const QString& ) ));
00958         connect(job, SIGNAL(result(KIO::Job *)),
00959                 SLOT(slotSetAnnotationResult(KIO::Job *)));
00960         break;
00961       }
00962     }
00963 
00964   case SYNC_STATE_SET_ACLS:
00965     mSyncState = SYNC_STATE_GET_ACLS;
00966 
00967     if( !noContent() && mAccount->hasACLSupport() &&
00968       ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) {
00969       bool hasChangedACLs = false;
00970       ACLList::ConstIterator it = mACLList.begin();
00971       for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) {
00972         hasChangedACLs = (*it).changed;
00973       }
00974       if ( hasChangedACLs ) {
00975         newState( mProgress, i18n("Setting permissions"));
00976         KURL url = mAccount->getUrl();
00977         url.setPath( imapPath() );
00978         KIO::Job* job = KMail::ACLJobs::multiSetACL( mAccount->slave(), url, mACLList );
00979         ImapAccountBase::jobData jd( url.url(), folder() );
00980         mAccount->insertJob(job, jd);
00981 
00982         connect(job, SIGNAL(result(KIO::Job *)),
00983                 SLOT(slotMultiSetACLResult(KIO::Job *)));
00984         connect(job, SIGNAL(aclChanged( const QString&, int )),
00985                 SLOT(slotACLChanged( const QString&, int )) );
00986         break;
00987       }
00988     }
00989 
00990   case SYNC_STATE_GET_ACLS:
00991     // Continue with the subfolders
00992     mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
00993 
00994     if( !noContent() && mAccount->hasACLSupport() ) {
00995       newState( mProgress, i18n( "Retrieving permissions" ) );
00996       mAccount->getACL( folder(), mImapPath );
00997       connect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
00998                this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
00999       break;
01000     }
01001 
01002   case SYNC_STATE_FIND_SUBFOLDERS:
01003     {
01004       mProgress = 98;
01005       newState( mProgress, i18n("Updating cache file"));
01006 
01007       mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
01008       mSubfoldersForSync.clear();
01009       mCurrentSubfolder = 0;
01010       if( folder() && folder()->child() ) {
01011         KMFolderNode *node = folder()->child()->first();
01012         while( node ) {
01013           if( !node->isDir() ) {
01014             KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01015             // Only sync folders that have been accepted by the server
01016             if ( !storage->imapPath().isEmpty()
01017                  // and that were not just deleted from it
01018                  && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) {
01019               mSubfoldersForSync << storage;
01020             } else {
01021               kdDebug(5006) << "Do not add " << storage->label()
01022                 << " to synclist" << endl;
01023             }
01024           }
01025           node = folder()->child()->next();
01026         }
01027       }
01028 
01029     // All done for this folder.
01030     mProgress = 100; // all done
01031     newState( mProgress, i18n("Synchronization done"));
01032       KURL url = mAccount->getUrl();
01033       url.setPath( imapPath() );
01034       kmkernel->iCalIface().folderSynced( folder(), url );
01035     }
01036 
01037     if ( !mRecurse ) // "check mail for this folder" only
01038       mSubfoldersForSync.clear();
01039 
01040     // Carry on
01041   case SYNC_STATE_SYNC_SUBFOLDERS:
01042     {
01043       if( mCurrentSubfolder ) {
01044         disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
01045                     this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
01046         mCurrentSubfolder = 0;
01047       }
01048 
01049       if( mSubfoldersForSync.isEmpty() ) {
01050         mSyncState = SYNC_STATE_INITIAL;
01051         mAccount->addUnreadMsgCount( this, countUnread() ); // before closing
01052         close();
01053         emit folderComplete( this, true );
01054       } else {
01055         mCurrentSubfolder = mSubfoldersForSync.front();
01056         mSubfoldersForSync.pop_front();
01057         connect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
01058                  this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
01059 
01060         //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
01061         assert( !mCurrentSubfolder->imapPath().isEmpty() );
01062         mCurrentSubfolder->setAccount( account() );
01063         bool recurse = mCurrentSubfolder->noChildren() ? false : true;
01064         mCurrentSubfolder->serverSync( recurse );
01065       }
01066     }
01067     break;
01068 
01069   default:
01070     kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
01071               << mSyncState << endl;
01072   }
01073 }
01074 
01075 /* Connected to the imap account's connectionResult signal.
01076    Emitted when the slave connected or failed to connect.
01077 */
01078 void KMFolderCachedImap::slotConnectionResult( int errorCode, const QString& errorMsg )
01079 {
01080   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01081               this, SLOT( slotConnectionResult(int, const QString&) ) );
01082   if ( !errorCode ) {
01083     // Success
01084     mSyncState = SYNC_STATE_GET_USERRIGHTS;
01085     mProgress += 5;
01086     serverSyncInternal();
01087   } else {
01088     // Error (error message already shown by the account)
01089     newState( mProgress, KIO::buildErrorString( errorCode, errorMsg ));
01090     emit folderComplete(this, FALSE);
01091   }
01092 }
01093 
01094 /* find new messages (messages without a UID) */
01095 QValueList<unsigned long> KMFolderCachedImap::findNewMessages()
01096 {
01097   QValueList<unsigned long> result;
01098   for( int i = 0; i < count(); ++i ) {
01099     KMMsgBase *msg = getMsgBase( i );
01100     if( !msg ) continue; /* what goes on if getMsg() returns 0? */
01101     if ( msg->UID() == 0 )
01102       result.append( msg->getMsgSerNum() );
01103   }
01104   return result;
01105 }
01106 
01107 /* Upload new messages to server */
01108 void KMFolderCachedImap::uploadNewMessages()
01109 {
01110   QValueList<unsigned long> newMsgs = findNewMessages();
01111   if( !newMsgs.isEmpty() ) {
01112     if ( mUserRights <= 0 || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) {
01113       newState( mProgress, i18n("Uploading messages to server"));
01114       CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
01115       connect( job, SIGNAL( progress( unsigned long, unsigned long) ),
01116                this, SLOT( slotPutProgress(unsigned long, unsigned long) ) );
01117       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01118       job->start();
01119       return;
01120     } else {
01121       const QString msg ( i18n( "<p>There are new messages in this folder (%1), which "
01122             "have not been uploaded to the server yet, but you do not seem to "
01123             "have sufficient access rights on the folder now to upload them. "
01124             "Please contact your administrator to allow upload of new messages "
01125             "to you, or move them out of this folder.</p> "
01126             "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyURL() ) );
01127       if ( KMessageBox::warningYesNo( 0, msg, QString::null, i18n("Move"), i18n("Do Not Move") ) == KMessageBox::Yes ) {
01128         KMail::KMFolderSelDlg dlg( kmkernel->getKMMainWidget(),
01129             i18n("Move Messages to Folder"), true );
01130         if ( dlg.exec() ) {
01131           KMFolder* dest = dlg.folder();
01132           if ( dest ) {
01133             QPtrList<KMMsgBase> msgs;
01134             for( int i = 0; i < count(); ++i ) {
01135               KMMsgBase *msg = getMsgBase( i );
01136               if( !msg ) continue; /* what goes on if getMsg() returns 0? */
01137               if ( msg->UID() == 0 )
01138                 msgs.append( msg );
01139             }
01140             KMCommand *command = new KMMoveCommand( dest, msgs );
01141             connect( command, SIGNAL( completed( KMCommand * ) ),
01142                      this, SLOT( serverSyncInternal() ) );
01143             command->start();
01144             return;
01145           }
01146         }
01147       }
01148     }
01149   }
01150   newState( mProgress, i18n("No messages to upload to server"));
01151   serverSyncInternal();
01152 }
01153 
01154 /* Progress info during uploadNewMessages */
01155 void KMFolderCachedImap::slotPutProgress( unsigned long done, unsigned long total )
01156 {
01157   // (going from mProgress to mProgress+10)
01158   int progressSpan = 10;
01159   newState( mProgress + (progressSpan * done) / total, QString::null );
01160   if ( done == total ) // we're done
01161     mProgress += progressSpan;
01162 }
01163 
01164 /* Upload message flags to server */
01165 void KMFolderCachedImap::uploadFlags()
01166 {
01167   if ( !uidMap.isEmpty() ) {
01168     mStatusFlagsJobs = 0;
01169     newState( mProgress, i18n("Uploading status of messages to server"));
01170 
01171     // FIXME DUPLICATED FROM KMFOLDERIMAP
01172     QMap< QString, QStringList > groups;
01173     //open(); //already done
01174     for( int i = 0; i < count(); ++i ) {
01175       KMMsgBase* msg = getMsgBase( i );
01176       if( !msg || msg->UID() == 0 )
01177         // Either not a valid message or not one that is on the server yet
01178         continue;
01179 
01180       QString flags = KMFolderImap::statusToFlags(msg->status());
01181       // Collect uids for each typem of flags.
01182       QString uid;
01183       uid.setNum( msg->UID() );
01184       groups[flags].append(uid);
01185     }
01186     QMapIterator< QString, QStringList > dit;
01187     for( dit = groups.begin(); dit != groups.end(); ++dit ) {
01188       QCString flags = dit.key().latin1();
01189       QStringList sets = KMFolderImap::makeSets( (*dit), true );
01190       mStatusFlagsJobs += sets.count(); // ### that's not in kmfolderimap....
01191       // Send off a status setting job for each set.
01192       for( QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01193         QString imappath = imapPath() + ";UID=" + ( *slit );
01194         mAccount->setImapStatus(folder(), imappath, flags);
01195       }
01196     }
01197     // FIXME END DUPLICATED FROM KMFOLDERIMAP
01198 
01199     if ( mStatusFlagsJobs ) {
01200       connect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01201                this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01202       return;
01203     }
01204   }
01205   newState( mProgress, i18n("No messages to upload to server"));
01206   serverSyncInternal();
01207 }
01208 
01209 void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const QString&, bool cont)
01210 {
01211   if ( mSyncState == SYNC_STATE_INITIAL ){
01212       kdDebug(5006) << "IMAP status changed but reset " << endl;
01213       return; // we were reset
01214   }
01215   if ( folder->storage() == this ) {
01216     --mStatusFlagsJobs;
01217     if ( mStatusFlagsJobs == 0 || !cont ) // done or aborting
01218       disconnect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01219                   this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01220     if ( mStatusFlagsJobs == 0 && cont ) {
01221       mProgress += 5;
01222       serverSyncInternal();
01223     }
01224   }
01225 }
01226 
01227 // This is not perfect, what if the status didn't really change? Oh well ...
01228 void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle)
01229 {
01230   KMFolderMaildir::setStatus( idx, status, toggle );
01231   mStatusChangedLocally = true;
01232 }
01233 
01234 void KMFolderCachedImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01235 {
01236   KMFolderMaildir::setStatus(ids, status, toggle);
01237   mStatusChangedLocally = true;
01238 }
01239 
01240 /* Upload new folders to server */
01241 void KMFolderCachedImap::createNewFolders()
01242 {
01243   QValueList<KMFolderCachedImap*> newFolders = findNewFolders();
01244   //kdDebug(5006) << label() << " createNewFolders:" << newFolders.count() << " new folders." << endl;
01245   if( !newFolders.isEmpty() ) {
01246     newState( mProgress, i18n("Creating subfolders on server"));
01247     CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this );
01248     connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
01249     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01250     job->start();
01251   } else {
01252     serverSyncInternal();
01253   }
01254 }
01255 
01256 QValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
01257 {
01258   QValueList<KMFolderCachedImap*> newFolders;
01259   if( folder() && folder()->child() ) {
01260     KMFolderNode *node = folder()->child()->first();
01261     while( node ) {
01262       if( !node->isDir() ) {
01263         if( static_cast<KMFolder*>(node)->folderType() != KMFolderTypeCachedImap ) {
01264           kdError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
01265                         << node->name() << " is not an IMAP folder\n";
01266           node = folder()->child()->next();
01267           assert(0);
01268         }
01269         KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01270         if( folder->imapPath().isEmpty() ) {
01271           newFolders << folder;
01272         }
01273       }
01274       node = folder()->child()->next();
01275     }
01276   }
01277   return newFolders;
01278 }
01279 
01280 bool KMFolderCachedImap::deleteMessages()
01281 {
01282   if ( mUserRights > 0 && !( mUserRights & KMail::ACLJobs::Delete ) )
01283     return false;
01284   /* Delete messages from cache that are gone from the server */
01285   QPtrList<KMMessage> msgsForDeletion;
01286 
01287   // It is not possible to just go over all indices and remove
01288   // them one by one because the index list can get resized under
01289   // us. So use msg pointers instead
01290 
01291   QMap<ulong,int>::const_iterator it = uidMap.constBegin();
01292   for( ; it != uidMap.end(); it++ ) {
01293     ulong uid ( it.key() );
01294     if( uid!=0 && !uidsOnServer.find( uid ) )
01295       msgsForDeletion.append( getMsg( *it ) );
01296   }
01297 
01298   if( !msgsForDeletion.isEmpty() ) {
01299     removeMsg( msgsForDeletion );
01300   }
01301 
01302   /* Delete messages from the server that we dont have anymore */
01303   if( !uidsForDeletionOnServer.isEmpty() ) {
01304     newState( mProgress, i18n("Deleting removed messages from server"));
01305     QStringList sets = KMFolderImap::makeSets( uidsForDeletionOnServer, true );
01306     uidsForDeletionOnServer.clear();
01307     kdDebug(5006) << "Deleting " << sets.count() << " sets of messages from server folder " << imapPath() << endl;
01308     CachedImapJob *job = new CachedImapJob( sets, CachedImapJob::tDeleteMessage, this );
01309     connect( job, SIGNAL( result(KMail::FolderJob *) ),
01310              this, SLOT( slotDeleteMessagesResult(KMail::FolderJob *) ) );
01311     job->start();
01312     return true;
01313   } else {
01314     return false;
01315   }
01316 }
01317 
01318 void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob* job )
01319 {
01320   if ( job->error() ) {
01321     // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages
01322     mSyncState = SYNC_STATE_GET_MESSAGES;
01323   }
01324   mProgress += 10;
01325   serverSyncInternal();
01326 }
01327 
01328 void KMFolderCachedImap::checkUidValidity() {
01329   // IMAP root folders don't seem to have a UID validity setting.
01330   // Also, don't try the uid validity on new folders
01331   if( imapPath().isEmpty() || imapPath() == "/" )
01332     // Just proceed
01333     serverSyncInternal();
01334   else {
01335     newState( mProgress, i18n("Checking folder validity"));
01336     CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this );
01337     connect( job, SIGNAL( result( KMail::FolderJob* ) ),
01338              this, SLOT( slotCheckUidValidityResult( KMail::FolderJob* ) ) );
01339     job->start();
01340   }
01341 }
01342 
01343 void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob* job )
01344 {
01345   if ( job->error() ) { // there was an error and the user chose "continue"
01346     // We can't continue doing anything in the same folder though, it would delete all mails.
01347     // But we can continue to subfolders if any. Well we can also try annotation/acl stuff...
01348     mSyncState = SYNC_STATE_HANDLE_INBOX;
01349   }
01350   mProgress += 5;
01351   serverSyncInternal();
01352 }
01353 
01354 /* This will only list the messages in a folder.
01355    No directory listing done*/
01356 void KMFolderCachedImap::listMessages() {
01357   if( imapPath() == "/" ) {
01358     // Don't list messages on the root folder
01359     serverSyncInternal();
01360     return;
01361   }
01362 
01363   if( !mAccount->slave() ) { // sync aborted
01364     resetSyncState();
01365     emit folderComplete( this, false );
01366     return;
01367   }
01368   uidsOnServer.clear();
01369   uidsOnServer.resize( count() * 2 );
01370   uidsForDeletionOnServer.clear();
01371   mMsgsForDownload.clear();
01372   mUidsForDownload.clear();
01373 
01374   CachedImapJob* job = new CachedImapJob( FolderJob::tListMessages, this );
01375   connect( job, SIGNAL( result(KMail::FolderJob *) ),
01376            this, SLOT( slotGetLastMessagesResult(KMail::FolderJob *) ) );
01377   job->start();
01378 }
01379 
01380 void KMFolderCachedImap::slotGetLastMessagesResult(KMail::FolderJob *job)
01381 {
01382   getMessagesResult(job, true);
01383 }
01384 
01385 // Connected to the listMessages job in CachedImapJob
01386 void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
01387 {
01388   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01389   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
01390     kdDebug(5006) << "could not find job!?!?!" << endl;
01391     // be sure to reset the sync state, if the listing was partial we would
01392     // otherwise delete not-listed mail locally, and on the next sync on the server
01393     // as well
01394     mSyncState = SYNC_STATE_HANDLE_INBOX;
01395     serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
01396     return;
01397   }
01398   (*it).cdata += QCString(data, data.size() + 1);
01399   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
01400   if (pos > 0) {
01401     int a = (*it).cdata.find("\r\nX-uidValidity:");
01402     if (a != -1) {
01403       int b = (*it).cdata.find("\r\n", a + 17);
01404       setUidValidity((*it).cdata.mid(a + 17, b - a - 17));
01405     }
01406     a = (*it).cdata.find("\r\nX-Access:");
01407     // Only trust X-Access (i.e. the imap select info) if we don't know mUserRights.
01408     // The latter is more accurate (checked on every sync) whereas X-Access is only
01409     // updated when selecting the folder again, which might not happen if using
01410     // RMB / Check Mail in this folder. We don't need two (potentially conflicting)
01411     // sources for the readonly setting, in any case.
01412     if (a != -1 && mUserRights == -1 ) {
01413       int b = (*it).cdata.find("\r\n", a + 12);
01414       const QString access = (*it).cdata.mid(a + 12, b - a - 12);
01415       setReadOnly( access == "Read only" );
01416     }
01417     (*it).cdata.remove(0, pos);
01418   }
01419   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01420   // Start with something largish when rebuilding the cache
01421   if ( uidsOnServer.size() == 0 )
01422     uidsOnServer.resize( KMail::nextPrime( 2000 ) );
01423   int flags;
01424   const int v = 42;
01425   while (pos >= 0) {
01426     KMMessage msg;
01427     msg.fromString((*it).cdata.mid(16, pos - 16));
01428     flags = msg.headerField("X-Flags").toInt();
01429     bool deleted = ( flags & 8 );
01430     ulong uid = msg.UID();
01431     if ( !deleted ) {
01432       if( uid != 0 ) {
01433         if ( uidsOnServer.count() == uidsOnServer.size() ) {
01434           uidsOnServer.resize( KMail::nextPrime( uidsOnServer.size() * 2 ) );
01435           kdDebug( 5006 ) << "Resizing to: " << uidsOnServer.size() << endl;
01436         }
01437         uidsOnServer.insert( uid, &v );
01438       }
01439       bool redownload = false;
01440       if (  uid <= lastUid() ) {
01441        /*
01442         * If this message UID is not present locally, then it must
01443         * have been deleted by the user, so we delete it on the
01444         * server also. If we don't have delete permissions on the server,
01445         * re-download the message, it must have vanished by some error, or
01446         * while we still thought we were allowed to delete (ACL change).
01447         *
01448         * This relies heavily on lastUid() being correct at all times.
01449         */
01450         // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<label()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl;
01451         KMMsgBase *existingMessage = findByUID(uid);
01452         if( !existingMessage ) {
01453           if ( mUserRights <= 0 || ( mUserRights & KMail::ACLJobs::Delete ) ) {
01454             // kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
01455             uidsForDeletionOnServer << uid;
01456           } else {
01457             redownload = true;
01458           }
01459         } else {
01460           // if this is a read only folder, ignore status updates from the server
01461           // since we can't write our status back our local version is what has to
01462           // be considered correct.
01463           if (!mReadOnly) {
01464             /* The message is OK, update flags */
01465             KMFolderImap::flagsToStatus( existingMessage, flags );
01466           }
01467         }
01468         // kdDebug(5006) << "message with uid " << uid << " found in the local cache. " << endl;
01469       }
01470       if ( uid > lastUid() || redownload ) {
01471         // The message is new since the last sync, but we might have just uploaded it, in which case
01472         // the uid map already contains it.
01473         if ( !uidMap.contains( uid ) ) {
01474           ulong size = msg.headerField("X-Length").toULong();
01475           mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size);
01476           if( imapPath() == "/INBOX/" )
01477             mUidsForDownload << uid;
01478         }
01479         // Remember the highest uid and once the download is completed, update mLastUid
01480         if ( uid > mTentativeHighestUid )
01481           mTentativeHighestUid = uid;
01482       }
01483     }
01484     (*it).cdata.remove(0, pos);
01485     (*it).done++;
01486     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01487   }
01488 }
01489 
01490 void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet )
01491 {
01492   mProgress += 10;
01493   if( job->error() ) { // error listing messages but the user chose to continue
01494     mContentState = imapNoInformation;
01495     mSyncState = SYNC_STATE_HANDLE_INBOX; // be sure not to continue in this folder
01496   } else {
01497     if( lastSet ) { // always true here (this comes from online-imap...)
01498       mContentState = imapFinished;
01499       mStatusChangedLocally = false; // we are up to date again
01500     }
01501   }
01502   serverSyncInternal();
01503 }
01504 
01505 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
01506 {
01507   int progressSpan = 100 - 5 - mProgress;
01508   //kdDebug(5006) << "KMFolderCachedImap::slotProgress done=" << done << " total=" << total << "=> mProgress=" << mProgress + ( progressSpan * done ) / total << endl;
01509   // Progress info while retrieving new emails
01510   // (going from mProgress to mProgress+progressSpan)
01511   newState( mProgress + (progressSpan * done) / total, QString::null );
01512 }
01513 
01514 
01515 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
01516 {
01517   assert( aAccount->isA("KMAcctCachedImap") );
01518   mAccount = aAccount;
01519   if( imapPath()=="/" ) aAccount->setFolder( folder() );
01520 
01521   // Folder was renamed in a previous session, and the user didn't sync yet
01522   QString newName = mAccount->renamedFolder( imapPath() );
01523   if ( !newName.isEmpty() )
01524     folder()->setLabel( newName );
01525 
01526   if( !folder() || !folder()->child() || !folder()->child()->count() ) return;
01527   for( KMFolderNode* node = folder()->child()->first(); node;
01528        node = folder()->child()->next() )
01529     if (!node->isDir())
01530       static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
01531 }
01532 
01533 void KMFolderCachedImap::listNamespaces()
01534 {
01535   ImapAccountBase::ListType type = ImapAccountBase::List;
01536   if ( mAccount->onlySubscribedFolders() )
01537     type = ImapAccountBase::ListSubscribed;
01538 
01539   kdDebug(5006) << "listNamespaces " << mNamespacesToList << endl;
01540   if ( mNamespacesToList.isEmpty() ) {
01541     mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
01542     mPersonalNamespacesCheckDone = true;
01543 
01544     QStringList ns = mAccount->namespaces()[ImapAccountBase::OtherUsersNS];
01545     ns += mAccount->namespaces()[ImapAccountBase::SharedNS];
01546     mNamespacesToCheck = ns.count();
01547     for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
01548     {
01549       if ( (*it).isEmpty() ) {
01550         // ignore empty listings as they have been listed before
01551         --mNamespacesToCheck;
01552         continue;
01553       }
01554       KMail::ListJob* job = new KMail::ListJob( mAccount, type, this, mAccount->addPathToNamespace( *it ) );
01555       connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
01556               const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
01557           this, SLOT(slotCheckNamespace(const QStringList&, const QStringList&,
01558               const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
01559       job->start();
01560     }
01561     if ( mNamespacesToCheck == 0 ) {
01562       serverSyncInternal();
01563     }
01564     return;
01565   }
01566   mPersonalNamespacesCheckDone = false;
01567 
01568   QString ns = mNamespacesToList.front();
01569   mNamespacesToList.pop_front();
01570 
01571   mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
01572   newState( mProgress, i18n("Retrieving folders for namespace %1").arg(ns));
01573   KMail::ListJob* job = new KMail::ListJob( mAccount, type, this,
01574       mAccount->addPathToNamespace( ns ) );
01575   job->setNamespace( ns );
01576   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
01577           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
01578       this, SLOT(slotListResult(const QStringList&, const QStringList&,
01579           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
01580   job->start();
01581 }
01582 
01583 void KMFolderCachedImap::slotCheckNamespace( const QStringList& subfolderNames,
01584                                              const QStringList& subfolderPaths,
01585                                              const QStringList& subfolderMimeTypes,
01586                                              const QStringList& subfolderAttributes,
01587                                              const ImapAccountBase::jobData& jobData )
01588 {
01589   Q_UNUSED( subfolderPaths );
01590   Q_UNUSED( subfolderMimeTypes );
01591   Q_UNUSED( subfolderAttributes );
01592   --mNamespacesToCheck;
01593   kdDebug(5006) << "slotCheckNamespace " << subfolderNames << ",remain=" <<
01594    mNamespacesToCheck << endl;
01595 
01596   // get a correct foldername:
01597   // strip / and make sure it does not contain the delimiter
01598   QString name = jobData.path.mid( 1, jobData.path.length()-2 );
01599   name.remove( mAccount->delimiterForNamespace( name ) );
01600   if ( name.isEmpty() ) {
01601     // should not happen
01602     kdWarning(5006) << "slotCheckNamespace: ignoring empty folder!" << endl;
01603     return;
01604   }
01605 
01606   folder()->createChildFolder();
01607   KMFolderNode *node = 0;
01608   for ( node = folder()->child()->first(); node;
01609         node = folder()->child()->next())
01610   {
01611     if ( !node->isDir() && node->name() == name )
01612       break;
01613   }
01614   if ( !subfolderNames.isEmpty() ) {
01615     if ( node ) {
01616       // folder exists so we have nothing to do - it will be listed later
01617       kdDebug(5006) << "found namespace folder " << name << endl;
01618     } else
01619     {
01620       // create folder
01621       kdDebug(5006) << "create namespace folder " << name << endl;
01622       KMFolder* newFolder = folder()->child()->createFolder( name, false,
01623           KMFolderTypeCachedImap );
01624       if ( newFolder ) {
01625         KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>( newFolder->storage() );
01626         f->setImapPath( mAccount->addPathToNamespace( name ) );
01627         f->setNoContent( true );
01628         f->setAccount( mAccount );
01629         f->close();
01630         kmkernel->dimapFolderMgr()->contentsChanged();
01631       }
01632     }
01633   } else {
01634     if ( node ) {
01635       kdDebug(5006) << "delete namespace folder " << name << endl;
01636       KMFolder* fld = static_cast<KMFolder*>(node);
01637       kmkernel->dimapFolderMgr()->remove( fld );
01638     }
01639   }
01640 
01641   if ( mNamespacesToCheck == 0 ) {
01642     // all namespaces are done so continue with the next step
01643     serverSyncInternal();
01644   }
01645 }
01646 
01647 // This lists the subfolders on the server
01648 // and (in slotListResult) takes care of folders that have been removed on the server
01649 bool KMFolderCachedImap::listDirectory()
01650 {
01651   if( !mAccount->slave() ) { // sync aborted
01652     resetSyncState();
01653     emit folderComplete( this, false );
01654     return false;
01655   }
01656   mSubfolderState = imapInProgress;
01657 
01658   // get the folders
01659   ImapAccountBase::ListType type = ImapAccountBase::List;
01660   if ( mAccount->onlySubscribedFolders() )
01661     type = ImapAccountBase::ListSubscribed;
01662   KMail::ListJob* job = new KMail::ListJob( mAccount, type, this );
01663   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
01664           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
01665       this, SLOT(slotListResult(const QStringList&, const QStringList&,
01666           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
01667   job->start();
01668 
01669   return true;
01670 }
01671 
01672 void KMFolderCachedImap::slotListResult( const QStringList& folderNames,
01673                                          const QStringList& folderPaths,
01674                                          const QStringList& folderMimeTypes,
01675                                          const QStringList& folderAttributes,
01676                                          const ImapAccountBase::jobData& jobData )
01677 {
01678   Q_UNUSED( jobData );
01679   //kdDebug(5006) << label() << ": folderNames=" << folderNames << " folderPaths="
01680   //<< folderPaths << " mimeTypes=" << folderMimeTypes << endl;
01681   mSubfolderNames = folderNames;
01682   mSubfolderPaths = folderPaths;
01683   mSubfolderMimeTypes = folderMimeTypes;
01684   mSubfolderAttributes = folderAttributes;
01685 
01686   mSubfolderState = imapFinished;
01687 
01688   folder()->createChildFolder();
01689   KMFolderNode *node = folder()->child()->first();
01690   bool root = ( this == mAccount->rootFolder() );
01691 
01692   QPtrList<KMFolder> toRemove;
01693   bool emptyList = ( root && mSubfolderNames.empty() );
01694   if ( !emptyList ) {
01695     while (node) {
01696       if (!node->isDir() ) {
01697         KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01698 
01699         if ( mSubfolderNames.findIndex(node->name()) == -1 ) {
01700           QString name = node->name();
01701           // as more than one namespace can be listed in the root folder we need to make sure
01702           // that the folder is within the current namespace
01703           bool isInNamespace = ( jobData.curNamespace.isEmpty() ||
01704               jobData.curNamespace == mAccount->namespaceForFolder( f ) );
01705           // ignore some cases
01706           bool ignore = root && ( f->imapPath() == "/INBOX/" ||
01707               mAccount->isNamespaceFolder( name ) || !isInNamespace );
01708 
01709           // This subfolder isn't present on the server
01710           if( !f->imapPath().isEmpty() && !ignore  ) {
01711             // The folder has an imap path set, so it has been
01712             // on the server before. Delete it locally.
01713             toRemove.append( f->folder() );
01714             kdDebug(5006) << node->name() << " isn't on the server. It has an imapPath -> delete it locally" << endl;
01715           }
01716         } else { // folder both local and on server
01717           //kdDebug(5006) << node->name() << " is on the server." << endl;
01718         }
01719       } else {
01720         //kdDebug(5006) << "skipping dir node:" << node->name() << endl;
01721       }
01722       node = folder()->child()->next();
01723     }
01724   }
01725 
01726   for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) {
01727     kmkernel->dimapFolderMgr()->remove( doomed );
01728   }
01729 
01730   mProgress += 5;
01731   serverSyncInternal();
01732 }
01733 
01734 // This synchronizes the local folders as needed (creation/deletion). No network communication here.
01735 void KMFolderCachedImap::listDirectory2()
01736 {
01737   QString path = folder()->path();
01738   KMFolderCachedImap *f = 0;
01739   kmkernel->dimapFolderMgr()->quiet(true);
01740 
01741   KMFolderNode *node;
01742   bool root = ( this == mAccount->rootFolder() );
01743   if ( root && !mAccount->hasInbox() ) {
01744     kdDebug(5006) << "check INBOX" << endl;
01745     // create the INBOX
01746     for (node = folder()->child()->first(); node; node = folder()->child()->next())
01747       if (!node->isDir() && node->name() == "INBOX") break;
01748     if (node) {
01749       f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01750     } else {
01751       KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap);
01752       if ( newFolder ) {
01753         f = static_cast<KMFolderCachedImap*>(newFolder->storage());
01754       }
01755     }
01756     if ( f ) {
01757       f->setAccount( mAccount );
01758       f->setImapPath( "/INBOX/" );
01759       f->folder()->setLabel( i18n("inbox") );
01760     }
01761     if (!node) {
01762       if ( f )
01763         f->close();
01764       kmkernel->dimapFolderMgr()->contentsChanged();
01765     }
01766     // so we have an INBOX
01767     mAccount->setHasInbox( true );
01768   }
01769 
01770   if ( root && !mSubfolderNames.isEmpty() ) {
01771     KMFolderCachedImap* parent =
01772       findParent( mSubfolderPaths.first(), mSubfolderNames.first() );
01773     if ( parent ) {
01774       kdDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to "
01775         << parent->label() << endl;
01776       mSubfolderNames.clear();
01777     }
01778   }
01779 
01780   // Find all subfolders present on server but not on disk
01781   for (uint i = 0; i < mSubfolderNames.count(); i++) {
01782 
01783     // Find the subdir, if already present
01784     for (node = folder()->child()->first(); node;
01785          node = folder()->child()->next())
01786       if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
01787 
01788     if (!node) {
01789       // This folder is not present here
01790       // Either it's new on the server, or we just deleted it.
01791       QString subfolderPath = mSubfolderPaths[i];
01792       // The code used to look at the uidcache to know if it was "just deleted".
01793       // But this breaks with noContent folders and with shared folders.
01794       // So instead we keep a list in the account.
01795       bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath );
01796       // That list is saved/restored across sessions, but to avoid any mistake,
01797       // ask for confirmation if the folder was deleted in a previous session
01798       // (could be that the folder was deleted & recreated meanwhile from another client...)
01799       if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) {
01800            locallyDeleted = KMessageBox::warningYesNo(
01801              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;
01802       }
01803 
01804       if ( locallyDeleted ) {
01805         kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl;
01806         foldersForDeletionOnServer += mAccount->deletedFolderPaths( subfolderPath ); // grab all subsubfolders too
01807       } else {
01808         kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl;
01809         KMFolder* newFolder = folder()->child()->createFolder(mSubfolderNames[i], false, KMFolderTypeCachedImap);
01810         if ( newFolder ) {
01811           f = static_cast<KMFolderCachedImap*>(newFolder->storage());
01812         }
01813         if (f) {
01814           f->close();
01815           f->setAccount(mAccount);
01816           kmkernel->dimapFolderMgr()->contentsChanged();
01817           f->mAnnotationFolderType = "FROMSERVER";
01818           //kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl;
01819         } else {
01820           kdDebug(5006) << "can't create folder " << mSubfolderNames[i] <<endl;
01821         }
01822       }
01823     } else { // Folder found locally
01824       if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap )
01825         f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01826     }
01827 
01828     if( f ) {
01829       // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath()
01830       //               << "\nSetting imapPath " << mSubfolderPaths[i] << endl;
01831       // Write folder settings
01832       f->setAccount(mAccount);
01833       f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
01834       f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest");
01835       f->setImapPath(mSubfolderPaths[i]);
01836     }
01837   }
01838   kmkernel->dimapFolderMgr()->quiet(false);
01839   emit listComplete(this);
01840   if ( !mPersonalNamespacesCheckDone ) {
01841     // we're not done with the namespaces
01842     mSyncState = SYNC_STATE_LIST_NAMESPACES;
01843   }
01844   serverSyncInternal();
01845 }
01846 
01847 //-----------------------------------------------------------------------------
01848 KMFolderCachedImap* KMFolderCachedImap::findParent( const QString& path,
01849                                                     const QString& name )
01850 {
01851   QString parent = path.left( path.length() - name.length() - 2 );
01852   if ( parent.length() > 1 )
01853   {
01854     // extract name of the parent
01855     parent = parent.right( parent.length() - 1 );
01856     if ( parent != label() )
01857     {
01858       KMFolderNode *node = folder()->child()->first();
01859       // look for a better parent
01860       while ( node )
01861       {
01862         if ( node->name() == parent )
01863         {
01864           KMFolder* fld = static_cast<KMFolder*>(node);
01865           KMFolderCachedImap* imapFld =
01866             static_cast<KMFolderCachedImap*>( fld->storage() );
01867           return imapFld;
01868         }
01869         node = folder()->child()->next();
01870       }
01871     }
01872   }
01873   return 0;
01874 }
01875 
01876 void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success)
01877 {
01878   Q_UNUSED(sub);
01879   //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl;
01880   if ( success ) {
01881     serverSyncInternal();
01882   }
01883   else
01884   {
01885     // success == false means the sync was aborted.
01886     if ( mCurrentSubfolder ) {
01887       Q_ASSERT( sub == mCurrentSubfolder );
01888       disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
01889                   this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
01890       mCurrentSubfolder = 0;
01891     }
01892 
01893     mSubfoldersForSync.clear();
01894     mSyncState = SYNC_STATE_INITIAL;
01895     close();
01896     emit folderComplete( this, false );
01897   }
01898 }
01899 
01900 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01901 {
01902   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01903   if (it == mAccount->jobsEnd()) return;
01904   QBuffer buff((*it).data);
01905   buff.open(IO_WriteOnly | IO_Append);
01906   buff.writeBlock(data.data(), data.size());
01907   buff.close();
01908 }
01909 
01910 
01911 FolderJob*
01912 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
01913                                  QString, const AttachmentStrategy* ) const
01914 {
01915   QPtrList<KMMessage> msgList;
01916   msgList.append( msg );
01917   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
01918   job->setParentFolder( this );
01919   return job;
01920 }
01921 
01922 FolderJob*
01923 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01924                                  FolderJob::JobType jt, KMFolder *folder ) const
01925 {
01926   //FIXME: how to handle sets here?
01927   Q_UNUSED( sets );
01928   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
01929   job->setParentFolder( this );
01930   return job;
01931 }
01932 
01933 void
01934 KMFolderCachedImap::setUserRights( unsigned int userRights )
01935 {
01936   mUserRights = userRights;
01937 }
01938 
01939 void
01940 KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder )
01941 {
01942   if ( folder->storage() == this ) {
01943     disconnect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
01944                 this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
01945     if ( mUserRights == 0 ) // didn't work
01946       mUserRights = -1; // error code (used in folderdia)
01947     else
01948       setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 );
01949     mProgress += 5;
01950     serverSyncInternal();
01951   }
01952 }
01953 
01954 void
01955 KMFolderCachedImap::setReadOnly( bool readOnly )
01956 {
01957   if ( readOnly != mReadOnly ) {
01958     mReadOnly = readOnly;
01959     emit readOnlyChanged( folder() );
01960   }
01961 }
01962 
01963 void
01964 KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job*, const KMail::ACLList& aclList )
01965 {
01966   if ( folder->storage() == this ) {
01967     disconnect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
01968                 this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
01969     mACLList = aclList;
01970     serverSyncInternal();
01971   }
01972 }
01973 
01974 void
01975 KMFolderCachedImap::setACLList( const ACLList& arr )
01976 {
01977   mACLList = arr;
01978 }
01979 
01980 void
01981 KMFolderCachedImap::slotMultiSetACLResult(KIO::Job *job)
01982 {
01983   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01984   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
01985   if ( (*it).parent != folder() ) return; // Shouldn't happen
01986 
01987   if ( job->error() )
01988     // Display error but don't abort the sync just for this
01989     // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel
01990     job->showErrorDialog();
01991   else
01992     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
01993 
01994   if (mAccount->slave()) mAccount->removeJob(job);
01995   serverSyncInternal();
01996 }
01997 
01998 void
01999 KMFolderCachedImap::slotACLChanged( const QString& userId, int permissions )
02000 {
02001   // The job indicates success in changing the permissions for this user
02002   // -> we note that it's been done.
02003   for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) {
02004     if ( (*it).userId == userId && (*it).permissions == permissions ) {
02005       if ( permissions == -1 ) // deleted
02006         mACLList.erase( it );
02007       else // added/modified
02008         (*it).changed = false;
02009       return;
02010     }
02011   }
02012 }
02013 
02014 // called by KMAcctCachedImap::killAllJobs
02015 void KMFolderCachedImap::resetSyncState()
02016 {
02017   if ( mSyncState == SYNC_STATE_INITIAL ) return;
02018   mSubfoldersForSync.clear();
02019   mSyncState = SYNC_STATE_INITIAL;
02020   close();
02021   // Don't use newState here, it would revert to mProgress (which is < current value when listing messages)
02022   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02023   QString str = i18n("Aborted");
02024   if (progressItem)
02025      progressItem->setStatus( str );
02026   emit statusMsg( str );
02027 }
02028 
02029 void KMFolderCachedImap::slotIncreaseProgress()
02030 {
02031   mProgress += 5;
02032 }
02033 
02034 void KMFolderCachedImap::newState( int progress, const QString& syncStatus )
02035 {
02036   //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl;
02037   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02038   if( progressItem )
02039     progressItem->setCompletedItems( progress );
02040   if ( !syncStatus.isEmpty() ) {
02041     QString str;
02042     // For a subfolder, show the label. But for the main folder, it's already shown.
02043     if ( mAccount->imapFolder() == this )
02044       str = syncStatus;
02045     else
02046       str = QString( "%1: %2" ).arg( label() ).arg( syncStatus );
02047     if( progressItem )
02048       progressItem->setStatus( str );
02049     emit statusMsg( str );
02050   }
02051   if( progressItem )
02052     progressItem->updateProgress();
02053 }
02054 
02055 void KMFolderCachedImap::setSubfolderState( imapState state )
02056 {
02057   mSubfolderState = state;
02058   if ( state == imapNoInformation && folder()->child() )
02059   {
02060     // pass through to childs
02061     KMFolderNode* node;
02062     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02063     for ( ; (node = it.current()); )
02064     {
02065       ++it;
02066       if (node->isDir()) continue;
02067       KMFolder *folder = static_cast<KMFolder*>(node);
02068       static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state );
02069     }
02070   }
02071 }
02072 
02073 void KMFolderCachedImap::setImapPath(const QString &path)
02074 {
02075   mImapPath = path;
02076 }
02077 
02078 // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
02079 // It is updated from the folder contents type and whether it's a standard resource folder.
02080 // This happens during the syncing phase and during initFolder for a new folder.
02081 // Don't do it earlier, e.g. from setContentsType:
02082 // on startup, it's too early there to know if this is a standard resource folder.
02083 void KMFolderCachedImap::updateAnnotationFolderType()
02084 {
02085   QString oldType = mAnnotationFolderType;
02086   QString oldSubType;
02087   int dot = oldType.find( '.' );
02088   if ( dot != -1 ) {
02089     oldType.truncate( dot );
02090     oldSubType = mAnnotationFolderType.mid( dot + 1 );
02091   }
02092 
02093   QString newType, newSubType;
02094   // We want to store an annotation on the folder only if using the kolab storage.
02095   if ( kmkernel->iCalIface().storageFormat( folder() ) == KMailICalIfaceImpl::StorageXML ) {
02096     newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType );
02097     if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) )
02098       newSubType = "default";
02099     else
02100       newSubType = oldSubType; // preserve unknown subtypes, like drafts etc. And preserve ".default" too.
02101   }
02102 
02103   //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl;
02104   if ( newType != oldType || newSubType != oldSubType ) {
02105     mAnnotationFolderType = newType + ( newSubType.isEmpty() ? QString::null : "."+newSubType );
02106     mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync
02107     kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl;
02108   }
02109   // Ensure that further readConfig()s don't lose mAnnotationFolderType
02110   writeAnnotationConfig();
02111 }
02112 
02113 void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor )
02114 {
02115   if ( mIncidencesFor != incfor ) {
02116     mIncidencesFor = incfor;
02117     mIncidencesForChanged = true;
02118   }
02119 }
02120 
02121 void KMFolderCachedImap::slotAnnotationResult(const QString& entry, const QString& value, bool found)
02122 {
02123   if ( entry == KOLAB_FOLDERTYPE ) {
02124     // There are four cases.
02125     // 1) no content-type on server -> set it
02126     // 2) different content-type on server, locally changed -> set it (we don't even come here)
02127     // 3) different (known) content-type on server, no local change -> get it
02128     // 4) different unknown content-type on server, probably some older version -> set it
02129     if ( found ) {
02130       QString type = value;
02131       QString subtype;
02132       int dot = value.find( '.' );
02133       if ( dot != -1 ) {
02134         type.truncate( dot );
02135         subtype = value.mid( dot + 1 );
02136       }
02137       bool foundKnownType = false;
02138       for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
02139         FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
02140         if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) {
02141           // Case 3: known content-type on server, get it
02142           //kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: found known type of annotation" << endl;
02143           if ( contentsType != ContentsTypeMail )
02144             kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
02145           mAnnotationFolderType = value;
02146           if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent()
02147                && GlobalSettings::self()->theIMAPResourceEnabled()
02148                && subtype == "default" ) {
02149             // Truncate subtype if this folder can't be a default resource folder for us,
02150             // although it apparently is for someone else.
02151             mAnnotationFolderType = type;
02152             kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: parent folder is " << folder()->parent()->owner()->idString() << " => truncating annotation to " << value << endl;
02153           }
02154           setContentsType( contentsType );
02155           mAnnotationFolderTypeChanged = false; // we changed it, not the user
02156           foundKnownType = true;
02157 
02158           // Users don't read events/contacts/etc. in kmail, so mark them all as read.
02159           // This is done in cachedimapjob when getting new messages, but do it here too,
02160           // for the initial set of messages when we didn't know this was a resource folder yet,
02161           // for old folders, etc.
02162           if ( contentsType != ContentsTypeMail )
02163             markUnreadAsRead();
02164 
02165           // Ensure that further readConfig()s don't lose mAnnotationFolderType
02166           writeAnnotationConfig();
02167           break;
02168         }
02169       }
02170       if ( !foundKnownType && !mReadOnly ) {
02171         //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, will need to set it" << endl;
02172         // Case 4: server has strange content-type, set it to what we need
02173         mAnnotationFolderTypeChanged = true;
02174       }
02175       // TODO handle subtype (inbox, drafts, sentitems, junkemail)
02176     }
02177     else if ( !mReadOnly ) {
02178       // Case 1: server doesn't have content-type, set it
02179       //kdDebug(5006) << "slotGetAnnotationResult: no annotation found, will need to set it" << endl;
02180       mAnnotationFolderTypeChanged = true;
02181     }
02182   } else if ( entry == KOLAB_INCIDENCESFOR ) {
02183     if ( found ) {
02184       mIncidencesFor = incidencesForFromString( value );
02185       Q_ASSERT( mIncidencesForChanged == false );
02186     }
02187   }
02188 }
02189 
02190 void KMFolderCachedImap::slotGetAnnotationResult( KIO::Job* job )
02191 {
02192   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02193   Q_ASSERT( it != mAccount->jobsEnd() );
02194   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02195   Q_ASSERT( (*it).parent == folder() );
02196   if ( (*it).parent != folder() ) return; // Shouldn't happen
02197 
02198   AnnotationJobs::GetAnnotationJob* annjob = static_cast<AnnotationJobs::GetAnnotationJob *>( job );
02199   if ( annjob->error() ) {
02200     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02201       // that's when the imap server doesn't support annotations
02202       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02203            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02204     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() ) );
02205       mAccount->setHasNoAnnotationSupport();
02206     }
02207     else
02208       kdWarning(5006) << "slotGetAnnotationResult: " << job->errorString() << endl;
02209   }
02210 
02211   if (mAccount->slave()) mAccount->removeJob(job);
02212   mProgress += 2;
02213   serverSyncInternal();
02214 }
02215 
02216 void
02217 KMFolderCachedImap::slotAnnotationChanged( const QString& entry, const QString& attribute, const QString& value )
02218 {
02219   kdDebug(5006) << k_funcinfo << entry << " " << attribute << " " << value << endl;
02220   if ( entry == KOLAB_FOLDERTYPE )
02221     mAnnotationFolderTypeChanged = false;
02222   else if ( entry == KOLAB_INCIDENCESFOR ) {
02223     mIncidencesForChanged = false;
02224     // The incidences-for changed, we must trigger the freebusy creation.
02225     // HACK: in theory we would need a new enum value for this.
02226     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02227   }
02228 }
02229 
02230 void KMFolderCachedImap::slotTestAnnotationResult(KIO::Job *job)
02231 {
02232   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02233   Q_ASSERT( it != mAccount->jobsEnd() );
02234   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02235   Q_ASSERT( (*it).parent == folder() );
02236   if ( (*it).parent != folder() ) return; // Shouldn't happen
02237 
02238   mAccount->setAnnotationCheckPassed( true );
02239   if ( job->error() ) {
02240     kdDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl;
02241     mAccount->setHasNoAnnotationSupport( );
02242   } else {
02243     kdDebug(5006) << "Test Annotation was passed   OK" << endl;
02244   }
02245   if (mAccount->slave()) mAccount->removeJob(job);
02246   serverSyncInternal();
02247 }
02248 
02249 void
02250 KMFolderCachedImap::slotSetAnnotationResult(KIO::Job *job)
02251 {
02252   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02253   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02254   if ( (*it).parent != folder() ) return; // Shouldn't happen
02255 
02256   bool cont = true;
02257   if ( job->error() ) {
02258     // Don't show error if the server doesn't support ANNOTATEMORE and this folder only contains mail
02259     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION && contentsType() == ContentsTypeMail )
02260       if (mAccount->slave()) mAccount->removeJob(job);
02261     else
02262       cont = mAccount->handleJobError( job, i18n( "Error while setting annotation: " ) + '\n' );
02263   } else {
02264     if (mAccount->slave()) mAccount->removeJob(job);
02265   }
02266   if ( cont )
02267     serverSyncInternal();
02268 }
02269 
02270 void KMFolderCachedImap::slotUpdateLastUid()
02271 {
02272   if( mTentativeHighestUid != 0 )
02273     setLastUid( mTentativeHighestUid );
02274   mTentativeHighestUid = 0;
02275 }
02276 
02277 bool KMFolderCachedImap::isMoveable() const
02278 {
02279   return ( hasChildren() == HasNoChildren &&
02280       !folder()->isSystemFolder() ) ? true : false;
02281 }
02282 
02283 void KMFolderCachedImap::slotFolderDeletionOnServerFinished()
02284 {
02285   for ( QStringList::const_iterator it = foldersForDeletionOnServer.constBegin();
02286       it != foldersForDeletionOnServer.constEnd(); ++it ) {
02287     KURL url( mAccount->getUrl() );
02288     url.setPath( *it );
02289     kmkernel->iCalIface().folderDeletedOnServer( url );
02290   }
02291   serverSyncInternal();
02292 }
02293 
02294 #include "kmfoldercachedimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys