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