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