kmail

kmfoldercachedimap.cpp

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