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); // this removes the local kmfolderimap if its imapPath is somehow empty, and removing it calls createFolderList, invalidating mMailCheckFolders, and causing a crash
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         // since the list of folders might have been updated at this point, mMailCheckFolders may be invalid, so break
00356         break;
00357           }
00358         }
00359       }
00360     }
00361   } // end for
00362   if ( gotError )
00363     slotUpdateFolderList();
00364   // for the case the account is down and all folders report errors
00365   if ( mCountRemainChecks == 0 )
00366   {
00367     mCountLastUnread = 0; // => mCountUnread - mCountLastUnread == new count
00368     ImapAccountBase::postProcessNewMail();
00369     mUnreadBeforeCheck.clear();
00370     mCheckingSingleFolder = false;
00371   }
00372 }
00373 
00374 //-----------------------------------------------------------------------------
00375 void KMAcctImap::postProcessNewMail(KMFolderImap* folder, bool)
00376 {
00377   disconnect(folder, SIGNAL(folderComplete(KMFolderImap*, bool)),
00378       this, SLOT(postProcessNewMail(KMFolderImap*, bool)));
00379   postProcessNewMail(static_cast<KMFolder*>(folder->folder()));
00380 }
00381 
00382 void KMAcctImap::postProcessNewMail( KMFolder * folder ) 
00383 {
00384   disconnect( folder->storage(), SIGNAL(numUnreadMsgsChanged(KMFolder*)),
00385               this, SLOT(postProcessNewMail(KMFolder*)) );
00386 
00387   if ( mMailCheckProgressItem ) {
00388     mMailCheckProgressItem->incCompletedItems();
00389     mMailCheckProgressItem->updateProgress();
00390     mMailCheckProgressItem->setStatus( folder->prettyURL() + i18n(" completed") );
00391   }
00392   mCountRemainChecks--;
00393 
00394   // count the unread messages
00395   const QString folderId = folder->idString();
00396   int newInFolder = folder->countUnread();
00397   if ( mUnreadBeforeCheck.find( folderId ) != mUnreadBeforeCheck.end() )
00398     newInFolder -= mUnreadBeforeCheck[folderId];
00399   if ( newInFolder > 0 ) {
00400     addToNewInFolder( folderId, newInFolder );
00401     mCountUnread += newInFolder;
00402   }
00403 
00404   // Filter messages
00405   QValueListIterator<Q_UINT32> filterIt = mFilterSerNums.begin();
00406   QValueList<Q_UINT32> inTransit;
00407 
00408   if (ActionScheduler::isEnabled() || 
00409       kmkernel->filterMgr()->atLeastOneOnlineImapFolderTarget()) {
00410     KMFilterMgr::FilterSet set = KMFilterMgr::Inbound;
00411     QValueList<KMFilter*> filters = kmkernel->filterMgr()->filters();
00412     if (!mScheduler) {
00413     mScheduler = new KMail::ActionScheduler( set, filters );
00414     mScheduler->setAccountId( id() );
00415     connect( mScheduler, SIGNAL(filtered(Q_UINT32)), this, SLOT(slotFiltered(Q_UINT32)) );
00416     } else {
00417     mScheduler->setFilterList( filters );
00418     }
00419   }
00420 
00421   while (filterIt != mFilterSerNums.end()) {
00422     int idx = -1;
00423     KMFolder *folder = 0;
00424     KMMessage *msg = 0;
00425     KMMsgDict::instance()->getLocation( *filterIt, &folder, &idx );
00426     // It's possible that the message has been deleted or moved into a
00427     // different folder, or that the serNum is stale
00428     if ( !folder ) {
00429       mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
00430       ++filterIt;
00431       continue;
00432     }
00433     
00434     KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder->storage());
00435     if (!imapFolder ||
00436     !imapFolder->folder()->isSystemFolder() ||
00437         !(imapFolder->imapPath() == "/INBOX/") ) { // sanity checking
00438       mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
00439       ++filterIt;
00440       continue;
00441     }
00442 
00443     if (idx != -1) {
00444 
00445       msg = folder->getMsg( idx );
00446       if (!msg) { // sanity checking
00447         mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
00448         ++filterIt;
00449         continue;
00450       }
00451 
00452       if (ActionScheduler::isEnabled() || 
00453       kmkernel->filterMgr()->atLeastOneOnlineImapFolderTarget()) {
00454     mScheduler->execFilters( msg );
00455       } else {
00456     if (msg->transferInProgress()) {
00457       inTransit.append( *filterIt );
00458       ++filterIt;
00459       continue;
00460     }
00461     msg->setTransferInProgress(true);
00462     if ( !msg->isComplete() ) {
00463       FolderJob *job = folder->createJob(msg);
00464       connect(job, SIGNAL(messageRetrieved(KMMessage*)),
00465           SLOT(slotFilterMsg(KMMessage*)));
00466       job->start();
00467     } else {
00468       mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
00469       if (slotFilterMsg(msg) == 2) break;
00470     }
00471       }
00472     }
00473     ++filterIt;
00474   }
00475   mFilterSerNums = inTransit;
00476   
00477   if (mCountRemainChecks == 0)
00478   {
00479     // all checks are done
00480     mCountLastUnread = 0; // => mCountUnread - mCountLastUnread == new count
00481     // when we check only one folder (=selected) and we have new mails
00482     // then do not display a summary as the normal status message is better
00483     bool showStatus = ( mCheckingSingleFolder && mCountUnread > 0 ) ? false : true;
00484     ImapAccountBase::postProcessNewMail( showStatus );
00485     mUnreadBeforeCheck.clear();
00486     mCheckingSingleFolder = false;
00487   }
00488 }
00489 
00490 //-----------------------------------------------------------------------------
00491 void KMAcctImap::slotFiltered(Q_UINT32 serNum)
00492 {
00493     mFilterSerNumsToSave.remove( QString( "%1" ).arg( serNum ) );
00494 }
00495 
00496 //-----------------------------------------------------------------------------
00497 void KMAcctImap::slotUpdateFolderList()
00498 {
00499   if ( !mFolder || !mFolder->folder() || !mFolder->folder()->child() )
00500   {
00501     kdWarning(5006) << "KMAcctImap::slotUpdateFolderList return" << endl;
00502     return;
00503   }
00504   QStringList strList;
00505   mMailCheckFolders.clear();
00506   kmkernel->imapFolderMgr()->createFolderList(&strList, &mMailCheckFolders,
00507     mFolder->folder()->child(), QString::null, false);
00508   // the new list
00509   QValueList<QGuardedPtr<KMFolder> > includedFolders;
00510   // check for excluded folders
00511   QValueList<QGuardedPtr<KMFolder> >::Iterator it;
00512   for (it = mMailCheckFolders.begin(); it != mMailCheckFolders.end(); ++it)
00513   {
00514     KMFolderImap* folder = static_cast<KMFolderImap*>(((KMFolder*)(*it))->storage());
00515     if (folder->includeInMailCheck())
00516       includedFolders.append(*it);
00517   }
00518   mMailCheckFolders = includedFolders;
00519 }
00520 
00521 //-----------------------------------------------------------------------------
00522 void KMAcctImap::listDirectory()
00523 {
00524   mFolder->listDirectory();
00525 }
00526 
00527 //-----------------------------------------------------------------------------
00528 void KMAcctImap::readConfig(KConfig& config)
00529 {
00530   ImapAccountBase::readConfig( config );
00531 }
00532 
00533 //-----------------------------------------------------------------------------
00534 void KMAcctImap::slotMailCheckCanceled()
00535 {
00536   if( mMailCheckProgressItem )
00537     mMailCheckProgressItem->setComplete();
00538   cancelMailCheck();
00539 }
00540 
00541 //-----------------------------------------------------------------------------
00542 FolderStorage* const KMAcctImap::rootFolder() const
00543 {
00544   return mFolder;
00545 }
00546 
00547 ImapAccountBase::ConnectionState KMAcctImap::makeConnection() 
00548 {
00549   if ( mSlaveConnectionError )
00550   {
00551     mErrorTimer.start(100, true); // Clear error flag
00552     return Error;
00553   }
00554   return ImapAccountBase::makeConnection();
00555 }
00556 
00557 void KMAcctImap::slotResetConnectionError()
00558 {
00559   mSlaveConnectionError = false;
00560   kdDebug(5006) << k_funcinfo << endl;
00561 }
00562     
00563 void KMAcctImap::slotFolderSelected( KMFolderImap* folder, bool )
00564 {
00565   folder->setSelected( false );
00566   disconnect( folder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
00567           this, SLOT( slotFolderSelected( KMFolderImap*, bool) ) );
00568   postProcessNewMail( static_cast<KMFolder*>(folder->folder()) );
00569   folder->close();
00570 }
00571 
00572 void KMAcctImap::execFilters(Q_UINT32 serNum)
00573 {
00574   if ( !kmkernel->filterMgr()->atLeastOneFilterAppliesTo( id() ) ) return;
00575   QValueListIterator<Q_UINT32> findIt = mFilterSerNums.find( serNum );
00576   if ( findIt != mFilterSerNums.end() )
00577       return;
00578   mFilterSerNums.append( serNum );
00579   mFilterSerNumsToSave.insert( QString( "%1" ).arg( serNum ), (const int *)1 );
00580 }
00581 
00582 int KMAcctImap::slotFilterMsg( KMMessage *msg )
00583 {
00584   if ( !msg ) {
00585     // messageRetrieved(0) is always possible
00586     return -1;
00587   }
00588   msg->setTransferInProgress(false);
00589   Q_UINT32 serNum = msg->getMsgSerNum();
00590   if ( serNum )
00591     mFilterSerNumsToSave.remove( QString( "%1" ).arg( serNum ) );
00592 
00593   int filterResult = kmkernel->filterMgr()->process(msg, 
00594                             KMFilterMgr::Inbound,
00595                             true,
00596                             id() );
00597   if (filterResult == 2) {
00598     // something went horribly wrong (out of space?)
00599     kmkernel->emergencyExit( i18n("Unable to process messages: " ) + QString::fromLocal8Bit(strerror(errno)));
00600     return 2;
00601   }
00602   if (msg->parent()) { // unGet this msg
00603     int idx = -1;
00604     KMFolder * p = 0;
00605     KMMsgDict::instance()->getLocation( msg, &p, &idx );
00606     assert( p == msg->parent() ); assert( idx >= 0 );
00607     p->unGetMsg( idx );
00608   }
00609 
00610   return filterResult;
00611 }
00612 
00613 #include "kmacctimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys