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