summaryrefslogtreecommitdiffstats
path: root/kmail/kmfolderimap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmail/kmfolderimap.cpp')
-rw-r--r--kmail/kmfolderimap.cpp2434
1 files changed, 2434 insertions, 0 deletions
diff --git a/kmail/kmfolderimap.cpp b/kmail/kmfolderimap.cpp
new file mode 100644
index 00000000..f30e11b6
--- /dev/null
+++ b/kmail/kmfolderimap.cpp
@@ -0,0 +1,2434 @@
+/**
+ * kmfolderimap.cpp
+ *
+ * Copyright (c) 2001 Kurt Granroth <granroth@kde.org>
+ * Copyright (c) 2000-2002 Michael Haeckel <haeckel@kde.org>
+ *
+ * This file is based on kmacctimap.coo by Michael Haeckel which was
+ * 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 "kmfolder.h"
+#include "kmfolderimap.h"
+#include "kmfoldermbox.h"
+#include "kmfoldertree.h"
+#include "kmmsgdict.h"
+#include "undostack.h"
+#include "kmfoldermgr.h"
+#include "kmfiltermgr.h"
+#include "kmmsgdict.h"
+#include "imapaccountbase.h"
+using KMail::ImapAccountBase;
+#include "imapjob.h"
+using KMail::ImapJob;
+#include "attachmentstrategy.h"
+using KMail::AttachmentStrategy;
+#include "progressmanager.h"
+using KPIM::ProgressItem;
+using KPIM::ProgressManager;
+#include "listjob.h"
+using KMail::ListJob;
+#include "kmsearchpattern.h"
+#include "searchjob.h"
+using KMail::SearchJob;
+#include "renamejob.h"
+using KMail::RenameJob;
+
+#include <kdebug.h>
+#include <kio/scheduler.h>
+#include <kconfig.h>
+
+#include <qbuffer.h>
+#include <qtextcodec.h>
+#include <qstylesheet.h>
+
+#include <assert.h>
+
+KMFolderImap::KMFolderImap(KMFolder* folder, const char* aName)
+ : KMFolderMbox(folder, aName),
+ mUploadAllFlags( false )
+{
+ mContentState = imapNoInformation;
+ mSubfolderState = imapNoInformation;
+ mAccount = 0;
+ mIsSelected = false;
+ mLastUid = 0;
+ mCheckFlags = true;
+ mCheckMail = true;
+ mCheckingValidity = false;
+ mUserRights = 0;
+ mAlreadyRemoved = false;
+ mHasChildren = ChildrenUnknown;
+ mMailCheckProgressItem = 0;
+ mListDirProgressItem = 0;
+ mAddMessageProgressItem = 0;
+ mReadOnly = false;
+
+ connect (this, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
+ this, SLOT( slotCompleteMailCheckProgress()) );
+}
+
+KMFolderImap::~KMFolderImap()
+{
+ if (mAccount) {
+ mAccount->removeSlaveJobsForFolder( folder() );
+ /* Now that we've removed ourselves from the accounts jobs map, kill all
+ ongoing operations and reset mailcheck if we were deleted during an
+ ongoing mailcheck of our account. Not very gracefull, but safe, and the
+ only way I can see to reset the account state cleanly. */
+ if ( mAccount->checkingMail( folder() ) ) {
+ mAccount->killAllJobs();
+ }
+ }
+ writeConfig();
+ if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
+ mMetaDataMap.setAutoDelete( true );
+ mMetaDataMap.clear();
+ mUidMetaDataMap.setAutoDelete( true );
+ mUidMetaDataMap.clear();
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::reallyDoClose(const char* owner)
+{
+ if (isSelected()) {
+ kdWarning(5006) << "Trying to close the selected folder " << label() <<
+ " - ignoring!" << endl;
+ return;
+ }
+
+ // FIXME is this still needed?
+ if (account())
+ account()->ignoreJobsForFolder( folder() );
+ int idx = count();
+ while (--idx >= 0) {
+ if ( mMsgList[idx]->isMessage() ) {
+ KMMessage *msg = static_cast<KMMessage*>(mMsgList[idx]);
+ if (msg->transferInProgress())
+ msg->setTransferInProgress( false );
+ }
+ }
+ KMFolderMbox::reallyDoClose( owner );
+}
+
+KMFolder* KMFolderImap::trashFolder() const
+{
+ QString trashStr = account()->trash();
+ return kmkernel->imapFolderMgr()->findIdString( trashStr );
+}
+
+//-----------------------------------------------------------------------------
+KMMessage* KMFolderImap::getMsg(int idx)
+{
+ if(!(idx >= 0 && idx <= count()))
+ return 0;
+
+ KMMsgBase* mb = getMsgBase(idx);
+ if (!mb) return 0;
+ if (mb->isMessage())
+ {
+ return ((KMMessage*)mb);
+ } else {
+ KMMessage* msg = FolderStorage::getMsg( idx );
+ if ( msg ) // set it incomplete as the msg was not transferred from the server
+ msg->setComplete( false );
+ return msg;
+ }
+}
+
+//-----------------------------------------------------------------------------
+KMAcctImap* KMFolderImap::account() const
+{
+ if ( !mAccount ) {
+ KMFolderDir *parentFolderDir = dynamic_cast<KMFolderDir*>( folder()->parent() );
+ if ( !parentFolderDir ) {
+ kdWarning() << k_funcinfo << "No parent folder dir found for " << name() << endl;
+ return 0;
+ }
+ KMFolder *parentFolder = parentFolderDir->owner();
+ if ( !parentFolder ) {
+ kdWarning() << k_funcinfo << "No parent folder found for " << name() << endl;
+ return 0;
+ }
+ KMFolderImap *parentStorage = dynamic_cast<KMFolderImap*>( parentFolder->storage() );
+ if ( parentStorage )
+ mAccount = parentStorage->account();
+ }
+ return mAccount;
+}
+
+void KMFolderImap::setAccount(KMAcctImap *aAccount)
+{
+ mAccount = aAccount;
+ if( !folder() || !folder()->child() ) return;
+ KMFolderNode* node;
+ for (node = folder()->child()->first(); node;
+ node = folder()->child()->next())
+ {
+ if (!node->isDir())
+ static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::readConfig()
+{
+ KConfig* config = KMKernel::config();
+ KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
+ mCheckMail = config->readBoolEntry("checkmail", true);
+
+ mUidValidity = config->readEntry("UidValidity");
+ if ( mImapPath.isEmpty() ) {
+ setImapPath( config->readEntry("ImapPath") );
+ }
+ if (QString(name()).upper() == "INBOX" && mImapPath == "/INBOX/")
+ {
+ folder()->setSystemFolder( true );
+ folder()->setLabel( i18n("inbox") );
+ }
+ mNoContent = config->readBoolEntry("NoContent", false);
+ mReadOnly = config->readBoolEntry("ReadOnly", false);
+ mUploadAllFlags = config->readBoolEntry( "UploadAllFlags", true );
+ mPermanentFlags = config->readNumEntry( "PermanentFlags", 31 /* default flags */ );
+
+ KMFolderMbox::readConfig();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::writeConfig()
+{
+ KConfig* config = KMKernel::config();
+ KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
+ config->writeEntry("checkmail", mCheckMail);
+ config->writeEntry("UidValidity", mUidValidity);
+ config->writeEntry("ImapPath", mImapPath);
+ config->writeEntry("NoContent", mNoContent);
+ config->writeEntry("ReadOnly", mReadOnly);
+ config->writeEntry( "UploadAllFlags", mUploadAllFlags );
+ config->writeEntry( "PermanentFlags", mPermanentFlags );
+ KMFolderMbox::writeConfig();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::remove()
+{
+ if ( mAlreadyRemoved || !account() )
+ {
+ // override
+ FolderStorage::remove();
+ return;
+ }
+ KURL url = account()->getUrl();
+ url.setPath(imapPath());
+ if ( account()->makeConnection() == ImapAccountBase::Error ||
+ imapPath().isEmpty() )
+ {
+ emit removed(folder(), false);
+ return;
+ }
+ KIO::SimpleJob *job = KIO::file_delete(url, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ ImapAccountBase::jobData jd(url.url());
+ jd.progressItem = ProgressManager::createProgressItem(
+ "ImapFolderRemove" + ProgressManager::getUniqueID(),
+ i18n("Removing folder"),
+ i18n( "URL: %1" ).arg( QStyleSheet::escape( folder()->prettyURL() ) ),
+ false,
+ account()->useSSL() || account()->useTLS() );
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ this, SLOT(slotRemoveFolderResult(KIO::Job *)));
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotRemoveFolderResult(KIO::Job *job)
+{
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+ if (job->error())
+ {
+ account()->handleJobError( job, i18n("Error while removing a folder.") );
+ emit removed(folder(), false);
+ } else {
+ account()->removeJob(it);
+ FolderStorage::remove();
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::removeMsg(int idx, bool quiet)
+{
+ if (idx < 0)
+ return;
+
+ if (!quiet)
+ {
+ KMMessage *msg = getMsg(idx);
+ deleteMessage(msg);
+ }
+
+ mLastUid = 0;
+ KMFolderMbox::removeMsg(idx);
+}
+
+void KMFolderImap::removeMsg( const QPtrList<KMMessage>& msgList, bool quiet )
+{
+ if ( msgList.isEmpty() ) return;
+ if (!quiet)
+ deleteMessage(msgList);
+
+ mLastUid = 0;
+
+ /* Remove the messages from the local store as well.
+ We don't call KMFolderInherited::removeMsg(QPtrList<KMMessage>) but
+ iterate ourselves, as that would call KMFolderImap::removeMsg(int)
+ and not the one from the store we want to be used. */
+
+ QPtrListIterator<KMMessage> it( msgList );
+ KMMessage *msg;
+ while ( (msg = it.current()) != 0 ) {
+ ++it;
+ int idx = find(msg);
+ assert( idx != -1);
+ // ATTENTION port me to maildir
+ KMFolderMbox::removeMsg(idx, quiet);
+ }
+}
+
+//-----------------------------------------------------------------------------
+int KMFolderImap::rename( const QString& newName, KMFolderDir *aParent )
+{
+ if ( !aParent )
+ KMFolderMbox::rename( newName );
+ kmkernel->folderMgr()->contentsChanged();
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::addMsgQuiet(KMMessage* aMsg)
+{
+ KMFolder *aFolder = aMsg->parent();
+ Q_UINT32 serNum = 0;
+ aMsg->setTransferInProgress( false );
+ if (aFolder) {
+ serNum = aMsg->getMsgSerNum();
+ kmkernel->undoStack()->pushSingleAction( serNum, aFolder, folder() );
+ int idx = aFolder->find( aMsg );
+ assert( idx != -1 );
+ aFolder->take( idx );
+ } else {
+ kdDebug(5006) << k_funcinfo << "no parent" << endl;
+ }
+ if ( !account()->hasCapability("uidplus") ) {
+ // Remember the status with the MD5 as key
+ // so it can be transfered to the new message
+ mMetaDataMap.insert( aMsg->msgIdMD5(),
+ new KMMsgMetaData(aMsg->status(), serNum) );
+ }
+
+ delete aMsg;
+ aMsg = 0;
+ getFolder();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::addMsgQuiet(QPtrList<KMMessage> msgList)
+{
+ if ( mAddMessageProgressItem )
+ {
+ mAddMessageProgressItem->setComplete();
+ mAddMessageProgressItem = 0;
+ }
+ KMFolder *aFolder = msgList.first()->parent();
+ int undoId = -1;
+ bool uidplus = account()->hasCapability("uidplus");
+ for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
+ {
+ if ( undoId == -1 )
+ undoId = kmkernel->undoStack()->newUndoAction( aFolder, folder() );
+ if ( msg->getMsgSerNum() > 0 )
+ kmkernel->undoStack()->addMsgToAction( undoId, msg->getMsgSerNum() );
+ if ( !uidplus ) {
+ // Remember the status with the MD5 as key
+ // so it can be transfered to the new message
+ mMetaDataMap.insert( msg->msgIdMD5(),
+ new KMMsgMetaData(msg->status(), msg->getMsgSerNum()) );
+ }
+ msg->setTransferInProgress( false );
+ }
+ if ( aFolder ) {
+ aFolder->take( msgList );
+ } else {
+ kdDebug(5006) << k_funcinfo << "no parent" << endl;
+ }
+ msgList.setAutoDelete(true);
+ msgList.clear();
+ getFolder();
+}
+
+//-----------------------------------------------------------------------------
+int KMFolderImap::addMsg(KMMessage* aMsg, int* aIndex_ret)
+{
+ QPtrList<KMMessage> list;
+ list.append(aMsg);
+ QValueList<int> index;
+ int ret = addMsg(list, index);
+ aIndex_ret = &index.first();
+ return ret;
+}
+
+int KMFolderImap::addMsg(QPtrList<KMMessage>& msgList, QValueList<int>& aIndex_ret)
+{
+ KMMessage *aMsg = msgList.getFirst();
+ KMFolder *msgParent = aMsg->parent();
+
+ ImapJob *imapJob = 0;
+ if (msgParent)
+ {
+ if (msgParent->folderType() == KMFolderTypeImap)
+ {
+ if (static_cast<KMFolderImap*>(msgParent->storage())->account() == account())
+ {
+ // make sure the messages won't be deleted while we work with them
+ for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
+ msg->setTransferInProgress(true);
+
+ if (folder() == msgParent)
+ {
+ // transfer the whole message, e.g. a draft-message is canceled and re-added to the drafts-folder
+ for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
+ {
+ if (!msg->isComplete())
+ {
+ int idx = msgParent->find(msg);
+ assert(idx != -1);
+ msg = msgParent->getMsg(idx);
+ }
+ imapJob = new ImapJob(msg, ImapJob::tPutMessage, this);
+ connect(imapJob, SIGNAL(messageStored(KMMessage*)),
+ SLOT(addMsgQuiet(KMMessage*)));
+ connect(imapJob, SIGNAL(result(KMail::FolderJob*)),
+ SLOT(slotCopyMsgResult(KMail::FolderJob*)));
+ imapJob->start();
+ }
+
+ } else {
+
+ // get the messages and the uids
+ QValueList<ulong> uids;
+ getUids(msgList, uids);
+
+ // get the sets (do not sort the uids)
+ QStringList sets = makeSets(uids, false);
+
+ for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
+ {
+ // we need the messages that belong to the current set to pass them to the ImapJob
+ QPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList);
+ if ( temp_msgs.isEmpty() ) kdDebug(5006) << "Wow! KMFolderImap::splitMessageList() returned an empty list!" << endl;
+ imapJob = new ImapJob(temp_msgs, *it, ImapJob::tMoveMessage, this);
+ connect(imapJob, SIGNAL(messageCopied(QPtrList<KMMessage>)),
+ SLOT(addMsgQuiet(QPtrList<KMMessage>)));
+ connect(imapJob, SIGNAL(result(KMail::FolderJob*)),
+ SLOT(slotCopyMsgResult(KMail::FolderJob*)));
+ imapJob->start();
+ }
+ }
+ return 0;
+ }
+ else
+ {
+ // different account, check if messages can be added
+ QPtrListIterator<KMMessage> it( msgList );
+ KMMessage *msg;
+ while ( (msg = it.current()) != 0 )
+ {
+ ++it;
+ int index;
+ if (!canAddMsgNow(msg, &index)) {
+ aIndex_ret << index;
+ msgList.remove(msg);
+ } else {
+ if (!msg->transferInProgress())
+ msg->setTransferInProgress(true);
+ }
+ }
+ }
+ } // if imap
+ }
+
+ if ( !msgList.isEmpty() )
+ {
+ // transfer from local folders or other accounts
+ QPtrListIterator<KMMessage> it( msgList );
+ KMMessage* msg;
+ while ( ( msg = it.current() ) != 0 )
+ {
+ ++it;
+ if ( !msg->transferInProgress() )
+ msg->setTransferInProgress( true );
+ }
+ imapJob = new ImapJob( msgList, QString::null, ImapJob::tPutMessage, this );
+ if ( !mAddMessageProgressItem && msgList.count() > 1 )
+ {
+ // use a parent progress if we have more than 1 message
+ // otherwise the normal progress is more accurate
+ mAddMessageProgressItem = ProgressManager::createProgressItem(
+ "Uploading"+ProgressManager::getUniqueID(),
+ i18n("Uploading message data"),
+ i18n("Destination folder: %1").arg( QStyleSheet::escape( folder()->prettyURL() ) ),
+ true,
+ account()->useSSL() || account()->useTLS() );
+ mAddMessageProgressItem->setTotalItems( msgList.count() );
+ connect ( mAddMessageProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem*)),
+ account(), SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
+ imapJob->setParentProgressItem( mAddMessageProgressItem );
+ }
+ connect( imapJob, SIGNAL( messageCopied(QPtrList<KMMessage>) ),
+ SLOT( addMsgQuiet(QPtrList<KMMessage>) ) );
+ connect( imapJob, SIGNAL(result(KMail::FolderJob*)),
+ SLOT(slotCopyMsgResult(KMail::FolderJob*)) );
+ imapJob->start();
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotCopyMsgResult( KMail::FolderJob* job )
+{
+ kdDebug(5006) << k_funcinfo << job->error() << endl;
+ if ( job->error() ) // getFolder() will not be called in this case
+ emit folderComplete( this, false );
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::copyMsg(QPtrList<KMMessage>& msgList)
+{
+ if ( !account()->hasCapability("uidplus") ) {
+ for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
+ // Remember the status with the MD5 as key
+ // so it can be transfered to the new message
+ mMetaDataMap.insert( msg->msgIdMD5(), new KMMsgMetaData(msg->status()) );
+ }
+ }
+
+ QValueList<ulong> uids;
+ getUids(msgList, uids);
+ QStringList sets = makeSets(uids, false);
+ for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
+ {
+ // we need the messages that belong to the current set to pass them to the ImapJob
+ QPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList);
+
+ ImapJob *job = new ImapJob(temp_msgs, *it, ImapJob::tCopyMessage, this);
+ connect(job, SIGNAL(result(KMail::FolderJob*)),
+ SLOT(slotCopyMsgResult(KMail::FolderJob*)));
+ job->start();
+ }
+}
+
+//-----------------------------------------------------------------------------
+QPtrList<KMMessage> KMFolderImap::splitMessageList(const QString& set,
+ QPtrList<KMMessage>& msgList)
+{
+ int lastcomma = set.findRev(",");
+ int lastdub = set.findRev(":");
+ int last = 0;
+ if (lastdub > lastcomma) last = lastdub;
+ else last = lastcomma;
+ last++;
+ if (last < 0) last = set.length();
+ // the last uid of the current set
+ const QString last_uid = set.right(set.length() - last);
+ QPtrList<KMMessage> temp_msgs;
+ QString uid;
+ if (!last_uid.isEmpty())
+ {
+ QPtrListIterator<KMMessage> it( msgList );
+ KMMessage* msg = 0;
+ while ( (msg = it.current()) != 0 )
+ {
+ // append the msg to the new list and delete it from the old
+ temp_msgs.append(msg);
+ uid.setNum( msg->UID() );
+ // remove modifies the current
+ msgList.remove(msg);
+ if (uid == last_uid) break;
+ }
+ }
+ else
+ {
+ // probably only one element
+ temp_msgs = msgList;
+ }
+
+ return temp_msgs;
+}
+
+//-----------------------------------------------------------------------------
+KMMessage* KMFolderImap::take(int idx)
+{
+ KMMsgBase* mb(mMsgList[idx]);
+ if (!mb) return 0;
+ if (!mb->isMessage()) readMsg(idx);
+
+ KMMessage *msg = static_cast<KMMessage*>(mb);
+ deleteMessage(msg);
+
+ mLastUid = 0;
+ return KMFolderMbox::take(idx);
+}
+
+void KMFolderImap::take(QPtrList<KMMessage> msgList)
+{
+ deleteMessage(msgList);
+
+ mLastUid = 0;
+ KMFolderMbox::take(msgList);
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotListNamespaces()
+{
+ disconnect( account(), SIGNAL( connectionResult(int, const QString&) ),
+ this, SLOT( slotListNamespaces() ) );
+ if ( account()->makeConnection() == ImapAccountBase::Error )
+ {
+ kdWarning(5006) << "slotListNamespaces - got no connection" << endl;
+ return;
+ } else if ( account()->makeConnection() == ImapAccountBase::Connecting )
+ {
+ // wait for the connectionResult
+ kdDebug(5006) << "slotListNamespaces - waiting for connection" << endl;
+ connect( account(), SIGNAL( connectionResult(int, const QString&) ),
+ this, SLOT( slotListNamespaces() ) );
+ return;
+ }
+ kdDebug(5006) << "slotListNamespaces" << endl;
+ // reset subfolder states recursively
+ setSubfolderState( imapNoInformation );
+ mSubfolderState = imapListingInProgress;
+ account()->setHasInbox( false );
+
+ ImapAccountBase::ListType type = ImapAccountBase::List;
+ if ( account()->onlySubscribedFolders() )
+ type = ImapAccountBase::ListSubscribed;
+
+ ImapAccountBase::nsMap map = account()->namespaces();
+ QStringList personal = map[ImapAccountBase::PersonalNS];
+ // start personal namespace listing and send it directly to slotListResult
+ for ( QStringList::Iterator it = personal.begin(); it != personal.end(); ++it )
+ {
+ KMail::ListJob* job = new KMail::ListJob( account(), type, this,
+ account()->addPathToNamespace( *it ) );
+ job->setNamespace( *it );
+ job->setHonorLocalSubscription( true );
+ connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
+ const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
+ this, SLOT(slotListResult(const QStringList&, const QStringList&,
+ const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
+ job->start();
+ }
+
+ // and now we list all other namespaces and check them ourself
+ QStringList ns = map[ImapAccountBase::OtherUsersNS];
+ ns += map[ImapAccountBase::SharedNS];
+ for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
+ {
+ KMail::ListJob* job = new KMail::ListJob( account(), type, this, account()->addPathToNamespace( *it ) );
+ job->setHonorLocalSubscription( true );
+ connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
+ const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
+ this, SLOT(slotCheckNamespace(const QStringList&, const QStringList&,
+ const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
+ job->start();
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotCheckNamespace( const QStringList& subfolderNames,
+ const QStringList& subfolderPaths,
+ const QStringList& subfolderMimeTypes,
+ const QStringList& subfolderAttributes,
+ const ImapAccountBase::jobData& jobData )
+{
+ kdDebug(5006) << "slotCheckNamespace - " << subfolderNames.join(",") << endl;
+
+ // get a correct foldername:
+ // strip / and make sure it does not contain the delimiter
+ QString name = jobData.path.mid( 1, jobData.path.length()-2 );
+ name.remove( account()->delimiterForNamespace( name ) );
+ if ( name.isEmpty() ) {
+ // happens when an empty namespace is defined
+ slotListResult( subfolderNames, subfolderPaths,
+ subfolderMimeTypes, subfolderAttributes, jobData );
+ return;
+ }
+
+ folder()->createChildFolder();
+ KMFolderNode *node = 0;
+ for ( node = folder()->child()->first(); node;
+ node = folder()->child()->next())
+ {
+ if ( !node->isDir() && node->name() == name )
+ break;
+ }
+ if ( subfolderNames.isEmpty() )
+ {
+ if ( node )
+ {
+ kdDebug(5006) << "delete namespace folder " << name << endl;
+ KMFolder *fld = static_cast<KMFolder*>(node);
+ KMFolderImap* nsFolder = static_cast<KMFolderImap*>(fld->storage());
+ nsFolder->setAlreadyRemoved( true );
+ kmkernel->imapFolderMgr()->remove( fld );
+ }
+ } else {
+ if ( node )
+ {
+ // folder exists so pass on the attributes
+ kdDebug(5006) << "found namespace folder " << name << endl;
+ if ( !account()->listOnlyOpenFolders() )
+ {
+ KMFolderImap* nsFolder =
+ static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
+ nsFolder->slotListResult( subfolderNames, subfolderPaths,
+ subfolderMimeTypes, subfolderAttributes, jobData );
+ }
+ } else
+ {
+ // create folder
+ kdDebug(5006) << "create namespace folder " << name << endl;
+ KMFolder *fld = folder()->child()->createFolder( name );
+ if ( fld ) {
+ KMFolderImap* f = static_cast<KMFolderImap*> ( fld->storage() );
+ f->initializeFrom( this, account()->addPathToNamespace( name ),
+ "inode/directory" );
+ f->close( "kmfolderimap_create" );
+ if ( !account()->listOnlyOpenFolders() )
+ {
+ f->slotListResult( subfolderNames, subfolderPaths,
+ subfolderMimeTypes, subfolderAttributes, jobData );
+ }
+ }
+ kmkernel->imapFolderMgr()->contentsChanged();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool KMFolderImap::listDirectory()
+{
+ if ( !account() ||
+ ( account() && account()->makeConnection() == ImapAccountBase::Error ) )
+ {
+ kdDebug(5006) << "KMFolderImap::listDirectory - got no connection" << endl;
+ return false;
+ }
+
+ if ( this == account()->rootFolder() )
+ {
+ // a new listing started
+ slotListNamespaces();
+ return true;
+ }
+ mSubfolderState = imapListingInProgress;
+
+ // get the folders
+ ImapAccountBase::ListType type = ImapAccountBase::List;
+ if ( account()->onlySubscribedFolders() )
+ type = ImapAccountBase::ListSubscribed;
+ KMail::ListJob* job = new KMail::ListJob( account(), type, this );
+ job->setParentProgressItem( account()->listDirProgressItem() );
+ job->setHonorLocalSubscription( true );
+ connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
+ const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
+ this, SLOT(slotListResult(const QStringList&, const QStringList&,
+ const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
+ job->start();
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotListResult( const QStringList& subfolderNames,
+ const QStringList& subfolderPaths,
+ const QStringList& subfolderMimeTypes,
+ const QStringList& subfolderAttributes,
+ const ImapAccountBase::jobData& jobData )
+{
+ mSubfolderState = imapFinished;
+ //kdDebug(5006) << label() << ": folderNames=" << subfolderNames << " folderPaths="
+ //<< subfolderPaths << " mimeTypes=" << subfolderMimeTypes << endl;
+
+ // don't react on changes
+ kmkernel->imapFolderMgr()->quiet(true);
+
+ bool root = ( this == account()->rootFolder() );
+ folder()->createChildFolder();
+ if ( root && !account()->hasInbox() )
+ {
+ // create the INBOX
+ initInbox();
+ }
+
+ // see if we have a better parent
+ // if you have a prefix that contains a folder (e.g "INBOX.") the folders
+ // need to be created underneath it
+ if ( root && !subfolderNames.empty() )
+ {
+ KMFolderImap* parent = findParent( subfolderPaths.first(), subfolderNames.first() );
+ if ( parent )
+ {
+ kdDebug(5006) << "KMFolderImap::slotListResult - pass listing to "
+ << parent->label() << endl;
+ parent->slotListResult( subfolderNames, subfolderPaths,
+ subfolderMimeTypes, subfolderAttributes, jobData );
+ // cleanup
+ QStringList list;
+ checkFolders( list, jobData.curNamespace );
+ // finish
+ emit directoryListingFinished( this );
+ kmkernel->imapFolderMgr()->quiet( false );
+ return;
+ }
+ }
+
+ bool emptyList = ( root && subfolderNames.empty() );
+ if ( !emptyList )
+ {
+ checkFolders( subfolderNames, jobData.curNamespace );
+ }
+
+ KMFolderImap *f = 0;
+ KMFolderNode *node = 0;
+ for ( uint i = 0; i < subfolderNames.count(); i++ )
+ {
+ bool settingsChanged = false;
+ // create folders if necessary
+ for ( node = folder()->child()->first(); node;
+ node = folder()->child()->next() ) {
+ if ( !node->isDir() && node->name() == subfolderNames[i] )
+ break;
+ }
+ if ( node ) {
+ f = static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
+ }
+ else if ( subfolderPaths[i].upper() != "/INBOX/" )
+ {
+ kdDebug(5006) << "create folder " << subfolderNames[i] << endl;
+ KMFolder *fld = folder()->child()->createFolder(subfolderNames[i]);
+ if ( fld ) {
+ f = static_cast<KMFolderImap*> ( fld->storage() );
+ f->close( "kmfolderimap_create" );
+ settingsChanged = true;
+ } else {
+ kdWarning(5006) << "can't create folder " << subfolderNames[i] << endl;
+ }
+ }
+ if ( f )
+ {
+ // sanity check
+ if ( f->imapPath().isEmpty() ) {
+ settingsChanged = true;
+ }
+ // update progress
+ account()->listDirProgressItem()->incCompletedItems();
+ account()->listDirProgressItem()->updateProgress();
+ account()->listDirProgressItem()->setStatus( folder()->prettyURL() + i18n(" completed") );
+
+ f->initializeFrom( this, subfolderPaths[i], subfolderMimeTypes[i] );
+ f->setChildrenState( subfolderAttributes[i] );
+ if ( account()->listOnlyOpenFolders() &&
+ f->hasChildren() != FolderStorage::ChildrenUnknown )
+ {
+ settingsChanged = true;
+ }
+
+ if ( settingsChanged )
+ {
+ // tell the tree our information changed
+ kmkernel->imapFolderMgr()->contentsChanged();
+ }
+ if ( ( subfolderMimeTypes[i] == "message/directory" ||
+ subfolderMimeTypes[i] == "inode/directory" ) &&
+ !account()->listOnlyOpenFolders() )
+ {
+ f->listDirectory();
+ }
+ } else {
+ kdWarning(5006) << "can't find folder " << subfolderNames[i] << endl;
+ }
+ } // for subfolders
+
+ // now others should react on the changes
+ kmkernel->imapFolderMgr()->quiet( false );
+ emit directoryListingFinished( this );
+ account()->listDirProgressItem()->setComplete();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::initInbox()
+{
+ KMFolderImap *f = 0;
+ KMFolderNode *node = 0;
+
+ for (node = folder()->child()->first(); node;
+ node = folder()->child()->next()) {
+ if (!node->isDir() && node->name() == "INBOX") break;
+ }
+ if (node) {
+ f = static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
+ } else {
+ f = static_cast<KMFolderImap*>
+ (folder()->child()->createFolder("INBOX", true)->storage());
+ if ( f )
+ {
+ f->folder()->setLabel( i18n("inbox") );
+ f->close( "kmfolderimap" );
+ }
+ kmkernel->imapFolderMgr()->contentsChanged();
+ }
+ if ( f ) {
+ f->initializeFrom( this, "/INBOX/", "message/directory" );
+ f->setChildrenState( QString::null );
+ }
+ // so we have an INBOX
+ account()->setHasInbox( true );
+}
+
+//-----------------------------------------------------------------------------
+KMFolderImap* KMFolderImap::findParent( const QString& path, const QString& name )
+{
+ QString parent = path.left( path.length() - name.length() - 2 );
+ if ( parent.length() > 1 )
+ {
+ // extract name of the parent
+ parent = parent.right( parent.length() - 1 );
+ if ( parent != label() )
+ {
+ KMFolderNode *node = folder()->child()->first();
+ // look for a better parent
+ while ( node )
+ {
+ if ( node->name() == parent )
+ {
+ KMFolder* fld = static_cast<KMFolder*>(node);
+ KMFolderImap* imapFld = static_cast<KMFolderImap*>( fld->storage() );
+ return imapFld;
+ }
+ node = folder()->child()->next();
+ }
+ }
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::checkFolders( const QStringList& subfolderNames,
+ const QString& myNamespace )
+{
+ QPtrList<KMFolder> toRemove;
+ KMFolderNode *node = folder()->child()->first();
+ while ( node )
+ {
+ if ( !node->isDir() && subfolderNames.findIndex(node->name()) == -1 )
+ {
+ KMFolder* fld = static_cast<KMFolder*>(node);
+ KMFolderImap* imapFld = static_cast<KMFolderImap*>( fld->storage() );
+ // as more than one namespace can be listed in the root folder we need to make sure
+ // that the folder is within the current namespace
+ bool isInNamespace = ( myNamespace.isEmpty() ||
+ myNamespace == account()->namespaceForFolder( imapFld ) );
+ kdDebug(5006) << node->name() << " in namespace " << myNamespace << ":" <<
+ isInNamespace << endl;
+ // ignore some cases
+ QString name = node->name();
+ bool ignore = ( ( this == account()->rootFolder() ) &&
+ ( imapFld->imapPath() == "/INBOX/" ||
+ account()->isNamespaceFolder( name ) ||
+ !isInNamespace ) );
+ // additional sanity check for broken folders
+ if ( imapFld->imapPath().isEmpty() ) {
+ ignore = false;
+ }
+ if ( !ignore )
+ {
+ // remove the folder without server round trip
+ kdDebug(5006) << "checkFolders - " << node->name() << " disappeared" << endl;
+ imapFld->setAlreadyRemoved( true );
+ toRemove.append( fld );
+ } else {
+ kdDebug(5006) << "checkFolders - " << node->name() << " ignored" << endl;
+ }
+ }
+ node = folder()->child()->next();
+ }
+ // remove folders
+ for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() )
+ kmkernel->imapFolderMgr()->remove( doomed );
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::initializeFrom( KMFolderImap* parent, QString folderPath,
+ QString mimeType )
+{
+ setAccount( parent->account() );
+ setImapPath( folderPath );
+ setNoContent( mimeType == "inode/directory" );
+ setNoChildren( mimeType == "message/digest" );
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::setChildrenState( QString attributes )
+{
+ // update children state
+ if ( attributes.find( "haschildren", 0, false ) != -1 )
+ {
+ setHasChildren( FolderStorage::HasChildren );
+ } else if ( attributes.find( "hasnochildren", 0, false ) != -1 ||
+ attributes.find( "noinferiors", 0, false ) != -1 )
+ {
+ setHasChildren( FolderStorage::HasNoChildren );
+ } else
+ {
+ if ( account()->listOnlyOpenFolders() ) {
+ setHasChildren( FolderStorage::HasChildren );
+ } else {
+ setHasChildren( FolderStorage::ChildrenUnknown );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::checkValidity()
+{
+ if (!account()) {
+ emit folderComplete(this, false);
+ close("checkvalidity");
+ return;
+ }
+ KURL url = account()->getUrl();
+ url.setPath(imapPath() + ";UID=0:0");
+ kdDebug(5006) << "KMFolderImap::checkValidity of: " << imapPath() << endl;
+
+ // Start with a clean slate
+ disconnect( account(), SIGNAL( connectionResult(int, const QString&) ),
+ this, SLOT( checkValidity() ) );
+
+ KMAcctImap::ConnectionState connectionState = account()->makeConnection();
+ if ( connectionState == ImapAccountBase::Error ) {
+ kdDebug(5006) << "KMFolderImap::checkValidity - got no connection" << endl;
+ emit folderComplete(this, false);
+ mContentState = imapNoInformation;
+ close("checkvalidity");
+ return;
+ } else if ( connectionState == ImapAccountBase::Connecting ) {
+ // We'll wait for the connectionResult signal from the account. If it
+ // errors, the above will catch it.
+ kdDebug(5006) << "CheckValidity - waiting for connection" << endl;
+ connect( account(), SIGNAL( connectionResult(int, const QString&) ),
+ this, SLOT( checkValidity() ) );
+ return;
+ }
+ // Only check once at a time.
+ if (mCheckingValidity) {
+ kdDebug(5006) << "KMFolderImap::checkValidity - already checking" << endl;
+ close("checkvalidity");
+ return;
+ }
+ // otherwise we already are inside a mailcheck
+ if ( !mMailCheckProgressItem ) {
+ ProgressItem* parent = ( account()->checkingSingleFolder() ? 0 :
+ account()->mailCheckProgressItem() );
+ mMailCheckProgressItem = ProgressManager::createProgressItem(
+ parent,
+ "MailCheck" + folder()->prettyURL(),
+ QStyleSheet::escape( folder()->prettyURL() ),
+ i18n("checking"),
+ false,
+ account()->useSSL() || account()->useTLS() );
+ } else {
+ mMailCheckProgressItem->setProgress(0);
+ }
+ if ( account()->mailCheckProgressItem() ) {
+ account()->mailCheckProgressItem()->setStatus( folder()->prettyURL() );
+ }
+ ImapAccountBase::jobData jd( url.url() );
+ KIO::SimpleJob *job = KIO::get(url, false, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ SLOT(slotCheckValidityResult(KIO::Job *)));
+ connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ SLOT(slotSimpleData(KIO::Job *, const QByteArray &)));
+ // Only check once at a time.
+ mCheckingValidity = true;
+}
+
+
+//-----------------------------------------------------------------------------
+ulong KMFolderImap::lastUid()
+{
+ if ( mLastUid > 0 )
+ return mLastUid;
+ open("lastuid");
+ if (count() > 0)
+ {
+ KMMsgBase * base = getMsgBase(count()-1);
+ mLastUid = base->UID();
+ }
+ close("lastuid");
+ return mLastUid;
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotCheckValidityResult(KIO::Job * job)
+{
+ kdDebug(5006) << "KMFolderImap::slotCheckValidityResult of: " << fileName() << endl;
+ mCheckingValidity = false;
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+ if (job->error()) {
+ if ( job->error() != KIO::ERR_ACCESS_DENIED ) {
+ // we suppress access denied messages because they are normally a result of
+ // explicitely set ACLs. Do not save this information (e.g. setNoContent) so that
+ // we notice when this changes
+ account()->handleJobError( job, i18n("Error while querying the server status.") );
+ }
+ mContentState = imapNoInformation;
+ emit folderComplete(this, false);
+ close("checkvalidity");
+ } else {
+ QCString cstr((*it).data.data(), (*it).data.size() + 1);
+ int a = cstr.find("X-uidValidity: ");
+ int b = cstr.find("\r\n", a);
+ QString uidv;
+ if ( (b - a - 15) >= 0 )
+ uidv = cstr.mid(a + 15, b - a - 15);
+ a = cstr.find("X-Access: ");
+ b = cstr.find("\r\n", a);
+ QString access;
+ if ( (b - a - 10) >= 0 )
+ access = cstr.mid(a + 10, b - a - 10);
+ mReadOnly = access == "Read only";
+ a = cstr.find("X-Count: ");
+ b = cstr.find("\r\n", a);
+ int exists = -1;
+ bool ok = false;
+ if ( (b - a - 9) >= 0 )
+ exists = cstr.mid(a + 9, b - a - 9).toInt(&ok);
+ if ( !ok ) exists = -1;
+ a = cstr.find( "X-PermanentFlags: " );
+ b = cstr.find( "\r\n", a );
+ if ( a >= 0 && (b - a - 18) >= 0 )
+ mPermanentFlags = cstr.mid( a + 18, b - a - 18 ).toInt(&ok);
+ if ( !ok ) mPermanentFlags = 0;
+ QString startUid;
+ if (uidValidity() != uidv)
+ {
+ // uidValidity changed
+ kdDebug(5006) << k_funcinfo << "uidValidty changed from "
+ << uidValidity() << " to " << uidv << endl;
+ if ( !uidValidity().isEmpty() )
+ {
+ account()->ignoreJobsForFolder( folder() );
+ mUidMetaDataMap.clear();
+ }
+ mLastUid = 0;
+ setUidValidity(uidv);
+ writeConfig();
+ } else {
+ if (!mCheckFlags)
+ startUid = QString::number(lastUid() + 1);
+ }
+ account()->removeJob(it);
+ if ( mMailCheckProgressItem )
+ {
+ if ( startUid.isEmpty() ) {
+ // flags for all messages are loaded
+ mMailCheckProgressItem->setTotalItems( exists );
+ } else {
+ // only an approximation but doesn't hurt
+ int remain = exists - count();
+ if ( remain < 0 ) remain = 1;
+ mMailCheckProgressItem->setTotalItems( remain );
+ }
+ mMailCheckProgressItem->setCompletedItems( 0 );
+ }
+ reallyGetFolder(startUid);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::getAndCheckFolder(bool force)
+{
+ if (mNoContent)
+ return getFolder(force);
+
+ if ( account() )
+ account()->processNewMailSingleFolder( folder() );
+ if (force) {
+ // force an update
+ mCheckFlags = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::getFolder(bool force)
+{
+ mGuessedUnreadMsgs = -1;
+ if (mNoContent)
+ {
+ mContentState = imapFinished;
+ emit folderComplete(this, true);
+ return;
+ }
+ open("getfolder");
+ mContentState = imapListingInProgress;
+ if (force) {
+ // force an update
+ mCheckFlags = true;
+ }
+ checkValidity();
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::reallyGetFolder(const QString &startUid)
+{
+ KURL url = account()->getUrl();
+ if ( account()->makeConnection() != ImapAccountBase::Connected )
+ {
+ mContentState = imapNoInformation;
+ emit folderComplete(this, false);
+ close("listfolder");
+ return;
+ }
+ quiet(true);
+ if (startUid.isEmpty())
+ {
+ if ( mMailCheckProgressItem )
+ mMailCheckProgressItem->setStatus( i18n("Retrieving message status") );
+ url.setPath(imapPath() + ";SECTION=UID FLAGS");
+ KIO::SimpleJob *job = KIO::listDir(url, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ ImapAccountBase::jobData jd( url.url(), folder() );
+ jd.cancellable = true;
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ this, SLOT(slotListFolderResult(KIO::Job *)));
+ connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
+ this, SLOT(slotListFolderEntries(KIO::Job *,
+ const KIO::UDSEntryList &)));
+ } else {
+ mContentState = imapDownloadInProgress;
+ if ( mMailCheckProgressItem )
+ mMailCheckProgressItem->setStatus( i18n("Retrieving messages") );
+ url.setPath(imapPath() + ";UID=" + startUid
+ + ":*;SECTION=ENVELOPE");
+ KIO::SimpleJob *newJob = KIO::get(url, false, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), newJob);
+ ImapAccountBase::jobData jd( url.url(), folder() );
+ jd.cancellable = true;
+ account()->insertJob(newJob, jd);
+ connect(newJob, SIGNAL(result(KIO::Job *)),
+ this, SLOT(slotGetLastMessagesResult(KIO::Job *)));
+ connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotListFolderResult(KIO::Job * job)
+{
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+ QString uids;
+ if (job->error())
+ {
+ account()->handleJobError( job,
+ i18n("Error while listing the contents of the folder %1.").arg( label() ) );
+ account()->removeJob(it);
+ finishMailCheck( "listfolder", imapNoInformation );
+ return;
+ }
+ mCheckFlags = false;
+ QStringList::Iterator uid;
+ /*
+ The code below does the following:
+ - for each mail in the local store and each entry we got from the server,
+ compare the local uid with the one from the server and update the status
+ flags of the mails
+ - for all mails that are not already locally present, start a job which
+ gets the envelope of each
+ - remove all locally present mails if the server does not list them anymore
+ */
+ if ( count() ) {
+ int idx = 0, c, serverFlags;
+ ulong mailUid, serverUid;
+ uid = (*it).items.begin();
+ while ( idx < count() && uid != (*it).items.end() ) {
+ KMMsgBase *msgBase = getMsgBase( idx );
+ mailUid = msgBase->UID();
+ // parse the uid from the server and the flags out of the list from
+ // the server. Format: 1234, 1
+ c = (*uid).find(",");
+ serverUid = (*uid).left( c ).toLong();
+ serverFlags = (*uid).mid( c+1 ).toInt();
+ if ( mailUid < serverUid ) {
+ removeMsg( idx, true );
+ } else if ( mailUid == serverUid ) {
+ // if this is a read only folder, ignore status updates from the server
+ // since we can't write our status back our local version is what has to
+ // be considered correct.
+ if ( !mReadOnly || !GlobalSettings::allowLocalFlags() ) {
+ int supportedFlags = mUploadAllFlags ? 31 : mPermanentFlags;
+ if ( mReadOnly )
+ supportedFlags = INT_MAX;
+ flagsToStatus( msgBase, serverFlags, false, supportedFlags );
+ } else
+ seenFlagToStatus( msgBase, serverFlags, false );
+ idx++;
+ uid = (*it).items.remove(uid);
+ if ( msgBase->getMsgSerNum() > 0 ) {
+ saveMsgMetaData( static_cast<KMMessage*>(msgBase) );
+ }
+ }
+ else break; // happens only, if deleted mails reappear on the server
+ }
+ // remove all remaining entries in the local cache, they are no longer
+ // present on the server
+ while (idx < count()) removeMsg(idx, true);
+ }
+ // strip the flags from the list of uids, so it can be reused
+ for (uid = (*it).items.begin(); uid != (*it).items.end(); ++uid)
+ (*uid).truncate((*uid).find(","));
+ ImapAccountBase::jobData jd( QString::null, (*it).parent );
+ jd.total = (*it).items.count();
+ if (jd.total == 0)
+ {
+ finishMailCheck( "listfolder", imapFinished );
+ account()->removeJob(it);
+ return;
+ }
+ if ( mMailCheckProgressItem )
+ {
+ // next step for the progressitem
+ mMailCheckProgressItem->setCompletedItems( 0 );
+ mMailCheckProgressItem->setTotalItems( jd.total );
+ mMailCheckProgressItem->setProgress( 0 );
+ mMailCheckProgressItem->setStatus( i18n("Retrieving messages") );
+ }
+
+ QStringList sets;
+ uid = (*it).items.begin();
+ if (jd.total == 1) sets.append(*uid + ":" + *uid);
+ else sets = makeSets( (*it).items );
+ account()->removeJob(it); // don't use *it below
+
+ // Now kick off the getting of envelopes for the new mails in the folder
+ for (QStringList::Iterator i = sets.begin(); i != sets.end(); ++i)
+ {
+ mContentState = imapDownloadInProgress;
+ KURL url = account()->getUrl();
+ url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE");
+ KIO::SimpleJob *newJob = KIO::get(url, false, false);
+ jd.url = url.url();
+ KIO::Scheduler::assignJobToSlave(account()->slave(), newJob);
+ account()->insertJob(newJob, jd);
+ connect(newJob, SIGNAL(result(KIO::Job *)),
+ this, (i == sets.at(sets.count() - 1))
+ ? SLOT(slotGetLastMessagesResult(KIO::Job *))
+ : SLOT(slotGetMessagesResult(KIO::Job *)));
+ connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotListFolderEntries(KIO::Job * job,
+ const KIO::UDSEntryList & uds)
+{
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+ QString mimeType, name;
+ long int flags = 0;
+ for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
+ udsIt != uds.end(); udsIt++)
+ {
+ for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
+ eIt != (*udsIt).end(); eIt++)
+ {
+ if ((*eIt).m_uds == KIO::UDS_NAME)
+ name = (*eIt).m_str;
+ else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
+ mimeType = (*eIt).m_str;
+ else if ((*eIt).m_uds == KIO::UDS_ACCESS)
+ flags = (*eIt).m_long;
+ }
+ if ((mimeType == "message/rfc822-imap" || mimeType == "message/rfc822") &&
+ !(flags & 8)) {
+ (*it).items.append(name + "," + QString::number(flags));
+ if ( mMailCheckProgressItem ) {
+ mMailCheckProgressItem->incCompletedItems();
+ mMailCheckProgressItem->updateProgress();
+ }
+ }
+ }
+}
+
+
+// debugging helper
+//X static QString flagsToString( int flags )
+//X {
+//X QString str("(");
+//X if ( flags & 4 ) {
+//X str += "\\Flagged ";
+//X }
+//X if ( flags & 2 ) {
+//X str += "\\Answered ";
+//X }
+//X if ( flags & 1 ) {
+//X str += "\\Seen";
+//X }
+//X str += ")";
+//X return str;
+//X }
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg, int supportedFlags )
+{
+ if ( !msg ) return;
+
+ // see imap4/imapinfo.h for the magic numbers
+ static const struct {
+ const int imapFlag;
+ const int kmFlag;
+ const bool standardFlag;
+ } imapFlagMap[] = {
+ { 2, KMMsgStatusReplied, true },
+ { 4, KMMsgStatusFlag, true },
+ { 128, KMMsgStatusForwarded, false },
+ { 256, KMMsgStatusTodo, false },
+ { 512, KMMsgStatusWatched, false },
+ { 1024, KMMsgStatusIgnored, false }
+ };
+ static const int numFlags = sizeof imapFlagMap / sizeof *imapFlagMap;
+
+ const KMMsgStatus oldStatus = msg->status();
+ for ( int i = 0; i < numFlags; ++i ) {
+ if ( ( (supportedFlags & imapFlagMap[i].imapFlag) == 0 && (supportedFlags & 64) == 0 )
+ && !imapFlagMap[i].standardFlag ) {
+ continue;
+ }
+ if ( ((flags & imapFlagMap[i].imapFlag) > 0) != ((oldStatus & imapFlagMap[i].kmFlag) > 0) ) {
+ msg->toggleStatus( imapFlagMap[i].kmFlag );
+ }
+ }
+
+ seenFlagToStatus( msg, flags, newMsg );
+}
+
+void KMFolderImap::seenFlagToStatus(KMMsgBase * msg, int flags, bool newMsg)
+{
+ if ( !msg ) return;
+
+ const KMMsgStatus oldStatus = msg->status();
+ if ( (flags & 1) && (oldStatus & KMMsgStatusOld) == 0 )
+ msg->setStatus( KMMsgStatusOld );
+
+ // In case the message does not have the seen flag set, override our local
+ // notion that it is read. Otherwise the count of unread messages and the
+ // number of messages which actually show up as read can go out of sync.
+ if ( msg->isOfUnknownStatus() || (!(flags&1) && !(oldStatus&(KMMsgStatusNew|KMMsgStatusUnread)) ) ) {
+ if (newMsg) {
+ if ( (oldStatus & KMMsgStatusNew) == 0 )
+ msg->setStatus( KMMsgStatusNew );
+ } else {
+ if ( (oldStatus & KMMsgStatusUnread) == 0 )
+ msg->setStatus( KMMsgStatusUnread );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+QString KMFolderImap::statusToFlags(KMMsgStatus status, int supportedFlags)
+{
+ QString flags;
+ if (status & KMMsgStatusDeleted)
+ flags = "\\DELETED";
+ else {
+ if (status & KMMsgStatusOld || status & KMMsgStatusRead)
+ flags = "\\SEEN ";
+ if (status & KMMsgStatusReplied)
+ flags += "\\ANSWERED ";
+ if (status & KMMsgStatusFlag)
+ flags += "\\FLAGGED ";
+ // non standard flags
+ if ( (status & KMMsgStatusForwarded) && ((supportedFlags & 64) || (supportedFlags & 128)) )
+ flags += "$FORWARDED ";
+ if ( (status & KMMsgStatusTodo) && ((supportedFlags & 64) || (supportedFlags & 256)) )
+ flags += "$TODO ";
+ if ( (status & KMMsgStatusWatched) && ((supportedFlags & 64) || (supportedFlags & 512)) )
+ flags += "$WATCHED ";
+ if ( (status & KMMsgStatusIgnored) && ((supportedFlags & 64) || (supportedFlags & 1024)) )
+ flags += "$IGNORED ";
+ }
+
+ return flags.simplifyWhiteSpace();
+}
+
+//-------------------------------------------------------------
+void
+KMFolderImap::ignoreJobsForMessage( KMMessage* msg )
+{
+ if ( !msg || msg->transferInProgress() ||
+ !msg->parent() || msg->parent()->folderType() != KMFolderTypeImap )
+ return;
+ KMAcctImap *account;
+ if ( !(account = static_cast<KMFolderImap*>(msg->storage())->account()) )
+ return;
+
+ account->ignoreJobsForMessage( msg );
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
+{
+ if ( data.isEmpty() ) return; // optimization
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+ (*it).cdata += QCString(data, data.size() + 1);
+ int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
+ if ( pos == -1 ) {
+ // if we do not find the pattern in the complete string we will not find
+ // it in a substring.
+ return;
+ }
+ if (pos > 0)
+ {
+ int p = (*it).cdata.find("\r\nX-uidValidity:");
+ if (p != -1) setUidValidity((*it).cdata
+ .mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
+ int c = (*it).cdata.find("\r\nX-Count:");
+ if ( c != -1 )
+ {
+ bool ok;
+ int exists = (*it).cdata.mid( c+10,
+ (*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok);
+ if ( ok && exists < count() ) {
+ kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" <<
+ exists << ") then folder (" << count() << "), so reload" << endl;
+ open("getMessage");
+ reallyGetFolder( QString::null );
+ (*it).cdata.remove(0, pos);
+ return;
+ } else if ( ok ) {
+ int delta = exists - count();
+ if ( mMailCheckProgressItem ) {
+ mMailCheckProgressItem->setTotalItems( delta );
+ }
+ }
+ }
+ (*it).cdata.remove(0, pos);
+ }
+ pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
+ int flags;
+ while (pos >= 0)
+ {
+ KMMessage *msg = new KMMessage;
+ msg->setComplete( false );
+ msg->setReadyToShow( false );
+ // nothing between the boundaries, older UWs do that
+ if ( pos != 14 ) {
+ msg->fromString( (*it).cdata.mid(16, pos - 16) );
+ flags = msg->headerField("X-Flags").toInt();
+ ulong uid = msg->UID();
+ KMMsgMetaData *md = 0;
+ if ( mUidMetaDataMap.find( uid ) ) {
+ md = mUidMetaDataMap[uid];
+ }
+ ulong serNum = 0;
+ if ( md ) {
+ serNum = md->serNum();
+ }
+ bool ok = true;
+ if ( uid <= lastUid() && serNum > 0 ) {
+ // the UID is already known so no need to create it
+ ok = false;
+ }
+ // deleted flag
+ if ( flags & 8 )
+ ok = false;
+ if ( !ok ) {
+ delete msg;
+ msg = 0;
+ } else {
+ if ( serNum > 0 ) {
+ // assign the sernum from the cache
+ msg->setMsgSerNum( serNum );
+ }
+ // Transfer the status, if it is cached.
+ if ( md ) {
+ msg->setStatus( md->status() );
+ } else if ( !account()->hasCapability("uidplus") ) {
+ // see if we have cached the msgIdMD5 and get the status +
+ // serial number from there
+ QString id = msg->msgIdMD5();
+ if ( mMetaDataMap.find( id ) ) {
+ md = mMetaDataMap[id];
+ msg->setStatus( md->status() );
+ if ( md->serNum() != 0 && serNum == 0 ) {
+ msg->setMsgSerNum( md->serNum() );
+ }
+ mMetaDataMap.remove( id );
+ delete md;
+ }
+ }
+ KMFolderMbox::addMsg(msg, 0);
+ // Merge with the flags from the server.
+ flagsToStatus((KMMsgBase*)msg, flags, true, mUploadAllFlags ? 31 : mPermanentFlags);
+ // set the correct size
+ msg->setMsgSizeServer( msg->headerField("X-Length").toUInt() );
+ msg->setUID(uid);
+ if ( msg->getMsgSerNum() > 0 ) {
+ saveMsgMetaData( msg );
+ }
+ // Filter messages that have arrived in the inbox folder
+ if ( folder()->isSystemFolder() && imapPath() == "/INBOX/"
+ && kmkernel->filterMgr()->atLeastOneIncomingFilterAppliesTo( account()->id() ) )
+ account()->execFilters( msg->getMsgSerNum() );
+
+ if ( count() > 1 ) {
+ unGetMsg(count() - 1);
+ }
+ mLastUid = uid;
+ if ( mMailCheckProgressItem ) {
+ mMailCheckProgressItem->incCompletedItems();
+ mMailCheckProgressItem->updateProgress();
+ }
+ }
+ }
+ (*it).cdata.remove(0, pos);
+ (*it).done++;
+ pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
+ } // while
+}
+
+//-------------------------------------------------------------
+FolderJob*
+KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
+ KMFolder *folder, QString partSpecifier,
+ const AttachmentStrategy *as ) const
+{
+ KMFolderImap* kmfi = folder? dynamic_cast<KMFolderImap*>(folder->storage()) : 0;
+ if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" &&
+ account() && account()->loadOnDemand() &&
+ ( msg->msgSizeServer() > 5000 || msg->msgSizeServer() == 0 ) &&
+ ( msg->signatureState() == KMMsgNotSigned ||
+ msg->signatureState() == KMMsgSignatureStateUnknown ) &&
+ ( msg->encryptionState() == KMMsgNotEncrypted ||
+ msg->encryptionState() == KMMsgEncryptionStateUnknown ) )
+ {
+ // load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers
+ // this is not activated for small or signed messages
+ ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" );
+ job->start();
+ ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as );
+ job2->start();
+ job->setParentFolder( this );
+ return job;
+ } else {
+ // download complete message or part (attachment)
+ if ( partSpecifier == "STRUCTURE" ) // hide from outside
+ partSpecifier = QString::null;
+
+ ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier );
+ job->setParentFolder( this );
+ return job;
+ }
+}
+
+//-------------------------------------------------------------
+FolderJob*
+KMFolderImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
+ FolderJob::JobType jt, KMFolder *folder ) const
+{
+ KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder->storage());
+ ImapJob *job = new ImapJob( msgList, sets, jt, kmfi );
+ job->setParentFolder( this );
+ return job;
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet)
+{
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+ if (job->error()) {
+ account()->handleJobError( job, i18n("Error while retrieving messages.") );
+ finishMailCheck( "getMessage", imapNoInformation );
+ return;
+ }
+ if (lastSet) {
+ finishMailCheck( "getMessage", imapFinished );
+ account()->removeJob(it);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job)
+{
+ getMessagesResult(job, true);
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotGetMessagesResult(KIO::Job * job)
+{
+ getMessagesResult(job, false);
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::createFolder(const QString &name, const QString& parentPath,
+ bool askUser)
+{
+ kdDebug(5006) << "KMFolderImap::createFolder - name=" << name << ",parent=" <<
+ parentPath << ",askUser=" << askUser << endl;
+ if ( account()->makeConnection() != ImapAccountBase::Connected ) {
+ kdWarning(5006) << "KMFolderImap::createFolder - got no connection" << endl;
+ return;
+ }
+ KURL url = account()->getUrl();
+ QString parent = ( parentPath.isEmpty() ? imapPath() : parentPath );
+ QString path = account()->createImapPath( parent, name );
+ if ( askUser ) {
+ path += "/;INFO=ASKUSER";
+ }
+ url.setPath( path );
+
+ KIO::SimpleJob *job = KIO::mkdir(url);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ ImapAccountBase::jobData jd( url.url(), folder() );
+ jd.items = name;
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ this, SLOT(slotCreateFolderResult(KIO::Job *)));
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotCreateFolderResult(KIO::Job * job)
+{
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+
+ QString name;
+ if ( it.data().items.count() > 0 )
+ name = it.data().items.first();
+
+ if (job->error())
+ {
+ if ( job->error() == KIO::ERR_COULD_NOT_MKDIR ) {
+ // Creating a folder failed, remove it from the tree.
+ account()->listDirectory( );
+ }
+ account()->handleJobError( job, i18n("Error while creating a folder.") );
+ emit folderCreationResult( name, false );
+ } else {
+ listDirectory();
+ account()->removeJob(job);
+ emit folderCreationResult( name, true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+static QTextCodec *sUtf7Codec = 0;
+
+QTextCodec * KMFolderImap::utf7Codec()
+{
+ if (!sUtf7Codec) sUtf7Codec = QTextCodec::codecForName("utf-7");
+ return sUtf7Codec;
+}
+
+
+//-----------------------------------------------------------------------------
+QString KMFolderImap::encodeFileName(const QString &name)
+{
+ QString result = utf7Codec()->fromUnicode(name);
+ return KURL::encode_string_no_slash(result);
+}
+
+
+//-----------------------------------------------------------------------------
+QString KMFolderImap::decodeFileName(const QString &name)
+{
+ QString result = KURL::decode_string(name);
+ return utf7Codec()->toUnicode(result.latin1());
+}
+
+//-----------------------------------------------------------------------------
+bool KMFolderImap::autoExpunge()
+{
+ if (account())
+ return account()->autoExpunge();
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
+{
+ if ( data.isEmpty() ) return; // optimization
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+ QBuffer buff((*it).data);
+ buff.open(IO_WriteOnly | IO_Append);
+ buff.writeBlock(data.data(), data.size());
+ buff.close();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::deleteMessage(KMMessage * msg)
+{
+ mUidMetaDataMap.remove( msg->UID() );
+ mMetaDataMap.remove( msg->msgIdMD5() );
+ KURL url = account()->getUrl();
+ KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msg->storage());
+ ulong uid = msg->UID();
+ /* If the uid is empty the delete job below will nuke all mail in the
+ folder, so we better safeguard against that. See ::expungeFolder, as
+ to why. :( */
+ if ( uid == 0 ) {
+ kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete "
+ "an empty UID. Aborting." << endl;
+ return;
+ }
+ url.setPath(msg_parent->imapPath() + ";UID=" + QString::number(uid) );
+ if ( account()->makeConnection() != ImapAccountBase::Connected )
+ return;
+ KIO::SimpleJob *job = KIO::file_delete(url, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ ImapAccountBase::jobData jd( url.url(), 0 );
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ account(), SLOT(slotSimpleResult(KIO::Job *)));
+}
+
+void KMFolderImap::deleteMessage(const QPtrList<KMMessage>& msgList)
+{
+ QPtrListIterator<KMMessage> it( msgList );
+ KMMessage *msg;
+ while ( (msg = it.current()) != 0 ) {
+ ++it;
+ mUidMetaDataMap.remove( msg->UID() );
+ mMetaDataMap.remove( msg->msgIdMD5() );
+ }
+
+ QValueList<ulong> uids;
+ getUids(msgList, uids);
+ QStringList sets = makeSets(uids);
+
+ KURL url = account()->getUrl();
+ KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msgList.getFirst()->storage());
+ for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
+ {
+ QString uid = *it;
+ // Don't delete with no uid, that nukes the folder. Should not happen, but
+ // better safe than sorry.
+ if ( uid.isEmpty() ) continue;
+ url.setPath(msg_parent->imapPath() + ";UID=" + uid);
+ if ( account()->makeConnection() != ImapAccountBase::Connected )
+ return;
+ KIO::SimpleJob *job = KIO::file_delete(url, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ ImapAccountBase::jobData jd( url.url(), 0 );
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ account(), SLOT(slotSimpleResult(KIO::Job *)));
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::setStatus(int idx, KMMsgStatus status, bool toggle)
+{
+ QValueList<int> ids; ids.append(idx);
+ setStatus(ids, status, toggle);
+}
+
+void KMFolderImap::setStatus(QValueList<int>& _ids, KMMsgStatus status, bool toggle)
+{
+ FolderStorage::setStatus(_ids, status, toggle);
+ QValueList<int> ids;
+ if ( mUploadAllFlags ) {
+ kdDebug(5006) << k_funcinfo << "Migrating all flags to the server" << endl;
+ ids.clear();
+ for ( int i = 0; i < count(); ++i )
+ ids << i;
+ mUploadAllFlags = false;
+ } else {
+ ids = _ids;
+ }
+
+ /* The status has been already set in the local index. Update the flags on
+ * the server. To avoid doing that for each message individually, group them
+ * by the status string they will be assigned and make sets for each of those
+ * groups of mails. This is necessary because the imap kio_slave status job
+ * does not append flags but overwrites them. Example:
+ *
+ * 2 important mails and 3 unimportant mail, all unread. Mark all as read calls
+ * this method with a list of uids. The 2 important mails need to get the string
+ * \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each
+ * of those and sort them, so the server can handle them efficiently. */
+
+ if ( mReadOnly ) { // mUserRights is not available here
+ // FIXME duplicated code in KMFolderCachedImap
+ QValueList<ulong> seenUids, unseenUids;
+ for ( QValueList<int>::ConstIterator it = ids.constBegin(); it != ids.constEnd(); ++it ) {
+ KMMessage *msg = 0;
+ bool unget = !isMessage(*it);
+ msg = getMsg(*it);
+ if (!msg) continue;
+ if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
+ seenUids.append( msg->UID() );
+ else
+ unseenUids.append( msg->UID() );
+ if (unget) unGetMsg(*it);
+ }
+ if ( !seenUids.isEmpty() ) {
+ QStringList sets = KMFolderImap::makeSets( seenUids, true );
+ for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
+ QString imappath = imapPath() + ";UID=" + ( *it );
+ account()->setImapSeenStatus( folder(), imappath, true );
+ }
+ }
+ if ( !unseenUids.isEmpty() ) {
+ QStringList sets = KMFolderImap::makeSets( unseenUids, true );
+ for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
+ QString imappath = imapPath() + ";UID=" + ( *it );
+ account()->setImapSeenStatus( folder(), imappath, false );
+ }
+ }
+ return;
+ }
+
+ QMap< QString, QStringList > groups;
+ for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) {
+ KMMessage *msg = 0;
+ bool unget = !isMessage(*it);
+ msg = getMsg(*it);
+ if (!msg) continue;
+ QString flags = statusToFlags(msg->status(), mPermanentFlags);
+ // Collect uids for each type of flags.
+ groups[flags].append(QString::number(msg->UID()));
+ if (unget) unGetMsg(*it);
+ }
+ QMapIterator< QString, QStringList > dit;
+ for ( dit = groups.begin(); dit != groups.end(); ++dit ) {
+ QCString flags = dit.key().latin1();
+ QStringList sets = makeSets( (*dit), true );
+ // Send off a status setting job for each set.
+ for ( QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
+ QString imappath = imapPath() + ";UID=" + ( *slit );
+ account()->setImapStatus(folder(), imappath, flags);
+ }
+ }
+ if ( mContentState == imapListingInProgress ) {
+ // we're currently get'ing this folder
+ // to make sure that we get the latest flags abort the current listing and
+ // create a new one
+ kdDebug(5006) << "Set status during folder listing, restarting listing." << endl;
+ disconnect(this, SLOT(slotListFolderResult(KIO::Job *)));
+ quiet( false );
+ reallyGetFolder( QString::null );
+ }
+}
+
+//-----------------------------------------------------------------------------
+QStringList KMFolderImap::makeSets(const QStringList& uids, bool sort)
+{
+ QValueList<ulong> tmp;
+ for ( QStringList::ConstIterator it = uids.begin(); it != uids.end(); ++it )
+ tmp.append( (*it).toInt() );
+ return makeSets(tmp, sort);
+}
+
+QStringList KMFolderImap::makeSets( QValueList<ulong>& uids, bool sort )
+{
+ QStringList sets;
+ QString set;
+
+ if (uids.size() == 1)
+ {
+ sets.append(QString::number(uids.first()));
+ return sets;
+ }
+
+ if (sort) qHeapSort(uids);
+
+ ulong last = 0;
+ // needed to make a uid like 124 instead of 124:124
+ bool inserted = false;
+ /* iterate over uids and build sets like 120:122,124,126:150 */
+ for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
+ {
+ if (it == uids.begin() || set.isEmpty()) {
+ set = QString::number(*it);
+ inserted = true;
+ } else
+ {
+ if (last+1 != *it)
+ {
+ // end this range
+ if (inserted)
+ set += ',' + QString::number(*it);
+ else
+ set += ':' + QString::number(last) + ',' + QString::number(*it);
+ inserted = true;
+ if (set.length() > 100)
+ {
+ // just in case the server has a problem with longer lines..
+ sets.append(set);
+ set = "";
+ }
+ } else {
+ inserted = false;
+ }
+ }
+ last = *it;
+ }
+ // last element
+ if (!inserted)
+ set += ':' + QString::number(uids.last());
+
+ if (!set.isEmpty()) sets.append(set);
+
+ return sets;
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::getUids(QValueList<int>& ids, QValueList<ulong>& uids)
+{
+ KMMsgBase *msg = 0;
+ // get the uids
+ for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
+ {
+ msg = getMsgBase(*it);
+ if (!msg) continue;
+ uids.append(msg->UID());
+ }
+}
+
+void KMFolderImap::getUids(const QPtrList<KMMessage>& msgList, QValueList<ulong>& uids)
+{
+ KMMessage *msg = 0;
+
+ QPtrListIterator<KMMessage> it( msgList );
+ while ( (msg = it.current()) != 0 ) {
+ ++it;
+ if ( msg->UID() > 0 ) {
+ uids.append( msg->UID() );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet)
+{
+ aFolder->setNeedsCompacting(false);
+ KURL url = account()->getUrl();
+ url.setPath(aFolder->imapPath() + ";UID=*");
+ if ( account()->makeConnection() != ImapAccountBase::Connected )
+ return;
+ KIO::SimpleJob *job = KIO::file_delete(url, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ ImapAccountBase::jobData jd( url.url(), 0 );
+ jd.quiet = quiet;
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ account(), SLOT(slotSimpleResult(KIO::Job *)));
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotProcessNewMail( int errorCode, const QString &errorMsg )
+{
+ Q_UNUSED( errorMsg );
+ disconnect( account(), SIGNAL( connectionResult(int, const QString&) ),
+ this, SLOT( slotProcessNewMail(int, const QString&) ) );
+ if ( !errorCode )
+ processNewMail( false );
+ else
+ emit numUnreadMsgsChanged( folder() );
+}
+
+//-----------------------------------------------------------------------------
+bool KMFolderImap::processNewMail(bool)
+{
+ // a little safety
+ if ( !account() ) {
+ kdDebug(5006) << "KMFolderImap::processNewMail - account is null!" << endl;
+ return false;
+ }
+ if ( imapPath().isEmpty() ) {
+ kdDebug(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl;
+ // remove it locally
+ setAlreadyRemoved( true );
+ kmkernel->imapFolderMgr()->remove( folder() );
+ return false;
+ }
+ // check the connection
+ if ( account()->makeConnection() == ImapAccountBase::Error ) {
+ kdDebug(5006) << "KMFolderImap::processNewMail - got no connection!" << endl;
+ return false;
+ } else if ( account()->makeConnection() == ImapAccountBase::Connecting )
+ {
+ // wait
+ kdDebug(5006) << "KMFolderImap::processNewMail - waiting for connection: " << label() << endl;
+ connect( account(), SIGNAL( connectionResult(int, const QString&) ),
+ this, SLOT( slotProcessNewMail(int, const QString&) ) );
+ return true;
+ }
+ KURL url = account()->getUrl();
+ if (mReadOnly)
+ url.setPath(imapPath() + ";SECTION=UIDNEXT");
+ else
+ url.setPath(imapPath() + ";SECTION=UNSEEN");
+
+ mMailCheckProgressItem = ProgressManager::createProgressItem(
+ "MailCheckAccount" + account()->name(),
+ "MailCheck" + folder()->prettyURL(),
+ QStyleSheet::escape( folder()->prettyURL() ),
+ i18n("updating message counts"),
+ false,
+ account()->useSSL() || account()->useTLS() );
+
+ KIO::SimpleJob *job = KIO::stat(url, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ ImapAccountBase::jobData jd(url.url(), folder() );
+ jd.cancellable = true;
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ SLOT(slotStatResult(KIO::Job *)));
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotStatResult(KIO::Job * job)
+{
+ slotCompleteMailCheckProgress();
+ ImapAccountBase::JobIterator it = account()->findJob(job);
+ if ( it == account()->jobsEnd() ) return;
+ account()->removeJob(it);
+ if (job->error())
+ {
+ account()->handleJobError( job, i18n("Error while getting folder information.") );
+ } else {
+ KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult();
+ for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++)
+ {
+ if ((*it).m_uds == KIO::UDS_SIZE)
+ {
+ if (mReadOnly)
+ {
+ mGuessedUnreadMsgs = -1;
+ mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1;
+ if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0;
+ } else {
+ mGuessedUnreadMsgs = (*it).m_long;
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+int KMFolderImap::create()
+{
+ readConfig();
+ mUnreadMsgs = -1;
+ return KMFolderMbox::create();
+}
+
+QValueList<ulong> KMFolderImap::splitSets(const QString uids)
+{
+ QValueList<ulong> uidlist;
+
+ // ex: 1205,1204,1203,1202,1236:1238
+ QString buffer = QString::null;
+ int setstart = -1;
+ // iterate over the uids
+ for (uint i = 0; i < uids.length(); i++)
+ {
+ QChar chr = uids[i];
+ if (chr == ',')
+ {
+ if (setstart > -1)
+ {
+ // a range (uid:uid) was before
+ for (int j = setstart; j <= buffer.toInt(); j++)
+ {
+ uidlist.append(j);
+ }
+ setstart = -1;
+ } else {
+ // single uid
+ uidlist.append(buffer.toInt());
+ }
+ buffer = "";
+ } else if (chr == ':') {
+ // remember the start of the range
+ setstart = buffer.toInt();
+ buffer = "";
+ } else if (chr.category() == QChar::Number_DecimalDigit) {
+ // digit
+ buffer += chr;
+ } else {
+ // ignore
+ }
+ }
+ // process the last data
+ if (setstart > -1)
+ {
+ for (int j = setstart; j <= buffer.toInt(); j++)
+ {
+ uidlist.append(j);
+ }
+ } else {
+ uidlist.append(buffer.toInt());
+ }
+
+ return uidlist;
+}
+
+//-----------------------------------------------------------------------------
+int KMFolderImap::expungeContents()
+{
+ // nuke the local cache
+ int rc = KMFolderMbox::expungeContents();
+
+ // set the deleted flag for all messages in the folder
+ KURL url = account()->getUrl();
+ url.setPath( imapPath() + ";UID=1:*");
+ if ( account()->makeConnection() == ImapAccountBase::Connected )
+ {
+ KIO::SimpleJob *job = KIO::file_delete(url, false);
+ KIO::Scheduler::assignJobToSlave(account()->slave(), job);
+ ImapAccountBase::jobData jd( url.url(), 0 );
+ jd.quiet = true;
+ account()->insertJob(job, jd);
+ connect(job, SIGNAL(result(KIO::Job *)),
+ account(), SLOT(slotSimpleResult(KIO::Job *)));
+ }
+ /* Is the below correct? If we are expunging (in the folder sense, not the imap sense),
+ why delete but not (imap-)expunge? Since the folder is not active there is no concept
+ of "leaving the folder", so the setting really has little to do with it. */
+ // if ( autoExpunge() )
+ expungeFolder(this, true);
+ getFolder();
+
+ return rc;
+}
+
+//-----------------------------------------------------------------------------
+void
+KMFolderImap::setUserRights( unsigned int userRights )
+{
+ mUserRights = userRights;
+ kdDebug(5006) << imapPath() << " setUserRights: " << userRights << endl;
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotCompleteMailCheckProgress()
+{
+ if ( mMailCheckProgressItem ) {
+ mMailCheckProgressItem->setComplete();
+ mMailCheckProgressItem = 0;
+ emit numUnreadMsgsChanged( folder() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::setSubfolderState( imapState state )
+{
+ mSubfolderState = state;
+ if ( state == imapNoInformation && folder()->child() )
+ {
+ // pass through to children
+ KMFolderNode* node;
+ QPtrListIterator<KMFolderNode> it( *folder()->child() );
+ for ( ; (node = it.current()); )
+ {
+ ++it;
+ if (node->isDir()) continue;
+ KMFolder *folder = static_cast<KMFolder*>(node);
+ static_cast<KMFolderImap*>(folder->storage())->setSubfolderState( state );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::setIncludeInMailCheck( bool check )
+{
+ bool changed = ( mCheckMail != check );
+ mCheckMail = check;
+ if ( changed )
+ account()->slotUpdateFolderList();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::setAlreadyRemoved( bool removed )
+{
+ mAlreadyRemoved = removed;
+ if ( folder()->child() )
+ {
+ // pass through to childs
+ KMFolderNode* node;
+ QPtrListIterator<KMFolderNode> it( *folder()->child() );
+ for ( ; (node = it.current()); )
+ {
+ ++it;
+ if (node->isDir()) continue;
+ KMFolder *folder = static_cast<KMFolder*>(node);
+ static_cast<KMFolderImap*>(folder->storage())->setAlreadyRemoved( removed );
+ }
+ }
+}
+
+void KMFolderImap::slotCreatePendingFolders( int errorCode, const QString& errorMsg )
+{
+ Q_UNUSED( errorMsg );
+ disconnect( account(), SIGNAL( connectionResult( int, const QString& ) ),
+ this, SLOT( slotCreatePendingFolders( int, const QString& ) ) );
+ if ( !errorCode ) {
+ QStringList::Iterator it = mFoldersPendingCreation.begin();
+ for ( ; it != mFoldersPendingCreation.end(); ++it ) {
+ createFolder( *it );
+ }
+ }
+ mFoldersPendingCreation.clear();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::search( const KMSearchPattern* pattern )
+{
+ if ( !pattern || pattern->isEmpty() )
+ {
+ // not much to do here
+ QValueList<Q_UINT32> serNums;
+ emit searchResult( folder(), serNums, pattern, true );
+ return;
+ }
+ SearchJob* job = new SearchJob( this, account(), pattern );
+ connect( job, SIGNAL( searchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ),
+ this, SLOT( slotSearchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ) );
+ job->start();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotSearchDone( QValueList<Q_UINT32> serNums,
+ const KMSearchPattern* pattern,
+ bool complete )
+{
+ emit searchResult( folder(), serNums, pattern, complete );
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::search( const KMSearchPattern* pattern, Q_UINT32 serNum )
+{
+ if ( !pattern || pattern->isEmpty() )
+ {
+ // not much to do here
+ emit searchDone( folder(), serNum, pattern, false );
+ return;
+ }
+ SearchJob* job = new SearchJob( this, account(), pattern, serNum );
+ connect( job, SIGNAL( searchDone( Q_UINT32, const KMSearchPattern*, bool ) ),
+ this, SLOT( slotSearchDone( Q_UINT32, const KMSearchPattern*, bool ) ) );
+ job->start();
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::slotSearchDone( Q_UINT32 serNum, const KMSearchPattern* pattern,
+ bool matches )
+{
+ emit searchDone( folder(), serNum, pattern, matches );
+}
+
+//-----------------------------------------------------------------------------
+bool KMFolderImap::isMoveable() const
+{
+ return ( hasChildren() == HasNoChildren &&
+ !folder()->isSystemFolder() ) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+const ulong KMFolderImap::serNumForUID( ulong uid )
+{
+ if ( mUidMetaDataMap.find( uid ) ) {
+ KMMsgMetaData *md = mUidMetaDataMap[uid];
+ return md->serNum();
+ } else {
+ kdDebug(5006) << "serNumForUID: unknown uid " << uid << endl;
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::saveMsgMetaData( KMMessage* msg, ulong uid )
+{
+ if ( uid == 0 ) {
+ uid = msg->UID();
+ }
+ ulong serNum = msg->getMsgSerNum();
+ mUidMetaDataMap.replace( uid, new KMMsgMetaData(msg->status(), serNum) );
+}
+
+//-----------------------------------------------------------------------------
+void KMFolderImap::setImapPath( const QString& path )
+{
+ if ( path.isEmpty() ) {
+ kdWarning(5006) << k_funcinfo << "ignoring empty path" << endl;
+ } else {
+ mImapPath = path;
+ }
+}
+
+void KMFolderImap::finishMailCheck( const char *dbg, imapState state )
+{
+ quiet( false );
+ mContentState = state;
+ emit folderComplete( this, mContentState == imapFinished );
+ close(dbg);
+}
+
+#include "kmfolderimap.moc"