kmail

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 "kmacctcachedimap.h"
00045 #include "accountmanager.h"
00046 using KMail::AccountManager;
00047 #include "kmailicalifaceimpl.h"
00048 #include "kmfolder.h"
00049 #include "kmglobal.h"
00050 #include "acljobs.h"
00051 #include "broadcaststatus.h"
00052 using KPIM::BroadcastStatus;
00053 #include "progressmanager.h"
00054 
00055 using KMail::CachedImapJob;
00056 #include "imapaccountbase.h"
00057 using KMail::ImapAccountBase;
00058 #include "listjob.h"
00059 using KMail::ListJob;
00060 
00061 #include "kmfolderseldlg.h"
00062 #include "kmcommands.h"
00063 #include "kmmainwidget.h"
00064 
00065 #include <kapplication.h>
00066 #include <kmessagebox.h>
00067 #include <klocale.h>
00068 #include <kdebug.h>
00069 #include <kconfig.h>
00070 #include <kio/global.h>
00071 #include <kio/scheduler.h>
00072 #include <qbuffer.h>
00073 #include <qbuttongroup.h>
00074 #include <qcombobox.h>
00075 #include <qfile.h>
00076 #include <qhbox.h>
00077 #include <qlabel.h>
00078 #include <qlayout.h>
00079 #include <qradiobutton.h>
00080 #include <qvaluelist.h>
00081 #include "annotationjobs.h"
00082 #include "quotajobs.h"
00083 using namespace KMail;
00084 #include <globalsettings.h>
00085 
00086 #define UIDCACHE_VERSION 1
00087 #define MAIL_LOSS_DEBUGGING 0
00088 
00089 static QString incidencesForToString( KMFolderCachedImap::IncidencesFor r ) {
00090   switch (r) {
00091   case KMFolderCachedImap::IncForNobody: return "nobody";
00092   case KMFolderCachedImap::IncForAdmins: return "admins";
00093   case KMFolderCachedImap::IncForReaders: return "readers";
00094   }
00095   return QString::null; // can't happen
00096 }
00097 
00098 static KMFolderCachedImap::IncidencesFor incidencesForFromString( const QString& str ) {
00099   if ( str == "nobody" ) return KMFolderCachedImap::IncForNobody;
00100   if ( str == "admins" ) return KMFolderCachedImap::IncForAdmins;
00101   if ( str == "readers" ) return KMFolderCachedImap::IncForReaders;
00102   return KMFolderCachedImap::IncForAdmins; // by default
00103 }
00104 
00105 DImapTroubleShootDialog::DImapTroubleShootDialog( QWidget* parent,
00106                                                   const char* name )
00107   : KDialogBase( Plain, i18n( "Troubleshooting IMAP Cache" ),
00108                  Ok | Cancel, Cancel, parent, name, true ),
00109     rc( None )
00110 {
00111   QFrame* page = plainPage();
00112   QVBoxLayout *topLayout = new QVBoxLayout( page, 0 );
00113   // spell "lose" correctly. but don't cause a fuzzy.
00114   QString txt = i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>"
00115                       "<p>If you have problems with synchronizing an IMAP "
00116                       "folder, you should first try rebuilding the index "
00117                       "file. This will take some time to rebuild, but will "
00118                       "not cause any problems.</p><p>If that is not enough, "
00119                       "you can try refreshing the IMAP cache. If you do this, "
00120                       "you will loose all your local changes for this folder "
00121                       "and all its subfolders.</p>",
00122                       "<p><b>Troubleshooting the IMAP cache.</b></p>"
00123                       "<p>If you have problems with synchronizing an IMAP "
00124                       "folder, you should first try rebuilding the index "
00125                       "file. This will take some time to rebuild, but will "
00126                       "not cause any problems.</p><p>If that is not enough, "
00127                       "you can try refreshing the IMAP cache. If you do this, "
00128                       "you will lose all your local changes for this folder "
00129                       "and all its subfolders.</p>" );
00130   topLayout->addWidget( new QLabel( txt, page ) );
00131 
00132   QButtonGroup *group = new QButtonGroup( 0 );
00133 
00134   mIndexButton = new QRadioButton( page );
00135   mIndexButton->setText( i18n( "Rebuild &Index" ) );
00136   group->insert( mIndexButton );
00137   topLayout->addWidget( mIndexButton );
00138 
00139   QHBox *hbox = new QHBox( page );
00140   QLabel *scopeLabel = new QLabel( i18n( "Scope:" ), hbox );
00141   scopeLabel->setEnabled( false );
00142   mIndexScope = new QComboBox( hbox );
00143   mIndexScope->insertItem( i18n( "Only current folder" ) );
00144   mIndexScope->insertItem( i18n( "Current folder and all subfolders" ) );
00145   mIndexScope->insertItem( i18n( "All folders of this account" ) );
00146   mIndexScope->setEnabled( false );
00147   topLayout->addWidget( hbox );
00148 
00149   mCacheButton = new QRadioButton( page );
00150   mCacheButton->setText( i18n( "Refresh &Cache" ) );
00151   group->insert( mCacheButton );
00152   topLayout->addWidget( mCacheButton );
00153 
00154   enableButtonSeparator( true );
00155 
00156   connect ( mIndexButton, SIGNAL(toggled(bool)), mIndexScope, SLOT(setEnabled(bool)) );
00157   connect ( mIndexButton, SIGNAL(toggled(bool)), scopeLabel, SLOT(setEnabled(bool)) );
00158 
00159   connect( this, SIGNAL( okClicked () ), this, SLOT( slotDone() ) );
00160 }
00161 
00162 int DImapTroubleShootDialog::run()
00163 {
00164   DImapTroubleShootDialog d;
00165   d.exec();
00166   return d.rc;
00167 }
00168 
00169 void DImapTroubleShootDialog::slotDone()
00170 {
00171   rc = None;
00172   if ( mIndexButton->isOn() )
00173     rc = mIndexScope->currentItem();
00174   else if ( mCacheButton->isOn() )
00175     rc = RefreshCache;
00176   done( Ok );
00177 }
00178 
00179 KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName )
00180   : KMFolderMaildir( folder, aName ),
00181     mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ),
00182     mSubfolderState( imapNoInformation ),
00183     mIncidencesFor( IncForAdmins ),
00184     mSharedSeenFlags( false ),
00185     mIsSelected( false ),
00186     mCheckFlags( true ), mReadOnly( false ), mAccount( NULL ), uidMapDirty( true ),
00187     uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ),
00188     mFoundAnIMAPDigest( false ),
00189     mUserRights( 0 ), mOldUserRights( 0 ), mSilentUpload( false ),
00190     /*mHoldSyncs( false ),*/
00191     mFolderRemoved( false ),
00192     mRecurse( true ),
00193     mStatusChangedLocally( false ), mAnnotationFolderTypeChanged( false ),
00194     mIncidencesForChanged( false ),
00195     mSharedSeenFlagsChanged( false ),
00196     mPersonalNamespacesCheckDone( true ),
00197     mQuotaInfo(), mAlarmsBlocked( false ),
00198     mRescueCommandCount( 0 ),
00199     mPermanentFlags( 31 ) // assume standard flags by default (see imap4/imapinfo.h for bit fields values)
00200 {
00201   setUidValidity("");
00202   // if we fail to read a uid file but there is one, nuke it
00203   if ( readUidCache() == -1 ) {
00204     if ( QFile::exists( uidCacheLocation() ) ) {
00205         KMessageBox::error( 0,
00206         i18n( "The UID cache file for folder %1 could not be read. There "
00207               "could be a problem with file system permission, or it is corrupted."
00208               ).arg( folder->prettyURL() ) );
00209         // try to unlink it, in case it was corruped. If it couldn't be read
00210         // because of permissions, this will fail, which is fine
00211         unlink( QFile::encodeName( uidCacheLocation() ) );
00212     }
00213   }
00214 
00215   mProgress = 0;
00216 }
00217 
00218 KMFolderCachedImap::~KMFolderCachedImap()
00219 {
00220   if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
00221   writeConfig();
00222 }
00223 
00224 void KMFolderCachedImap::reallyDoClose( const char* owner )
00225 {
00226   if( !mFolderRemoved ) {
00227     writeUidCache();
00228   }
00229   KMFolderMaildir::reallyDoClose( owner );
00230 }
00231 
00232 void KMFolderCachedImap::initializeFrom( KMFolderCachedImap* parent )
00233 {
00234   setAccount( parent->account() );
00235   // Now that we have an account, tell it that this folder was created:
00236   // if this folder was just removed, then we don't really want to remove it from the server.
00237   mAccount->removeDeletedFolder( imapPath() );
00238   setUserRights( parent->userRights() );
00239 }
00240 
00241 void KMFolderCachedImap::readConfig()
00242 {
00243   KConfig* config = KMKernel::config();
00244   KConfigGroupSaver saver( config, "Folder-" + folder()->idString() );
00245   if( mImapPath.isEmpty() ) mImapPath = config->readEntry( "ImapPath" );
00246   if( QString( name() ).upper() == "INBOX" && mImapPath == "/INBOX/" )
00247   {
00248     folder()->setLabel( i18n( "inbox" ) );
00249     // for the icon
00250     folder()->setSystemFolder( true );
00251   }
00252   mNoContent = config->readBoolEntry( "NoContent", false );
00253   mReadOnly = config->readBoolEntry( "ReadOnly", false );
00254   if ( !config->readEntry( "FolderAttributes" ).isEmpty() )
00255     mFolderAttributes = config->readEntry( "FolderAttributes" );
00256 
00257   if ( mAnnotationFolderType != "FROMSERVER" ) {
00258     mAnnotationFolderType = config->readEntry( "Annotation-FolderType" );
00259     // if there is an annotation, it has to be XML
00260     if ( !mAnnotationFolderType.isEmpty() && !mAnnotationFolderType.startsWith( "mail" ) )
00261       kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
00262 //    kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
00263 //                  << " readConfig: mAnnotationFolderType=" << mAnnotationFolderType << endl;
00264   }
00265   mIncidencesFor = incidencesForFromString( config->readEntry( "IncidencesFor" ) );
00266   mAlarmsBlocked = config->readBoolEntry( "AlarmsBlocked", false );
00267 //  kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
00268 //                << " readConfig: mIncidencesFor=" << mIncidencesFor << endl;
00269   mSharedSeenFlags = config->readBoolEntry( "SharedSeenFlags", false );
00270 
00271   mUserRights = config->readNumEntry( "UserRights", 0 ); // default is we don't know
00272   mOldUserRights = mUserRights;
00273 
00274   int storageQuotaUsage = config->readNumEntry( "StorageQuotaUsage", -1 );
00275   int storageQuotaLimit = config->readNumEntry( "StorageQuotaLimit", -1 );
00276   QString storageQuotaRoot = config->readEntry( "StorageQuotaRoot", QString::null );
00277   if ( !storageQuotaRoot.isNull() ) { // isEmpty() means we know there is no quota set
00278       mQuotaInfo.setName( "STORAGE" );
00279       mQuotaInfo.setRoot( storageQuotaRoot );
00280 
00281       if ( storageQuotaUsage > -1 )
00282         mQuotaInfo.setCurrent( storageQuotaUsage );
00283       if ( storageQuotaLimit > -1 )
00284         mQuotaInfo.setMax( storageQuotaLimit );
00285   }
00286 
00287   KMFolderMaildir::readConfig();
00288 
00289   mStatusChangedLocally =
00290     config->readBoolEntry( "StatusChangedLocally", false );
00291 
00292   mAnnotationFolderTypeChanged = config->readBoolEntry( "AnnotationFolderTypeChanged", false );
00293   mIncidencesForChanged = config->readBoolEntry( "IncidencesForChanged", false );
00294   mSharedSeenFlagsChanged = config->readBoolEntry( "SharedSeenFlagsChanged", false );
00295   if ( mImapPath.isEmpty() ) {
00296     mImapPathCreation = config->readEntry("ImapPathCreation");
00297   }
00298 
00299   QStringList uids = config->readListEntry( "UIDSDeletedSinceLastSync" );
00300 #if MAIL_LOSS_DEBUGGING
00301   kdDebug( 5006 ) << "READING IN UIDSDeletedSinceLastSync: " << folder()->prettyURL() << endl << uids << endl;
00302 #endif
00303   for ( QStringList::iterator it = uids.begin(); it != uids.end(); it++ ) {
00304       mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0);
00305   }
00306 }
00307 
00308 void KMFolderCachedImap::writeConfig()
00309 {
00310   // don't re-write the config of a removed folder, this has just been deleted in
00311   // the folder manager
00312   if ( mFolderRemoved )
00313     return;
00314 
00315   KConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
00316   configGroup.writeEntry( "ImapPath", mImapPath );
00317   configGroup.writeEntry( "NoContent", mNoContent );
00318   configGroup.writeEntry( "ReadOnly", mReadOnly );
00319   configGroup.writeEntry( "FolderAttributes", mFolderAttributes );
00320   configGroup.writeEntry( "StatusChangedLocally", mStatusChangedLocally );
00321   if ( !mImapPathCreation.isEmpty() ) {
00322     if ( mImapPath.isEmpty() ) {
00323       configGroup.writeEntry( "ImapPathCreation", mImapPathCreation );
00324     } else {
00325       configGroup.deleteEntry( "ImapPathCreation" );
00326     }
00327   }
00328   if ( !mDeletedUIDsSinceLastSync.isEmpty() ) {
00329       QValueList<ulong> uids = mDeletedUIDsSinceLastSync.keys();
00330       QStringList uidstrings;
00331       for( QValueList<ulong>::iterator it = uids.begin(); it != uids.end(); it++ ) {
00332           uidstrings.append(  QString::number( (*it) ) );
00333       }
00334       configGroup.writeEntry( "UIDSDeletedSinceLastSync", uidstrings );
00335 #if MAIL_LOSS_DEBUGGING
00336       kdDebug( 5006 ) << "WRITING OUT UIDSDeletedSinceLastSync in: " << folder( )->prettyURL( ) << endl << uidstrings << endl;
00337 #endif
00338   } else {
00339     configGroup.deleteEntry( "UIDSDeletedSinceLastSync" );
00340   }
00341   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
00342   KMFolderMaildir::writeConfig();
00343 }
00344 
00345 void KMFolderCachedImap::writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig()
00346 {
00347   KConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
00348   if ( !folder()->noContent() )
00349   {
00350     configGroup.writeEntry( "AnnotationFolderTypeChanged", mAnnotationFolderTypeChanged );
00351     configGroup.writeEntry( "Annotation-FolderType", mAnnotationFolderType );
00352     configGroup.writeEntry( "IncidencesForChanged", mIncidencesForChanged );
00353     configGroup.writeEntry( "IncidencesFor", incidencesForToString( mIncidencesFor ) );
00354     configGroup.writeEntry( "AlarmsBlocked", mAlarmsBlocked );
00355     configGroup.writeEntry( "SharedSeenFlags", mSharedSeenFlags );
00356     configGroup.writeEntry( "SharedSeenFlagsChanged", mSharedSeenFlagsChanged );
00357     configGroup.writeEntry( "UserRights", mUserRights );
00358 
00359     configGroup.deleteEntry( "StorageQuotaUsage");
00360     configGroup.deleteEntry( "StorageQuotaRoot");
00361     configGroup.deleteEntry( "StorageQuotaLimit");
00362 
00363     if ( mQuotaInfo.isValid() ) {
00364       if ( mQuotaInfo.current().isValid() ) {
00365         configGroup.writeEntry( "StorageQuotaUsage", mQuotaInfo.current().toInt() );
00366       }
00367       if ( mQuotaInfo.max().isValid() ) {
00368         configGroup.writeEntry( "StorageQuotaLimit", mQuotaInfo.max().toInt() );
00369       }
00370       configGroup.writeEntry( "StorageQuotaRoot", mQuotaInfo.root() );
00371     }
00372   }
00373 }
00374 
00375 int KMFolderCachedImap::create()
00376 {
00377   int rc = KMFolderMaildir::create();
00378   // FIXME why the below? - till
00379   readConfig();
00380   mUnreadMsgs = -1;
00381   return rc;
00382 }
00383 
00384 void KMFolderCachedImap::remove()
00385 {
00386   mFolderRemoved = true;
00387 
00388   QString part1 = folder()->path() + "/." + dotEscape(name());
00389   QString uidCacheFile = part1 + ".uidcache";
00390   // This is the account folder of an account that was just removed
00391   // When this happens, be sure to delete all traces of the cache
00392   if( QFile::exists(uidCacheFile) )
00393     unlink( QFile::encodeName( uidCacheFile ) );
00394 
00395   FolderStorage::remove();
00396 }
00397 
00398 QString KMFolderCachedImap::uidCacheLocation() const
00399 {
00400   QString sLocation(folder()->path());
00401   if (!sLocation.isEmpty()) sLocation += '/';
00402   return sLocation + '.' + dotEscape(fileName()) + ".uidcache";
00403 }
00404 
00405 int KMFolderCachedImap::readUidCache()
00406 {
00407   QFile uidcache( uidCacheLocation() );
00408   if( uidcache.open( IO_ReadOnly ) ) {
00409     char buf[1024];
00410     int len = uidcache.readLine( buf, sizeof(buf) );
00411     if( len > 0 ) {
00412       int cacheVersion;
00413       sscanf( buf, "# KMail-UidCache V%d\n",  &cacheVersion );
00414       if( cacheVersion == UIDCACHE_VERSION ) {
00415         len = uidcache.readLine( buf, sizeof(buf) );
00416         if( len > 0 ) {
00417           setUidValidity( QString::fromLocal8Bit(buf).stripWhiteSpace() );
00418           len = uidcache.readLine( buf, sizeof(buf) );
00419           if( len > 0 ) {
00420 #if MAIL_LOSS_DEBUGGING
00421             kdDebug(5006) << "Reading in last uid from cache: " << QString::fromLocal8Bit(buf).stripWhiteSpace() << " in " << folder()->prettyURL() << endl;
00422 #endif
00423             // load the last known highest uid from the on disk cache
00424             setLastUid( QString::fromLocal8Bit(buf).stripWhiteSpace().toULong() );
00425             return 0;
00426           }
00427         }
00428       }
00429     }
00430   }
00431   return -1;
00432 }
00433 
00434 int KMFolderCachedImap::writeUidCache()
00435 {
00436   if( uidValidity().isEmpty() || uidValidity() == "INVALID" ) {
00437     // No info from the server yet, remove the file.
00438     if( QFile::exists( uidCacheLocation() ) )
00439       return unlink( QFile::encodeName( uidCacheLocation() ) );
00440     return 0;
00441   }
00442 #if MAIL_LOSS_DEBUGGING
00443   kdDebug(5006) << "Writing out UID cache lastuid: " << lastUid()  << " in: " << folder()->prettyURL() << endl;
00444 #endif
00445   QFile uidcache( uidCacheLocation() );
00446   if( uidcache.open( IO_WriteOnly ) ) {
00447     QTextStream str( &uidcache );
00448     str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl;
00449     str << uidValidity() << endl;
00450     str << lastUid() << endl;
00451     uidcache.flush();
00452     if ( uidcache.status() == IO_Ok ) {
00453       fsync( uidcache.handle() ); /* this is probably overkill */
00454       uidcache.close();
00455       if ( uidcache.status() == IO_Ok )
00456         return 0;
00457     }
00458   }
00459   KMessageBox::error( 0,
00460         i18n( "The UID cache file for folder %1 could not be written. There "
00461               "could be a problem with file system permission." ).arg( folder()->prettyURL() ) );
00462 
00463   return -1;
00464 }
00465 
00466 void KMFolderCachedImap::reloadUidMap()
00467 {
00468   //kdDebug(5006) << "Reloading Uid Map " << endl;
00469   uidMap.clear();
00470   open("reloadUdi");
00471   for( int i = 0; i < count(); ++i ) {
00472     KMMsgBase *msg = getMsgBase( i );
00473     if( !msg ) continue;
00474     ulong uid = msg->UID();
00475     //kdDebug(5006) << "Inserting: " << i << " with uid: " << uid << endl;
00476     uidMap.insert( uid, i );
00477   }
00478   close("reloadUdi");
00479   uidMapDirty = false;
00480 }
00481 
00482 /* Reimplemented from KMFolderMaildir */
00483 KMMessage* KMFolderCachedImap::take(int idx)
00484 {
00485   uidMapDirty = true;
00486   rememberDeletion( idx );
00487   return KMFolderMaildir::take(idx);
00488 }
00489 
00490 // Add a message without clearing it's X-UID field.
00491 int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail,
00492                                         int* index_return )
00493 {
00494   // Possible optimization: Only dirty if not filtered below
00495   ulong uid = msg->UID();
00496   if( uid != 0 ) {
00497     uidMapDirty = true;
00498   }
00499 
00500   KMFolderOpener openThis(folder(), "KMFolderCachedImap::addMsgInternal");
00501   int rc = openThis.openResult();
00502   if ( rc ) {
00503     kdDebug(5006) << k_funcinfo << "open: " << rc << " of folder: " << label() << endl;
00504     return rc;
00505   }
00506 
00507   // Add the message
00508   rc = KMFolderMaildir::addMsg(msg, index_return);
00509 
00510   if( newMail && ( imapPath() == "/INBOX/" ||
00511       ( (userRights() <= 0 || userRights() & ACLJobs::Administer)
00512       && (contentsType() == ContentsTypeMail || GlobalSettings::self()->filterGroupwareFolders()) ) ) )
00513   {
00514     // This is a new message. Filter it - maybe
00515     bool filter = false;
00516     if ( GlobalSettings::filterSourceFolders().isEmpty() ) {
00517       if ( imapPath() == "/INBOX/" )
00518         filter = true;
00519     } else {
00520       if ( GlobalSettings::filterSourceFolders().contains( folder()->id() ) )
00521         filter = true;
00522     }
00523     if ( filter )
00524       mAccount->processNewMsg( msg );
00525   }
00526 
00527   return rc;
00528 }
00529 
00530 /* Reimplemented from KMFolderMaildir */
00531 int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return)
00532 {
00533   if ( !canAddMsgNow( msg, index_return ) ) return 0;
00534   // Add it to storage
00535   int rc = KMFolderMaildir::addMsgInternal(msg, index_return, true /*stripUID*/);
00536   return rc;
00537 }
00538 
00539 void KMFolderCachedImap::rememberDeletion( int idx )
00540 {
00541   KMMsgBase *msg = getMsgBase( idx );
00542   assert(msg);
00543   long uid = msg->UID();
00544   assert(uid>=0);
00545   mDeletedUIDsSinceLastSync.insert(uid, 0);
00546   kdDebug(5006) << "Explicit delete of UID " << uid << " at index: " << idx << " in " << folder()->prettyURL() << endl;
00547 }
00548 
00549 /* Reimplemented from KMFolderMaildir */
00550 void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet)
00551 {
00552   uidMapDirty = true;
00553   rememberDeletion( idx );
00554   // Remove it from disk
00555   KMFolderMaildir::removeMsg(idx,imapQuiet);
00556 }
00557 
00558 bool KMFolderCachedImap::canRemoveFolder() const {
00559   // If this has subfolders it can't be removed
00560   if( folder() && folder()->child() && folder()->child()->count() > 0 )
00561     return false;
00562 
00563 #if 0
00564   // No special condition here, so let base class decide
00565   return KMFolderMaildir::canRemoveFolder();
00566 #endif
00567   return true;
00568 }
00569 
00570 /* Reimplemented from KMFolderDir */
00571 int KMFolderCachedImap::rename( const QString& aName,
00572                                 KMFolderDir* /*aParent*/ )
00573 {
00574   QString oldName = mAccount->renamedFolder( imapPath() );
00575   if ( oldName.isEmpty() ) oldName = name();
00576   if ( aName == oldName )
00577     // Stupid user trying to rename it to it's old name :)
00578     return 0;
00579 
00580   if( account() == 0 || imapPath().isEmpty() ) { // I don't think any of this can happen anymore
00581     QString err = i18n("You must synchronize with the server before renaming IMAP folders.");
00582     KMessageBox::error( 0, err );
00583     return -1;
00584   }
00585 
00586   // Make the change appear to the user with setLabel, but we'll do the change
00587   // on the server during the next sync. The name() is the name at the time of
00588   // the last sync. Only rename if the new one is different. If it's the same,
00589   // don't rename, but also make sure the rename is reset, in the case of
00590   // A -> B -> A renames.
00591   if ( name() != aName )
00592     mAccount->addRenamedFolder( imapPath(), folder()->label(), aName );
00593   else
00594     mAccount->removeRenamedFolder( imapPath() );
00595 
00596   folder()->setLabel( aName );
00597   emit nameChanged(); // for kmailicalifaceimpl
00598 
00599   return 0;
00600 }
00601 
00602 KMFolder* KMFolderCachedImap::trashFolder() const
00603 {
00604   QString trashStr = account()->trash();
00605   return kmkernel->dimapFolderMgr()->findIdString( trashStr );
00606 }
00607 
00608 void KMFolderCachedImap::setLastUid( ulong uid )
00609 {
00610 #if MAIL_LOSS_DEBUGGING
00611   kdDebug(5006) << "Setting mLastUid to: " << uid  <<  " in " << folder()->prettyURL() << endl;
00612 #endif
00613   mLastUid = uid;
00614   if( uidWriteTimer == -1 )
00615     // Write in one minute
00616     uidWriteTimer = startTimer( 60000 );
00617 }
00618 
00619 void KMFolderCachedImap::timerEvent( QTimerEvent* )
00620 {
00621   killTimer( uidWriteTimer );
00622   uidWriteTimer = -1;
00623   if ( writeUidCache() == -1 )
00624     unlink( QFile::encodeName( uidCacheLocation() ) );
00625 }
00626 
00627 ulong KMFolderCachedImap::lastUid()
00628 {
00629   return mLastUid;
00630 }
00631 
00632 KMMsgBase* KMFolderCachedImap::findByUID( ulong uid )
00633 {
00634   bool mapReloaded = false;
00635   if( uidMapDirty ) {
00636     reloadUidMap();
00637     mapReloaded = true;
00638   }
00639 
00640   QMap<ulong,int>::Iterator it = uidMap.find( uid );
00641   if( it != uidMap.end() ) {
00642     KMMsgBase *msg = getMsgBase( *it );
00643 #if MAIL_LOSS_DEBUGGING
00644     kdDebug(5006) << "Folder: " << folder()->prettyURL() << endl;
00645     kdDebug(5006) << "UID " << uid << " is supposed to be in the map" << endl;
00646     kdDebug(5006) << "UID's index is to be " << *it << endl;
00647     kdDebug(5006) << "There is a message there? " << (msg != 0) << endl;
00648     if ( msg ) {
00649       kdDebug(5006) << "Its UID is: " << msg->UID() << endl;
00650     }
00651 #endif
00652 
00653     if( msg && msg->UID() == uid )
00654       return msg;
00655     kdDebug(5006) << "########## Didn't find uid: " << uid << "in cache athough it's supposed to be there!" << endl;
00656   } else {
00657 #if MAIL_LOSS_DEBUGGING
00658     kdDebug(5006) << "Didn't find uid: " << uid << "in cache!" << endl;
00659 #endif
00660   }
00661   // Not found by now
00662  // if( mapReloaded )
00663     // Not here then
00664     return 0;
00665   // There could be a problem in the maps. Rebuild them and try again
00666   reloadUidMap();
00667   it = uidMap.find( uid );
00668   if( it != uidMap.end() )
00669     // Since the uid map is just rebuilt, no need for the sanity check
00670     return getMsgBase( *it );
00671 #if MAIL_LOSS_DEBUGGING
00672   else
00673     kdDebug(5006) << "Reloaded, but stil didn't find uid: " << uid << endl;
00674 #endif
00675   // Then it's not here
00676   return 0;
00677 }
00678 
00679 // This finds and sets the proper account for this folder if it has
00680 // not been done
00681 KMAcctCachedImap *KMFolderCachedImap::account() const
00682 {
00683   if( (KMAcctCachedImap *)mAccount == 0 && kmkernel && kmkernel->acctMgr() ) {
00684     // Find the account
00685     mAccount = static_cast<KMAcctCachedImap *>( kmkernel->acctMgr()->findByName( name() ) );
00686   }
00687 
00688   return mAccount;
00689 }
00690 
00691 void KMFolderCachedImap::slotTroubleshoot()
00692 {
00693   const int rc = DImapTroubleShootDialog::run();
00694 
00695   if( rc == DImapTroubleShootDialog::RefreshCache ) {
00696     // Refresh cache
00697     if( !account() ) {
00698       KMessageBox::sorry( 0, i18n("No account setup for this folder.\n"
00699                                   "Please try running a sync before this.") );
00700       return;
00701     }
00702     QString str = i18n("Are you sure you want to refresh the IMAP cache of "
00703                        "the folder %1 and all its subfolders?\nThis will "
00704                        "remove all changes you have done locally to your "
00705                        "folders.").arg( label() );
00706     QString s1 = i18n("Refresh IMAP Cache");
00707     QString s2 = i18n("&Refresh");
00708     if( KMessageBox::warningContinueCancel( 0, str, s1, s2 ) ==
00709         KMessageBox::Continue )
00710       account()->invalidateIMAPFolders( this );
00711   } else {
00712     // Rebuild index file
00713     switch ( rc ) {
00714       case DImapTroubleShootDialog::ReindexAll:
00715       {
00716         KMFolderCachedImap *rootStorage = dynamic_cast<KMFolderCachedImap*>( account()->rootFolder() );
00717         if ( rootStorage )
00718           rootStorage->createIndexFromContentsRecursive();
00719         break;
00720       }
00721       case DImapTroubleShootDialog::ReindexCurrent:
00722         createIndexFromContents();
00723         break;
00724       case DImapTroubleShootDialog::ReindexRecursive:
00725         createIndexFromContentsRecursive();
00726         break;
00727       default:
00728         return;
00729     }
00730     KMessageBox::information( 0, i18n( "The index of this folder has been "
00731                                        "recreated." ) );
00732     writeIndex();
00733     kmkernel->getKMMainWidget()->folderSelected();
00734   }
00735 }
00736 
00737 void KMFolderCachedImap::serverSync( bool recurse )
00738 {
00739   if( mSyncState != SYNC_STATE_INITIAL ) {
00740     if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset it to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ), QString::null, i18n("Reset && Sync"), KStdGuiItem::cancel() ) == KMessageBox::Yes ) {
00741       mSyncState = SYNC_STATE_INITIAL;
00742     } else return;
00743   }
00744 
00745   mRecurse = recurse;
00746   assert( account() );
00747 
00748   ProgressItem *progressItem = mAccount->mailCheckProgressItem();
00749   if ( progressItem ) {
00750     progressItem->reset();
00751     progressItem->setTotalItems( 100 );
00752   }
00753   mProgress = 0;
00754 
00755 #if 0
00756   if( mHoldSyncs ) {
00757     // All done for this folder.
00758     account()->mailCheckProgressItem()->setProgress( 100 );
00759     mProgress = 100; // all done
00760     newState( mProgress, i18n("Synchronization skipped"));
00761     mSyncState = SYNC_STATE_INITIAL;
00762     emit folderComplete( this, true );
00763     return;
00764   }
00765 #endif
00766   mTentativeHighestUid = 0; // reset, last sync could have been canceled
00767 
00768   serverSyncInternal();
00769 }
00770 
00771 QString KMFolderCachedImap::state2String( int state ) const
00772 {
00773   switch( state ) {
00774   case SYNC_STATE_INITIAL:           return "SYNC_STATE_INITIAL";
00775   case SYNC_STATE_GET_USERRIGHTS:    return "SYNC_STATE_GET_USERRIGHTS";
00776   case SYNC_STATE_PUT_MESSAGES:      return "SYNC_STATE_PUT_MESSAGES";
00777   case SYNC_STATE_UPLOAD_FLAGS:      return "SYNC_STATE_UPLOAD_FLAGS";
00778   case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
00779   case SYNC_STATE_LIST_SUBFOLDERS:   return "SYNC_STATE_LIST_SUBFOLDERS";
00780   case SYNC_STATE_LIST_NAMESPACES:   return "SYNC_STATE_LIST_NAMESPACES";
00781   case SYNC_STATE_LIST_SUBFOLDERS2:  return "SYNC_STATE_LIST_SUBFOLDERS2";
00782   case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
00783   case SYNC_STATE_LIST_MESSAGES:     return "SYNC_STATE_LIST_MESSAGES";
00784   case SYNC_STATE_DELETE_MESSAGES:   return "SYNC_STATE_DELETE_MESSAGES";
00785   case SYNC_STATE_GET_MESSAGES:      return "SYNC_STATE_GET_MESSAGES";
00786   case SYNC_STATE_EXPUNGE_MESSAGES:  return "SYNC_STATE_EXPUNGE_MESSAGES";
00787   case SYNC_STATE_HANDLE_INBOX:      return "SYNC_STATE_HANDLE_INBOX";
00788   case SYNC_STATE_TEST_ANNOTATIONS:  return "SYNC_STATE_TEST_ANNOTATIONS";
00789   case SYNC_STATE_GET_ANNOTATIONS:   return "SYNC_STATE_GET_ANNOTATIONS";
00790   case SYNC_STATE_SET_ANNOTATIONS:   return "SYNC_STATE_SET_ANNOTATIONS";
00791   case SYNC_STATE_GET_ACLS:          return "SYNC_STATE_GET_ACLS";
00792   case SYNC_STATE_SET_ACLS:          return "SYNC_STATE_SET_ACLS";
00793   case SYNC_STATE_GET_QUOTA:         return "SYNC_STATE_GET_QUOTA";
00794   case SYNC_STATE_FIND_SUBFOLDERS:   return "SYNC_STATE_FIND_SUBFOLDERS";
00795   case SYNC_STATE_SYNC_SUBFOLDERS:   return "SYNC_STATE_SYNC_SUBFOLDERS";
00796   case SYNC_STATE_RENAME_FOLDER:     return "SYNC_STATE_RENAME_FOLDER";
00797   case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
00798   default:                           return "Unknown state";
00799   }
00800 }
00801 
00802 /*
00803   Progress calculation: each step is assigned a span. Initially the total is 100.
00804   But if we skip a step, don't increase the progress.
00805   This leaves more room for the step a with variable size (get_messages)
00806    connecting 5
00807    getuserrights 5
00808    rename 5
00809    check_uidvalidity 5
00810    create_subfolders 5
00811    put_messages 10 (but it can take a very long time, with many messages....)
00812    upload_flags 5
00813    list_subfolders 5
00814    list_subfolders2 0 (all local)
00815    delete_subfolders 5
00816    list_messages 10
00817    delete_messages 10
00818    expunge_messages 5
00819    get_messages variable (remaining-5) i.e. minimum 15.
00820    check_annotations 0 (rare)
00821    set_annotations 0 (rare)
00822    get_annotations 2
00823    set_acls 0 (rare)
00824    get_acls 3
00825 
00826   noContent folders have only a few of the above steps
00827   (permissions, and all subfolder stuff), so its steps should be given more span
00828 
00829  */
00830 
00831 // While the server synchronization is running, mSyncState will hold
00832 // the state that should be executed next
00833 void KMFolderCachedImap::serverSyncInternal()
00834 {
00835   // This is used to stop processing when we're about to exit
00836   // and the current job wasn't cancellable.
00837   // For user-requested abort, we'll use signalAbortRequested instead.
00838   if( kmkernel->mailCheckAborted() ) {
00839     resetSyncState();
00840     emit folderComplete( this, false );
00841     return;
00842   }
00843 
00844   //kdDebug(5006) << label() << ": " << state2String( mSyncState ) << endl;
00845   switch( mSyncState ) {
00846   case SYNC_STATE_INITIAL:
00847   {
00848     mProgress = 0;
00849     foldersForDeletionOnServer.clear();
00850     newState( mProgress, i18n("Synchronizing"));
00851 
00852     open("cachedimap");
00853     if ( !noContent() )
00854         mAccount->addLastUnreadMsgCount( this, countUnread() );
00855 
00856     // Connect to the server (i.e. prepare the slave)
00857     ImapAccountBase::ConnectionState cs = mAccount->makeConnection();
00858     if ( cs == ImapAccountBase::Error ) {
00859       // Cancelled by user, or slave can't start
00860       // kdDebug(5006) << "makeConnection said Error, aborting." << endl;
00861       // We stop here. We're already in SYNC_STATE_INITIAL for the next time.
00862       newState( mProgress, i18n( "Error connecting to server %1" ).arg( mAccount->host() ) );
00863       close("cachedimap");
00864       emit folderComplete(this, false);
00865       break;
00866     } else if ( cs == ImapAccountBase::Connecting ) {
00867       mAccount->setAnnotationCheckPassed( false );
00868       // kdDebug(5006) << "makeConnection said Connecting, waiting for signal." << endl;
00869       newState( mProgress, i18n("Connecting to %1").arg( mAccount->host() ) );
00870       // We'll wait for the connectionResult signal from the account.
00871       connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
00872                this, SLOT( slotConnectionResult(int, const QString&) ) );
00873       break;
00874     } else {
00875       // Connected
00876       // kdDebug(5006) << "makeConnection said Connected, proceeding." << endl;
00877       mSyncState = SYNC_STATE_GET_USERRIGHTS;
00878       // Fall through to next state
00879     }
00880   }
00881 
00882 
00883   case SYNC_STATE_GET_USERRIGHTS:
00884     //kdDebug(5006) << "===== Syncing " << ( mImapPath.isEmpty() ? label() : mImapPath ) << endl;
00885 
00886     mSyncState = SYNC_STATE_RENAME_FOLDER;
00887 
00888     if( !noContent() && mAccount->hasACLSupport() ) {
00889       // Check the user's own rights. We do this every time in case they changed.
00890       mOldUserRights = mUserRights;
00891       newState( mProgress, i18n("Checking permissions"));
00892       connect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
00893                this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
00894       mAccount->getUserRights( folder(), imapPath() ); // after connecting, due to the INBOX case
00895       break;
00896     }
00897 
00898   case SYNC_STATE_RENAME_FOLDER:
00899   {
00900     mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
00901     // Returns the new name if the folder was renamed, empty otherwise.
00902     bool isResourceFolder = kmkernel->iCalIface().isStandardResourceFolder( folder() );
00903     QString newName = mAccount->renamedFolder( imapPath() );
00904     if ( !newName.isEmpty() && !folder()->isSystemFolder() && !isResourceFolder ) {
00905       newState( mProgress, i18n("Renaming folder") );
00906       CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this );
00907       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
00908       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00909       job->start();
00910       break;
00911     }
00912   }
00913 
00914   case SYNC_STATE_CHECK_UIDVALIDITY:
00915     mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
00916     if( !noContent() ) {
00917       checkUidValidity();
00918       break;
00919     }
00920     // Else carry on
00921 
00922   case SYNC_STATE_CREATE_SUBFOLDERS:
00923     mSyncState = SYNC_STATE_PUT_MESSAGES;
00924     createNewFolders();
00925     break;
00926 
00927   case SYNC_STATE_PUT_MESSAGES:
00928     mSyncState = SYNC_STATE_UPLOAD_FLAGS;
00929     if( !noContent() ) {
00930       uploadNewMessages();
00931       break;
00932     }
00933     // Else carry on
00934   case SYNC_STATE_UPLOAD_FLAGS:
00935     mSyncState = SYNC_STATE_LIST_NAMESPACES;
00936     if( !noContent() ) {
00937        // We haven't downloaded messages yet, so we need to build the map.
00938        if( uidMapDirty )
00939          reloadUidMap();
00940        // Upload flags, unless we know from the ACL that we're not allowed
00941        // to do that or they did not change locally
00942        if ( mUserRights <= 0 || ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) {
00943          if ( mStatusChangedLocally ) {
00944            uploadFlags();
00945            break;
00946          } else {
00947            //kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl;
00948          }
00949        } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
00950          if ( mStatusChangedLocally ) {
00951            uploadSeenFlags();
00952            break;
00953          }
00954        }
00955     }
00956     // Else carry on
00957 
00958   case SYNC_STATE_LIST_NAMESPACES:
00959     if ( this == mAccount->rootFolder() ) {
00960       listNamespaces();
00961       break;
00962     }
00963     mSyncState = SYNC_STATE_LIST_SUBFOLDERS;
00964     // Else carry on
00965 
00966   case SYNC_STATE_LIST_SUBFOLDERS:
00967     newState( mProgress, i18n("Retrieving folderlist"));
00968     mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
00969     if( !listDirectory() ) {
00970       mSyncState = SYNC_STATE_INITIAL;
00971       KMessageBox::error(0, i18n("Error while retrieving the folderlist"));
00972     }
00973     break;
00974 
00975   case SYNC_STATE_LIST_SUBFOLDERS2:
00976     mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
00977     mProgress += 10;
00978     newState( mProgress, i18n("Retrieving subfolders"));
00979     listDirectory2();
00980     break;
00981 
00982   case SYNC_STATE_DELETE_SUBFOLDERS:
00983     mSyncState = SYNC_STATE_LIST_MESSAGES;
00984     if( !foldersForDeletionOnServer.isEmpty() ) {
00985       newState( mProgress, i18n("Deleting folders from server"));
00986       CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer,
00987                                                   CachedImapJob::tDeleteFolders, this );
00988       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
00989       connect( job, SIGNAL( finished() ), this, SLOT( slotFolderDeletionOnServerFinished() ) );
00990       job->start();
00991       break;
00992     }
00993     // Not needed, the next step emits newState very quick
00994     //newState( mProgress, i18n("No folders to delete from server"));
00995       // Carry on
00996 
00997   case SYNC_STATE_LIST_MESSAGES:
00998     mSyncState = SYNC_STATE_DELETE_MESSAGES;
00999     if( !noContent() ) {
01000       newState( mProgress, i18n("Retrieving message list"));
01001       listMessages();
01002       break;
01003     }
01004     // Else carry on
01005 
01006   case SYNC_STATE_DELETE_MESSAGES:
01007     mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
01008     if( !noContent() ) {
01009       if( deleteMessages() ) {
01010         // Fine, we will continue with the next state
01011       } else {
01012         // No messages to delete, skip to GET_MESSAGES
01013         newState( mProgress, i18n("No messages to delete..."));
01014         mSyncState = SYNC_STATE_GET_MESSAGES;
01015         serverSyncInternal();
01016       }
01017       break;
01018     }
01019     // Else carry on
01020 
01021   case SYNC_STATE_EXPUNGE_MESSAGES:
01022     mSyncState = SYNC_STATE_GET_MESSAGES;
01023     if( !noContent() ) {
01024       newState( mProgress, i18n("Expunging deleted messages"));
01025       CachedImapJob *job = new CachedImapJob( QString::null,
01026                                               CachedImapJob::tExpungeFolder, this );
01027       connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
01028       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01029       job->start();
01030       break;
01031     }
01032     // Else carry on
01033 
01034   case SYNC_STATE_GET_MESSAGES:
01035     mSyncState = SYNC_STATE_HANDLE_INBOX;
01036     if( !noContent() ) {
01037       if( !mMsgsForDownload.isEmpty() ) {
01038         newState( mProgress, i18n("Retrieving new messages"));
01039         CachedImapJob *job = new CachedImapJob( mMsgsForDownload,
01040                                                 CachedImapJob::tGetMessage,
01041                                                 this );
01042         connect( job, SIGNAL( progress(unsigned long, unsigned long) ),
01043                  this, SLOT( slotProgress(unsigned long, unsigned long) ) );
01044         connect( job, SIGNAL( finished() ), this, SLOT( slotUpdateLastUid() ) );
01045         connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01046         job->start();
01047         mMsgsForDownload.clear();
01048         break;
01049       } else {
01050         newState( mProgress, i18n("No new messages from server"));
01051         /* There were no messages to download, but it could be that we uploaded some
01052            which we didn't need to download again because we already knew the uid.
01053            Now that we are sure there is nothing to download, and everything that had
01054            to be deleted on the server has been deleted, adjust our local notion of the
01055            highes uid seen thus far. */
01056         slotUpdateLastUid();
01057         if( mLastUid == 0 && uidWriteTimer == -1 ) {
01058           // This is probably a new and empty folder. Write the UID cache
01059           if ( writeUidCache() == -1 ) {
01060             resetSyncState();
01061             emit folderComplete( this, false );
01062             return;
01063           }
01064         }
01065       }
01066     }
01067 
01068     // Else carry on
01069 
01070   case SYNC_STATE_HANDLE_INBOX:
01071     // Wrap up the 'download emails' stage. We always end up at 95 here.
01072     mProgress = 95;
01073     mSyncState = SYNC_STATE_TEST_ANNOTATIONS;
01074 
01075   #define KOLAB_FOLDERTEST "/vendor/kolab/folder-test"
01076   case SYNC_STATE_TEST_ANNOTATIONS:
01077     mSyncState = SYNC_STATE_GET_ANNOTATIONS;
01078     // The first folder with user rights to write annotations
01079     if( !mAccount->annotationCheckPassed() &&
01080          ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) )
01081          && !imapPath().isEmpty() && imapPath() != "/" ) {
01082       kdDebug(5006) << "Setting test attribute on folder: "<< folder()->prettyURL() << endl;
01083       newState( mProgress, i18n("Checking annotation support"));
01084 
01085       KURL url = mAccount->getUrl();
01086       url.setPath( imapPath() );
01087       KMail::AnnotationList annotations; // to be set
01088 
01089       KMail::AnnotationAttribute attr( KOLAB_FOLDERTEST, "value.shared", "true" );
01090       annotations.append( attr );
01091 
01092       kdDebug(5006) << "Setting test attribute to "<< url << endl;
01093       KIO::Job* job = AnnotationJobs::multiSetAnnotation( mAccount->slave(),
01094           url, annotations );
01095       ImapAccountBase::jobData jd( url.url(), folder() );
01096       jd.cancellable = true; // we can always do so later
01097       mAccount->insertJob(job, jd);
01098        connect(job, SIGNAL(result(KIO::Job *)),
01099               SLOT(slotTestAnnotationResult(KIO::Job *)));
01100       break;
01101     }
01102 
01103   case SYNC_STATE_GET_ANNOTATIONS: {
01104 #define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type"
01105 #define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for"
01106 #define KOLAB_SHAREDSEEN "/vendor/cmu/cyrus-imapd/sharedseen"
01107 //#define KOLAB_FOLDERTYPE "/comment"  //for testing, while cyrus-imap doesn't support /vendor/*
01108     mSyncState = SYNC_STATE_SET_ANNOTATIONS;
01109 
01110     bool needToGetInitialAnnotations = false;
01111     if ( !noContent() ) {
01112       // for a folder we didn't create ourselves: get annotation from server
01113       if ( mAnnotationFolderType == "FROMSERVER" ) {
01114         needToGetInitialAnnotations = true;
01115         mAnnotationFolderType = QString::null;
01116       } else {
01117         updateAnnotationFolderType();
01118       }
01119     }
01120 
01121     // First retrieve the annotation, so that we know we have to set it if it's not set.
01122     // On the other hand, if the user changed the contentstype, there's no need to get first.
01123     if ( !noContent() && mAccount->hasAnnotationSupport() &&
01124         ( kmkernel->iCalIface().isEnabled() || needToGetInitialAnnotations ) ) {
01125       QStringList annotations; // list of annotations to be fetched
01126       if ( !mAnnotationFolderTypeChanged || mAnnotationFolderType.isEmpty() )
01127         annotations << KOLAB_FOLDERTYPE;
01128       if ( !mIncidencesForChanged )
01129         annotations << KOLAB_INCIDENCESFOR;
01130       if ( !mSharedSeenFlagsChanged )
01131         annotations << KOLAB_SHAREDSEEN;
01132       if ( !annotations.isEmpty() ) {
01133         newState( mProgress, i18n("Retrieving annotations"));
01134         KURL url = mAccount->getUrl();
01135         url.setPath( imapPath() );
01136         AnnotationJobs::MultiGetAnnotationJob* job =
01137           AnnotationJobs::multiGetAnnotation( mAccount->slave(), url, annotations );
01138         ImapAccountBase::jobData jd( url.url(), folder() );
01139         jd.cancellable = true;
01140         mAccount->insertJob(job, jd);
01141 
01142         connect( job, SIGNAL(annotationResult(const QString&, const QString&, bool)),
01143                  SLOT(slotAnnotationResult(const QString&, const QString&, bool)) );
01144         connect( job, SIGNAL(result(KIO::Job *)),
01145                  SLOT(slotGetAnnotationResult(KIO::Job *)) );
01146         break;
01147       }
01148     }
01149   } // case
01150   case SYNC_STATE_SET_ANNOTATIONS:
01151 
01152     mSyncState = SYNC_STATE_SET_ACLS;
01153     if ( !noContent() && mAccount->hasAnnotationSupport() &&
01154          ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) {
01155       newState( mProgress, i18n("Setting annotations"));
01156       KURL url = mAccount->getUrl();
01157       url.setPath( imapPath() );
01158       KMail::AnnotationList annotations; // to be set
01159       if ( mAnnotationFolderTypeChanged && !mAnnotationFolderType.isEmpty() ) {
01160         KMail::AnnotationAttribute attr( KOLAB_FOLDERTYPE, "value.shared", mAnnotationFolderType );
01161         annotations.append( attr );
01162         kdDebug(5006) << "Setting folder-type annotation for " << label() << " to " << mAnnotationFolderType << endl;
01163       }
01164       if ( mIncidencesForChanged ) {
01165         const QString val = incidencesForToString( mIncidencesFor );
01166         KMail::AnnotationAttribute attr( KOLAB_INCIDENCESFOR, "value.shared", val );
01167         annotations.append( attr );
01168         kdDebug(5006) << "Setting incidences-for annotation for " << label() << " to " << val << endl;
01169       }
01170       if ( mSharedSeenFlagsChanged ) {
01171         const QString val = mSharedSeenFlags ? "true" : "false";
01172         KMail::AnnotationAttribute attr( KOLAB_SHAREDSEEN, "value.shared", val );
01173         annotations.append( attr );
01174         kdDebug(5006) << k_funcinfo << "Setting sharedseen annotation for " << label() << " to " << val << endl;
01175       }
01176       if ( !annotations.isEmpty() ) {
01177         KIO::Job* job =
01178           AnnotationJobs::multiSetAnnotation( mAccount->slave(), url, annotations );
01179         ImapAccountBase::jobData jd( url.url(), folder() );
01180         jd.cancellable = true; // we can always do so later
01181         mAccount->insertJob(job, jd);
01182 
01183         connect(job, SIGNAL(annotationChanged( const QString&, const QString&, const QString& ) ),
01184                 SLOT( slotAnnotationChanged( const QString&, const QString&, const QString& ) ));
01185         connect(job, SIGNAL(result(KIO::Job *)),
01186                 SLOT(slotSetAnnotationResult(KIO::Job *)));
01187         break;
01188       }
01189     }
01190 
01191   case SYNC_STATE_SET_ACLS:
01192     mSyncState = SYNC_STATE_GET_ACLS;
01193 
01194     if( !noContent() && mAccount->hasACLSupport() &&
01195       ( mUserRights <= 0 || ( mUserRights & ACLJobs::Administer ) ) ) {
01196       bool hasChangedACLs = false;
01197       ACLList::ConstIterator it = mACLList.begin();
01198       for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) {
01199         hasChangedACLs = (*it).changed;
01200       }
01201       if ( hasChangedACLs ) {
01202         newState( mProgress, i18n("Setting permissions"));
01203         KURL url = mAccount->getUrl();
01204         url.setPath( imapPath() );
01205         KIO::Job* job = KMail::ACLJobs::multiSetACL( mAccount->slave(), url, mACLList );
01206         ImapAccountBase::jobData jd( url.url(), folder() );
01207         mAccount->insertJob(job, jd);
01208 
01209         connect(job, SIGNAL(result(KIO::Job *)),
01210                 SLOT(slotMultiSetACLResult(KIO::Job *)));
01211         connect(job, SIGNAL(aclChanged( const QString&, int )),
01212                 SLOT(slotACLChanged( const QString&, int )) );
01213         break;
01214       }
01215     }
01216 
01217   case SYNC_STATE_GET_ACLS:
01218     mSyncState = SYNC_STATE_GET_QUOTA;
01219 
01220     if( !noContent() && mAccount->hasACLSupport() ) {
01221       newState( mProgress, i18n( "Retrieving permissions" ) );
01222       mAccount->getACL( folder(), mImapPath );
01223       connect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
01224                this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
01225       break;
01226     }
01227   case SYNC_STATE_GET_QUOTA:
01228     // Continue with the subfolders
01229     mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
01230     if( !noContent() && mAccount->hasQuotaSupport() ) {
01231       newState( mProgress, i18n("Getting quota information"));
01232       KURL url = mAccount->getUrl();
01233       url.setPath( imapPath() );
01234       KIO::Job* job = KMail::QuotaJobs::getStorageQuota( mAccount->slave(), url );
01235       ImapAccountBase::jobData jd( url.url(), folder() );
01236       mAccount->insertJob(job, jd);
01237       connect( job, SIGNAL( storageQuotaResult( const QuotaInfo& ) ),
01238           SLOT( slotStorageQuotaResult( const QuotaInfo& ) ) );
01239       connect( job, SIGNAL(result(KIO::Job *)),
01240           SLOT(slotQuotaResult(KIO::Job *)) );
01241       break;
01242     }
01243   case SYNC_STATE_FIND_SUBFOLDERS:
01244     {
01245       mProgress = 98;
01246       newState( mProgress, i18n("Updating cache file"));
01247 
01248       mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
01249       mSubfoldersForSync.clear();
01250       mCurrentSubfolder = 0;
01251       if( folder() && folder()->child() ) {
01252         KMFolderNode *node = folder()->child()->first();
01253         while( node ) {
01254           if( !node->isDir() ) {
01255             KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01256             // Only sync folders that have been accepted by the server
01257             if ( !storage->imapPath().isEmpty()
01258                  // and that were not just deleted from it
01259                  && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) {
01260               mSubfoldersForSync << storage;
01261             } else {
01262               kdDebug(5006) << "Do not add " << storage->label()
01263                 << " to synclist" << endl;
01264             }
01265           }
01266           node = folder()->child()->next();
01267         }
01268       }
01269 
01270     // All done for this folder.
01271     mProgress = 100; // all done
01272     newState( mProgress, i18n("Synchronization done"));
01273       KURL url = mAccount->getUrl();
01274       url.setPath( imapPath() );
01275       kmkernel->iCalIface().folderSynced( folder(), url );
01276     }
01277 
01278     if ( !mRecurse ) // "check mail for this folder" only
01279       mSubfoldersForSync.clear();
01280 
01281     // Carry on
01282   case SYNC_STATE_SYNC_SUBFOLDERS:
01283     {
01284       if( mCurrentSubfolder ) {
01285         disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
01286                     this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
01287         mCurrentSubfolder = 0;
01288       }
01289 
01290       if( mSubfoldersForSync.isEmpty() ) {
01291         mSyncState = SYNC_STATE_INITIAL;
01292         mAccount->addUnreadMsgCount( this, countUnread() ); // before closing
01293         close("cachedimap");
01294         emit folderComplete( this, true );
01295       } else {
01296         mCurrentSubfolder = mSubfoldersForSync.front();
01297         mSubfoldersForSync.pop_front();
01298         connect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
01299                  this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
01300 
01301         //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
01302         assert( !mCurrentSubfolder->imapPath().isEmpty() );
01303         mCurrentSubfolder->setAccount( account() );
01304         bool recurse = mCurrentSubfolder->noChildren() ? false : true;
01305         mCurrentSubfolder->serverSync( recurse );
01306       }
01307     }
01308     break;
01309 
01310   default:
01311     kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
01312               << mSyncState << endl;
01313   }
01314 }
01315 
01316 /* Connected to the imap account's connectionResult signal.
01317    Emitted when the slave connected or failed to connect.
01318 */
01319 void KMFolderCachedImap::slotConnectionResult( int errorCode, const QString& errorMsg )
01320 {
01321   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01322               this, SLOT( slotConnectionResult(int, const QString&) ) );
01323   if ( !errorCode ) {
01324     // Success
01325     mSyncState = SYNC_STATE_GET_USERRIGHTS;
01326     mProgress += 5;
01327     serverSyncInternal();
01328   } else {
01329     // Error (error message already shown by the account)
01330     newState( mProgress, KIO::buildErrorString( errorCode, errorMsg ));
01331     emit folderComplete(this, false);
01332   }
01333 }
01334 
01335 /* find new messages (messages without a UID) */
01336 QValueList<unsigned long> KMFolderCachedImap::findNewMessages()
01337 {
01338   QValueList<unsigned long> result;
01339   for( int i = 0; i < count(); ++i ) {
01340     KMMsgBase *msg = getMsgBase( i );
01341     if( !msg ) continue; /* what goes on if getMsg() returns 0? */
01342     if ( msg->UID() == 0 )
01343       result.append( msg->getMsgSerNum() );
01344   }
01345   return result;
01346 }
01347 
01348 /* Upload new messages to server */
01349 void KMFolderCachedImap::uploadNewMessages()
01350 {
01351   QValueList<unsigned long> newMsgs = findNewMessages();
01352   if( !newMsgs.isEmpty() ) {
01353     if ( mUserRights <= 0 || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) {
01354       newState( mProgress, i18n("Uploading messages to server"));
01355       CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
01356       connect( job, SIGNAL( progress( unsigned long, unsigned long) ),
01357                this, SLOT( slotPutProgress(unsigned long, unsigned long) ) );
01358       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01359       job->start();
01360       return;
01361     } else {
01362       KMCommand *command = rescueUnsyncedMessages();
01363       connect( command, SIGNAL( completed( KMCommand * ) ),
01364                this, SLOT( serverSyncInternal() ) );
01365     }
01366   } else { // nothing to upload
01367     if ( mUserRights != mOldUserRights && (mOldUserRights & KMail::ACLJobs::Insert)
01368          && !(mUserRights & KMail::ACLJobs::Insert) ) {
01369       // write access revoked
01370       KMessageBox::information( 0, i18n("<p>Your access rights to folder <b>%1</b> have been restricted, "
01371           "it will no longer be possible to add messages to this folder.</p>").arg( folder()->prettyURL() ),
01372           i18n("Acces rights revoked"), "KMailACLRevocationNotification" );
01373     }
01374   }
01375   newState( mProgress, i18n("No messages to upload to server"));
01376   serverSyncInternal();
01377 }
01378 
01379 /* Progress info during uploadNewMessages */
01380 void KMFolderCachedImap::slotPutProgress( unsigned long done, unsigned long total )
01381 {
01382   // (going from mProgress to mProgress+10)
01383   int progressSpan = 10;
01384   newState( mProgress + (progressSpan * done) / total, QString::null );
01385   if ( done == total ) // we're done
01386     mProgress += progressSpan;
01387 }
01388 
01389 /* Upload message flags to server */
01390 void KMFolderCachedImap::uploadFlags()
01391 {
01392   if ( !uidMap.isEmpty() ) {
01393     mStatusFlagsJobs = 0;
01394     newState( mProgress, i18n("Uploading status of messages to server"));
01395 
01396     // FIXME DUPLICATED FROM KMFOLDERIMAP
01397     QMap< QString, QStringList > groups;
01398     //open(); //already done
01399     for( int i = 0; i < count(); ++i ) {
01400       KMMsgBase* msg = getMsgBase( i );
01401       if( !msg || msg->UID() == 0 )
01402         // Either not a valid message or not one that is on the server yet
01403         continue;
01404 
01405       QString flags = KMFolderImap::statusToFlags(msg->status(), mPermanentFlags);
01406       // Collect uids for each typem of flags.
01407       QString uid;
01408       uid.setNum( msg->UID() );
01409       groups[flags].append(uid);
01410     }
01411     QMapIterator< QString, QStringList > dit;
01412     for( dit = groups.begin(); dit != groups.end(); ++dit ) {
01413       QCString flags = dit.key().latin1();
01414       QStringList sets = KMFolderImap::makeSets( (*dit), true );
01415       mStatusFlagsJobs += sets.count(); // ### that's not in kmfolderimap....
01416       // Send off a status setting job for each set.
01417       for( QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01418         QString imappath = imapPath() + ";UID=" + ( *slit );
01419         mAccount->setImapStatus(folder(), imappath, flags);
01420       }
01421     }
01422     // FIXME END DUPLICATED FROM KMFOLDERIMAP
01423 
01424     if ( mStatusFlagsJobs ) {
01425       connect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01426                this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01427       return;
01428     }
01429   }
01430   newState( mProgress, i18n("No messages to upload to server"));
01431   serverSyncInternal();
01432 }
01433 
01434 void KMFolderCachedImap::uploadSeenFlags()
01435 {
01436   if ( !uidMap.isEmpty() ) {
01437     mStatusFlagsJobs = 0;
01438     newState( mProgress, i18n("Uploading status of messages to server"));
01439 
01440     QValueList<ulong> seenUids, unseenUids;
01441     for( int i = 0; i < count(); ++i ) {
01442       KMMsgBase* msg = getMsgBase( i );
01443       if( !msg || msg->UID() == 0 )
01444         // Either not a valid message or not one that is on the server yet
01445         continue;
01446 
01447       if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
01448         seenUids.append( msg->UID() );
01449       else
01450         unseenUids.append( msg->UID() );
01451     }
01452     if ( !seenUids.isEmpty() ) {
01453       QStringList sets = KMFolderImap::makeSets( seenUids, true );
01454       mStatusFlagsJobs += sets.count();
01455       for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
01456         QString imappath = imapPath() + ";UID=" + ( *it );
01457         mAccount->setImapSeenStatus( folder(), imappath, true );
01458       }
01459     }
01460     if ( !unseenUids.isEmpty() ) {
01461       QStringList sets = KMFolderImap::makeSets( unseenUids, true );
01462       mStatusFlagsJobs += sets.count();
01463       for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
01464         QString imappath = imapPath() + ";UID=" + ( *it );
01465         mAccount->setImapSeenStatus( folder(), imappath, false );
01466       }
01467     }
01468 
01469     if ( mStatusFlagsJobs ) {
01470       connect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01471                this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01472       return;
01473     }
01474   }
01475   newState( mProgress, i18n("No messages to upload to server"));
01476   serverSyncInternal();
01477 }
01478 
01479 void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const QString&, bool cont)
01480 {
01481   if ( mSyncState == SYNC_STATE_INITIAL ){
01482       //kdDebug(5006) << "IMAP status changed but reset " << endl;
01483       return; // we were reset
01484   }
01485   //kdDebug(5006) << "IMAP status changed for folder: " << folder->prettyURL() << endl;
01486   if ( folder->storage() == this ) {
01487     --mStatusFlagsJobs;
01488     if ( mStatusFlagsJobs == 0 || !cont ) // done or aborting
01489       disconnect( mAccount, SIGNAL( imapStatusChanged(KMFolder*, const QString&, bool) ),
01490                   this, SLOT( slotImapStatusChanged(KMFolder*, const QString&, bool) ) );
01491     if ( mStatusFlagsJobs == 0 && cont ) {
01492       mProgress += 5;
01493       serverSyncInternal();
01494       //kdDebug(5006) << "Proceeding with mailcheck." << endl;
01495     }
01496   }
01497 }
01498 
01499 // This is not perfect, what if the status didn't really change? Oh well ...
01500 void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle)
01501 {
01502   KMFolderMaildir::setStatus( idx, status, toggle );
01503   mStatusChangedLocally = true;
01504 }
01505 
01506 void KMFolderCachedImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01507 {
01508   KMFolderMaildir::setStatus(ids, status, toggle);
01509   mStatusChangedLocally = true;
01510 }
01511 
01512 /* Upload new folders to server */
01513 void KMFolderCachedImap::createNewFolders()
01514 {
01515   QValueList<KMFolderCachedImap*> newFolders = findNewFolders();
01516   //kdDebug(5006) << label() << " createNewFolders:" << newFolders.count() << " new folders." << endl;
01517   if( !newFolders.isEmpty() ) {
01518     newState( mProgress, i18n("Creating subfolders on server"));
01519     CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this );
01520     connect( job, SIGNAL( result(KMail::FolderJob *) ), this, SLOT( slotIncreaseProgress() ) );
01521     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
01522     job->start();
01523   } else {
01524     serverSyncInternal();
01525   }
01526 }
01527 
01528 QValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
01529 {
01530   QValueList<KMFolderCachedImap*> newFolders;
01531   if( folder() && folder()->child() ) {
01532     KMFolderNode *node = folder()->child()->first();
01533     while( node ) {
01534       if( !node->isDir() ) {
01535         if( static_cast<KMFolder*>(node)->folderType() != KMFolderTypeCachedImap ) {
01536           kdError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
01537                         << node->name() << " is not an IMAP folder\n";
01538           node = folder()->child()->next();
01539           assert(0);
01540         }
01541         KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
01542         if( folder->imapPath().isEmpty() ) {
01543           newFolders << folder;
01544         }
01545       }
01546       node = folder()->child()->next();
01547     }
01548   }
01549   return newFolders;
01550 }
01551 
01552 bool KMFolderCachedImap::deleteMessages()
01553 {
01554   /* Delete messages from cache that are gone from the server */
01555   QPtrList<KMMessage> msgsForDeletion;
01556 
01557   // It is not possible to just go over all indices and remove
01558   // them one by one because the index list can get resized under
01559   // us. So use msg pointers instead
01560 
01561   QStringList uids;
01562   QMap<ulong,int>::const_iterator it = uidMap.constBegin();
01563   for( ; it != uidMap.end(); it++ ) {
01564     ulong uid ( it.key() );
01565     if( uid!=0 && !uidsOnServer.find( uid ) ) {
01566       uids << QString::number( uid );
01567       msgsForDeletion.append( getMsg( *it ) );
01568     }
01569   }
01570 
01571   if( !msgsForDeletion.isEmpty() ) {
01572 #if MAIL_LOSS_DEBUGGING
01573       if ( KMessageBox::warningYesNo(
01574              0, i18n( "<qt><p>Mails on the server in folder <b>%1</b> were deleted. "
01575                  "Do you want to delete them locally?<br>UIDs: %2</p></qt>" )
01576              .arg( folder()->prettyURL() ).arg( uids.join(",") ) ) == KMessageBox::Yes )
01577 #endif
01578         removeMsg( msgsForDeletion );
01579   }
01580 
01581   if ( mUserRights > 0 && !( mUserRights & KMail::ACLJobs::Delete ) )
01582     return false;
01583 
01584   /* Delete messages from the server that we dont have anymore */
01585   if( !uidsForDeletionOnServer.isEmpty() ) {
01586     newState( mProgress, i18n("Deleting removed messages from server"));
01587     QStringList sets = KMFolderImap::makeSets( uidsForDeletionOnServer, true );
01588     uidsForDeletionOnServer.clear();
01589     kdDebug(5006) << "Deleting " << sets.count() << " sets of messages from server folder " << imapPath() << endl;
01590     CachedImapJob *job = new CachedImapJob( sets, CachedImapJob::tDeleteMessage, this );
01591     connect( job, SIGNAL( result(KMail::FolderJob *) ),
01592              this, SLOT( slotDeleteMessagesResult(KMail::FolderJob *) ) );
01593     job->start();
01594     return true;
01595   } else {
01596     return false;
01597   }
01598 }
01599 
01600 void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob* job )
01601 {
01602   if ( job->error() ) {
01603     // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages
01604     mSyncState = SYNC_STATE_GET_MESSAGES;
01605   } else {
01606     // deleting on the server went fine, clear the pending deletions cache
01607     mDeletedUIDsSinceLastSync.clear();
01608   }
01609   mProgress += 10;
01610   serverSyncInternal();
01611 }
01612 
01613 void KMFolderCachedImap::checkUidValidity() {
01614   // IMAP root folders don't seem to have a UID validity setting.
01615   // Also, don't try the uid validity on new folders
01616   if( imapPath().isEmpty() || imapPath() == "/" )
01617     // Just proceed
01618     serverSyncInternal();
01619   else {
01620     newState( mProgress, i18n("Checking folder validity"));
01621     CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this );
01622     connect( job, SIGNAL(permanentFlags(int)), SLOT(slotPermanentFlags(int)) );
01623     connect( job, SIGNAL( result( KMail::FolderJob* ) ),
01624              this, SLOT( slotCheckUidValidityResult( KMail::FolderJob* ) ) );
01625     job->start();
01626   }
01627 }
01628 
01629 void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob* job )
01630 {
01631   if ( job->error() ) { // there was an error and the user chose "continue"
01632     // We can't continue doing anything in the same folder though, it would delete all mails.
01633     // But we can continue to subfolders if any. Well we can also try annotation/acl stuff...
01634     mSyncState = SYNC_STATE_HANDLE_INBOX;
01635   }
01636   mProgress += 5;
01637   serverSyncInternal();
01638 }
01639 
01640 void KMFolderCachedImap::slotPermanentFlags(int flags)
01641 {
01642   mPermanentFlags = flags;
01643 }
01644 
01645 /* This will only list the messages in a folder.
01646    No directory listing done*/
01647 void KMFolderCachedImap::listMessages() {
01648   bool groupwareOnly = GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
01649                && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
01650                && folder()->isSystemFolder()
01651                && mImapPath == "/INBOX/";
01652   // Don't list messages on the root folder, and skip the inbox, if this is
01653   // the inbox of a groupware-only dimap account
01654   if( imapPath() == "/" || groupwareOnly ) {
01655     serverSyncInternal();
01656     return;
01657   }
01658 
01659   if( !mAccount->slave() ) { // sync aborted
01660     resetSyncState();
01661     emit folderComplete( this, false );
01662     return;
01663   }
01664   uidsOnServer.clear();
01665   uidsOnServer.resize( count() * 2 );
01666   uidsForDeletionOnServer.clear();
01667   mMsgsForDownload.clear();
01668   mUidsForDownload.clear();
01669   // listing is only considered successful if saw a syntactically correct imapdigest
01670   mFoundAnIMAPDigest = false;
01671 
01672   CachedImapJob* job = new CachedImapJob( FolderJob::tListMessages, this );
01673   connect( job, SIGNAL( result(KMail::FolderJob *) ),
01674            this, SLOT( slotGetLastMessagesResult(KMail::FolderJob *) ) );
01675   job->start();
01676 }
01677 
01678 void KMFolderCachedImap::slotGetLastMessagesResult(KMail::FolderJob *job)
01679 {
01680   getMessagesResult(job, true);
01681 }
01682 
01683 // Connected to the listMessages job in CachedImapJob
01684 void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
01685 {
01686   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01687   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
01688     kdDebug(5006) << "could not find job!?!?!" << endl;
01689     // be sure to reset the sync state, if the listing was partial we would
01690     // otherwise delete not-listed mail locally, and on the next sync on the server
01691     // as well
01692     mSyncState = SYNC_STATE_HANDLE_INBOX;
01693     serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
01694     return;
01695   }
01696   (*it).cdata += QCString(data, data.size() + 1);
01697   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
01698   if (pos > 0) {
01699     int a = (*it).cdata.find("\r\nX-uidValidity:");
01700     if (a != -1) {
01701       int b = (*it).cdata.find("\r\n", a + 17);
01702       setUidValidity((*it).cdata.mid(a + 17, b - a - 17));
01703     }
01704     a = (*it).cdata.find("\r\nX-Access:");
01705     // Only trust X-Access (i.e. the imap select info) if we don't know mUserRights.
01706     // The latter is more accurate (checked on every sync) whereas X-Access is only
01707     // updated when selecting the folder again, which might not happen if using
01708     // RMB / Check Mail in this folder. We don't need two (potentially conflicting)
01709     // sources for the readonly setting, in any case.
01710     if (a != -1 && mUserRights == -1 ) {
01711       int b = (*it).cdata.find("\r\n", a + 12);
01712       const QString access = (*it).cdata.mid(a + 12, b - a - 12);
01713       setReadOnly( access == "Read only" );
01714     }
01715     (*it).cdata.remove(0, pos);
01716     mFoundAnIMAPDigest = true;
01717   }
01718   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01719   // Start with something largish when rebuilding the cache
01720   if ( uidsOnServer.size() == 0 )
01721     uidsOnServer.resize( KMail::nextPrime( 2000 ) );
01722   const int v = 42;
01723   while (pos >= 0) {
01724       /*
01725     KMMessage msg;
01726     msg.fromString((*it).cdata.mid(16, pos - 16));
01727     const int flags = msg.headerField("X-Flags").toInt();
01728     const ulong size = msg.headerField("X-Length").toULong();
01729     const ulong uid = msg.UID();
01730        */
01731     // The below is optimized for speed, not prettiness. The commented out chunk
01732     // above was the solution copied from kmfolderimap, and it's 15-20% slower.
01733     const QCString& entry( (*it).cdata );
01734     const int indexOfUID = entry.find("X-UID", 16);
01735     const int startOfUIDValue = indexOfUID  + 7;
01736     const int indexOfLength = entry.find("X-Length", startOfUIDValue ); // we know length comes after UID
01737     const int startOfLengthValue = indexOfLength + 10;
01738     const int indexOfFlags = entry.find("X-Flags", startOfLengthValue ); // we know flags comes last
01739     const int startOfFlagsValue = indexOfFlags + 9;
01740 
01741     const int flags = entry.mid( startOfFlagsValue, entry.find( '\r', startOfFlagsValue ) - startOfFlagsValue ).toInt();
01742     const ulong size = entry.mid( startOfLengthValue, entry.find( '\r', startOfLengthValue ) - startOfLengthValue ).toULong();
01743     const ulong uid = entry.mid( startOfUIDValue, entry.find( '\r', startOfUIDValue ) - startOfUIDValue ).toULong();
01744 
01745     const bool deleted = ( flags & 8 );
01746     if ( !deleted ) {
01747       if( uid != 0 ) {
01748         if ( uidsOnServer.count() == uidsOnServer.size() ) {
01749           uidsOnServer.resize( KMail::nextPrime( uidsOnServer.size() * 2 ) );
01750           //kdDebug( 5006 ) << "Resizing to: " << uidsOnServer.size() << endl;
01751         }
01752         uidsOnServer.insert( uid, &v );
01753       }
01754       bool redownload = false;
01755       if (  uid <= lastUid() ) {
01756        /*
01757         * If this message UID is not present locally, then it must
01758         * have been deleted by the user, so we delete it on the
01759         * server also. If we don't have delete permissions on the server,
01760         * re-download the message, it must have vanished by some error, or
01761         * while we still thought we were allowed to delete (ACL change).
01762         *
01763         * This relies heavily on lastUid() being correct at all times.
01764         */
01765         // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<label()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl;
01766         KMMsgBase *existingMessage = findByUID(uid);
01767         if( !existingMessage ) {
01768 #if MAIL_LOSS_DEBUGGING
01769            kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should delete it" << endl;
01770 #endif
01771           // double check we deleted it since the last sync
01772            if ( mDeletedUIDsSinceLastSync.contains(uid) ) {
01773                if ( mUserRights <= 0 || ( mUserRights & KMail::ACLJobs::Delete ) ) {
01774 #if MAIL_LOSS_DEBUGGING
01775                    kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
01776 #endif
01777                    uidsForDeletionOnServer << uid;
01778                } else {
01779                    redownload = true;
01780                }
01781            } else {
01782                kdDebug(5006) << "WARNING: ####### " << endl;
01783                kdDebug(5006) << "Message locally missing but not deleted in folder: " << folder()->prettyURL() << endl;
01784                kdDebug(5006) << "The missing UID: " << uid << ". It will be redownloaded " << endl;
01785                redownload = true;
01786            }
01787 
01788         } else {
01789           // if this is a read only folder, ignore status updates from the server
01790           // since we can't write our status back our local version is what has to
01791           // be considered correct.
01792           if ( !mReadOnly || !GlobalSettings::allowLocalFlags() ) {
01793             /* The message is OK, update flags */
01794             KMFolderImap::flagsToStatus( existingMessage, flags,  false, mReadOnly ? INT_MAX : mPermanentFlags );
01795           } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
01796             KMFolderImap::seenFlagToStatus( existingMessage, flags );
01797           }
01798         }
01799         // kdDebug(5006) << "message with uid " << uid << " found in the local cache. " << endl;
01800       }
01801       if ( uid > lastUid() || redownload ) {
01802 #if MAIL_LOSS_DEBUGGING
01803         kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should download it" << endl;
01804 #endif
01805         // The message is new since the last sync, but we might have just uploaded it, in which case
01806         // the uid map already contains it.
01807         if ( !uidMap.contains( uid ) ) {
01808           mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size);
01809           if( imapPath() == "/INBOX/" )
01810             mUidsForDownload << uid;
01811         }
01812         // Remember the highest uid and once the download is completed, update mLastUid
01813         if ( uid > mTentativeHighestUid ) {
01814 #if MAIL_LOSS_DEBUGGING
01815           kdDebug(5006) << "Setting the tentative highest UID to: " << uid << endl;
01816 #endif
01817           mTentativeHighestUid = uid;
01818         }
01819       }
01820     }
01821     (*it).cdata.remove(0, pos);
01822     (*it).done++;
01823     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01824   }
01825 }
01826 
01827 void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet )
01828 {
01829   mProgress += 10;
01830   if ( !job->error() && !mFoundAnIMAPDigest ) {
01831       kdWarning(5006) << "######## Folderlisting did not complete, but there was no error! "
01832           "Aborting sync of folder: " << folder()->prettyURL() << endl;
01833 #if MAIL_LOSS_DEBUGGING
01834       kmkernel->emergencyExit( i18n("Folder listing failed in interesting ways." ) );
01835 #endif
01836   }
01837   if( job->error() ) { // error listing messages but the user chose to continue
01838     mContentState = imapNoInformation;
01839     mSyncState = SYNC_STATE_HANDLE_INBOX; // be sure not to continue in this folder
01840   } else {
01841     if( lastSet ) { // always true here (this comes from online-imap...)
01842       mContentState = imapFinished;
01843       mStatusChangedLocally = false; // we are up to date again
01844     }
01845   }
01846   serverSyncInternal();
01847 }
01848 
01849 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
01850 {
01851   int progressSpan = 100 - 5 - mProgress;
01852   //kdDebug(5006) << "KMFolderCachedImap::slotProgress done=" << done << " total=" << total << "=> mProgress=" << mProgress + ( progressSpan * done ) / total << endl;
01853   // Progress info while retrieving new emails
01854   // (going from mProgress to mProgress+progressSpan)
01855   newState( mProgress + (progressSpan * done) / total, QString::null );
01856 }
01857 
01858 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
01859 {
01860   assert( aAccount->isA("KMAcctCachedImap") );
01861   mAccount = aAccount;
01862   if( imapPath()=="/" ) aAccount->setFolder( folder() );
01863 
01864   // Folder was renamed in a previous session, and the user didn't sync yet
01865   QString newName = mAccount->renamedFolder( imapPath() );
01866   if ( !newName.isEmpty() )
01867     folder()->setLabel( newName );
01868 
01869   if( !folder() || !folder()->child() || !folder()->child()->count() ) return;
01870   for( KMFolderNode* node = folder()->child()->first(); node;
01871        node = folder()->child()->next() )
01872     if (!node->isDir())
01873       static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
01874 }
01875 
01876 void KMFolderCachedImap::listNamespaces()
01877 {
01878   ImapAccountBase::ListType type = ImapAccountBase::List;
01879   if ( mAccount->onlySubscribedFolders() )
01880     type = ImapAccountBase::ListSubscribed;
01881 
01882   kdDebug(5006) << "listNamespaces " << mNamespacesToList << endl;
01883   if ( mNamespacesToList.isEmpty() ) {
01884     mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
01885     mPersonalNamespacesCheckDone = true;
01886 
01887     QStringList ns = mAccount->namespaces()[ImapAccountBase::OtherUsersNS];
01888     ns += mAccount->namespaces()[ImapAccountBase::SharedNS];
01889     mNamespacesToCheck = ns.count();
01890     for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
01891     {
01892       if ( (*it).isEmpty() ) {
01893         // ignore empty listings as they have been listed before
01894         --mNamespacesToCheck;
01895         continue;
01896       }
01897       KMail::ListJob* job = new KMail::ListJob( mAccount, type, this, mAccount->addPathToNamespace( *it ) );
01898       job->setHonorLocalSubscription( true );
01899       connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
01900               const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
01901           this, SLOT(slotCheckNamespace(const QStringList&, const QStringList&,
01902               const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
01903       job->start();
01904     }
01905     if ( mNamespacesToCheck == 0 ) {
01906       serverSyncInternal();
01907     }
01908     return;
01909   }
01910   mPersonalNamespacesCheckDone = false;
01911 
01912   QString ns = mNamespacesToList.front();
01913   mNamespacesToList.pop_front();
01914 
01915   mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
01916   newState( mProgress, i18n("Retrieving folders for namespace %1").arg(ns));
01917   KMail::ListJob* job = new KMail::ListJob( mAccount, type, this,
01918       mAccount->addPathToNamespace( ns ) );
01919   job->setNamespace( ns );
01920   job->setHonorLocalSubscription( true );
01921   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
01922           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
01923       this, SLOT(slotListResult(const QStringList&, const QStringList&,
01924           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
01925   job->start();
01926 }
01927 
01928 void KMFolderCachedImap::slotCheckNamespace( const QStringList& subfolderNames,
01929                                              const QStringList& subfolderPaths,
01930                                              const QStringList& subfolderMimeTypes,
01931                                              const QStringList& subfolderAttributes,
01932                                              const ImapAccountBase::jobData& jobData )
01933 {
01934   Q_UNUSED( subfolderPaths );
01935   Q_UNUSED( subfolderMimeTypes );
01936   Q_UNUSED( subfolderAttributes );
01937   --mNamespacesToCheck;
01938   kdDebug(5006) << "slotCheckNamespace " << subfolderNames << ",remain=" <<
01939    mNamespacesToCheck << endl;
01940 
01941   // get a correct foldername:
01942   // strip / and make sure it does not contain the delimiter
01943   QString name = jobData.path.mid( 1, jobData.path.length()-2 );
01944   name.remove( mAccount->delimiterForNamespace( name ) );
01945   if ( name.isEmpty() ) {
01946     // should not happen
01947     kdWarning(5006) << "slotCheckNamespace: ignoring empty folder!" << endl;
01948     return;
01949   }
01950 
01951   folder()->createChildFolder();
01952   KMFolderNode *node = 0;
01953   for ( node = folder()->child()->first(); node;
01954         node = folder()->child()->next())
01955   {
01956     if ( !node->isDir() && node->name() == name )
01957       break;
01958   }
01959   if ( !subfolderNames.isEmpty() ) {
01960     if ( node ) {
01961       // folder exists so we have nothing to do - it will be listed later
01962       kdDebug(5006) << "found namespace folder " << name << endl;
01963     } else
01964     {
01965       // create folder
01966       kdDebug(5006) << "create namespace folder " << name << endl;
01967       KMFolder* newFolder = folder()->child()->createFolder( name, false,
01968           KMFolderTypeCachedImap );
01969       if ( newFolder ) {
01970         KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>( newFolder->storage() );
01971         f->setImapPath( mAccount->addPathToNamespace( name ) );
01972         f->setNoContent( true );
01973         f->setAccount( mAccount );
01974         f->close("cachedimap");
01975         kmkernel->dimapFolderMgr()->contentsChanged();
01976       }
01977     }
01978   } else {
01979     if ( node ) {
01980       kdDebug(5006) << "delete namespace folder " << name << endl;
01981       KMFolder* fld = static_cast<KMFolder*>(node);
01982       kmkernel->dimapFolderMgr()->remove( fld );
01983     }
01984   }
01985 
01986   if ( mNamespacesToCheck == 0 ) {
01987     // all namespaces are done so continue with the next step
01988     serverSyncInternal();
01989   }
01990 }
01991 
01992 // This lists the subfolders on the server
01993 // and (in slotListResult) takes care of folders that have been removed on the server
01994 bool KMFolderCachedImap::listDirectory()
01995 {
01996   if( !mAccount->slave() ) { // sync aborted
01997     resetSyncState();
01998     emit folderComplete( this, false );
01999     return false;
02000   }
02001   mSubfolderState = imapInProgress;
02002 
02003   // get the folders
02004   ImapAccountBase::ListType type = ImapAccountBase::List;
02005   if ( mAccount->onlySubscribedFolders() )
02006     type = ImapAccountBase::ListSubscribed;
02007   KMail::ListJob* job = new KMail::ListJob( mAccount, type, this );
02008   job->setHonorLocalSubscription( true );
02009   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
02010           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
02011       this, SLOT(slotListResult(const QStringList&, const QStringList&,
02012           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
02013   job->start();
02014 
02015   return true;
02016 }
02017 
02018 void KMFolderCachedImap::slotListResult( const QStringList& folderNames,
02019                                          const QStringList& folderPaths,
02020                                          const QStringList& folderMimeTypes,
02021                                          const QStringList& folderAttributes,
02022                                          const ImapAccountBase::jobData& jobData )
02023 {
02024   Q_UNUSED( jobData );
02025   //kdDebug(5006) << label() << ": folderNames=" << folderNames << " folderPaths="
02026   //<< folderPaths << " mimeTypes=" << folderMimeTypes << endl;
02027   mSubfolderNames = folderNames;
02028   mSubfolderPaths = folderPaths;
02029   mSubfolderMimeTypes = folderMimeTypes;
02030   mSubfolderState = imapFinished;
02031   mSubfolderAttributes = folderAttributes;
02032   kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl;
02033 
02034   folder()->createChildFolder();
02035   KMFolderNode *node = folder()->child()->first();
02036   bool root = ( this == mAccount->rootFolder() );
02037 
02038   QPtrList<KMFolder> toRemove;
02039   bool emptyList = ( root && mSubfolderNames.empty() );
02040   if ( !emptyList ) {
02041     while (node) {
02042       if (!node->isDir() ) {
02043         KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02044 
02045         if ( mSubfolderNames.findIndex(node->name()) == -1 ) {
02046           QString name = node->name();
02047           // as more than one namespace can be listed in the root folder we need to make sure
02048           // that the folder is within the current namespace
02049           bool isInNamespace = ( jobData.curNamespace.isEmpty() ||
02050               jobData.curNamespace == mAccount->namespaceForFolder( f ) );
02051           // ignore some cases
02052           bool ignore = root && ( f->imapPath() == "/INBOX/" ||
02053               mAccount->isNamespaceFolder( name ) || !isInNamespace );
02054 
02055           // This subfolder isn't present on the server
02056           if( !f->imapPath().isEmpty() && !ignore  ) {
02057             // The folder has an imap path set, so it has been
02058             // on the server before. Delete it locally.
02059             toRemove.append( f->folder() );
02060             kdDebug(5006) << node->name() << " isn't on the server. It has an imapPath -> delete it locally" << endl;
02061           }
02062         } else { // folder both local and on server
02063           //kdDebug(5006) << node->name() << " is on the server." << endl;
02064 
02068           int index = mSubfolderNames.findIndex( node->name() );
02069           f->mFolderAttributes = folderAttributes[ index ];
02070         }
02071       } else {
02072         //kdDebug(5006) << "skipping dir node:" << node->name() << endl;
02073       }
02074       node = folder()->child()->next();
02075     }
02076   }
02077 
02078   for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) {
02079     rescueUnsyncedMessagesAndDeleteFolder( doomed );
02080   }
02081 
02082   mProgress += 5;
02083 
02084   // just in case there is nothing to rescue
02085   slotRescueDone( 0 );
02086 }
02087 
02088 // This synchronizes the local folders as needed (creation/deletion). No network communication here.
02089 void KMFolderCachedImap::listDirectory2()
02090 {
02091   QString path = folder()->path();
02092   kmkernel->dimapFolderMgr()->quiet(true);
02093 
02094   bool root = ( this == mAccount->rootFolder() );
02095   if ( root && !mAccount->hasInbox() )
02096   {
02097     KMFolderCachedImap *f = 0;
02098     KMFolderNode *node;
02099     // create the INBOX
02100     for (node = folder()->child()->first(); node; node = folder()->child()->next())
02101       if (!node->isDir() && node->name() == "INBOX") break;
02102     if (node) {
02103       f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02104     } else {
02105       KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap);
02106       if ( newFolder ) {
02107         f = static_cast<KMFolderCachedImap*>(newFolder->storage());
02108       }
02109     }
02110     if ( f ) {
02111       f->setAccount( mAccount );
02112       f->setImapPath( "/INBOX/" );
02113       f->folder()->setLabel( i18n("inbox") );
02114     }
02115     if (!node) {
02116       if ( f )
02117         f->close("cachedimap");
02118       kmkernel->dimapFolderMgr()->contentsChanged();
02119     }
02120     // so we have an INBOX
02121     mAccount->setHasInbox( true );
02122   }
02123 
02124   if ( root && !mSubfolderNames.isEmpty() ) {
02125     KMFolderCachedImap* parent =
02126       findParent( mSubfolderPaths.first(), mSubfolderNames.first() );
02127     if ( parent ) {
02128       kdDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to "
02129         << parent->label() << endl;
02130       mSubfolderNames.clear();
02131     }
02132   }
02133 
02134   // Find all subfolders present on server but not on disk
02135   QValueVector<int> foldersNewOnServer;
02136   for (uint i = 0; i < mSubfolderNames.count(); i++) {
02137 
02138     // Find the subdir, if already present
02139     KMFolderCachedImap *f = 0;
02140     KMFolderNode *node = 0;
02141     for (node = folder()->child()->first(); node;
02142          node = folder()->child()->next())
02143       if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
02144 
02145     if (!node) {
02146       // This folder is not present here
02147       // Either it's new on the server, or we just deleted it.
02148       QString subfolderPath = mSubfolderPaths[i];
02149       // The code used to look at the uidcache to know if it was "just deleted".
02150       // But this breaks with noContent folders and with shared folders.
02151       // So instead we keep a list in the account.
02152       bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath );
02153       // That list is saved/restored across sessions, but to avoid any mistake,
02154       // ask for confirmation if the folder was deleted in a previous session
02155       // (could be that the folder was deleted & recreated meanwhile from another client...)
02156       if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) {
02157            locallyDeleted = KMessageBox::warningYesNo(
02158              0, i18n( "<qt><p>It seems that the folder <b>%1</b> was deleted. Do you want to delete it from the server?</p></qt>" ).arg( mSubfolderNames[i] ), QString::null, KStdGuiItem::del(), KStdGuiItem::cancel() ) == KMessageBox::Yes;
02159       }
02160 
02161       if ( locallyDeleted ) {
02162         kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl;
02163         foldersForDeletionOnServer += mAccount->deletedFolderPaths( subfolderPath ); // grab all subsubfolders too
02164       } else {
02165         kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl;
02166         foldersNewOnServer.append( i );
02167       }
02168     } else { // Folder found locally
02169       if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap )
02170         f = dynamic_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02171       if( f ) {
02172         // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath()
02173         //               << "\nSetting imapPath " << mSubfolderPaths[i] << endl;
02174         // Write folder settings
02175         f->setAccount(mAccount);
02176         f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
02177         f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest");
02178         f->setImapPath(mSubfolderPaths[i]);
02179       }
02180     }
02181   }
02182 
02183   /* In case we are ignoring non-groupware folders, and this is the groupware
02184    * main account, find out the contents types of folders that have newly
02185    * appeared on the server. Otherwise just create them and finish listing.
02186    * If a folder is already known to be locally unsubscribed, it won't be
02187    * listed at all, on this level, so these are only folders that we are
02188    * seeing for the first time. */
02189 
02190   /*  Note: We ask the globalsettings, and not the current state of the
02191    *  kmkernel->iCalIface().isEnabled(), since that is false during the
02192    *  very first sync, where we already want to filter. */
02193   if ( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
02194      && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
02195      && mAccount->hasAnnotationSupport()
02196      && GlobalSettings::self()->theIMAPResourceEnabled()
02197      && !foldersNewOnServer.isEmpty() ) {
02198 
02199     QStringList paths;
02200     for ( uint i = 0; i < foldersNewOnServer.count(); ++i )
02201       paths << mSubfolderPaths[ foldersNewOnServer[i] ];
02202 
02203     AnnotationJobs::MultiUrlGetAnnotationJob* job =
02204       AnnotationJobs::multiUrlGetAnnotation( mAccount->slave(), mAccount->getUrl(), paths, KOLAB_FOLDERTYPE );
02205     ImapAccountBase::jobData jd( QString::null, folder() );
02206     jd.cancellable = true;
02207     mAccount->insertJob(job, jd);
02208     connect( job, SIGNAL(result(KIO::Job *)),
02209         SLOT(slotMultiUrlGetAnnotationResult(KIO::Job *)) );
02210 
02211   } else {
02212     createFoldersNewOnServerAndFinishListing( foldersNewOnServer );
02213   }
02214 }
02215 
02216 void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const QValueVector<int> foldersNewOnServer )
02217 {
02218   for ( uint i = 0; i < foldersNewOnServer.count(); ++i ) {
02219     int idx = foldersNewOnServer[i];
02220     KMFolder* newFolder = folder()->child()->createFolder( mSubfolderNames[idx], false, KMFolderTypeCachedImap);
02221     if (newFolder) {
02222       KMFolderCachedImap *f = dynamic_cast<KMFolderCachedImap*>(newFolder->storage());
02223       kdDebug(5006) << " ####### Locally creating folder " << mSubfolderNames[idx] <<endl;
02224       f->close("cachedimap");
02225       f->setAccount(mAccount);
02226       f->mAnnotationFolderType = "FROMSERVER";
02227       f->setNoContent(mSubfolderMimeTypes[idx] == "inode/directory");
02228       f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest");
02229       f->setImapPath(mSubfolderPaths[idx]);
02230       f->mFolderAttributes = mSubfolderAttributes[idx];
02231       kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <<endl;
02232       //kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl;
02233       kmkernel->dimapFolderMgr()->contentsChanged();
02234     } else {
02235       kdDebug(5006) << "can't create folder " << mSubfolderNames[idx] <<endl;
02236     }
02237   }
02238 
02239   kmkernel->dimapFolderMgr()->quiet(false);
02240   emit listComplete(this);
02241   if ( !mPersonalNamespacesCheckDone ) {
02242     // we're not done with the namespaces
02243     mSyncState = SYNC_STATE_LIST_NAMESPACES;
02244   }
02245   serverSyncInternal();
02246 }
02247 
02248 //-----------------------------------------------------------------------------
02249 KMFolderCachedImap* KMFolderCachedImap::findParent( const QString& path,
02250                                                     const QString& name )
02251 {
02252   QString parent = path.left( path.length() - name.length() - 2 );
02253   if ( parent.length() > 1 )
02254   {
02255     // extract name of the parent
02256     parent = parent.right( parent.length() - 1 );
02257     if ( parent != label() )
02258     {
02259       KMFolderNode *node = folder()->child()->first();
02260       // look for a better parent
02261       while ( node )
02262       {
02263         if ( node->name() == parent )
02264         {
02265           KMFolder* fld = static_cast<KMFolder*>(node);
02266           KMFolderCachedImap* imapFld =
02267             static_cast<KMFolderCachedImap*>( fld->storage() );
02268           return imapFld;
02269         }
02270         node = folder()->child()->next();
02271       }
02272     }
02273   }
02274   return 0;
02275 }
02276 
02277 void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success)
02278 {
02279   Q_UNUSED(sub);
02280   //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl;
02281   if ( success ) {
02282     serverSyncInternal();
02283   }
02284   else
02285   {
02286     // success == false means the sync was aborted.
02287     if ( mCurrentSubfolder ) {
02288       Q_ASSERT( sub == mCurrentSubfolder );
02289       disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
02290                   this, SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
02291       mCurrentSubfolder = 0;
02292     }
02293 
02294     mSubfoldersForSync.clear();
02295     mSyncState = SYNC_STATE_INITIAL;
02296     close("cachedimap");
02297     emit folderComplete( this, false );
02298   }
02299 }
02300 
02301 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
02302 {
02303   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02304   if (it == mAccount->jobsEnd()) return;
02305   QBuffer buff((*it).data);
02306   buff.open(IO_WriteOnly | IO_Append);
02307   buff.writeBlock(data.data(), data.size());
02308   buff.close();
02309 }
02310 
02311 FolderJob*
02312 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
02313                                  QString, const AttachmentStrategy* ) const
02314 {
02315   QPtrList<KMMessage> msgList;
02316   msgList.append( msg );
02317   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
02318   job->setParentFolder( this );
02319   return job;
02320 }
02321 
02322 FolderJob*
02323 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
02324                                  FolderJob::JobType jt, KMFolder *folder ) const
02325 {
02326   //FIXME: how to handle sets here?
02327   Q_UNUSED( sets );
02328   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
02329   job->setParentFolder( this );
02330   return job;
02331 }
02332 
02333 void
02334 KMFolderCachedImap::setUserRights( unsigned int userRights )
02335 {
02336   mUserRights = userRights;
02337   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02338 }
02339 
02340 void
02341 KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder )
02342 {
02343   if ( folder->storage() == this ) {
02344     disconnect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
02345                 this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
02346     if ( mUserRights == 0 ) // didn't work
02347       mUserRights = -1; // error code (used in folderdia)
02348     else
02349       setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 );
02350     mProgress += 5;
02351     serverSyncInternal();
02352   }
02353 }
02354 
02355 void
02356 KMFolderCachedImap::setReadOnly( bool readOnly )
02357 {
02358   if ( readOnly != mReadOnly ) {
02359     mReadOnly = readOnly;
02360     emit readOnlyChanged( folder() );
02361   }
02362 }
02363 
02364 void
02365 KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job*, const KMail::ACLList& aclList )
02366 {
02367   if ( folder->storage() == this ) {
02368     disconnect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
02369                 this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
02370     mACLList = aclList;
02371     serverSyncInternal();
02372   }
02373 }
02374 
02375 void
02376 KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info )
02377 {
02378   setQuotaInfo( info );
02379 }
02380 
02381 void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info )
02382 {
02383     if ( info != mQuotaInfo ) {
02384       mQuotaInfo = info;
02385       writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02386       emit folderSizeChanged();
02387     }
02388 }
02389 
02390 void
02391 KMFolderCachedImap::setACLList( const ACLList& arr )
02392 {
02393   mACLList = arr;
02394 }
02395 
02396 void
02397 KMFolderCachedImap::slotMultiSetACLResult(KIO::Job *job)
02398 {
02399   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02400   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02401   if ( (*it).parent != folder() ) return; // Shouldn't happen
02402 
02403   if ( job->error() )
02404     // Display error but don't abort the sync just for this
02405     // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel
02406     job->showErrorDialog();
02407   else
02408     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02409 
02410   if (mAccount->slave()) mAccount->removeJob(job);
02411   serverSyncInternal();
02412 }
02413 
02414 void
02415 KMFolderCachedImap::slotACLChanged( const QString& userId, int permissions )
02416 {
02417   // The job indicates success in changing the permissions for this user
02418   // -> we note that it's been done.
02419   for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) {
02420     if ( (*it).userId == userId && (*it).permissions == permissions ) {
02421       if ( permissions == -1 ) // deleted
02422         mACLList.erase( it );
02423       else // added/modified
02424         (*it).changed = false;
02425       return;
02426     }
02427   }
02428 }
02429 
02430 // called by KMAcctCachedImap::killAllJobs
02431 void KMFolderCachedImap::resetSyncState()
02432 {
02433   if ( mSyncState == SYNC_STATE_INITIAL ) return;
02434   mSubfoldersForSync.clear();
02435   mSyncState = SYNC_STATE_INITIAL;
02436   close("cachedimap");
02437   // Don't use newState here, it would revert to mProgress (which is < current value when listing messages)
02438   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02439   QString str = i18n("Aborted");
02440   if (progressItem)
02441      progressItem->setStatus( str );
02442   emit statusMsg( str );
02443 }
02444 
02445 void KMFolderCachedImap::slotIncreaseProgress()
02446 {
02447   mProgress += 5;
02448 }
02449 
02450 void KMFolderCachedImap::newState( int progress, const QString& syncStatus )
02451 {
02452   //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl;
02453   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02454   if( progressItem )
02455     progressItem->setCompletedItems( progress );
02456   if ( !syncStatus.isEmpty() ) {
02457     QString str;
02458     // For a subfolder, show the label. But for the main folder, it's already shown.
02459     if ( mAccount->imapFolder() == this )
02460       str = syncStatus;
02461     else
02462       str = QString( "%1: %2" ).arg( label() ).arg( syncStatus );
02463     if( progressItem )
02464       progressItem->setStatus( str );
02465     emit statusMsg( str );
02466   }
02467   if( progressItem )
02468     progressItem->updateProgress();
02469 }
02470 
02471 void KMFolderCachedImap::setSubfolderState( imapState state )
02472 {
02473   mSubfolderState = state;
02474   if ( state == imapNoInformation && folder()->child() )
02475   {
02476     // pass through to childs
02477     KMFolderNode* node;
02478     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02479     for ( ; (node = it.current()); )
02480     {
02481       ++it;
02482       if (node->isDir()) continue;
02483       KMFolder *folder = static_cast<KMFolder*>(node);
02484       static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state );
02485     }
02486   }
02487 }
02488 
02489 void KMFolderCachedImap::setImapPath(const QString &path)
02490 {
02491   mImapPath = path;
02492 }
02493 
02494 // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
02495 // It is updated from the folder contents type and whether it's a standard resource folder.
02496 // This happens during the syncing phase and during initFolder for a new folder.
02497 // Don't do it earlier, e.g. from setContentsType:
02498 // on startup, it's too early there to know if this is a standard resource folder.
02499 void KMFolderCachedImap::updateAnnotationFolderType()
02500 {
02501   QString oldType = mAnnotationFolderType;
02502   QString oldSubType;
02503   int dot = oldType.find( '.' );
02504   if ( dot != -1 ) {
02505     oldType.truncate( dot );
02506     oldSubType = mAnnotationFolderType.mid( dot + 1 );
02507   }
02508 
02509   QString newType, newSubType;
02510   // We want to store an annotation on the folder only if using the kolab storage.
02511   if ( kmkernel->iCalIface().storageFormat( folder() ) == KMailICalIfaceImpl::StorageXML ) {
02512     newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType );
02513     if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) )
02514       newSubType = "default";
02515     else if ( oldSubType != "default" )
02516       newSubType = oldSubType; // preserve unknown subtypes, like drafts etc.
02517   }
02518 
02519   //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl;
02520   if ( newType != oldType || newSubType != oldSubType ) {
02521     mAnnotationFolderType = newType + ( newSubType.isEmpty() ? QString::null : "."+newSubType );
02522     mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync
02523     kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl;
02524   }
02525   // Ensure that further readConfig()s don't lose mAnnotationFolderType
02526   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02527 }
02528 
02529 void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor )
02530 {
02531   if ( mIncidencesFor != incfor ) {
02532     mIncidencesFor = incfor;
02533     mIncidencesForChanged = true;
02534   }
02535 }
02536 
02537 void KMFolderCachedImap::setSharedSeenFlags(bool b)
02538 {
02539   if ( mSharedSeenFlags != b ) {
02540     mSharedSeenFlags = b;
02541     mSharedSeenFlagsChanged = true;
02542   }
02543 }
02544 
02545 void KMFolderCachedImap::slotAnnotationResult(const QString& entry, const QString& value, bool found)
02546 {
02547   if ( entry == KOLAB_FOLDERTYPE ) {
02548     // There are four cases.
02549     // 1) no content-type on server -> set it
02550     // 2) different content-type on server, locally changed -> set it (we don't even come here)
02551     // 3) different (known) content-type on server, no local change -> get it
02552     // 4) different unknown content-type on server, probably some older version -> set it
02553     if ( found ) {
02554       QString type = value;
02555       QString subtype;
02556       int dot = value.find( '.' );
02557       if ( dot != -1 ) {
02558         type.truncate( dot );
02559         subtype = value.mid( dot + 1 );
02560       }
02561       bool foundKnownType = false;
02562       for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
02563         FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
02564         if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) {
02565           // Case 3: known content-type on server, get it
02566           //kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: found known type of annotation" << endl;
02567           if ( contentsType != ContentsTypeMail )
02568             kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
02569           mAnnotationFolderType = value;
02570           if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent()
02571                && GlobalSettings::self()->theIMAPResourceEnabled()
02572                && subtype == "default" ) {
02573             // Truncate subtype if this folder can't be a default resource folder for us,
02574             // although it apparently is for someone else.
02575             mAnnotationFolderType = type;
02576             kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: parent folder is " << folder()->parent()->owner()->idString() << " => truncating annotation to " << value << endl;
02577           }
02578           setContentsType( contentsType );
02579           mAnnotationFolderTypeChanged = false; // we changed it, not the user
02580           foundKnownType = true;
02581 
02582           // Users don't read events/contacts/etc. in kmail, so mark them all as read.
02583           // This is done in cachedimapjob when getting new messages, but do it here too,
02584           // for the initial set of messages when we didn't know this was a resource folder yet,
02585           // for old folders, etc.
02586           if ( contentsType != ContentsTypeMail )
02587             markUnreadAsRead();
02588 
02589           // Ensure that further readConfig()s don't lose mAnnotationFolderType
02590           writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02591           break;
02592         }
02593       }
02594       if ( !foundKnownType && !mReadOnly ) {
02595         //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, will need to set it" << endl;
02596         // Case 4: server has strange content-type, set it to what we need
02597         mAnnotationFolderTypeChanged = true;
02598       }
02599       // TODO handle subtype (inbox, drafts, sentitems, junkemail)
02600     }
02601     else if ( !mReadOnly ) {
02602       // Case 1: server doesn't have content-type, set it
02603       //kdDebug(5006) << "slotGetAnnotationResult: no annotation found, will need to set it" << endl;
02604       mAnnotationFolderTypeChanged = true;
02605     }
02606   } else if ( entry == KOLAB_INCIDENCESFOR ) {
02607     if ( found ) {
02608       mIncidencesFor = incidencesForFromString( value );
02609       Q_ASSERT( mIncidencesForChanged == false );
02610     }
02611   } else if ( entry == KOLAB_SHAREDSEEN ) {
02612     if ( found ) {
02613       mSharedSeenFlags = value == "true";
02614     }
02615   }
02616 }
02617 
02618 void KMFolderCachedImap::slotGetAnnotationResult( KIO::Job* job )
02619 {
02620   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02621   Q_ASSERT( it != mAccount->jobsEnd() );
02622   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02623   Q_ASSERT( (*it).parent == folder() );
02624   if ( (*it).parent != folder() ) return; // Shouldn't happen
02625 
02626   AnnotationJobs::GetAnnotationJob* annjob = static_cast<AnnotationJobs::GetAnnotationJob *>( job );
02627   if ( annjob->error() ) {
02628     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02629       // that's when the imap server doesn't support annotations
02630       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02631            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02632     KMessageBox::error( 0, i18n( "The IMAP server %1 does not have support for IMAP annotations. The XML storage cannot be used on this server; please re-configure KMail differently." ).arg( mAccount->host() ) );
02633       mAccount->setHasNoAnnotationSupport();
02634     }
02635     else
02636       kdWarning(5006) << "slotGetAnnotationResult: " << job->errorString() << endl;
02637   }
02638 
02639   if (mAccount->slave()) mAccount->removeJob(job);
02640   mProgress += 2;
02641   serverSyncInternal();
02642 }
02643 
02644 void KMFolderCachedImap::slotMultiUrlGetAnnotationResult( KIO::Job* job )
02645 {
02646   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02647   Q_ASSERT( it != mAccount->jobsEnd() );
02648   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02649   Q_ASSERT( (*it).parent == folder() );
02650   if ( (*it).parent != folder() ) return; // Shouldn't happen
02651 
02652   QValueVector<int> folders;
02653   AnnotationJobs::MultiUrlGetAnnotationJob* annjob
02654     = static_cast<AnnotationJobs::MultiUrlGetAnnotationJob *>( job );
02655   if ( annjob->error() ) {
02656     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02657       // that's when the imap server doesn't support annotations
02658       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02659            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02660         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() ) );
02661       mAccount->setHasNoAnnotationSupport();
02662     }
02663     else
02664       kdWarning(5006) << "slotGetMultiUrlAnnotationResult: " << job->errorString() << endl;
02665   } else {
02666     // we got the annotation allright, let's filter out the ones with the wrong type
02667     QMap<QString, QString> annotations = annjob->annotations();
02668     QMap<QString, QString>::Iterator it = annotations.begin();
02669     for ( ; it != annotations.end(); ++it ) {
02670       const QString folderPath = it.key();
02671       const QString annotation = it.data();
02672       kdDebug(5006) << k_funcinfo << "Folder: " << folderPath << " has type: " << annotation << endl;
02673       // we're only interested in the main type
02674       QString type(annotation);
02675       int dot = annotation.find( '.' );
02676       if ( dot != -1 ) type.truncate( dot );
02677       type = type.simplifyWhiteSpace();
02678 
02679       const int idx = mSubfolderPaths.findIndex( folderPath );
02680       const bool isNoContent =  mSubfolderMimeTypes[idx] == "inode/directory";
02681       if ( ( isNoContent && type.isEmpty() )
02682         || ( !type.isEmpty() && type != KMailICalIfaceImpl::annotationForContentsType( ContentsTypeMail ) ) ) {
02683         folders.append( idx );
02684         kdDebug(5006) << k_funcinfo << " subscribing to: " << folderPath << endl;
02685       } else {
02686         kdDebug(5006) << k_funcinfo << " automatically unsubscribing from: " << folderPath << endl;
02687         mAccount->changeLocalSubscription( folderPath, false );
02688       }
02689     }
02690   }
02691 
02692   if (mAccount->slave()) mAccount->removeJob(job);
02693   createFoldersNewOnServerAndFinishListing( folders );
02694 }
02695 
02696 void KMFolderCachedImap::slotQuotaResult( KIO::Job* job )
02697 {
02698   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02699   Q_ASSERT( it != mAccount->jobsEnd() );
02700   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02701   Q_ASSERT( (*it).parent == folder() );
02702   if ( (*it).parent != folder() ) return; // Shouldn't happen
02703 
02704   QuotaJobs::GetStorageQuotaJob* quotajob = static_cast<QuotaJobs::GetStorageQuotaJob *>( job );
02705   QuotaInfo empty;
02706   if ( quotajob->error() ) {
02707     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02708       // that's when the imap server doesn't support quota
02709       mAccount->setHasNoQuotaSupport();
02710       setQuotaInfo( empty );
02711     }
02712     else
02713       kdWarning(5006) << "slotGetQuotaResult: " << job->errorString() << endl;
02714   }
02715 
02716   if (mAccount->slave()) mAccount->removeJob(job);
02717   mProgress += 2;
02718   serverSyncInternal();
02719 }
02720 
02721 void
02722 KMFolderCachedImap::slotAnnotationChanged( const QString& entry, const QString& attribute, const QString& value )
02723 {
02724   Q_UNUSED( attribute );
02725   Q_UNUSED( value );
02726   //kdDebug(5006) << k_funcinfo << entry << " " << attribute << " " << value << endl;
02727   if ( entry == KOLAB_FOLDERTYPE )
02728     mAnnotationFolderTypeChanged = false;
02729   else if ( entry == KOLAB_INCIDENCESFOR ) {
02730     mIncidencesForChanged = false;
02731     // The incidences-for changed, we must trigger the freebusy creation.
02732     // HACK: in theory we would need a new enum value for this.
02733     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02734   } else if ( entry == KOLAB_SHAREDSEEN ) {
02735     mSharedSeenFlagsChanged = false;
02736   }
02737 }
02738 
02739 void KMFolderCachedImap::slotTestAnnotationResult(KIO::Job *job)
02740 {
02741   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02742   Q_ASSERT( it != mAccount->jobsEnd() );
02743   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02744   Q_ASSERT( (*it).parent == folder() );
02745   if ( (*it).parent != folder() ) return; // Shouldn't happen
02746 
02747   mAccount->setAnnotationCheckPassed( true );
02748   if ( job->error() ) {
02749     kdDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl;
02750     mAccount->setHasNoAnnotationSupport( );
02751   } else {
02752     kdDebug(5006) << "Test Annotation was passed   OK" << endl;
02753   }
02754   if (mAccount->slave()) mAccount->removeJob(job);
02755   serverSyncInternal();
02756 }
02757 
02758 void
02759 KMFolderCachedImap::slotSetAnnotationResult(KIO::Job *job)
02760 {
02761   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02762   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02763   if ( (*it).parent != folder() ) return; // Shouldn't happen
02764 
02765   bool cont = true;
02766   if ( job->error() ) {
02767     // Don't show error if the server doesn't support ANNOTATEMORE and this folder only contains mail
02768     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION && contentsType() == ContentsTypeMail ) {
02769       if (mAccount->slave()) mAccount->removeJob(job);
02770     } else {
02771       cont = mAccount->handleJobError( job, i18n( "Error while setting annotation: " ) + '\n' );
02772     }
02773   } else {
02774     if (mAccount->slave()) mAccount->removeJob(job);
02775   }
02776   if ( cont )
02777     serverSyncInternal();
02778 }
02779 
02780 void KMFolderCachedImap::slotUpdateLastUid()
02781 {
02782   if( mTentativeHighestUid != 0 ) {
02783 
02784       // Sanity checking:
02785       // By now all new mails should be downloaded, which means
02786       // that iteration over the folder should yield only UIDs
02787       // lower or equal to what we think the highes ist, and the
02788       // highest one as well. If not, our notion of the highest
02789       // uid we've seen thus far is wrong, which is dangerous, so
02790       // don't update the mLastUid, then.
02791       // Not entirely true though, mails might have been moved out
02792       // of the folder already by filters, thus giving us a higher tentative
02793       // uid than we actually observe here.
02794       bool sane = count() == 0;
02795 
02796       for (int i=0;i<count(); i++ ) {
02797           ulong uid = getMsgBase(i)->UID();
02798           if ( uid > mTentativeHighestUid && uid > lastUid() ) {
02799               kdWarning(5006) << "DANGER: Either the server listed a wrong highest uid, "
02800                   "or we parsed it wrong. Send email to adam@kde.org, please, and include this log." << endl;
02801               kdWarning(5006) << "uid: " << uid << " mTentativeHighestUid: " << mTentativeHighestUid << endl;
02802               assert( false );
02803               break;
02804           } else {
02805               sane = true;
02806           }
02807       }
02808       if (sane) {
02809 #if MAIL_LOSS_DEBUGGING
02810           kdDebug(5006) << "Tentative highest UID test was sane, writing out: " << mTentativeHighestUid << endl;
02811 #endif
02812           setLastUid( mTentativeHighestUid );
02813       }
02814   }
02815   mTentativeHighestUid = 0;
02816 }
02817 
02818 bool KMFolderCachedImap::isMoveable() const
02819 {
02820   return ( hasChildren() == HasNoChildren &&
02821       !folder()->isSystemFolder() ) ? true : false;
02822 }
02823 
02824 void KMFolderCachedImap::slotFolderDeletionOnServerFinished()
02825 {
02826   for ( QStringList::const_iterator it = foldersForDeletionOnServer.constBegin();
02827       it != foldersForDeletionOnServer.constEnd(); ++it ) {
02828     KURL url( mAccount->getUrl() );
02829     url.setPath( *it );
02830     kmkernel->iCalIface().folderDeletedOnServer( url );
02831   }
02832   serverSyncInternal();
02833 }
02834 
02835 int KMFolderCachedImap::createIndexFromContentsRecursive()
02836 {
02837   if ( !folder() || !folder()->child() )
02838     return 0;
02839 
02840   KMFolderNode *node = 0;
02841   for( QPtrListIterator<KMFolderNode> it( *folder()->child() ); (node = it.current()); ++it ) {
02842     if( !node->isDir() ) {
02843       KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02844       kdDebug() << k_funcinfo << "Re-indexing: " << storage->folder()->label() << endl;
02845       int rv = storage->createIndexFromContentsRecursive();
02846       if ( rv > 0 )
02847         return rv;
02848     }
02849   }
02850 
02851   return createIndexFromContents();
02852 }
02853 
02854 void KMFolderCachedImap::setAlarmsBlocked( bool blocked )
02855 {
02856   mAlarmsBlocked = blocked;
02857 }
02858 
02859 bool KMFolderCachedImap::alarmsBlocked() const
02860 {
02861   return mAlarmsBlocked;
02862 }
02863 
02864 bool KMFolderCachedImap::isCloseToQuota() const
02865 {
02866   bool closeToQuota = false;
02867   if ( mQuotaInfo.isValid() && mQuotaInfo.max().toInt() > 0 ) {
02868     const int ratio = mQuotaInfo.current().toInt() * 100  / mQuotaInfo.max().toInt();
02869     //kdDebug(5006) << "Quota ratio: " << ratio << "% " << mQuotaInfo.toString() << endl;
02870     closeToQuota = ( ratio > 0 && ratio >= GlobalSettings::closeToQuotaThreshold() );
02871   }
02872   //kdDebug(5006) << "Folder: " << folder()->prettyURL() << " is over quota: " << closeToQuota << endl;
02873   return closeToQuota;
02874 }
02875 
02876 KMCommand* KMFolderCachedImap::rescueUnsyncedMessages()
02877 {
02878   QValueList<unsigned long> newMsgs = findNewMessages();
02879   kdDebug() << k_funcinfo << newMsgs << " of " << count() << endl;
02880   if ( newMsgs.isEmpty() )
02881     return 0;
02882   KMFolder *dest = 0;
02883   bool manualMove = true;
02884   while ( GlobalSettings::autoLostFoundMove() ) {
02885     // find the inbox of this account
02886     KMFolder *inboxFolder = kmkernel->findFolderById( QString(".%1.directory/INBOX").arg( account()->id() ) );
02887     if ( !inboxFolder ) {
02888       kdWarning(5006) << k_funcinfo << "inbox not found!" << endl;
02889       break;
02890     }
02891     KMFolderDir *inboxDir = inboxFolder->child();
02892     if ( !inboxDir && !inboxFolder->storage() )
02893       break;
02894     assert( inboxFolder->storage()->folderType() == KMFolderTypeCachedImap );
02895 
02896     // create lost+found folder if needed
02897     KMFolderNode *node;
02898     KMFolder *lfFolder = 0;
02899     if ( !(node = inboxDir->hasNamedFolder( i18n("lost+found") )) ) {
02900       kdDebug(5006) << k_funcinfo << "creating lost+found folder" << endl;
02901       KMFolder* folder = kmkernel->dimapFolderMgr()->createFolder(
02902           i18n("lost+found"), false, KMFolderTypeCachedImap, inboxDir );
02903       if ( !folder || !folder->storage() )
02904         break;
02905       static_cast<KMFolderCachedImap*>( folder->storage() )->initializeFrom(
02906         static_cast<KMFolderCachedImap*>( inboxFolder->storage() ) );
02907       folder->storage()->setContentsType( KMail::ContentsTypeMail );
02908       folder->storage()->writeConfig();
02909       lfFolder = folder;
02910     } else {
02911       kdDebug(5006) << k_funcinfo << "found lost+found folder" << endl;
02912       lfFolder = dynamic_cast<KMFolder*>( node );
02913     }
02914     if ( !lfFolder || !lfFolder->createChildFolder() || !lfFolder->storage() )
02915       break;
02916 
02917     // create subfolder for this incident
02918     QDate today = QDate::currentDate();
02919     QString baseName = folder()->label() + "-" + QString::number( today.year() )
02920         + (today.month() < 10 ? "0" : "" ) + QString::number( today.month() )
02921         + (today.day() < 10 ? "0" : "" ) + QString::number( today.day() );
02922     QString name = baseName;
02923     int suffix = 0;
02924     while ( (node = lfFolder->child()->hasNamedFolder( name )) ) {
02925       ++suffix;
02926       name = baseName + '-' + QString::number( suffix );
02927     }
02928     kdDebug(5006) << k_funcinfo << "creating lost+found folder " << name << endl;
02929     dest = kmkernel->dimapFolderMgr()->createFolder( name, false, KMFolderTypeCachedImap, lfFolder->child() );
02930     if ( !dest || !dest->storage() )
02931         break;
02932     static_cast<KMFolderCachedImap*>( dest->storage() )->initializeFrom(
02933       static_cast<KMFolderCachedImap*>( lfFolder->storage() ) );
02934     dest->storage()->setContentsType( contentsType() );
02935     dest->storage()->writeConfig();
02936 
02937     KMessageBox::sorry( 0, i18n("<p>There are new messages in folder <b>%1</b>, which "
02938           "have not been uploaded to the server yet, but the folder has been deleted "
02939           "on the server or you do not "
02940           "have sufficient access rights on the folder to upload them.</p>"
02941           "<p>All affected messages will therefore be moved to <b>%2</b> "
02942           "to avoid data loss.</p>").arg( folder()->prettyURL() ).arg( dest->prettyURL() ),
02943           i18n("Insufficient access rights") );
02944     manualMove = false;
02945     break;
02946   }
02947 
02948   if ( manualMove ) {
02949     const QString msg ( i18n( "<p>There are new messages in this folder (%1), which "
02950           "have not been uploaded to the server yet, but the folder has been deleted "
02951           "on the server or you do not "
02952           "have sufficient access rights on the folder now to upload them. "
02953           "Please contact your administrator to allow upload of new messages "
02954           "to you, or move them out of this folder.</p> "
02955           "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyURL() ) );
02956     if ( KMessageBox::warningYesNo( 0, msg, QString::null, i18n("Move"), i18n("Do Not Move") ) == KMessageBox::Yes ) {
02957       KMail::KMFolderSelDlg dlg( kmkernel->getKMMainWidget(),
02958           i18n("Move Messages to Folder"), true );
02959       if ( dlg.exec() ) {
02960         dest = dlg.folder();
02961       }
02962     }
02963   }
02964   if ( dest ) {
02965     QPtrList<KMMsgBase> msgs;
02966     for( int i = 0; i < count(); ++i ) {
02967       KMMsgBase *msg = getMsgBase( i );
02968       if( !msg ) continue; /* what goes on if getMsg() returns 0? */
02969       if ( msg->UID() == 0 )
02970         msgs.append( msg );
02971     }
02972     KMCommand *command = new KMMoveCommand( dest, msgs );
02973     command->start();
02974     return command;
02975   }
02976   return 0;
02977 }
02978 
02979 void KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root )
02980 {
02981   kdDebug() << k_funcinfo << folder << " " << root << endl;
02982   if ( root )
02983     mToBeDeletedAfterRescue.append( folder );
02984   folder->open("cachedimap");
02985   KMFolderCachedImap* storage = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
02986   if ( storage ) {
02987     KMCommand *command = storage->rescueUnsyncedMessages();
02988     if ( command ) {
02989       connect( command, SIGNAL(completed(KMCommand*)),
02990                SLOT(slotRescueDone(KMCommand*)) );
02991       ++mRescueCommandCount;
02992     } else {
02993       // nothing to rescue, close folder
02994       // (we don't need to close it in the other case, it will be deleted anyway)
02995       folder->close("cachedimap");
02996     }
02997   }
02998   if ( folder->child() ) {
02999     KMFolderNode *node = folder->child()->first();
03000     while (node) {
03001       if (!node->isDir() ) {
03002         KMFolder *subFolder = static_cast<KMFolder*>( node );
03003         rescueUnsyncedMessagesAndDeleteFolder( subFolder, false );
03004       }
03005       node = folder->child()->next();
03006     }
03007   }
03008 }
03009 
03010 void KMFolderCachedImap::slotRescueDone(KMCommand * command)
03011 {
03012   // FIXME: check command result
03013   if ( command )
03014     --mRescueCommandCount;
03015   if ( mRescueCommandCount > 0 )
03016     return;
03017   for ( QValueList<KMFolder*>::ConstIterator it = mToBeDeletedAfterRescue.constBegin();
03018         it != mToBeDeletedAfterRescue.constEnd(); ++it ) {
03019     kmkernel->dimapFolderMgr()->remove( *it );
03020   }
03021   mToBeDeletedAfterRescue.clear();
03022   serverSyncInternal();
03023 }
03024 
03025 bool KMFolderCachedImap::canDeleteMessages() const
03026 {
03027   if ( isReadOnly() )
03028     return false;
03029   if ( userRights() > 0 && !(userRights() & ACLJobs::Delete) )
03030     return false;
03031   return true;
03032 }
03033 
03034 #include "kmfoldercachedimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys