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