kmail Library API Documentation

kmfoldercachedimap.cpp

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