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