kmail

kmacctimap.cpp

00001 
00022 #ifdef HAVE_CONFIG_H
00023 #include <config.h>
00024 #endif
00025 
00026 #include "kmacctimap.h"
00027 using KMail::SieveConfig;
00028 
00029 #include "kmmessage.h"
00030 #include "broadcaststatus.h"
00031 using KPIM::BroadcastStatus;
00032 #include "kmfoldertree.h"
00033 #include "kmfoldermgr.h"
00034 #include "kmfolderimap.h"
00035 #include "kmmainwin.h"
00036 #include "kmmsgdict.h"
00037 #include "kmfilter.h"
00038 #include "kmfiltermgr.h"
00039 #include "folderstorage.h"
00040 #include "imapjob.h"
00041 #include "actionscheduler.h"
00042 using KMail::ActionScheduler;
00043 using KMail::ImapJob;
00044 using KMail::ImapAccountBase;
00045 #include "progressmanager.h"
00046 using KPIM::ProgressItem;
00047 using KPIM::ProgressManager;
00048 #include <kio/scheduler.h>
00049 #include <kio/slave.h>
00050 #include <kmessagebox.h>
00051 #include <kdebug.h>
00052 
00053 #include <qstylesheet.h>
00054 
00055 #include <errno.h>
00056 
00057 //-----------------------------------------------------------------------------
00058 KMAcctImap::KMAcctImap(AccountManager* aOwner, const QString& aAccountName, uint id):
00059   KMail::ImapAccountBase(aOwner, aAccountName, id),
00060   mCountRemainChecks( 0 ),
00061   mErrorTimer( 0, "mErrorTimer" )
00062 {
00063   mFolder = 0;
00064   mScheduler = 0;
00065   mNoopTimer.start( 60000 ); // // send a noop every minute
00066   mOpenFolders.setAutoDelete(true);
00067   connect(kmkernel->imapFolderMgr(), SIGNAL(changed()),
00068       this, SLOT(slotUpdateFolderList()));
00069   connect(&mErrorTimer, SIGNAL(timeout()), SLOT(slotResetConnectionError()));
00070 
00071   QString serNumUri = locateLocal( "data", "kmail/unfiltered." +
00072                    QString("%1").arg(KAccount::id()) );
00073   KConfig config( serNumUri );
00074   QStringList serNums = config.readListEntry( "unfiltered" );
00075   mFilterSerNumsToSave.setAutoDelete( false );
00076 
00077   for ( QStringList::ConstIterator it = serNums.begin();
00078     it != serNums.end(); ++it ) {
00079       mFilterSerNums.append( (*it).toUInt() );
00080       mFilterSerNumsToSave.insert( *it, (const int *)1 );
00081     }
00082 }
00083 
00084 
00085 //-----------------------------------------------------------------------------
00086 KMAcctImap::~KMAcctImap()
00087 {
00088   killAllJobs( true );
00089 
00090   QString serNumUri = locateLocal( "data", "kmail/unfiltered." +
00091                    QString("%1").arg(KAccount::id()) );
00092   KConfig config( serNumUri );
00093   QStringList serNums;
00094   QDictIterator<int> it( mFilterSerNumsToSave );
00095   for( ; it.current(); ++it )
00096       serNums.append( it.currentKey() );
00097   config.writeEntry( "unfiltered", serNums );
00098 }
00099 
00100 
00101 //-----------------------------------------------------------------------------
00102 QString KMAcctImap::type() const
00103 {
00104   return "imap";
00105 }
00106 
00107 //-----------------------------------------------------------------------------
00108 void KMAcctImap::pseudoAssign( const KMAccount * a ) {
00109   killAllJobs( true );
00110   if (mFolder)
00111   {
00112     mFolder->setContentState(KMFolderImap::imapNoInformation);
00113     mFolder->setSubfolderState(KMFolderImap::imapNoInformation);
00114   }
00115   ImapAccountBase::pseudoAssign( a );
00116 }
00117 
00118 //-----------------------------------------------------------------------------
00119 void KMAcctImap::setImapFolder(KMFolderImap *aFolder)
00120 {
00121   mFolder = aFolder;
00122   mFolder->setImapPath( "/" );
00123 }
00124 
00125 
00126 //-----------------------------------------------------------------------------
00127 
00128 bool KMAcctImap::handleError( int errorCode, const QString &errorMsg, KIO::Job* job, const QString& context, bool abortSync )
00129 {
00130   /* TODO check where to handle this one better. */
00131   if ( errorCode == KIO::ERR_DOES_NOT_EXIST ) {
00132     // folder is gone, so reload the folderlist
00133     if ( mFolder )
00134       mFolder->listDirectory();
00135     return true;
00136   }
00137   return ImapAccountBase::handleError( errorCode, errorMsg, job, context, abortSync );
00138 }
00139 
00140 
00141 //-----------------------------------------------------------------------------
00142 void KMAcctImap::killAllJobs( bool disconnectSlave )
00143 {
00144   QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00145   for ( ; it != mapJobData.end(); ++it)
00146   {
00147     QPtrList<KMMessage> msgList = (*it).msgList;
00148     QPtrList<KMMessage>::Iterator it2 = msgList.begin();
00149     for ( ; it2 != msgList.end(); ++it2 ) {
00150        KMMessage *msg = *it2;
00151        if ( msg->transferInProgress() ) {
00152           kdDebug(5006) << "KMAcctImap::killAllJobs - resetting mail" << endl;
00153           msg->setTransferInProgress( false );
00154        }
00155     }
00156     if ((*it).parent)
00157     {
00158       // clear folder state
00159       KMFolderImap *fld = static_cast<KMFolderImap*>((*it).parent->storage());
00160       fld->setCheckingValidity(false);
00161       fld->quiet(false);
00162       fld->setContentState(KMFolderImap::imapNoInformation);
00163       fld->setSubfolderState(KMFolderImap::imapNoInformation);
00164       fld->sendFolderComplete(FALSE);
00165       fld->removeJobs();
00166     }
00167     if ( (*it).progressItem )
00168     {
00169       (*it).progressItem->setComplete();
00170     }
00171   }
00172   if (mSlave && mapJobData.begin() != mapJobData.end())
00173   {
00174     mSlave->kill();
00175     mSlave = 0;
00176   }
00177   // remove the jobs
00178   mapJobData.clear();
00179   // KMAccount::deleteFolderJobs(); doesn't work here always, it deletes jobs from
00180   // its own mJobList instead of our mJobList...
00181   KMAccount::deleteFolderJobs();
00182   QPtrListIterator<ImapJob> it2( mJobList );
00183   while ( it2.current() ) {
00184     ImapJob *job = it2.current();
00185     ++it2;
00186     job->kill();
00187   }
00188   mJobList.clear();
00189   // make sure that no new-mail-check is blocked
00190   if (mCountRemainChecks > 0)
00191   {
00192     checkDone( false, CheckOK ); // returned 0 new messages
00193     mCountRemainChecks = 0;
00194   }
00195   if ( disconnectSlave && slave() ) {
00196     KIO::Scheduler::disconnectSlave( slave() );
00197     mSlave = 0;
00198   }
00199 }
00200 
00201 //-----------------------------------------------------------------------------
00202 void KMAcctImap::ignoreJobsForMessage( KMMessage* msg )
00203 {
00204   if (!msg) return;
00205   QPtrListIterator<ImapJob> it( mJobList );
00206   while ( it.current() )
00207   {
00208     ImapJob *job = it.current();
00209     ++it;
00210     if ( job->msgList().first() == msg )
00211     {
00212       job->kill();
00213     }
00214   }
00215 }
00216 
00217 //-----------------------------------------------------------------------------
00218 void KMAcctImap::ignoreJobsForFolder( KMFolder* folder )
00219 {
00220   QPtrListIterator<ImapJob> it( mJobList );
00221   while ( it.current() )
00222   {
00223     ImapJob *job = it.current();
00224     ++it;
00225     if ( !job->msgList().isEmpty() && job->msgList().first()->parent() == folder )
00226     {
00227       job->kill();
00228     }
00229   }
00230 }
00231 
00232 //-----------------------------------------------------------------------------
00233 void KMAcctImap::removeSlaveJobsForFolder( KMFolder* folder )
00234 {
00235   // Make sure the folder is not referenced in any kio slave jobs
00236   QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00237   while ( it != mapJobData.end() ) {
00238      QMap<KIO::Job*, jobData>::Iterator i = it;
00239      it++;
00240      if ( (*i).parent ) {
00241         if ( (*i).parent == folder ) {
00242            mapJobData.remove(i);
00243         }
00244      }
00245   }
00246 }
00247 
00248 //-----------------------------------------------------------------------------
00249 void KMAcctImap::cancelMailCheck()
00250 {
00251   // Make list of folders to reset, like in killAllJobs
00252   QValueList<KMFolderImap*> folderList;
00253   QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00254   for (; it != mapJobData.end(); ++it) {
00255     if ( (*it).cancellable && (*it).parent ) {
00256       folderList << static_cast<KMFolderImap*>((*it).parent->storage());
00257     }
00258   }
00259   // Kill jobs
00260   // FIXME
00261   // ImapAccountBase::cancelMailCheck();
00262   killAllJobs( true );
00263   // emit folderComplete, this is important for
00264   // KMAccount::checkingMail() to be reset, in case we restart checking mail later.
00265   for( QValueList<KMFolderImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) {
00266     KMFolderImap *fld = *it;
00267     fld->sendFolderComplete(FALSE);
00268   }
00269 }
00270 
00271 //-----------------------------------------------------------------------------
00272 void KMAcctImap::processNewMail(bool interactive)
00273 {
00274   kdDebug() << "processNewMail " << mCheckingSingleFolder << ",status="<<makeConnection()<<endl;
00275   if (!mFolder || !mFolder->folder() || !mFolder->folder()->child() ||
00276       makeConnection() == ImapAccountBase::Error)
00277   {
00278     // checks for mCountRemainChecks
00279     checkDone( false, CheckError );
00280     mCountRemainChecks = 0;
00281     mCheckingSingleFolder = false;
00282     return;
00283   }
00284   // if necessary then initialize the list of folders which should be checked
00285   if( mMailCheckFolders.isEmpty() )
00286   {
00287     slotUpdateFolderList();
00288     // if no folders should be checked then the check is finished
00289     if( mMailCheckFolders.isEmpty() )
00290     {
00291       checkDone( false, CheckOK );
00292       mCheckingSingleFolder = false;
00293       return;
00294     }
00295   }
00296   // Ok, we're really checking, get a progress item;
00297   Q_ASSERT( !mMailCheckProgressItem );
00298   mMailCheckProgressItem =
00299     ProgressManager::createProgressItem(
00300         "MailCheckAccount" + name(),
00301         i18n("Checking account: %1" ).arg( QStyleSheet::escape( name() ) ),
00302         QString::null, // status
00303         true, // can be canceled
00304         useSSL() || useTLS() );
00305 
00306   mMailCheckProgressItem->setTotalItems( mMailCheckFolders.count() );
00307   connect ( mMailCheckProgressItem,
00308             SIGNAL( progressItemCanceled( KPIM::ProgressItem*) ),
00309             this,
00310             SLOT( slotMailCheckCanceled() ) );
00311 
00312   QValueList<QGuardedPtr<KMFolder> >::Iterator it;
00313   // first get the current count of unread-messages
00314   mCountRemainChecks = 0;
00315   mCountUnread = 0;
00316   mUnreadBeforeCheck.clear();
00317   for (it = mMailCheckFolders.begin(); it != mMailCheckFolders.end(); ++it)
00318   {
00319     KMFolder *folder = *it;
00320     if (folder && !folder->noContent())
00321     {
00322       mUnreadBeforeCheck[folder->idString()] = folder->countUnread();
00323     }
00324   }
00325   bool gotError = false;
00326   // then check for new mails
00327   for (it = mMailCheckFolders.begin(); it != mMailCheckFolders.end(); ++it)
00328   {
00329     KMFolder *folder = *it;
00330     if (folder && !folder->noContent())
00331     {
00332       KMFolderImap *imapFolder = static_cast<KMFolderImap*>(folder->storage());
00333       if ( imapFolder->getContentState() != KMFolderImap::imapListingInProgress
00334         && imapFolder->getContentState() != KMFolderImap::imapDownloadInProgress )
00335       {
00336         // connect the result-signals for new-mail-notification
00337         mCountRemainChecks++;
00338 
00339         if (imapFolder->isSelected()) {
00340           connect(imapFolder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00341               this, SLOT(postProcessNewMail(KMFolderImap*, bool)));
00342           imapFolder->getFolder();
00343         } else if ( kmkernel->filterMgr()->atLeastOneIncomingFilterAppliesTo( id() ) &&
00344                     imapFolder->folder()->isSystemFolder() &&
00345                     imapFolder->imapPath() == "/INBOX/" ) {
00346           imapFolder->open("acctimap"); // will be closed in the folderSelected slot
00347           // first get new headers before we select the folder
00348           imapFolder->setSelected( true );
00349           connect( imapFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
00350                    this, SLOT( slotFolderSelected( KMFolderImap*, bool) ) );
00351           imapFolder->getFolder();
00352         }
00353         else {
00354           connect(imapFolder, SIGNAL(numUnreadMsgsChanged(KMFolder*)),
00355               this, SLOT(postProcessNewMail(KMFolder*)));
00356           bool ok = imapFolder->processNewMail(interactive); // this removes the local kmfolderimap if its imapPath is somehow empty, and removing it calls createFolderList, invalidating mMailCheckFolders, and causing a crash
00357           if (!ok)
00358           {
00359             // there was an error so cancel
00360             mCountRemainChecks--;
00361             gotError = true;
00362             if ( mMailCheckProgressItem ) {
00363               mMailCheckProgressItem->incCompletedItems();
00364               mMailCheckProgressItem->updateProgress();
00365             }
00366             // since the list of folders might have been updated at this point, mMailCheckFolders may be invalid, so break
00367             break;
00368           }
00369         }
00370       }
00371     }
00372   } // end for
00373   if ( gotError )
00374     slotUpdateFolderList();
00375   // for the case the account is down and all folders report errors
00376   if ( mCountRemainChecks == 0 )
00377   {
00378     mCountLastUnread = 0; // => mCountUnread - mCountLastUnread == new count
00379     ImapAccountBase::postProcessNewMail();
00380     mUnreadBeforeCheck.clear();
00381     mCheckingSingleFolder = false;
00382   }
00383 }
00384 
00385 //-----------------------------------------------------------------------------
00386 void KMAcctImap::postProcessNewMail(KMFolderImap* folder, bool)
00387 {
00388   disconnect(folder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00389       this, SLOT(postProcessNewMail(KMFolderImap*, bool)));
00390   postProcessNewMail(static_cast<KMFolder*>(folder->folder()));
00391 }
00392 
00393 void KMAcctImap::postProcessNewMail( KMFolder * folder )
00394 {
00395   disconnect( folder->storage(), SIGNAL(numUnreadMsgsChanged(KMFolder*)),
00396               this, SLOT(postProcessNewMail(KMFolder*)) );
00397 
00398   if ( mMailCheckProgressItem ) {
00399     mMailCheckProgressItem->incCompletedItems();
00400     mMailCheckProgressItem->updateProgress();
00401     mMailCheckProgressItem->setStatus( folder->prettyURL() + i18n(" completed") );
00402   }
00403   mCountRemainChecks--;
00404 
00405   // count the unread messages
00406   const QString folderId = folder->idString();
00407   int newInFolder = folder->countUnread();
00408   if ( mUnreadBeforeCheck.find( folderId ) != mUnreadBeforeCheck.end() )
00409     newInFolder -= mUnreadBeforeCheck[folderId];
00410   if ( newInFolder > 0 ) {
00411     addToNewInFolder( folderId, newInFolder );
00412     mCountUnread += newInFolder;
00413   }
00414 
00415   // Filter messages
00416   QValueListIterator<Q_UINT32> filterIt = mFilterSerNums.begin();
00417   QValueList<Q_UINT32> inTransit;
00418 
00419   if (ActionScheduler::isEnabled() ||
00420       kmkernel->filterMgr()->atLeastOneOnlineImapFolderTarget()) {
00421     KMFilterMgr::FilterSet set = KMFilterMgr::Inbound;
00422     QValueList<KMFilter*> filters = kmkernel->filterMgr()->filters();
00423     if (!mScheduler) {
00424     mScheduler = new KMail::ActionScheduler( set, filters );
00425     mScheduler->setAccountId( id() );
00426     connect( mScheduler, SIGNAL(filtered(Q_UINT32)), this, SLOT(slotFiltered(Q_UINT32)) );
00427     } else {
00428     mScheduler->setFilterList( filters );
00429     }
00430   }
00431 
00432   while (filterIt != mFilterSerNums.end()) {
00433     int idx = -1;
00434     KMFolder *folder = 0;
00435     KMMessage *msg = 0;
00436     KMMsgDict::instance()->getLocation( *filterIt, &folder, &idx );
00437     // It's possible that the message has been deleted or moved into a
00438     // different folder, or that the serNum is stale
00439     if ( !folder ) {
00440       mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
00441       ++filterIt;
00442       continue;
00443     }
00444 
00445     KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder->storage());
00446     if (!imapFolder ||
00447     !imapFolder->folder()->isSystemFolder() ||
00448         !(imapFolder->imapPath() == "/INBOX/") ) { // sanity checking
00449       mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
00450       ++filterIt;
00451       continue;
00452     }
00453 
00454     if (idx != -1) {
00455 
00456       msg = folder->getMsg( idx );
00457       if (!msg) { // sanity checking
00458         mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
00459         ++filterIt;
00460         continue;
00461       }
00462 
00463       if (ActionScheduler::isEnabled() ||
00464       kmkernel->filterMgr()->atLeastOneOnlineImapFolderTarget()) {
00465     mScheduler->execFilters( msg );
00466       } else {
00467     if (msg->transferInProgress()) {
00468       inTransit.append( *filterIt );
00469       ++filterIt;
00470       continue;
00471     }
00472     msg->setTransferInProgress(true);
00473     if ( !msg->isComplete() ) {
00474       FolderJob *job = folder->createJob(msg);
00475       connect(job, SIGNAL(messageRetrieved(KMMessage*)),
00476           SLOT(slotFilterMsg(KMMessage*)));
00477       job->start();
00478     } else {
00479       mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
00480       if (slotFilterMsg(msg) == 2) break;
00481     }
00482       }
00483     }
00484     ++filterIt;
00485   }
00486   mFilterSerNums = inTransit;
00487 
00488   if (mCountRemainChecks == 0)
00489   {
00490     // all checks are done
00491     mCountLastUnread = 0; // => mCountUnread - mCountLastUnread == new count
00492     // when we check only one folder (=selected) and we have new mails
00493     // then do not display a summary as the normal status message is better
00494     bool showStatus = ( mCheckingSingleFolder && mCountUnread > 0 ) ? false : true;
00495     ImapAccountBase::postProcessNewMail( showStatus );
00496     mUnreadBeforeCheck.clear();
00497     mCheckingSingleFolder = false;
00498   }
00499 }
00500 
00501 //-----------------------------------------------------------------------------
00502 void KMAcctImap::slotFiltered(Q_UINT32 serNum)
00503 {
00504     mFilterSerNumsToSave.remove( QString( "%1" ).arg( serNum ) );
00505 }
00506 
00507 //-----------------------------------------------------------------------------
00508 void KMAcctImap::slotUpdateFolderList()
00509 {
00510   if ( !mFolder || !mFolder->folder() || !mFolder->folder()->child() )
00511   {
00512     kdWarning(5006) << "KMAcctImap::slotUpdateFolderList return" << endl;
00513     return;
00514   }
00515   QStringList strList;
00516   mMailCheckFolders.clear();
00517   kmkernel->imapFolderMgr()->createFolderList(&strList, &mMailCheckFolders,
00518     mFolder->folder()->child(), QString::null, false);
00519   // the new list
00520   QValueList<QGuardedPtr<KMFolder> > includedFolders;
00521   // check for excluded folders
00522   QValueList<QGuardedPtr<KMFolder> >::Iterator it;
00523   for (it = mMailCheckFolders.begin(); it != mMailCheckFolders.end(); ++it)
00524   {
00525     KMFolderImap* folder = static_cast<KMFolderImap*>(((KMFolder*)(*it))->storage());
00526     if (folder->includeInMailCheck())
00527       includedFolders.append(*it);
00528   }
00529   mMailCheckFolders = includedFolders;
00530 }
00531 
00532 //-----------------------------------------------------------------------------
00533 void KMAcctImap::listDirectory()
00534 {
00535   mFolder->listDirectory();
00536 }
00537 
00538 //-----------------------------------------------------------------------------
00539 void KMAcctImap::readConfig(KConfig& config)
00540 {
00541   ImapAccountBase::readConfig( config );
00542 }
00543 
00544 //-----------------------------------------------------------------------------
00545 void KMAcctImap::slotMailCheckCanceled()
00546 {
00547   if( mMailCheckProgressItem )
00548     mMailCheckProgressItem->setComplete();
00549   cancelMailCheck();
00550 }
00551 
00552 //-----------------------------------------------------------------------------
00553 FolderStorage* const KMAcctImap::rootFolder() const
00554 {
00555   return mFolder;
00556 }
00557 
00558 ImapAccountBase::ConnectionState KMAcctImap::makeConnection()
00559 {
00560   if ( mSlaveConnectionError )
00561   {
00562     mErrorTimer.start(100, true); // Clear error flag
00563     return Error;
00564   }
00565   return ImapAccountBase::makeConnection();
00566 }
00567 
00568 void KMAcctImap::slotResetConnectionError()
00569 {
00570   mSlaveConnectionError = false;
00571   kdDebug(5006) << k_funcinfo << endl;
00572 }
00573 
00574 void KMAcctImap::slotFolderSelected( KMFolderImap* folder, bool )
00575 {
00576   folder->setSelected( false );
00577   disconnect( folder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
00578           this, SLOT( slotFolderSelected( KMFolderImap*, bool) ) );
00579   postProcessNewMail( static_cast<KMFolder*>(folder->folder()) );
00580   folder->close( "acctimap" );
00581 }
00582 
00583 void KMAcctImap::execFilters(Q_UINT32 serNum)
00584 {
00585   if ( !kmkernel->filterMgr()->atLeastOneFilterAppliesTo( id() ) ) return;
00586   QValueListIterator<Q_UINT32> findIt = mFilterSerNums.find( serNum );
00587   if ( findIt != mFilterSerNums.end() )
00588       return;
00589   mFilterSerNums.append( serNum );
00590   mFilterSerNumsToSave.insert( QString( "%1" ).arg( serNum ), (const int *)1 );
00591 }
00592 
00593 int KMAcctImap::slotFilterMsg( KMMessage *msg )
00594 {
00595   if ( !msg ) {
00596     // messageRetrieved(0) is always possible
00597     return -1;
00598   }
00599   msg->setTransferInProgress(false);
00600   Q_UINT32 serNum = msg->getMsgSerNum();
00601   if ( serNum )
00602     mFilterSerNumsToSave.remove( QString( "%1" ).arg( serNum ) );
00603 
00604   int filterResult = kmkernel->filterMgr()->process(msg,
00605                             KMFilterMgr::Inbound,
00606                             true,
00607                             id() );
00608   if (filterResult == 2) {
00609     // something went horribly wrong (out of space?)
00610     kmkernel->emergencyExit( i18n("Unable to process messages: " ) + QString::fromLocal8Bit(strerror(errno)));
00611     return 2;
00612   }
00613   if (msg->parent()) { // unGet this msg
00614     int idx = -1;
00615     KMFolder * p = 0;
00616     KMMsgDict::instance()->getLocation( msg, &p, &idx );
00617     assert( p == msg->parent() ); assert( idx >= 0 );
00618     p->unGetMsg( idx );
00619   }
00620 
00621   return filterResult;
00622 }
00623 
00624 #include "kmacctimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys