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