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