kmail

kmfoldercachedimap.cpp

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