summaryrefslogtreecommitdiffstats
path: root/kmail/kmacctcachedimap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmail/kmacctcachedimap.cpp')
-rw-r--r--kmail/kmacctcachedimap.cpp479
1 files changed, 479 insertions, 0 deletions
diff --git a/kmail/kmacctcachedimap.cpp b/kmail/kmacctcachedimap.cpp
new file mode 100644
index 00000000..fbdc7925
--- /dev/null
+++ b/kmail/kmacctcachedimap.cpp
@@ -0,0 +1,479 @@
+/**
+ * kmacctcachedimap.cpp
+ *
+ * Copyright (c) 2002-2004 Bo Thorsen <bo@sonofthor.dk>
+ * Copyright (c) 2002-2003 Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
+ *
+ * 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.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of this program with any edition of
+ * the Qt library by Trolltech AS, Norway (or with modified versions
+ * of Qt that use the same license as Qt), and distribute linked
+ * combinations including the two. You must obey the GNU General
+ * Public License in all respects for all of the code used other than
+ * Qt. If you modify this file, you may extend this exception to
+ * your version of the file, but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from
+ * your version.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kmacctcachedimap.h"
+using KMail::SieveConfig;
+
+#include "kmfoldertree.h"
+#include "kmfoldermgr.h"
+#include "kmfiltermgr.h"
+#include "kmfoldercachedimap.h"
+#include "kmmainwin.h"
+#include "accountmanager.h"
+using KMail::AccountManager;
+#include "progressmanager.h"
+
+#include <kio/passdlg.h>
+#include <kio/scheduler.h>
+#include <kio/slave.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+#include <kconfig.h>
+
+#include <qstylesheet.h>
+
+KMAcctCachedImap::KMAcctCachedImap( AccountManager* aOwner,
+ const QString& aAccountName, uint id )
+ : KMail::ImapAccountBase( aOwner, aAccountName, id ), mFolder( 0 ),
+ mAnnotationCheckPassed(false),
+ mGroupwareType( GroupwareKolab ),
+ mSentCustomLoginCommand(false)
+{
+ // Never EVER set this for the cached IMAP account
+ mAutoExpunge = false;
+}
+
+
+//-----------------------------------------------------------------------------
+KMAcctCachedImap::~KMAcctCachedImap()
+{
+ killAllJobsInternal( true );
+}
+
+
+//-----------------------------------------------------------------------------
+QString KMAcctCachedImap::type() const
+{
+ return "cachedimap";
+}
+
+void KMAcctCachedImap::init() {
+ ImapAccountBase::init();
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctCachedImap::pseudoAssign( const KMAccount * a ) {
+ killAllJobs( true );
+ if (mFolder)
+ {
+ mFolder->setContentState(KMFolderCachedImap::imapNoInformation);
+ mFolder->setSubfolderState(KMFolderCachedImap::imapNoInformation);
+ }
+ ImapAccountBase::pseudoAssign( a );
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctCachedImap::setImapFolder(KMFolderCachedImap *aFolder)
+{
+ mFolder = aFolder;
+ mFolder->setImapPath( "/" );
+ mFolder->setAccount( this );
+}
+
+
+//-----------------------------------------------------------------------------
+void KMAcctCachedImap::setAutoExpunge( bool /*aAutoExpunge*/ )
+{
+ // Never EVER set this for the cached IMAP account
+ mAutoExpunge = false;
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctCachedImap::killAllJobs( bool disconnectSlave )
+{
+ //kdDebug(5006) << "killAllJobs: disconnectSlave=" << disconnectSlave << " " << mapJobData.count() << " jobs in map." << endl;
+ QValueList<KMFolderCachedImap*> folderList = killAllJobsInternal( disconnectSlave );
+ for( QValueList<KMFolderCachedImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) {
+ KMFolderCachedImap *fld = *it;
+ fld->resetSyncState();
+ fld->setContentState(KMFolderCachedImap::imapNoInformation);
+ fld->setSubfolderState(KMFolderCachedImap::imapNoInformation);
+ fld->sendFolderComplete(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Common between killAllJobs and the destructor - which shouldn't call sendFolderComplete
+QValueList<KMFolderCachedImap*> KMAcctCachedImap::killAllJobsInternal( bool disconnectSlave )
+{
+ // Make list of folders to reset. This must be done last, since folderComplete
+ // can trigger the next queued mail check already.
+ QValueList<KMFolderCachedImap*> folderList;
+ QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
+ for (; it != mapJobData.end(); ++it) {
+ if ((*it).parent)
+ folderList << static_cast<KMFolderCachedImap*>((*it).parent->storage());
+ // Kill the job - except if it's the one that already died and is calling us
+ if ( !it.key()->error() && mSlave ) {
+ it.key()->kill();
+ mSlave = 0; // killing a job, kills the slave
+ }
+ }
+ mapJobData.clear();
+
+ // Clear the joblist. Make SURE to stop the job emitting "finished"
+ for( QPtrListIterator<CachedImapJob> it( mJobList ); it.current(); ++it )
+ it.current()->setPassiveDestructor( true );
+ KMAccount::deleteFolderJobs();
+
+ if ( disconnectSlave && mSlave ) {
+ KIO::Scheduler::disconnectSlave( mSlave );
+ mSlave = 0;
+ }
+ return folderList;
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctCachedImap::cancelMailCheck()
+{
+ // Make list of folders to reset, like in killAllJobs
+ QValueList<KMFolderCachedImap*> folderList;
+ QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
+ for (; it != mapJobData.end(); ++it) {
+ if ( (*it).cancellable && (*it).parent )
+ folderList << static_cast<KMFolderCachedImap*>((*it).parent->storage());
+ }
+ // Kill jobs
+ ImapAccountBase::cancelMailCheck();
+ // Reset sync states and emit folderComplete, this is important for
+ // KMAccount::checkingMail() to be reset, in case we restart checking mail later.
+ for( QValueList<KMFolderCachedImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) {
+ KMFolderCachedImap *fld = *it;
+ fld->resetSyncState();
+ fld->setContentState(KMFolderCachedImap::imapNoInformation);
+ fld->setSubfolderState(KMFolderCachedImap::imapNoInformation);
+ fld->sendFolderComplete(false);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctCachedImap::killJobsForItem(KMFolderTreeItem * fti)
+{
+ QMap<KIO::Job *, jobData>::Iterator it = mapJobData.begin();
+ while (it != mapJobData.end())
+ {
+ if (it.data().parent == fti->folder())
+ {
+ killAllJobs();
+ break;
+ }
+ else ++it;
+ }
+}
+
+// Reimplemented from ImapAccountBase because we only check one folder at a time
+void KMAcctCachedImap::slotCheckQueuedFolders()
+{
+ mMailCheckFolders.clear();
+ mMailCheckFolders.append( mFoldersQueuedForChecking.front() );
+ mFoldersQueuedForChecking.pop_front();
+ if ( mFoldersQueuedForChecking.isEmpty() )
+ disconnect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
+ this, SLOT( slotCheckQueuedFolders() ) );
+
+ kmkernel->acctMgr()->singleCheckMail(this, true);
+ mMailCheckFolders.clear();
+}
+
+void KMAcctCachedImap::processNewMail( bool /*interactive*/ )
+{
+ assert( mFolder ); // George says "better to crash then lose mail"
+
+ if ( mMailCheckFolders.isEmpty() )
+ processNewMail( mFolder, true );
+ else {
+ KMFolder* f = mMailCheckFolders.front();
+ mMailCheckFolders.pop_front();
+ processNewMail( static_cast<KMFolderCachedImap *>( f->storage() ), false );
+ }
+}
+
+void KMAcctCachedImap::processNewMail( KMFolderCachedImap* folder,
+ bool recurse )
+{
+ assert( folder ); // George says "better to crash then lose mail"
+
+ // This should never be set for a cached IMAP account
+ mAutoExpunge = false;
+ mCountLastUnread = 0;
+ mUnreadBeforeCheck.clear();
+ // stop sending noops during sync, that will keep the connection open
+ mNoopTimer.stop();
+
+ // reset namespace todo
+ if ( folder == mFolder ) {
+ QStringList nsToList = namespaces()[PersonalNS];
+ QStringList otherNSToCheck = namespaces()[OtherUsersNS];
+ otherNSToCheck += namespaces()[SharedNS];
+ for ( QStringList::Iterator it = otherNSToCheck.begin();
+ it != otherNSToCheck.end(); ++it ) {
+ if ( (*it).isEmpty() ) {
+ // empty namespaces are included in the "normal" listing
+ // as the folders are created under the root folder
+ nsToList += *it;
+ }
+ }
+ folder->setNamespacesToList( nsToList );
+ }
+
+ Q_ASSERT( !mMailCheckProgressItem );
+ mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem(
+ "MailCheck" + QString::number( id() ),
+ QStyleSheet::escape( folder->label() ), // will be changed immediately in serverSync anyway
+ QString::null,
+ true, // can be cancelled
+ useSSL() || useTLS() );
+ connect( mMailCheckProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
+ this, SLOT( slotProgressItemCanceled( KPIM::ProgressItem* ) ) );
+
+ folder->setAccount(this);
+ connect(folder, SIGNAL(folderComplete(KMFolderCachedImap*, bool)),
+ this, SLOT(postProcessNewMail(KMFolderCachedImap*, bool)));
+ folder->serverSync( recurse );
+}
+
+void KMAcctCachedImap::postProcessNewMail( KMFolderCachedImap* folder, bool )
+{
+ mNoopTimer.start( 60000 ); // send a noop every minute to avoid "connection broken" errors
+ disconnect(folder, SIGNAL(folderComplete(KMFolderCachedImap*, bool)),
+ this, SLOT(postProcessNewMail(KMFolderCachedImap*, bool)));
+ mMailCheckProgressItem->setComplete();
+ mMailCheckProgressItem = 0;
+
+ if ( folder == mFolder ) {
+ // We remove everything from the deleted folders list after a full sync.
+ // Even if it fails (no permission), because on the next sync we want the folder to reappear,
+ // instead of the user being stuck with "can't delete" every time.
+ // And we do it for _all_ deleted folders, even those that were deleted on the server in the first place (slotListResult).
+ // Otherwise this might have side effects much later (e.g. when regaining permissions to a folder we could see before)
+
+#if 0 // this opens a race: delete a folder during a sync (after the sync checked that folder), and it'll be forgotten...
+ mDeletedFolders.clear();
+#endif
+ mPreviouslyDeletedFolders.clear();
+ }
+
+ KMail::ImapAccountBase::postProcessNewMail();
+}
+
+void KMAcctCachedImap::addUnreadMsgCount( const KMFolderCachedImap *folder,
+ int countUnread )
+{
+ if ( folder->imapPath() != "/INBOX/" ) {
+ // new mail in INBOX is processed with KMAccount::processNewMsg() and
+ // therefore doesn't need to be counted here
+ const QString folderId = folder->folder()->idString();
+ int newInFolder = countUnread;
+ if ( mUnreadBeforeCheck.find( folderId ) != mUnreadBeforeCheck.end() )
+ newInFolder -= mUnreadBeforeCheck[folderId];
+ if ( newInFolder > 0 )
+ addToNewInFolder( folderId, newInFolder );
+ }
+ mCountUnread += countUnread;
+}
+
+void KMAcctCachedImap::addLastUnreadMsgCount( const KMFolderCachedImap *folder,
+ int countLastUnread )
+{
+ mUnreadBeforeCheck[folder->folder()->idString()] = countLastUnread;
+ mCountLastUnread += countLastUnread;
+}
+
+//
+//
+// read/write config
+//
+//
+
+void KMAcctCachedImap::readConfig( /*const*/ KConfig/*Base*/ & config ) {
+ ImapAccountBase::readConfig( config );
+ // Apparently this method is only ever called once (from KMKernel::init) so this is ok
+ mPreviouslyDeletedFolders = config.readListEntry( "deleted-folders" );
+ mDeletedFolders.clear(); // but just in case...
+ const QStringList oldPaths = config.readListEntry( "renamed-folders-paths" );
+ const QStringList newNames = config.readListEntry( "renamed-folders-names" );
+ QStringList::const_iterator it = oldPaths.begin();
+ QStringList::const_iterator nameit = newNames.begin();
+ for( ; it != oldPaths.end() && nameit != newNames.end(); ++it, ++nameit ) {
+ addRenamedFolder( *it, QString::null, *nameit );
+ }
+ mGroupwareType = (GroupwareType)config.readNumEntry( "groupwareType", GroupwareKolab );
+}
+
+void KMAcctCachedImap::writeConfig( KConfig/*Base*/ & config ) /*const*/ {
+ ImapAccountBase::writeConfig( config );
+ config.writeEntry( "deleted-folders", mDeletedFolders + mPreviouslyDeletedFolders );
+ config.writeEntry( "renamed-folders-paths", mRenamedFolders.keys() );
+ const QValueList<RenamedFolder> values = mRenamedFolders.values();
+ QStringList lstNames;
+ QValueList<RenamedFolder>::const_iterator it = values.begin();
+ for ( ; it != values.end() ; ++it )
+ lstNames.append( (*it).mNewName );
+ config.writeEntry( "renamed-folders-names", lstNames );
+ config.writeEntry( "groupwareType", mGroupwareType );
+}
+
+void KMAcctCachedImap::invalidateIMAPFolders()
+{
+ invalidateIMAPFolders( mFolder );
+}
+
+void KMAcctCachedImap::invalidateIMAPFolders( KMFolderCachedImap* folder )
+{
+ if( !folder || !folder->folder() )
+ return;
+
+ folder->setAccount(this);
+
+ QStringList strList;
+ QValueList<QGuardedPtr<KMFolder> > folderList;
+ kmkernel->dimapFolderMgr()->createFolderList( &strList, &folderList,
+ folder->folder()->child(), QString::null,
+ false );
+ QValueList<QGuardedPtr<KMFolder> >::Iterator it;
+ mCountLastUnread = 0;
+ mUnreadBeforeCheck.clear();
+
+ for( it = folderList.begin(); it != folderList.end(); ++it ) {
+ KMFolder *f = *it;
+ if( f && f->folderType() == KMFolderTypeCachedImap ) {
+ KMFolderCachedImap *cfolder = static_cast<KMFolderCachedImap*>(f->storage());
+ // This invalidates the folder completely
+ cfolder->setUidValidity("INVALID");
+ cfolder->writeUidCache();
+ processNewMailSingleFolder( f );
+ }
+ }
+ folder->setUidValidity("INVALID");
+ folder->writeUidCache();
+
+ processNewMailSingleFolder( folder->folder() );
+}
+
+//-----------------------------------------------------------------------------
+void KMAcctCachedImap::addDeletedFolder( KMFolder* folder )
+{
+ if ( !folder || folder->folderType() != KMFolderTypeCachedImap )
+ return;
+ KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(folder->storage());
+ addDeletedFolder( storage->imapPath() );
+ kdDebug(5006) << k_funcinfo << storage->imapPath() << endl;
+
+ // Add all child folders too
+ if( folder->child() ) {
+ KMFolderNode *node = folder->child()->first();
+ while( node ) {
+ if( !node->isDir() ) {
+ addDeletedFolder( static_cast<KMFolder*>( node ) ); // recurse
+ }
+ node = folder->child()->next();
+ }
+ }
+}
+
+void KMAcctCachedImap::addDeletedFolder( const QString& imapPath )
+{
+ mDeletedFolders << imapPath;
+}
+
+QStringList KMAcctCachedImap::deletedFolderPaths( const QString& subFolderPath ) const
+{
+ QStringList lst;
+ for ( QStringList::const_iterator it = mDeletedFolders.begin(); it != mDeletedFolders.end(); ++it ) {
+ if ( (*it).startsWith( subFolderPath ) )
+ // We must reverse the order, so that sub sub sub folders are deleted first
+ lst.prepend( *it );
+ }
+ for ( QStringList::const_iterator it = mPreviouslyDeletedFolders.begin(); it != mPreviouslyDeletedFolders.end(); ++it ) {
+ if ( (*it).startsWith( subFolderPath ) )
+ lst.prepend( *it );
+ }
+ kdDebug(5006) << "KMAcctCachedImap::deletedFolderPaths for " << subFolderPath << " returning: " << lst << endl;
+ Q_ASSERT( !lst.isEmpty() );
+ return lst;
+}
+
+bool KMAcctCachedImap::isDeletedFolder( const QString& subFolderPath ) const
+{
+ return mDeletedFolders.find( subFolderPath ) != mDeletedFolders.end();
+}
+
+bool KMAcctCachedImap::isPreviouslyDeletedFolder( const QString& subFolderPath ) const
+{
+ return mPreviouslyDeletedFolders.find( subFolderPath ) != mPreviouslyDeletedFolders.end();
+}
+
+void KMAcctCachedImap::removeDeletedFolder( const QString& subFolderPath )
+{
+ mDeletedFolders.remove( subFolderPath );
+ mPreviouslyDeletedFolders.remove( subFolderPath );
+}
+
+void KMAcctCachedImap::addRenamedFolder( const QString& subFolderPath, const QString& oldLabel, const QString& newName )
+{
+ mRenamedFolders.insert( subFolderPath, RenamedFolder( oldLabel, newName ) );
+}
+
+void KMAcctCachedImap::removeRenamedFolder( const QString& subFolderPath )
+{
+ mRenamedFolders.remove( subFolderPath );
+}
+
+void KMAcctCachedImap::slotProgressItemCanceled( ProgressItem* )
+{
+ bool abortConnection = !mSlaveConnected;
+ killAllJobs( abortConnection );
+ if ( abortConnection ) {
+ // If we were trying to connect, tell kmfoldercachedimap so that it moves on
+ emit connectionResult( KIO::ERR_USER_CANCELED, QString::null );
+ }
+}
+
+FolderStorage* const KMAcctCachedImap::rootFolder() const
+{
+ return mFolder;
+}
+
+
+QString KMAcctCachedImap::renamedFolder( const QString& imapPath ) const
+{
+ QMap<QString, RenamedFolder>::ConstIterator renit = mRenamedFolders.find( imapPath );
+ if ( renit != mRenamedFolders.end() )
+ return (*renit).mNewName;
+ return QString::null;
+}
+
+#include "kmacctcachedimap.moc"