kmail

kmacctcachedimap.cpp

00001 
00032 #ifdef HAVE_CONFIG_H
00033 #include <config.h>
00034 #endif
00035 
00036 #include "kmacctcachedimap.h"
00037 using KMail::SieveConfig;
00038 
00039 #include "kmfoldertree.h"
00040 #include "kmfoldermgr.h"
00041 #include "kmfiltermgr.h"
00042 #include "kmfoldercachedimap.h"
00043 #include "kmmainwin.h"
00044 #include "accountmanager.h"
00045 using KMail::AccountManager;
00046 #include "progressmanager.h"
00047 
00048 #include <kio/passdlg.h>
00049 #include <kio/scheduler.h>
00050 #include <kio/slave.h>
00051 #include <kmessagebox.h>
00052 #include <kdebug.h>
00053 #include <kstandarddirs.h>
00054 #include <kapplication.h>
00055 #include <kconfig.h>
00056 
00057 #include <qstylesheet.h>
00058 
00059 KMAcctCachedImap::KMAcctCachedImap( AccountManager* aOwner,
00060                     const QString& aAccountName, uint id )
00061   : KMail::ImapAccountBase( aOwner, aAccountName, id ), mFolder( 0 ),
00062     mAnnotationCheckPassed(false)
00063 {
00064   // Never EVER set this for the cached IMAP account
00065   mAutoExpunge = false;
00066 }
00067 
00068 
00069 //-----------------------------------------------------------------------------
00070 KMAcctCachedImap::~KMAcctCachedImap()
00071 {
00072   killAllJobsInternal( true );
00073 }
00074 
00075 
00076 //-----------------------------------------------------------------------------
00077 QString KMAcctCachedImap::type() const
00078 {
00079   return "cachedimap";
00080 }
00081 
00082 void KMAcctCachedImap::init() {
00083   ImapAccountBase::init();
00084 }
00085 
00086 //-----------------------------------------------------------------------------
00087 void KMAcctCachedImap::pseudoAssign( const KMAccount * a ) {
00088   killAllJobs( true );
00089   if (mFolder)
00090   {
00091     mFolder->setContentState(KMFolderCachedImap::imapNoInformation);
00092     mFolder->setSubfolderState(KMFolderCachedImap::imapNoInformation);
00093   }
00094   ImapAccountBase::pseudoAssign( a );
00095 }
00096 
00097 //-----------------------------------------------------------------------------
00098 void KMAcctCachedImap::setImapFolder(KMFolderCachedImap *aFolder)
00099 {
00100   mFolder = aFolder;
00101   mFolder->setImapPath( "/" );
00102   mFolder->setAccount( this );
00103 }
00104 
00105 
00106 //-----------------------------------------------------------------------------
00107 void KMAcctCachedImap::setAutoExpunge( bool /*aAutoExpunge*/ )
00108 {
00109   // Never EVER set this for the cached IMAP account
00110   mAutoExpunge = false;
00111 }
00112 
00113 //-----------------------------------------------------------------------------
00114 void KMAcctCachedImap::killAllJobs( bool disconnectSlave )
00115 {
00116   //kdDebug(5006) << "killAllJobs: disconnectSlave=" << disconnectSlave << "  " << mapJobData.count() << " jobs in map." << endl;
00117   QValueList<KMFolderCachedImap*> folderList = killAllJobsInternal( disconnectSlave );
00118   for( QValueList<KMFolderCachedImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) {
00119     KMFolderCachedImap *fld = *it;
00120     fld->resetSyncState();
00121     fld->setContentState(KMFolderCachedImap::imapNoInformation);
00122     fld->setSubfolderState(KMFolderCachedImap::imapNoInformation);
00123     fld->sendFolderComplete(FALSE);
00124   }
00125 }
00126 
00127 //-----------------------------------------------------------------------------
00128 // Common between killAllJobs and the destructor - which shouldn't call sendFolderComplete
00129 QValueList<KMFolderCachedImap*> KMAcctCachedImap::killAllJobsInternal( bool disconnectSlave )
00130 {
00131   // Make list of folders to reset. This must be done last, since folderComplete
00132   // can trigger the next queued mail check already.
00133   QValueList<KMFolderCachedImap*> folderList;
00134   QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00135   for (; it != mapJobData.end(); ++it) {
00136     if ((*it).parent)
00137       folderList << static_cast<KMFolderCachedImap*>((*it).parent->storage());
00138     // Kill the job - except if it's the one that already died and is calling us
00139     if ( !it.key()->error() && mSlave ) {
00140       it.key()->kill();
00141       mSlave = 0; // killing a job, kills the slave
00142     }
00143   }
00144   mapJobData.clear();
00145 
00146   // Clear the joblist. Make SURE to stop the job emitting "finished"
00147   for( QPtrListIterator<CachedImapJob> it( mJobList ); it.current(); ++it )
00148     it.current()->setPassiveDestructor( true );
00149   KMAccount::deleteFolderJobs();
00150 
00151   if ( disconnectSlave && mSlave ) {
00152     KIO::Scheduler::disconnectSlave( mSlave );
00153     mSlave = 0;
00154   }
00155   return folderList;
00156 }
00157 
00158 //-----------------------------------------------------------------------------
00159 void KMAcctCachedImap::cancelMailCheck()
00160 {
00161   // Make list of folders to reset, like in killAllJobs
00162   QValueList<KMFolderCachedImap*> folderList;
00163   QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00164   for (; it != mapJobData.end(); ++it) {
00165     if ( (*it).cancellable && (*it).parent )
00166       folderList << static_cast<KMFolderCachedImap*>((*it).parent->storage());
00167   }
00168   // Kill jobs
00169   ImapAccountBase::cancelMailCheck();
00170   // Reset sync states and emit folderComplete, this is important for
00171   // KMAccount::checkingMail() to be reset, in case we restart checking mail later.
00172   for( QValueList<KMFolderCachedImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) {
00173     KMFolderCachedImap *fld = *it;
00174     fld->resetSyncState();
00175     fld->setContentState(KMFolderCachedImap::imapNoInformation);
00176     fld->setSubfolderState(KMFolderCachedImap::imapNoInformation);
00177     fld->sendFolderComplete(FALSE);
00178   }
00179 }
00180 
00181 //-----------------------------------------------------------------------------
00182 void KMAcctCachedImap::killJobsForItem(KMFolderTreeItem * fti)
00183 {
00184   QMap<KIO::Job *, jobData>::Iterator it = mapJobData.begin();
00185   while (it != mapJobData.end())
00186   {
00187     if (it.data().parent == fti->folder())
00188     {
00189       killAllJobs();
00190       break;
00191     }
00192     else ++it;
00193   }
00194 }
00195 
00196 // Reimplemented from ImapAccountBase because we only check one folder at a time
00197 void KMAcctCachedImap::slotCheckQueuedFolders()
00198 {
00199     mMailCheckFolders.clear();
00200     mMailCheckFolders.append( mFoldersQueuedForChecking.front() );
00201     mFoldersQueuedForChecking.pop_front();
00202     if ( mFoldersQueuedForChecking.isEmpty() )
00203       disconnect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
00204                   this, SLOT( slotCheckQueuedFolders() ) );
00205 
00206     kmkernel->acctMgr()->singleCheckMail(this, true);
00207     mMailCheckFolders.clear();
00208 }
00209 
00210 void KMAcctCachedImap::processNewMail( bool /*interactive*/ )
00211 {
00212   assert( mFolder );  // George says "better to crash then lose mail"
00213 
00214   if ( mMailCheckFolders.isEmpty() )
00215     processNewMail( mFolder, true );
00216   else {
00217     KMFolder* f = mMailCheckFolders.front();
00218     mMailCheckFolders.pop_front();
00219     processNewMail( static_cast<KMFolderCachedImap *>( f->storage() ), false );
00220   }
00221 }
00222 
00223 void KMAcctCachedImap::processNewMail( KMFolderCachedImap* folder,
00224                                        bool recurse )
00225 {
00226   assert( folder ); // George says "better to crash then lose mail"
00227 
00228   // This should never be set for a cached IMAP account
00229   mAutoExpunge = false;
00230   mCountLastUnread = 0;
00231   mUnreadBeforeCheck.clear();
00232   // stop sending noops during sync, that will keep the connection open
00233   mNoopTimer.stop();
00234 
00235   // reset namespace todo
00236   if ( folder == mFolder ) {
00237     QStringList nsToList = namespaces()[PersonalNS];
00238     QStringList otherNSToCheck = namespaces()[OtherUsersNS];
00239     otherNSToCheck += namespaces()[SharedNS];
00240     for ( QStringList::Iterator it = otherNSToCheck.begin();
00241           it != otherNSToCheck.end(); ++it ) {
00242       if ( (*it).isEmpty() ) {
00243         // empty namespaces are included in the "normal" listing
00244         // as the folders are created under the root folder
00245         nsToList += *it;
00246       }
00247     }
00248     folder->setNamespacesToList( nsToList );
00249   }
00250 
00251   Q_ASSERT( !mMailCheckProgressItem );
00252   mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem(
00253     "MailCheck" + QString::number( id() ),
00254     QStyleSheet::escape( folder->label() ), // will be changed immediately in serverSync anyway
00255     QString::null,
00256     true, // can be cancelled
00257     useSSL() || useTLS() );
00258   connect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
00259            this, SLOT( slotProgressItemCanceled( KPIM::ProgressItem* ) ) );
00260 
00261   folder->setAccount(this);
00262   connect(folder, SIGNAL(folderComplete(KMFolderCachedImap*, bool)),
00263           this, SLOT(postProcessNewMail(KMFolderCachedImap*, bool)));
00264   folder->serverSync( recurse );
00265 }
00266 
00267 void KMAcctCachedImap::postProcessNewMail( KMFolderCachedImap* folder, bool )
00268 {
00269   mNoopTimer.start( 60000 ); // send a noop every minute to avoid "connection broken" errors
00270   disconnect(folder, SIGNAL(folderComplete(KMFolderCachedImap*, bool)),
00271              this, SLOT(postProcessNewMail(KMFolderCachedImap*, bool)));
00272   mMailCheckProgressItem->setComplete();
00273   mMailCheckProgressItem = 0;
00274 
00275   if ( folder == mFolder ) {
00276     // We remove everything from the deleted folders list after a full sync.
00277     // Even if it fails (no permission), because on the next sync we want the folder to reappear,
00278     //  instead of the user being stuck with "can't delete" every time.
00279     // And we do it for _all_ deleted folders, even those that were deleted on the server in the first place (slotListResult).
00280     //  Otherwise this might have side effects much later (e.g. when regaining permissions to a folder we could see before)
00281 
00282 #if 0 // this opens a race: delete a folder during a sync (after the sync checked that folder), and it'll be forgotten...
00283     mDeletedFolders.clear();
00284 #endif
00285     mPreviouslyDeletedFolders.clear();
00286   }
00287 
00288   KMail::ImapAccountBase::postProcessNewMail();
00289 }
00290 
00291 void KMAcctCachedImap::addUnreadMsgCount( const KMFolderCachedImap *folder,
00292                                           int countUnread )
00293 {
00294   if ( folder->imapPath() != "/INBOX/" ) {
00295     // new mail in INBOX is processed with KMAccount::processNewMsg() and
00296     // therefore doesn't need to be counted here
00297     const QString folderId = folder->folder()->idString();
00298     int newInFolder = countUnread;
00299     if ( mUnreadBeforeCheck.find( folderId ) != mUnreadBeforeCheck.end() )
00300       newInFolder -= mUnreadBeforeCheck[folderId];
00301     if ( newInFolder > 0 )
00302       addToNewInFolder( folderId, newInFolder );
00303   }
00304   mCountUnread += countUnread;
00305 }
00306 
00307 void KMAcctCachedImap::addLastUnreadMsgCount( const KMFolderCachedImap *folder,
00308                                               int countLastUnread )
00309 {
00310   mUnreadBeforeCheck[folder->folder()->idString()] = countLastUnread;
00311   mCountLastUnread += countLastUnread;
00312 }
00313 
00314 //
00315 //
00316 // read/write config
00317 //
00318 //
00319 
00320 void KMAcctCachedImap::readConfig( /*const*/ KConfig/*Base*/ & config ) {
00321   ImapAccountBase::readConfig( config );
00322   // Apparently this method is only ever called once (from KMKernel::init) so this is ok
00323   mPreviouslyDeletedFolders = config.readListEntry( "deleted-folders" );
00324   mDeletedFolders.clear(); // but just in case...
00325   const QStringList oldPaths = config.readListEntry( "renamed-folders-paths" );
00326   const QStringList newNames = config.readListEntry( "renamed-folders-names" );
00327   QStringList::const_iterator it = oldPaths.begin();
00328   QStringList::const_iterator nameit = newNames.begin();
00329   for( ; it != oldPaths.end() && nameit != newNames.end(); ++it, ++nameit ) {
00330     addRenamedFolder( *it, QString::null, *nameit );
00331   }
00332 }
00333 
00334 void KMAcctCachedImap::writeConfig( KConfig/*Base*/ & config ) /*const*/ {
00335   ImapAccountBase::writeConfig( config );
00336   config.writeEntry( "deleted-folders", mDeletedFolders + mPreviouslyDeletedFolders );
00337   config.writeEntry( "renamed-folders-paths", mRenamedFolders.keys() );
00338   const QValueList<RenamedFolder> values = mRenamedFolders.values();
00339   QStringList lstNames;
00340   QValueList<RenamedFolder>::const_iterator it = values.begin();
00341   for ( ; it != values.end() ; ++it )
00342     lstNames.append( (*it).mNewName );
00343   config.writeEntry( "renamed-folders-names", lstNames );
00344 }
00345 
00346 void KMAcctCachedImap::invalidateIMAPFolders()
00347 {
00348   invalidateIMAPFolders( mFolder );
00349 }
00350 
00351 void KMAcctCachedImap::invalidateIMAPFolders( KMFolderCachedImap* folder )
00352 {
00353   if( !folder || !folder->folder() )
00354     return;
00355 
00356   folder->setAccount(this);
00357 
00358   QStringList strList;
00359   QValueList<QGuardedPtr<KMFolder> > folderList;
00360   kmkernel->dimapFolderMgr()->createFolderList( &strList, &folderList,
00361                         folder->folder()->child(), QString::null,
00362                         false );
00363   QValueList<QGuardedPtr<KMFolder> >::Iterator it;
00364   mCountLastUnread = 0;
00365   mUnreadBeforeCheck.clear();
00366 
00367   for( it = folderList.begin(); it != folderList.end(); ++it ) {
00368     KMFolder *f = *it;
00369     if( f && f->folderType() == KMFolderTypeCachedImap ) {
00370       KMFolderCachedImap *cfolder = static_cast<KMFolderCachedImap*>(f->storage());
00371       // This invalidates the folder completely
00372       cfolder->setUidValidity("INVALID");
00373       cfolder->writeUidCache();
00374       processNewMailSingleFolder( f );
00375     }
00376   }
00377   folder->setUidValidity("INVALID");
00378   folder->writeUidCache();
00379 
00380   processNewMailSingleFolder( folder->folder() );
00381 }
00382 
00383 //-----------------------------------------------------------------------------
00384 void KMAcctCachedImap::addDeletedFolder( KMFolder* folder )
00385 {
00386   if ( !folder || folder->folderType() != KMFolderTypeCachedImap )
00387     return;
00388   KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(folder->storage());
00389   addDeletedFolder( storage->imapPath() );
00390   kdDebug(5006) << k_funcinfo << storage->imapPath() << endl;
00391 
00392   // Add all child folders too
00393   if( folder->child() ) {
00394     KMFolderNode *node = folder->child()->first();
00395     while( node ) {
00396       if( !node->isDir() ) {
00397         addDeletedFolder( static_cast<KMFolder*>( node ) ); // recurse
00398       }
00399       node = folder->child()->next();
00400     }
00401   }
00402 }
00403 
00404 void KMAcctCachedImap::addDeletedFolder( const QString& imapPath )
00405 {
00406   mDeletedFolders << imapPath;
00407 }
00408 
00409 QStringList KMAcctCachedImap::deletedFolderPaths( const QString& subFolderPath ) const
00410 {
00411   QStringList lst;
00412   for ( QStringList::const_iterator it = mDeletedFolders.begin(); it != mDeletedFolders.end(); ++it ) {
00413     if ( (*it).startsWith( subFolderPath ) )
00414       // We must reverse the order, so that sub sub sub folders are deleted first
00415       lst.prepend( *it );
00416   }
00417   for ( QStringList::const_iterator it = mPreviouslyDeletedFolders.begin(); it != mPreviouslyDeletedFolders.end(); ++it ) {
00418     if ( (*it).startsWith( subFolderPath ) )
00419       lst.prepend( *it );
00420   }
00421   kdDebug(5006) << "KMAcctCachedImap::deletedFolderPaths for " << subFolderPath << " returning: " << lst << endl;
00422   Q_ASSERT( !lst.isEmpty() );
00423   return lst;
00424 }
00425 
00426 bool KMAcctCachedImap::isDeletedFolder( const QString& subFolderPath ) const
00427 {
00428   return mDeletedFolders.find( subFolderPath ) != mDeletedFolders.end();
00429 }
00430 
00431 bool KMAcctCachedImap::isPreviouslyDeletedFolder( const QString& subFolderPath ) const
00432 {
00433   return mPreviouslyDeletedFolders.find( subFolderPath ) != mPreviouslyDeletedFolders.end();
00434 }
00435 
00436 void KMAcctCachedImap::removeDeletedFolder( const QString& subFolderPath )
00437 {
00438   mDeletedFolders.remove( subFolderPath );
00439   mPreviouslyDeletedFolders.remove( subFolderPath );
00440 }
00441 
00442 void KMAcctCachedImap::addRenamedFolder( const QString& subFolderPath, const QString& oldLabel, const QString& newName )
00443 {
00444   mRenamedFolders.insert( subFolderPath, RenamedFolder( oldLabel, newName ) );
00445 }
00446 
00447 void KMAcctCachedImap::removeRenamedFolder( const QString& subFolderPath )
00448 {
00449   mRenamedFolders.remove( subFolderPath );
00450 }
00451 
00452 void KMAcctCachedImap::slotProgressItemCanceled( ProgressItem* )
00453 {
00454   bool abortConnection = !mSlaveConnected;
00455   killAllJobs( abortConnection );
00456   if ( abortConnection ) {
00457     // If we were trying to connect, tell kmfoldercachedimap so that it moves on
00458     emit connectionResult( KIO::ERR_USER_CANCELED, QString::null );
00459   }
00460 }
00461 
00462 FolderStorage* const KMAcctCachedImap::rootFolder() const
00463 {
00464   return mFolder;
00465 }
00466 
00467 
00468 QString KMAcctCachedImap::renamedFolder( const QString& imapPath ) const
00469 {
00470   QMap<QString, RenamedFolder>::ConstIterator renit = mRenamedFolders.find( imapPath );
00471   if ( renit != mRenamedFolders.end() )
00472     return (*renit).mNewName;
00473   return QString::null;
00474 }
00475 
00476 #include "kmacctcachedimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys