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