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