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