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