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