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