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