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   for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) {
02229     rescueUnsyncedMessagesAndDeleteFolder( doomed );
02230   }
02231 
02232   mProgress += 5;
02233 
02234   // just in case there is nothing to rescue
02235   slotRescueDone( 0 );
02236 }
02237 
02238 // This synchronizes the local folders as needed (creation/deletion). No network communication here.
02239 void KMFolderCachedImap::listDirectory2()
02240 {
02241   QString path = folder()->path();
02242   kmkernel->dimapFolderMgr()->quiet(true);
02243 
02244   bool root = ( this == mAccount->rootFolder() );
02245   if ( root && !mAccount->hasInbox() )
02246   {
02247     KMFolderCachedImap *f = 0;
02248     KMFolderNode *node;
02249     // create the INBOX
02250     for (node = folder()->child()->first(); node; node = folder()->child()->next())
02251       if (!node->isDir() && node->name() == "INBOX") break;
02252     if (node) {
02253       f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02254     } else {
02255       KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap);
02256       if ( newFolder ) {
02257         f = static_cast<KMFolderCachedImap*>(newFolder->storage());
02258       }
02259     }
02260     if ( f ) {
02261       f->setAccount( mAccount );
02262       f->setImapPath( "/INBOX/" );
02263       f->folder()->setLabel( i18n("inbox") );
02264     }
02265     if (!node) {
02266       if ( f )
02267         f->close("cachedimap");
02268       kmkernel->dimapFolderMgr()->contentsChanged();
02269     }
02270     // so we have an INBOX
02271     mAccount->setHasInbox( true );
02272   }
02273 
02274   if ( root && !mSubfolderNames.isEmpty() ) {
02275     KMFolderCachedImap* parent =
02276       findParent( mSubfolderPaths.first(), mSubfolderNames.first() );
02277     if ( parent ) {
02278       kdDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to "
02279         << parent->label() << endl;
02280       mSubfolderNames.clear();
02281     }
02282   }
02283 
02284   // Find all subfolders present on server but not on disk
02285   QValueVector<int> foldersNewOnServer;
02286   for (uint i = 0; i < mSubfolderNames.count(); i++) {
02287 
02288     // Find the subdir, if already present
02289     KMFolderCachedImap *f = 0;
02290     KMFolderNode *node = 0;
02291     for (node = folder()->child()->first(); node;
02292          node = folder()->child()->next())
02293       if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
02294 
02295     if (!node) {
02296       // This folder is not present here
02297       // Either it's new on the server, or we just deleted it.
02298       QString subfolderPath = mSubfolderPaths[i];
02299       // The code used to look at the uidcache to know if it was "just deleted".
02300       // But this breaks with noContent folders and with shared folders.
02301       // So instead we keep a list in the account.
02302       bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath );
02303       // That list is saved/restored across sessions, but to avoid any mistake,
02304       // ask for confirmation if the folder was deleted in a previous session
02305       // (could be that the folder was deleted & recreated meanwhile from another client...)
02306       if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) {
02307            locallyDeleted = KMessageBox::warningYesNo(
02308              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;
02309       }
02310 
02311       if ( locallyDeleted ) {
02312         kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl;
02313         foldersForDeletionOnServer += mAccount->deletedFolderPaths( subfolderPath ); // grab all subsubfolders too
02314       } else {
02315         kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl;
02316         foldersNewOnServer.append( i );
02317       }
02318     } else { // Folder found locally
02319       if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap )
02320         f = dynamic_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
02321       if( f ) {
02322         // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath()
02323         //               << "\nSetting imapPath " << mSubfolderPaths[i] << endl;
02324         // Write folder settings
02325         f->setAccount(mAccount);
02326         f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
02327         f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest");
02328         f->setImapPath(mSubfolderPaths[i]);
02329       }
02330     }
02331   }
02332 
02333   /* In case we are ignoring non-groupware folders, and this is the groupware
02334    * main account, find out the contents types of folders that have newly
02335    * appeared on the server. Otherwise just create them and finish listing.
02336    * If a folder is already known to be locally unsubscribed, it won't be
02337    * listed at all, on this level, so these are only folders that we are
02338    * seeing for the first time. */
02339 
02340   /*  Note: We ask the globalsettings, and not the current state of the
02341    *  kmkernel->iCalIface().isEnabled(), since that is false during the
02342    *  very first sync, where we already want to filter. */
02343   if ( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
02344      && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
02345      && mAccount->hasAnnotationSupport()
02346      && GlobalSettings::self()->theIMAPResourceEnabled()
02347      && !foldersNewOnServer.isEmpty() ) {
02348 
02349     QStringList paths;
02350     for ( uint i = 0; i < foldersNewOnServer.count(); ++i )
02351       paths << mSubfolderPaths[ foldersNewOnServer[i] ];
02352 
02353     AnnotationJobs::MultiUrlGetAnnotationJob* job =
02354       AnnotationJobs::multiUrlGetAnnotation( mAccount->slave(), mAccount->getUrl(), paths, KOLAB_FOLDERTYPE );
02355     ImapAccountBase::jobData jd( QString::null, folder() );
02356     jd.cancellable = true;
02357     mAccount->insertJob(job, jd);
02358     connect( job, SIGNAL(result(KIO::Job *)),
02359         SLOT(slotMultiUrlGetAnnotationResult(KIO::Job *)) );
02360 
02361   } else {
02362     createFoldersNewOnServerAndFinishListing( foldersNewOnServer );
02363   }
02364 }
02365 
02366 void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const QValueVector<int> foldersNewOnServer )
02367 {
02368   for ( uint i = 0; i < foldersNewOnServer.count(); ++i ) {
02369     int idx = foldersNewOnServer[i];
02370     KMFolder* newFolder = folder()->child()->createFolder( mSubfolderNames[idx], false, KMFolderTypeCachedImap);
02371     if (newFolder) {
02372       KMFolderCachedImap *f = dynamic_cast<KMFolderCachedImap*>(newFolder->storage());
02373       kdDebug(5006) << " ####### Locally creating folder " << mSubfolderNames[idx] <<endl;
02374       f->close("cachedimap");
02375       f->setAccount(mAccount);
02376       f->mAnnotationFolderType = "FROMSERVER";
02377       f->setNoContent(mSubfolderMimeTypes[idx] == "inode/directory");
02378       f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest");
02379       f->setImapPath(mSubfolderPaths[idx]);
02380       f->mFolderAttributes = mSubfolderAttributes[idx];
02381       mNewlyCreatedSubfolders.append( QGuardedPtr<KMFolderCachedImap>( f ) );
02382       kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <<endl;
02383       //kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl;
02384       kmkernel->dimapFolderMgr()->contentsChanged();
02385     } else {
02386       kdDebug(5006) << "can't create folder " << mSubfolderNames[idx] <<endl;
02387     }
02388   }
02389 
02390   kmkernel->dimapFolderMgr()->quiet(false);
02391   emit listComplete(this);
02392   if ( !mPersonalNamespacesCheckDone ) {
02393     // we're not done with the namespaces
02394     mSyncState = SYNC_STATE_LIST_NAMESPACES;
02395   }
02396   serverSyncInternal();
02397 }
02398 
02399 //-----------------------------------------------------------------------------
02400 KMFolderCachedImap* KMFolderCachedImap::findParent( const QString& path,
02401                                                     const QString& name )
02402 {
02403   QString parent = path.left( path.length() - name.length() - 2 );
02404   if ( parent.length() > 1 )
02405   {
02406     // extract name of the parent
02407     parent = parent.right( parent.length() - 1 );
02408     if ( parent != label() )
02409     {
02410       KMFolderNode *node = folder()->child()->first();
02411       // look for a better parent
02412       while ( node )
02413       {
02414         if ( node->name() == parent )
02415         {
02416           KMFolder* fld = static_cast<KMFolder*>(node);
02417           KMFolderCachedImap* imapFld =
02418             static_cast<KMFolderCachedImap*>( fld->storage() );
02419           return imapFld;
02420         }
02421         node = folder()->child()->next();
02422       }
02423     }
02424   }
02425   return 0;
02426 }
02427 
02428 void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success)
02429 {
02430   Q_UNUSED(sub);
02431   //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl;
02432   if ( success ) {
02433     serverSyncInternal();
02434   }
02435   else
02436   {
02437     // success == false means the sync was aborted.
02438     if ( mCurrentSubfolder ) {
02439       Q_ASSERT( sub == mCurrentSubfolder );
02440       disconnectSubFolderSignals();
02441     }
02442 
02443     // Next step would be to check quota limits and then to close the folder, but don't bother with
02444     // both and close the folder right here, since we aborted.
02445     mSubfoldersForSync.clear();
02446     mSyncState = SYNC_STATE_INITIAL;
02447     close("cachedimap");
02448     emit syncStateChanged();
02449     emit folderComplete( this, false );
02450   }
02451 }
02452 
02453 void KMFolderCachedImap::slotSubFolderCloseToQuotaChanged()
02454 {
02455   if ( !mQuotaOnly ) {
02456     mSomeSubFolderCloseToQuotaChanged = true;
02457   }
02458 }
02459 
02460 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
02461 {
02462   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02463   if (it == mAccount->jobsEnd()) return;
02464   QBuffer buff((*it).data);
02465   buff.open(IO_WriteOnly | IO_Append);
02466   buff.writeBlock(data.data(), data.size());
02467   buff.close();
02468 }
02469 
02470 FolderJob*
02471 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
02472                                  QString, const AttachmentStrategy* ) const
02473 {
02474   QPtrList<KMMessage> msgList;
02475   msgList.append( msg );
02476   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
02477   job->setParentFolder( this );
02478   return job;
02479 }
02480 
02481 FolderJob*
02482 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
02483                                  FolderJob::JobType jt, KMFolder *folder ) const
02484 {
02485   //FIXME: how to handle sets here?
02486   Q_UNUSED( sets );
02487   CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
02488   job->setParentFolder( this );
02489   return job;
02490 }
02491 
02492 void
02493 KMFolderCachedImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState state )
02494 {
02495   mUserRights = userRights;
02496   mUserRightsState = state;
02497   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02498 }
02499 
02500 void
02501 KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder )
02502 {
02503   if ( folder->storage() == this ) {
02504     disconnect( mAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
02505                 this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
02506     if ( mUserRightsState == KMail::ACLJobs::Ok ) {
02507       setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 );
02508     }
02509     mProgress += 5;
02510     serverSyncInternal();
02511   }
02512 }
02513 
02514 void
02515 KMFolderCachedImap::setReadOnly( bool readOnly )
02516 {
02517   if ( readOnly != mReadOnly ) {
02518     mReadOnly = readOnly;
02519     emit readOnlyChanged( folder() );
02520   }
02521 }
02522 
02523 void
02524 KMFolderCachedImap::slotReceivedACL( KMFolder* folder, KIO::Job* job, const KMail::ACLList& aclList )
02525 {
02526   if ( folder->storage() == this ) {
02527     disconnect( mAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
02528                 this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
02529     mACLListState = job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok;
02530     mACLList = aclList;
02531     serverSyncInternal();
02532   }
02533 }
02534 
02535 void
02536 KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info )
02537 {
02538   setQuotaInfo( info );
02539 }
02540 
02541 void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info )
02542 {
02543     if ( info != mQuotaInfo ) {
02544       const bool wasCloseToQuota = isCloseToQuota();
02545       mQuotaInfo = info;
02546       writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02547       if ( wasCloseToQuota != isCloseToQuota() ) {
02548         emit closeToQuotaChanged();
02549       }
02550       emit folderSizeChanged();
02551     }
02552 }
02553 
02554 void
02555 KMFolderCachedImap::setACLList( const ACLList& arr )
02556 {
02557   mACLList = arr;
02558   mACLListState = KMail::ACLJobs::Ok;
02559 }
02560 
02561 void
02562 KMFolderCachedImap::slotMultiSetACLResult(KIO::Job *job)
02563 {
02564   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02565   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02566   if ( (*it).parent != folder() ) return; // Shouldn't happen
02567 
02568   if ( job->error() )
02569     // Display error but don't abort the sync just for this
02570     // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel
02571     job->showErrorDialog();
02572   else
02573     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02574 
02575   if (mAccount->slave()) mAccount->removeJob(job);
02576   serverSyncInternal();
02577 }
02578 
02579 void
02580 KMFolderCachedImap::slotACLChanged( const QString& userId, int permissions )
02581 {
02582   // The job indicates success in changing the permissions for this user
02583   // -> we note that it's been done.
02584   for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) {
02585     if ( (*it).userId == userId && (*it).permissions == permissions ) {
02586       if ( permissions == -1 ) // deleted
02587         mACLList.erase( it );
02588       else // added/modified
02589         (*it).changed = false;
02590       return;
02591     }
02592   }
02593 }
02594 
02595 // called by KMAcctCachedImap::killAllJobs
02596 void KMFolderCachedImap::resetSyncState()
02597 {
02598   if ( mSyncState == SYNC_STATE_INITIAL ) return;
02599   mSubfoldersForSync.clear();
02600   mNewlyCreatedSubfolders.clear();
02601   mSyncState = SYNC_STATE_INITIAL;
02602   close("cachedimap");
02603   // Don't use newState here, it would revert to mProgress (which is < current value when listing messages)
02604   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02605   QString str = i18n("Aborted");
02606   if (progressItem)
02607      progressItem->setStatus( str );
02608   emit statusMsg( str );
02609   emit syncStateChanged();
02610 }
02611 
02612 void KMFolderCachedImap::slotIncreaseProgress()
02613 {
02614   mProgress += 5;
02615 }
02616 
02617 void KMFolderCachedImap::newState( int progress, const QString& syncStatus )
02618 {
02619   //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl;
02620   KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
02621   if( progressItem )
02622     progressItem->setCompletedItems( progress );
02623   if ( !syncStatus.isEmpty() ) {
02624     QString str;
02625     // For a subfolder, show the label. But for the main folder, it's already shown.
02626     if ( mAccount->imapFolder() == this )
02627       str = syncStatus;
02628     else
02629       str = QString( "%1: %2" ).arg( label() ).arg( syncStatus );
02630     if( progressItem )
02631       progressItem->setStatus( str );
02632     emit statusMsg( str );
02633   }
02634   if( progressItem )
02635     progressItem->updateProgress();
02636 }
02637 
02638 void KMFolderCachedImap::setSubfolderState( imapState state )
02639 {
02640   mSubfolderState = state;
02641   if ( state == imapNoInformation && folder()->child() )
02642   {
02643     // pass through to childs
02644     KMFolderNode* node;
02645     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02646     for ( ; (node = it.current()); )
02647     {
02648       ++it;
02649       if (node->isDir()) continue;
02650       KMFolder *folder = static_cast<KMFolder*>(node);
02651       static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state );
02652     }
02653   }
02654 }
02655 
02656 void KMFolderCachedImap::setImapPath(const QString &path)
02657 {
02658   mImapPath = path;
02659 }
02660 
02661 static bool isFolderTypeKnownToUs( const QString &type )
02662 {
02663   for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
02664     FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
02665     if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) )
02666       return true;
02667   }
02668   return false;
02669 }
02670 
02671 // mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
02672 // It is updated from the folder contents type and whether it's a standard resource folder.
02673 // This happens during the syncing phase and during initFolder for a new folder.
02674 // Don't do it earlier, e.g. from setContentsType:
02675 // on startup, it's too early there to know if this is a standard resource folder.
02676 void KMFolderCachedImap::updateAnnotationFolderType()
02677 {
02678   QString oldType = mAnnotationFolderType;
02679   QString oldSubType;
02680   int dot = oldType.find( '.' );
02681   if ( dot != -1 ) {
02682     oldType.truncate( dot );
02683     oldSubType = mAnnotationFolderType.mid( dot + 1 );
02684   }
02685 
02686   QString newType, newSubType;
02687   // We want to store an annotation on the folder only if using the kolab storage.
02688   if ( kmkernel->iCalIface().storageFormat( folder() ) == KMailICalIfaceImpl::StorageXML ) {
02689     newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType );
02690     if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) )
02691       newSubType = "default";
02692     else if ( oldSubType != "default" )
02693       newSubType = oldSubType; // preserve unknown subtypes, like drafts etc.
02694   }
02695 
02696   // We do not want to overwrite custom folder types (which we treat as mail folders).
02697   // So only overwrite custom folder types if the user changed the folder type himself to something
02698   // other than mail.
02699   const bool changingTypeAllowed = isFolderTypeKnownToUs( oldType ) ||
02700                                    ( mContentsType != ContentsTypeMail );
02701 
02702   //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl;
02703   if ( ( newType != oldType || newSubType != oldSubType ) && changingTypeAllowed ) {
02704     mAnnotationFolderType = newType + ( newSubType.isEmpty() ? QString::null : "."+newSubType );
02705     mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync
02706     kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl;
02707   }
02708   // Ensure that further readConfig()s don't lose mAnnotationFolderType
02709   writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02710 }
02711 
02712 void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor )
02713 {
02714   if ( mIncidencesFor != incfor ) {
02715     mIncidencesFor = incfor;
02716     mIncidencesForChanged = true;
02717   }
02718 }
02719 
02720 void KMFolderCachedImap::setSharedSeenFlags(bool b)
02721 {
02722   if ( mSharedSeenFlags != b ) {
02723     mSharedSeenFlags = b;
02724     mSharedSeenFlagsChanged = true;
02725   }
02726 }
02727 
02728 void KMFolderCachedImap::slotAnnotationResult(const QString& entry, const QString& value, bool found)
02729 {
02730   if ( entry == KOLAB_FOLDERTYPE ) {
02731     // There are four cases.
02732     // 1) no content-type on server -> set it
02733     // 2) different content-type on server, locally changed -> set it (we don't even come here)
02734     // 3) different (known) content-type on server, no local change -> get it
02735     // 4) different unknown content-type on server, probably some older version -> set it
02736     if ( found ) {
02737       QString type = value;
02738       QString subtype;
02739       int dot = value.find( '.' );
02740       if ( dot != -1 ) {
02741         type.truncate( dot );
02742         subtype = value.mid( dot + 1 );
02743       }
02744       bool foundKnownType = false;
02745       for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
02746         FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
02747         if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) {
02748           // Case 3: known content-type on server, get it
02749           //kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: found known type of annotation" << endl;
02750           if ( contentsType != ContentsTypeMail )
02751             kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
02752           mAnnotationFolderType = value;
02753           if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent()
02754                && GlobalSettings::self()->theIMAPResourceEnabled()
02755                && subtype == "default" ) {
02756             // Truncate subtype if this folder can't be a default resource folder for us,
02757             // although it apparently is for someone else.
02758             mAnnotationFolderType = type;
02759             kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: parent folder is " << folder()->parent()->owner()->idString() << " => truncating annotation to " << value << endl;
02760           }
02761           setContentsType( contentsType );
02762           mAnnotationFolderTypeChanged = false; // we changed it, not the user
02763           foundKnownType = true;
02764 
02765           // Users don't read events/contacts/etc. in kmail, so mark them all as read.
02766           // This is done in cachedimapjob when getting new messages, but do it here too,
02767           // for the initial set of messages when we didn't know this was a resource folder yet,
02768           // for old folders, etc.
02769           if ( contentsType != ContentsTypeMail )
02770             markUnreadAsRead();
02771 
02772           break;
02773         }
02774       }
02775       if ( !foundKnownType ) {
02776         //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, leaving it untouched" << endl;
02777 
02778         // Case 4: Server has strange content-type. We must not overwrite it, see https://issues.kolab.org/issue2069.
02779         //         Treat the content-type as mail until we change it ourselves.
02780         mAnnotationFolderTypeChanged = false;
02781         mAnnotationFolderType = value;
02782         setContentsType( ContentsTypeMail );
02783       }
02784 
02785       // Ensure that further readConfig()s don't lose mAnnotationFolderType
02786       writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
02787       // TODO handle subtype (inbox, drafts, sentitems, junkemail)
02788     }
02789     else if ( !mReadOnly ) {
02790       // Case 1: server doesn't have content-type, set it
02791       //kdDebug(5006) << "slotGetAnnotationResult: no annotation found, will need to set it" << endl;
02792       mAnnotationFolderTypeChanged = true;
02793     }
02794   } else if ( entry == KOLAB_INCIDENCESFOR ) {
02795     if ( found ) {
02796       mIncidencesFor = incidencesForFromString( value );
02797       Q_ASSERT( mIncidencesForChanged == false );
02798     }
02799   } else if ( entry == KOLAB_SHAREDSEEN ) {
02800     if ( found ) {
02801       mSharedSeenFlags = value == "true";
02802     }
02803   }
02804 }
02805 
02806 void KMFolderCachedImap::slotGetAnnotationResult( KIO::Job* job )
02807 {
02808   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02809   Q_ASSERT( it != mAccount->jobsEnd() );
02810   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02811   Q_ASSERT( (*it).parent == folder() );
02812   if ( (*it).parent != folder() ) return; // Shouldn't happen
02813 
02814   AnnotationJobs::GetAnnotationJob* annjob = static_cast<AnnotationJobs::GetAnnotationJob *>( job );
02815   if ( annjob->error() ) {
02816     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02817       // that's when the imap server doesn't support annotations
02818       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02819            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02820     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() ) );
02821       mAccount->setHasNoAnnotationSupport();
02822     }
02823     else
02824       kdWarning(5006) << "slotGetAnnotationResult: " << job->errorString() << endl;
02825   }
02826 
02827   if (mAccount->slave()) mAccount->removeJob(job);
02828   mProgress += 2;
02829   serverSyncInternal();
02830 }
02831 
02832 void KMFolderCachedImap::slotMultiUrlGetAnnotationResult( KIO::Job* job )
02833 {
02834   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02835   Q_ASSERT( it != mAccount->jobsEnd() );
02836   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02837   Q_ASSERT( (*it).parent == folder() );
02838   if ( (*it).parent != folder() ) return; // Shouldn't happen
02839 
02840   QValueVector<int> folders;
02841   AnnotationJobs::MultiUrlGetAnnotationJob* annjob
02842     = static_cast<AnnotationJobs::MultiUrlGetAnnotationJob *>( job );
02843   if ( annjob->error() ) {
02844     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02845       // that's when the imap server doesn't support annotations
02846       if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
02847            && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
02848         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() ) );
02849       mAccount->setHasNoAnnotationSupport();
02850     }
02851     else
02852       kdWarning(5006) << "slotGetMultiUrlAnnotationResult: " << job->errorString() << endl;
02853   } else {
02854     // we got the annotation allright, let's filter out the ones with the wrong type
02855     QMap<QString, QString> annotations = annjob->annotations();
02856     QMap<QString, QString>::Iterator it = annotations.begin();
02857     for ( ; it != annotations.end(); ++it ) {
02858       const QString folderPath = it.key();
02859       const QString annotation = it.data();
02860       kdDebug(5006) << k_funcinfo << "Folder: " << folderPath << " has type: " << annotation << endl;
02861       // we're only interested in the main type
02862       QString type(annotation);
02863       int dot = annotation.find( '.' );
02864       if ( dot != -1 ) type.truncate( dot );
02865       type = type.simplifyWhiteSpace();
02866 
02867       const int idx = mSubfolderPaths.findIndex( folderPath );
02868       const bool isNoContent =  mSubfolderMimeTypes[idx] == "inode/directory";
02869       if ( ( isNoContent && type.isEmpty() )
02870         || ( !type.isEmpty() && type != KMailICalIfaceImpl::annotationForContentsType( ContentsTypeMail ) ) ) {
02871         folders.append( idx );
02872         kdDebug(5006) << k_funcinfo << " subscribing to: " << folderPath << endl;
02873       } else {
02874         kdDebug(5006) << k_funcinfo << " automatically unsubscribing from: " << folderPath << endl;
02875         mAccount->changeLocalSubscription( folderPath, false );
02876       }
02877     }
02878   }
02879 
02880   if (mAccount->slave()) mAccount->removeJob(job);
02881   createFoldersNewOnServerAndFinishListing( folders );
02882 }
02883 
02884 void KMFolderCachedImap::slotQuotaResult( KIO::Job* job )
02885 {
02886   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02887   Q_ASSERT( it != mAccount->jobsEnd() );
02888   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02889   Q_ASSERT( (*it).parent == folder() );
02890   if ( (*it).parent != folder() ) return; // Shouldn't happen
02891 
02892   QuotaJobs::GetStorageQuotaJob* quotajob = static_cast<QuotaJobs::GetStorageQuotaJob *>( job );
02893   QuotaInfo empty;
02894   if ( quotajob->error() ) {
02895     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) {
02896       // that's when the imap server doesn't support quota
02897       mAccount->setHasNoQuotaSupport();
02898       setQuotaInfo( empty );
02899     }
02900     else
02901       kdWarning(5006) << "slotGetQuotaResult: " << job->errorString() << endl;
02902   }
02903 
02904   if (mAccount->slave()) mAccount->removeJob(job);
02905   mProgress += 2;
02906   serverSyncInternal();
02907 }
02908 
02909 void
02910 KMFolderCachedImap::slotAnnotationChanged( const QString& entry, const QString& attribute, const QString& value )
02911 {
02912   Q_UNUSED( attribute );
02913   Q_UNUSED( value );
02914   //kdDebug(5006) << k_funcinfo << entry << " " << attribute << " " << value << endl;
02915   if ( entry == KOLAB_FOLDERTYPE )
02916     mAnnotationFolderTypeChanged = false;
02917   else if ( entry == KOLAB_INCIDENCESFOR ) {
02918     mIncidencesForChanged = false;
02919     // The incidences-for changed, we must trigger the freebusy creation.
02920     // HACK: in theory we would need a new enum value for this.
02921     kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
02922   } else if ( entry == KOLAB_SHAREDSEEN ) {
02923     mSharedSeenFlagsChanged = false;
02924   }
02925 }
02926 
02927 void KMFolderCachedImap::slotTestAnnotationResult(KIO::Job *job)
02928 {
02929   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02930   Q_ASSERT( it != mAccount->jobsEnd() );
02931   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02932   Q_ASSERT( (*it).parent == folder() );
02933   if ( (*it).parent != folder() ) return; // Shouldn't happen
02934 
02935   mAccount->setAnnotationCheckPassed( true );
02936   if ( job->error() ) {
02937     kdDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl;
02938     mAccount->setHasNoAnnotationSupport( );
02939   } else {
02940     kdDebug(5006) << "Test Annotation was passed   OK" << endl;
02941   }
02942   if (mAccount->slave()) mAccount->removeJob(job);
02943   serverSyncInternal();
02944 }
02945 
02946 void
02947 KMFolderCachedImap::slotSetAnnotationResult(KIO::Job *job)
02948 {
02949   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
02950   if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
02951   if ( (*it).parent != folder() ) return; // Shouldn't happen
02952 
02953   bool cont = true;
02954   if ( job->error() ) {
02955     // Don't show error if the server doesn't support ANNOTATEMORE and this folder only contains mail
02956     if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION && contentsType() == ContentsTypeMail ) {
02957       if (mAccount->slave()) mAccount->removeJob(job);
02958     } else {
02959       cont = mAccount->handleJobError( job, i18n( "Error while setting annotation: " ) + '\n' );
02960     }
02961   } else {
02962     if (mAccount->slave()) mAccount->removeJob(job);
02963   }
02964   if ( cont )
02965     serverSyncInternal();
02966 }
02967 
02968 void KMFolderCachedImap::slotUpdateLastUid()
02969 {
02970   if( mTentativeHighestUid != 0 ) {
02971 
02972       // Sanity checking:
02973       // By now all new mails should be downloaded, which means
02974       // that iteration over the folder should yield only UIDs
02975       // lower or equal to what we think the highes ist, and the
02976       // highest one as well. If not, our notion of the highest
02977       // uid we've seen thus far is wrong, which is dangerous, so
02978       // don't update the mLastUid, then.
02979       // Not entirely true though, mails might have been moved out
02980       // of the folder already by filters, thus giving us a higher tentative
02981       // uid than we actually observe here.
02982       bool sane = count() == 0;
02983 
02984       for (int i=0;i<count(); i++ ) {
02985           ulong uid = getMsgBase(i)->UID();
02986           if ( uid > mTentativeHighestUid && uid > lastUid() ) {
02987               kdWarning(5006) << "DANGER: Either the server listed a wrong highest uid, "
02988                   "or we parsed it wrong. Send email to adam@kde.org, please, and include this log." << endl;
02989               kdWarning(5006) << "uid: " << uid << " mTentativeHighestUid: " << mTentativeHighestUid << endl;
02990               assert( false );
02991               break;
02992           } else {
02993               sane = true;
02994           }
02995       }
02996       if (sane) {
02997         if ( GlobalSettings::self()->mailLossDebug() ) {
02998           kdDebug(5006) << "Tentative highest UID test was sane, writing out: " << mTentativeHighestUid << endl;
02999         }
03000         setLastUid( mTentativeHighestUid );
03001       }
03002   }
03003   mTentativeHighestUid = 0;
03004 }
03005 
03006 bool KMFolderCachedImap::isMoveable() const
03007 {
03008   return ( hasChildren() == HasNoChildren &&
03009       !folder()->isSystemFolder() ) ? true : false;
03010 }
03011 
03012 void KMFolderCachedImap::slotFolderDeletionOnServerFinished()
03013 {
03014   for ( QStringList::const_iterator it = foldersForDeletionOnServer.constBegin();
03015       it != foldersForDeletionOnServer.constEnd(); ++it ) {
03016     KURL url( mAccount->getUrl() );
03017     url.setPath( *it );
03018     kmkernel->iCalIface().folderDeletedOnServer( url );
03019   }
03020   serverSyncInternal();
03021 }
03022 
03023 int KMFolderCachedImap::createIndexFromContentsRecursive()
03024 {
03025   if ( !folder() || !folder()->child() )
03026     return 0;
03027 
03028   KMFolderNode *node = 0;
03029   for( QPtrListIterator<KMFolderNode> it( *folder()->child() ); (node = it.current()); ++it ) {
03030     if( !node->isDir() ) {
03031       KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
03032       kdDebug() << k_funcinfo << "Re-indexing: " << storage->folder()->label() << endl;
03033       int rv = storage->createIndexFromContentsRecursive();
03034       if ( rv > 0 )
03035         return rv;
03036     }
03037   }
03038 
03039   return createIndexFromContents();
03040 }
03041 
03042 void KMFolderCachedImap::setAlarmsBlocked( bool blocked )
03043 {
03044   mAlarmsBlocked = blocked;
03045 }
03046 
03047 bool KMFolderCachedImap::alarmsBlocked() const
03048 {
03049   return mAlarmsBlocked;
03050 }
03051 
03052 bool KMFolderCachedImap::isCloseToQuota() const
03053 {
03054   bool closeToQuota = false;
03055   if ( mQuotaInfo.isValid() && mQuotaInfo.max().toInt() > 0 ) {
03056     const int ratio = mQuotaInfo.current().toInt() * 100  / mQuotaInfo.max().toInt();
03057     //kdDebug(5006) << "Quota ratio: " << ratio << "% " << mQuotaInfo.toString() << endl;
03058     closeToQuota = ( ratio > 0 && ratio >= GlobalSettings::closeToQuotaThreshold() );
03059   }
03060   //kdDebug(5006) << "Folder: " << folder()->prettyURL() << " is over quota: " << closeToQuota << endl;
03061   return closeToQuota;
03062 }
03063 
03064 KMCommand* KMFolderCachedImap::rescueUnsyncedMessages()
03065 {
03066   QValueList<unsigned long> newMsgs = findNewMessages();
03067   kdDebug() << k_funcinfo << newMsgs << " of " << count() << endl;
03068   if ( newMsgs.isEmpty() )
03069     return 0;
03070   KMFolder *dest = 0;
03071   bool manualMove = true;
03072   while ( GlobalSettings::autoLostFoundMove() ) {
03073     // find the inbox of this account
03074     KMFolder *inboxFolder = kmkernel->findFolderById( QString(".%1.directory/INBOX").arg( account()->id() ) );
03075     if ( !inboxFolder ) {
03076       kdWarning(5006) << k_funcinfo << "inbox not found!" << endl;
03077       break;
03078     }
03079     KMFolderDir *inboxDir = inboxFolder->child();
03080     if ( !inboxDir && !inboxFolder->storage() )
03081       break;
03082     assert( inboxFolder->storage()->folderType() == KMFolderTypeCachedImap );
03083 
03084     // create lost+found folder if needed
03085     KMFolderNode *node;
03086     KMFolder *lfFolder = 0;
03087     if ( !(node = inboxDir->hasNamedFolder( i18n("lost+found") )) ) {
03088       kdDebug(5006) << k_funcinfo << "creating lost+found folder" << endl;
03089       KMFolder* folder = kmkernel->dimapFolderMgr()->createFolder(
03090           i18n("lost+found"), false, KMFolderTypeCachedImap, inboxDir );
03091       if ( !folder || !folder->storage() )
03092         break;
03093       static_cast<KMFolderCachedImap*>( folder->storage() )->initializeFrom(
03094         static_cast<KMFolderCachedImap*>( inboxFolder->storage() ) );
03095       folder->storage()->setContentsType( KMail::ContentsTypeMail );
03096       folder->storage()->writeConfig();
03097       lfFolder = folder;
03098     } else {
03099       kdDebug(5006) << k_funcinfo << "found lost+found folder" << endl;
03100       lfFolder = dynamic_cast<KMFolder*>( node );
03101     }
03102     if ( !lfFolder || !lfFolder->createChildFolder() || !lfFolder->storage() )
03103       break;
03104 
03105     // create subfolder for this incident
03106     QDate today = QDate::currentDate();
03107     QString baseName = folder()->label() + "-" + QString::number( today.year() )
03108         + (today.month() < 10 ? "0" : "" ) + QString::number( today.month() )
03109         + (today.day() < 10 ? "0" : "" ) + QString::number( today.day() );
03110     QString name = baseName;
03111     int suffix = 0;
03112     while ( (node = lfFolder->child()->hasNamedFolder( name )) ) {
03113       ++suffix;
03114       name = baseName + '-' + QString::number( suffix );
03115     }
03116     kdDebug(5006) << k_funcinfo << "creating lost+found folder " << name << endl;
03117     dest = kmkernel->dimapFolderMgr()->createFolder( name, false, KMFolderTypeCachedImap, lfFolder->child() );
03118     if ( !dest || !dest->storage() )
03119         break;
03120     static_cast<KMFolderCachedImap*>( dest->storage() )->initializeFrom(
03121       static_cast<KMFolderCachedImap*>( lfFolder->storage() ) );
03122     dest->storage()->setContentsType( contentsType() );
03123     dest->storage()->writeConfig();
03124 
03125     KMessageBox::sorry( 0, i18n("<p>There are new messages in folder <b>%1</b>, which "
03126           "have not been uploaded to the server yet, but the folder has been deleted "
03127           "on the server or you do not "
03128           "have sufficient access rights on the folder to upload them.</p>"
03129           "<p>All affected messages will therefore be moved to <b>%2</b> "
03130           "to avoid data loss.</p>").arg( folder()->prettyURL() ).arg( dest->prettyURL() ),
03131           i18n("Insufficient access rights") );
03132     manualMove = false;
03133     break;
03134   }
03135 
03136   if ( manualMove ) {
03137     const QString msg ( i18n( "<p>There are new messages in this folder (%1), which "
03138           "have not been uploaded to the server yet, but the folder has been deleted "
03139           "on the server or you do not "
03140           "have sufficient access rights on the folder now to upload them. "
03141           "Please contact your administrator to allow upload of new messages "
03142           "to you, or move them out of this folder.</p> "
03143           "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyURL() ) );
03144     if ( KMessageBox::warningYesNo( 0, msg, QString::null, i18n("Move"), i18n("Do Not Move") ) == KMessageBox::Yes ) {
03145       KMail::KMFolderSelDlg dlg( kmkernel->getKMMainWidget(),
03146           i18n("Move Messages to Folder"), true );
03147       if ( dlg.exec() ) {
03148         dest = dlg.folder();
03149       }
03150     }
03151   }
03152   if ( dest ) {
03153     QPtrList<KMMsgBase> msgs;
03154     for( int i = 0; i < count(); ++i ) {
03155       KMMsgBase *msg = getMsgBase( i );
03156       if( !msg ) continue; /* what goes on if getMsg() returns 0? */
03157       if ( msg->UID() == 0 )
03158         msgs.append( msg );
03159     }
03160     KMCommand *command = new KMMoveCommand( dest, msgs );
03161     command->start();
03162     return command;
03163   }
03164   return 0;
03165 }
03166 
03167 void KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root )
03168 {
03169   kdDebug() << k_funcinfo << folder << " " << root << endl;
03170   if ( root )
03171     mToBeDeletedAfterRescue.append( folder );
03172   folder->open("cachedimap");
03173   KMFolderCachedImap* storage = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
03174   if ( storage ) {
03175     KMCommand *command = storage->rescueUnsyncedMessages();
03176     if ( command ) {
03177       connect( command, SIGNAL(completed(KMCommand*)),
03178                SLOT(slotRescueDone(KMCommand*)) );
03179       ++mRescueCommandCount;
03180     } else {
03181       // nothing to rescue, close folder
03182       // (we don't need to close it in the other case, it will be deleted anyway)
03183       folder->close("cachedimap");
03184     }
03185   }
03186   if ( folder->child() ) {
03187     KMFolderNode *node = folder->child()->first();
03188     while (node) {
03189       if (!node->isDir() ) {
03190         KMFolder *subFolder = static_cast<KMFolder*>( node );
03191         rescueUnsyncedMessagesAndDeleteFolder( subFolder, false );
03192       }
03193       node = folder->child()->next();
03194     }
03195   }
03196 }
03197 
03198 void KMFolderCachedImap::slotRescueDone(KMCommand * command)
03199 {
03200   // FIXME: check command result
03201   if ( command )
03202     --mRescueCommandCount;
03203   if ( mRescueCommandCount > 0 )
03204     return;
03205   for ( QValueList<KMFolder*>::ConstIterator it = mToBeDeletedAfterRescue.constBegin();
03206         it != mToBeDeletedAfterRescue.constEnd(); ++it ) {
03207     kmkernel->dimapFolderMgr()->remove( *it );
03208   }
03209   mToBeDeletedAfterRescue.clear();
03210   serverSyncInternal();
03211 }
03212 
03213 void KMFolderCachedImap::slotRenameFolderFinished()
03214 {
03215   // The syncing code assumes the folder was opened by us, and later closes it. So better
03216   // make sure the reference count is correct, since the folder was force-closed by the rename.
03217   // Otherwise bad things can happen, see https://issues.kolab.org/issue3853.
03218   open( "cachedimap" );
03219   serverSyncInternal();
03220 }
03221 
03222 bool KMFolderCachedImap::canDeleteMessages() const
03223 {
03224   if ( isReadOnly() )
03225     return false;
03226   if ( mUserRightsState == KMail::ACLJobs::Ok && !(userRights() & ACLJobs::Delete) )
03227     return false;
03228   return true;
03229 }
03230 
03231 bool KMFolderCachedImap::mailCheckInProgress() const
03232 {
03233   return mSyncState != SYNC_STATE_INITIAL;
03234 }
03235 
03236 #include "kmfoldercachedimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys