kmail Library API Documentation

kmfoldercachedimap.cpp

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