/** * kmacctimap.cpp * * Copyright (c) 2000-2002 Michael Haeckel * * This file is based on popaccount.cpp by Don Sanders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "kmacctimap.h" using KMail::SieveConfig; #include "kmmessage.h" #include "broadcaststatus.h" using KPIM::BroadcastStatus; #include "kmfoldertree.h" #include "kmfoldermgr.h" #include "kmfolderimap.h" #include "kmmainwin.h" #include "kmmsgdict.h" #include "kmfilter.h" #include "kmfiltermgr.h" #include "folderstorage.h" #include "imapjob.h" #include "actionscheduler.h" using KMail::ActionScheduler; using KMail::ImapJob; using KMail::ImapAccountBase; #include "progressmanager.h" using KPIM::ProgressItem; using KPIM::ProgressManager; #include #include #include #include #include #include //----------------------------------------------------------------------------- KMAcctImap::KMAcctImap(AccountManager* aOwner, const TQString& aAccountName, uint id): KMail::ImapAccountBase(aOwner, aAccountName, id), mCountRemainChecks( 0 ), mErrorTimer( 0, "mErrorTimer" ) { mFolder = 0; mScheduler = 0; mNoopTimer.start( 60000 ); // // send a noop every minute mOpenFolders.setAutoDelete(true); connect(kmkernel->imapFolderMgr(), TQT_SIGNAL(changed()), this, TQT_SLOT(slotUpdateFolderList())); connect(&mErrorTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotResetConnectionError())); TQString serNumUri = locateLocal( "data", "kmail/unfiltered." + TQString("%1").tqarg(KAccount::id()) ); KConfig config( serNumUri ); TQStringList serNums = config.readListEntry( "unfiltered" ); mFilterSerNumsToSave.setAutoDelete( false ); for ( TQStringList::ConstIterator it = serNums.begin(); it != serNums.end(); ++it ) { mFilterSerNums.append( (*it).toUInt() ); mFilterSerNumsToSave.insert( *it, (const int *)1 ); } } //----------------------------------------------------------------------------- KMAcctImap::~KMAcctImap() { killAllJobs( true ); TQString serNumUri = locateLocal( "data", "kmail/unfiltered." + TQString("%1").tqarg(KAccount::id()) ); KConfig config( serNumUri ); TQStringList serNums; TQDictIterator it( mFilterSerNumsToSave ); for( ; it.current(); ++it ) serNums.append( it.currentKey() ); config.writeEntry( "unfiltered", serNums ); } //----------------------------------------------------------------------------- TQString KMAcctImap::type() const { return "imap"; } //----------------------------------------------------------------------------- void KMAcctImap::pseudoAssign( const KMAccount * a ) { killAllJobs( true ); if (mFolder) { mFolder->setContentState(KMFolderImap::imapNoInformation); mFolder->setSubfolderState(KMFolderImap::imapNoInformation); } ImapAccountBase::pseudoAssign( a ); } //----------------------------------------------------------------------------- void KMAcctImap::setImapFolder(KMFolderImap *aFolder) { mFolder = aFolder; mFolder->setImapPath( "/" ); } //----------------------------------------------------------------------------- bool KMAcctImap::handleError( int errorCode, const TQString &errorMsg, KIO::Job* job, const TQString& context, bool abortSync ) { /* TODO check where to handle this one better. */ if ( errorCode == KIO::ERR_DOES_NOT_EXIST ) { // folder is gone, so reload the folderlist if ( mFolder ) mFolder->listDirectory(); return true; } return ImapAccountBase::handleError( errorCode, errorMsg, job, context, abortSync ); } //----------------------------------------------------------------------------- void KMAcctImap::killAllJobs( bool disconnectSlave ) { TQMap::Iterator it = mapJobData.begin(); for ( ; it != mapJobData.end(); ++it) { TQPtrList msgList = (*it).msgList; TQPtrList::Iterator it2 = msgList.begin(); for ( ; it2 != msgList.end(); ++it2 ) { KMMessage *msg = *it2; if ( msg->transferInProgress() ) { kdDebug(5006) << "KMAcctImap::killAllJobs - resetting mail" << endl; msg->setTransferInProgress( false ); } } if ((*it).parent) { // clear folder state KMFolderImap *fld = static_cast((*it).parent->storage()); fld->setCheckingValidity(false); fld->quiet(false); fld->setContentState(KMFolderImap::imapNoInformation); fld->setSubfolderState(KMFolderImap::imapNoInformation); fld->sendFolderComplete(FALSE); fld->removeJobs(); } if ( (*it).progressItem ) { (*it).progressItem->setComplete(); } } if (mSlave && mapJobData.begin() != mapJobData.end()) { mSlave->kill(); mSlave = 0; } // remove the jobs mapJobData.clear(); // KMAccount::deleteFolderJobs(); doesn't work here always, it deletes jobs from // its own mJobList instead of our mJobList... KMAccount::deleteFolderJobs(); TQPtrListIterator it2( mJobList ); while ( it2.current() ) { ImapJob *job = it2.current(); ++it2; job->kill(); } mJobList.clear(); // make sure that no new-mail-check is blocked if (mCountRemainChecks > 0) { checkDone( false, CheckOK ); // returned 0 new messages mCountRemainChecks = 0; } if ( disconnectSlave && slave() ) { KIO::Scheduler::disconnectSlave( slave() ); mSlave = 0; } } //----------------------------------------------------------------------------- void KMAcctImap::ignoreJobsForMessage( KMMessage* msg ) { if (!msg) return; TQPtrListIterator it( mJobList ); while ( it.current() ) { ImapJob *job = it.current(); ++it; if ( job->msgList().first() == msg ) { job->kill(); } } } //----------------------------------------------------------------------------- void KMAcctImap::ignoreJobsForFolder( KMFolder* folder ) { TQPtrListIterator it( mJobList ); while ( it.current() ) { ImapJob *job = it.current(); ++it; if ( !job->msgList().isEmpty() && job->msgList().first()->parent() == folder ) { job->kill(); } } } //----------------------------------------------------------------------------- void KMAcctImap::removeSlaveJobsForFolder( KMFolder* folder ) { // Make sure the folder is not referenced in any kio slave jobs TQMap::Iterator it = mapJobData.begin(); while ( it != mapJobData.end() ) { TQMap::Iterator i = it; it++; if ( (*i).parent ) { if ( (*i).parent == folder ) { mapJobData.remove(i); } } } } //----------------------------------------------------------------------------- void KMAcctImap::cancelMailCheck() { // Make list of folders to reset, like in killAllJobs TQValueList folderList; TQMap::Iterator it = mapJobData.begin(); for (; it != mapJobData.end(); ++it) { if ( (*it).cancellable && (*it).parent ) { folderList << static_cast((*it).parent->storage()); } } // Kill jobs // FIXME // ImapAccountBase::cancelMailCheck(); killAllJobs( true ); // emit folderComplete, this is important for // KMAccount::checkingMail() to be reset, in case we restart checking mail later. for( TQValueList::Iterator it = folderList.begin(); it != folderList.end(); ++it ) { KMFolderImap *fld = *it; fld->sendFolderComplete(FALSE); } } //----------------------------------------------------------------------------- void KMAcctImap::processNewMail(bool interactive) { kdDebug() << "processNewMail " << mCheckingSingleFolder << ",status="<folder() || !mFolder->folder()->child() || makeConnection() == ImapAccountBase::Error) { mCountRemainChecks = 0; mCheckingSingleFolder = false; checkDone( false, CheckError ); return; } // if necessary then initialize the list of folders which should be checked if( mMailCheckFolders.isEmpty() ) { slotUpdateFolderList(); // if no folders should be checked then the check is finished if( mMailCheckFolders.isEmpty() ) { checkDone( false, CheckOK ); mCheckingSingleFolder = false; return; } } // Ok, we're really checking, get a progress item; Q_ASSERT( !mMailCheckProgressItem ); mMailCheckProgressItem = ProgressManager::createProgressItem( "MailCheckAccount" + name(), i18n("Checking account: %1" ).tqarg( TQStyleSheet::escape( name() ) ), TQString(), // status true, // can be canceled useSSL() || useTLS() ); mMailCheckProgressItem->setTotalItems( mMailCheckFolders.count() ); connect ( mMailCheckProgressItem, TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem*) ), this, TQT_SLOT( slotMailCheckCanceled() ) ); TQValueList >::Iterator it; // first get the current count of unread-messages mCountRemainChecks = 0; mCountUnread = 0; mUnreadBeforeCheck.clear(); for (it = mMailCheckFolders.begin(); it != mMailCheckFolders.end(); ++it) { KMFolder *folder = *it; if (folder && !folder->noContent()) { mUnreadBeforeCheck[folder->idString()] = folder->countUnread(); } } bool gotError = false; // then check for new mails for (it = mMailCheckFolders.begin(); it != mMailCheckFolders.end(); ++it) { KMFolder *folder = *it; if (folder && !folder->noContent()) { KMFolderImap *imapFolder = static_cast(folder->storage()); if ( imapFolder->getContentState() != KMFolderImap::imapListingInProgress && imapFolder->getContentState() != KMFolderImap::imapDownloadInProgress ) { // connect the result-signals for new-mail-notification mCountRemainChecks++; if (imapFolder->isSelected()) { connect(imapFolder, TQT_SIGNAL(folderComplete(KMFolderImap*, bool)), this, TQT_SLOT(postProcessNewMail(KMFolderImap*, bool))); imapFolder->getFolder(); } else if ( kmkernel->filterMgr()->atLeastOneIncomingFilterAppliesTo( id() ) && imapFolder->folder()->isSystemFolder() && imapFolder->imapPath() == "/INBOX/" ) { imapFolder->open("acctimap"); // will be closed in the folderSelected slot // first get new headers before we select the folder imapFolder->setSelected( true ); connect( imapFolder, TQT_SIGNAL( folderComplete( KMFolderImap*, bool ) ), this, TQT_SLOT( slotFolderSelected( KMFolderImap*, bool) ) ); imapFolder->getFolder(); } else { connect(imapFolder, TQT_SIGNAL(numUnreadMsgsChanged(KMFolder*)), this, TQT_SLOT(postProcessNewMail(KMFolder*))); bool ok = imapFolder->processNewMail(interactive); if (!ok) { // there was an error so cancel mCountRemainChecks--; gotError = true; if ( mMailCheckProgressItem ) { mMailCheckProgressItem->incCompletedItems(); mMailCheckProgressItem->updateProgress(); } } } } } } // end for if ( gotError ) slotUpdateFolderList(); // for the case the account is down and all folders report errors if ( mCountRemainChecks == 0 ) { mCountLastUnread = 0; // => mCountUnread - mCountLastUnread == new count ImapAccountBase::postProcessNewMail(); mUnreadBeforeCheck.clear(); mCheckingSingleFolder = false; } } //----------------------------------------------------------------------------- void KMAcctImap::postProcessNewMail(KMFolderImap* folder, bool) { disconnect(folder, TQT_SIGNAL(folderComplete(KMFolderImap*, bool)), this, TQT_SLOT(postProcessNewMail(KMFolderImap*, bool))); postProcessNewMail(static_cast(folder->folder())); } void KMAcctImap::postProcessNewMail( KMFolder * folder ) { disconnect( folder->storage(), TQT_SIGNAL(numUnreadMsgsChanged(KMFolder*)), this, TQT_SLOT(postProcessNewMail(KMFolder*)) ); if ( mMailCheckProgressItem ) { mMailCheckProgressItem->incCompletedItems(); mMailCheckProgressItem->updateProgress(); mMailCheckProgressItem->seStatus( folder->prettyURL() + i18n(" completed") ); } mCountRemainChecks--; // count the unread messages const TQString folderId = folder->idString(); int newInFolder = folder->countUnread(); if ( mUnreadBeforeCheck.find( folderId ) != mUnreadBeforeCheck.end() ) newInFolder -= mUnreadBeforeCheck[folderId]; if ( newInFolder > 0 ) { addToNewInFolder( folderId, newInFolder ); mCountUnread += newInFolder; } // Filter messages TQValueListIterator filterIt = mFilterSerNums.begin(); TQValueList inTransit; if (ActionScheduler::isEnabled() || kmkernel->filterMgr()->atLeastOneOnlineImapFolderTarget()) { KMFilterMgr::FilterSet set = KMFilterMgr::Inbound; TQValueList filters = kmkernel->filterMgr()->filters(); if (!mScheduler) { mScheduler = new KMail::ActionScheduler( set, filters ); mScheduler->setAccountId( id() ); connect( mScheduler, TQT_SIGNAL(filtered(TQ_UINT32)), this, TQT_SLOT(slotFiltered(TQ_UINT32)) ); } else { mScheduler->setFilterList( filters ); } } while (filterIt != mFilterSerNums.end()) { int idx = -1; KMFolder *folder = 0; KMMessage *msg = 0; KMMsgDict::instance()->getLocation( *filterIt, &folder, &idx ); // It's possible that the message has been deleted or moved into a // different folder, or that the serNum is stale if ( !folder ) { mFilterSerNumsToSave.remove( TQString( "%1" ).tqarg( *filterIt ) ); ++filterIt; continue; } KMFolderImap *imapFolder = dynamic_cast(folder->storage()); if (!imapFolder || !imapFolder->folder()->isSystemFolder() || !(imapFolder->imapPath() == "/INBOX/") ) { // sanity checking mFilterSerNumsToSave.remove( TQString( "%1" ).tqarg( *filterIt ) ); ++filterIt; continue; } if (idx != -1) { msg = folder->getMsg( idx ); if (!msg) { // sanity checking mFilterSerNumsToSave.remove( TQString( "%1" ).tqarg( *filterIt ) ); ++filterIt; continue; } if (ActionScheduler::isEnabled() || kmkernel->filterMgr()->atLeastOneOnlineImapFolderTarget()) { mScheduler->execFilters( msg ); } else { if (msg->transferInProgress()) { inTransit.append( *filterIt ); ++filterIt; continue; } msg->setTransferInProgress(true); if ( !msg->isComplete() ) { FolderJob *job = folder->createJob(msg); connect(job, TQT_SIGNAL(messageRetrieved(KMMessage*)), TQT_SLOT(slotFilterMsg(KMMessage*))); job->start(); } else { mFilterSerNumsToSave.remove( TQString( "%1" ).tqarg( *filterIt ) ); if (slotFilterMsg(msg) == 2) break; } } } ++filterIt; } mFilterSerNums = inTransit; if (mCountRemainChecks == 0) { // all checks are done mCountLastUnread = 0; // => mCountUnread - mCountLastUnread == new count // when we check only one folder (=selected) and we have new mails // then do not display a summary as the normal status message is better bool showStatus = ( mCheckingSingleFolder && mCountUnread > 0 ) ? false : true; ImapAccountBase::postProcessNewMail( showStatus ); mUnreadBeforeCheck.clear(); mCheckingSingleFolder = false; } } //----------------------------------------------------------------------------- void KMAcctImap::slotFiltered(TQ_UINT32 serNum) { mFilterSerNumsToSave.remove( TQString( "%1" ).tqarg( serNum ) ); } //----------------------------------------------------------------------------- void KMAcctImap::slotUpdateFolderList() { if ( !mFolder || !mFolder->folder() || !mFolder->folder()->child() ) { kdWarning(5006) << "KMAcctImap::slotUpdateFolderList return" << endl; return; } TQStringList strList; mMailCheckFolders.clear(); kmkernel->imapFolderMgr()->createFolderList(&strList, &mMailCheckFolders, mFolder->folder()->child(), TQString(), false); // the new list TQValueList > includedFolders; // check for excluded folders TQValueList >::Iterator it; for (it = mMailCheckFolders.begin(); it != mMailCheckFolders.end(); ++it) { KMFolderImap* folder = static_cast(((KMFolder*)(*it))->storage()); if (folder->includeInMailCheck()) includedFolders.append(*it); } mMailCheckFolders = includedFolders; } //----------------------------------------------------------------------------- void KMAcctImap::listDirectory() { mFolder->listDirectory(); } //----------------------------------------------------------------------------- void KMAcctImap::readConfig(KConfig& config) { ImapAccountBase::readConfig( config ); } //----------------------------------------------------------------------------- void KMAcctImap::slotMailCheckCanceled() { if( mMailCheckProgressItem ) mMailCheckProgressItem->setComplete(); cancelMailCheck(); } //----------------------------------------------------------------------------- FolderStorage* KMAcctImap::rootFolder() const { return mFolder; } ImapAccountBase::ConnectionState KMAcctImap::makeConnection() { if ( mSlaveConnectionError ) { mErrorTimer.start(100, true); // Clear error flag return Error; } return ImapAccountBase::makeConnection(); } void KMAcctImap::slotResetConnectionError() { mSlaveConnectionError = false; kdDebug(5006) << k_funcinfo << endl; } void KMAcctImap::slotFolderSelected( KMFolderImap* folder, bool ) { folder->setSelected( false ); disconnect( folder, TQT_SIGNAL( folderComplete( KMFolderImap*, bool ) ), this, TQT_SLOT( slotFolderSelected( KMFolderImap*, bool) ) ); postProcessNewMail( static_cast(folder->folder()) ); folder->close( "acctimap" ); } void KMAcctImap::execFilters(TQ_UINT32 serNum) { if ( !kmkernel->filterMgr()->atLeastOneFilterAppliesTo( id() ) ) return; TQValueListIterator findIt = mFilterSerNums.find( serNum ); if ( findIt != mFilterSerNums.end() ) return; mFilterSerNums.append( serNum ); mFilterSerNumsToSave.insert( TQString( "%1" ).tqarg( serNum ), (const int *)1 ); } int KMAcctImap::slotFilterMsg( KMMessage *msg ) { if ( !msg ) { // messageRetrieved(0) is always possible return -1; } msg->setTransferInProgress(false); TQ_UINT32 serNum = msg->getMsgSerNum(); if ( serNum ) mFilterSerNumsToSave.remove( TQString( "%1" ).tqarg( serNum ) ); int filterResult = kmkernel->filterMgr()->process(msg, KMFilterMgr::Inbound, true, id() ); if (filterResult == 2) { // something went horribly wrong (out of space?) kmkernel->emergencyExit( i18n("Unable to process messages: " ) + TQString::fromLocal8Bit(strerror(errno))); return 2; } if (msg->parent()) { // unGet this msg int idx = -1; KMFolder * p = 0; KMMsgDict::instance()->getLocation( msg, &p, &idx ); assert( p == msg->parent() ); assert( idx >= 0 ); p->unGetMsg( idx ); } return filterResult; } #include "kmacctimap.moc"