kmail

kmfoldercachedimap.cpp

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