summaryrefslogtreecommitdiffstats
path: root/kmail/kmacctimap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmail/kmacctimap.cpp')
-rw-r--r--kmail/kmacctimap.cpp621
1 files changed, 621 insertions, 0 deletions
diff --git a/kmail/kmacctimap.cpp b/kmail/kmacctimap.cpp
new file mode 100644
index 000000000..9e83d243d
--- /dev/null
+++ b/kmail/kmacctimap.cpp
@@ -0,0 +1,621 @@
+/**
+ * kmacctimap.cpp
+ *
+ * Copyright (c) 2000-2002 Michael Haeckel <haeckel@kde.org>
+ *
+ * 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 <config.h>
+#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 <kio/scheduler.h>
+#include <kio/slave.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+
+#include <qstylesheet.h>
+
+#include <errno.h>
+
+//-----------------------------------------------------------------------------
+KMAcctImap::KMAcctImap(AccountManager* aOwner, const QString& 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(), SIGNAL(changed()),
+ this, SLOT(slotUpdateFolderList()));
+ connect(&mErrorTimer, SIGNAL(timeout()), SLOT(slotResetConnectionError()));
+
+ QString serNumUri = locateLocal( "data", "kmail/unfiltered." +
+ QString("%1").arg(KAccount::id()) );
+ KConfig config( serNumUri );
+ QStringList serNums = config.readListEntry( "unfiltered" );
+ mFilterSerNumsToSave.setAutoDelete( false );
+
+ for ( QStringList::ConstIterator it = serNums.begin();
+ it != serNums.end(); ++it ) {
+ mFilterSerNums.append( (*it).toUInt() );
+ mFilterSerNumsToSave.insert( *it, (const int *)1 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+KMAcctImap::~KMAcctImap()
+{
+ killAllJobs( true );
+
+ QString serNumUri = locateLocal( "data", "kmail/unfiltered." +
+ QString("%1").arg(KAccount::id()) );
+ KConfig config( serNumUri );
+ QStringList serNums;
+ QDictIterator<int> it( mFilterSerNumsToSave );
+ for( ; it.current(); ++it )
+ serNums.append( it.currentKey() );
+ config.writeEntry( "unfiltered", serNums );
+}
+
+
+//-----------------------------------------------------------------------------
+QString 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 QString &errorMsg, KIO::Job* job, const QString& 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 )
+{
+ QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
+ for ( ; it != mapJobData.end(); ++it)
+ {
+ QPtrList<KMMessage> msgList = (*it).msgList;
+ QPtrList<KMMessage>::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<KMFolderImap*>((*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();
+ QPtrListIterator<ImapJob> 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;
+ QPtrListIterator<ImapJob> it( mJobList );
+ while ( it.current() )
+ {
+ ImapJob *job = it.current();
+ ++it;
+ if ( job->msgList().first() == msg )
+ {
+ job->kill();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctImap::ignoreJobsForFolder( KMFolder* folder )
+{
+ QPtrListIterator<ImapJob> 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
+ QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
+ while ( it != mapJobData.end() ) {
+ QMap<KIO::Job*, jobData>::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
+ QValueList<KMFolderImap*> folderList;
+ QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
+ for (; it != mapJobData.end(); ++it) {
+ if ( (*it).cancellable && (*it).parent ) {
+ folderList << static_cast<KMFolderImap*>((*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( QValueList<KMFolderImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) {
+ KMFolderImap *fld = *it;
+ fld->sendFolderComplete(FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctImap::processNewMail(bool interactive)
+{
+ kdDebug() << "processNewMail " << mCheckingSingleFolder << ",status="<<makeConnection()<<endl;
+ if (!mFolder || !mFolder->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" ).arg( QStyleSheet::escape( name() ) ),
+ QString::null, // status
+ true, // can be canceled
+ useSSL() || useTLS() );
+
+ mMailCheckProgressItem->setTotalItems( mMailCheckFolders.count() );
+ connect ( mMailCheckProgressItem,
+ SIGNAL( progressItemCanceled( KPIM::ProgressItem*) ),
+ this,
+ SLOT( slotMailCheckCanceled() ) );
+
+ QValueList<QGuardedPtr<KMFolder> >::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<KMFolderImap*>(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, SIGNAL(folderComplete(KMFolderImap*, bool)),
+ this, 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, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
+ this, SLOT( slotFolderSelected( KMFolderImap*, bool) ) );
+ imapFolder->getFolder();
+ }
+ else {
+ connect(imapFolder, SIGNAL(numUnreadMsgsChanged(KMFolder*)),
+ this, 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, SIGNAL(folderComplete(KMFolderImap*, bool)),
+ this, SLOT(postProcessNewMail(KMFolderImap*, bool)));
+ postProcessNewMail(static_cast<KMFolder*>(folder->folder()));
+}
+
+void KMAcctImap::postProcessNewMail( KMFolder * folder )
+{
+ disconnect( folder->storage(), SIGNAL(numUnreadMsgsChanged(KMFolder*)),
+ this, SLOT(postProcessNewMail(KMFolder*)) );
+
+ if ( mMailCheckProgressItem ) {
+ mMailCheckProgressItem->incCompletedItems();
+ mMailCheckProgressItem->updateProgress();
+ mMailCheckProgressItem->setStatus( folder->prettyURL() + i18n(" completed") );
+ }
+ mCountRemainChecks--;
+
+ // count the unread messages
+ const QString 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
+ QValueListIterator<Q_UINT32> filterIt = mFilterSerNums.begin();
+ QValueList<Q_UINT32> inTransit;
+
+ if (ActionScheduler::isEnabled() ||
+ kmkernel->filterMgr()->atLeastOneOnlineImapFolderTarget()) {
+ KMFilterMgr::FilterSet set = KMFilterMgr::Inbound;
+ QValueList<KMFilter*> filters = kmkernel->filterMgr()->filters();
+ if (!mScheduler) {
+ mScheduler = new KMail::ActionScheduler( set, filters );
+ mScheduler->setAccountId( id() );
+ connect( mScheduler, SIGNAL(filtered(Q_UINT32)), this, SLOT(slotFiltered(Q_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( QString( "%1" ).arg( *filterIt ) );
+ ++filterIt;
+ continue;
+ }
+
+ KMFolderImap *imapFolder = dynamic_cast<KMFolderImap*>(folder->storage());
+ if (!imapFolder ||
+ !imapFolder->folder()->isSystemFolder() ||
+ !(imapFolder->imapPath() == "/INBOX/") ) { // sanity checking
+ mFilterSerNumsToSave.remove( QString( "%1" ).arg( *filterIt ) );
+ ++filterIt;
+ continue;
+ }
+
+ if (idx != -1) {
+
+ msg = folder->getMsg( idx );
+ if (!msg) { // sanity checking
+ mFilterSerNumsToSave.remove( QString( "%1" ).arg( *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, SIGNAL(messageRetrieved(KMMessage*)),
+ SLOT(slotFilterMsg(KMMessage*)));
+ job->start();
+ } else {
+ mFilterSerNumsToSave.remove( QString( "%1" ).arg( *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(Q_UINT32 serNum)
+{
+ mFilterSerNumsToSave.remove( QString( "%1" ).arg( serNum ) );
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctImap::slotUpdateFolderList()
+{
+ if ( !mFolder || !mFolder->folder() || !mFolder->folder()->child() )
+ {
+ kdWarning(5006) << "KMAcctImap::slotUpdateFolderList return" << endl;
+ return;
+ }
+ QStringList strList;
+ mMailCheckFolders.clear();
+ kmkernel->imapFolderMgr()->createFolderList(&strList, &mMailCheckFolders,
+ mFolder->folder()->child(), QString::null, false);
+ // the new list
+ QValueList<QGuardedPtr<KMFolder> > includedFolders;
+ // check for excluded folders
+ QValueList<QGuardedPtr<KMFolder> >::Iterator it;
+ for (it = mMailCheckFolders.begin(); it != mMailCheckFolders.end(); ++it)
+ {
+ KMFolderImap* folder = static_cast<KMFolderImap*>(((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* const 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, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
+ this, SLOT( slotFolderSelected( KMFolderImap*, bool) ) );
+ postProcessNewMail( static_cast<KMFolder*>(folder->folder()) );
+ folder->close( "acctimap" );
+}
+
+void KMAcctImap::execFilters(Q_UINT32 serNum)
+{
+ if ( !kmkernel->filterMgr()->atLeastOneFilterAppliesTo( id() ) ) return;
+ QValueListIterator<Q_UINT32> findIt = mFilterSerNums.find( serNum );
+ if ( findIt != mFilterSerNums.end() )
+ return;
+ mFilterSerNums.append( serNum );
+ mFilterSerNumsToSave.insert( QString( "%1" ).arg( serNum ), (const int *)1 );
+}
+
+int KMAcctImap::slotFilterMsg( KMMessage *msg )
+{
+ if ( !msg ) {
+ // messageRetrieved(0) is always possible
+ return -1;
+ }
+ msg->setTransferInProgress(false);
+ Q_UINT32 serNum = msg->getMsgSerNum();
+ if ( serNum )
+ mFilterSerNumsToSave.remove( QString( "%1" ).arg( 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: " ) + QString::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"