kmail

kmfoldercachedimap.cpp

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