/** * kmfolderimap.cpp * * Copyright (c) 2001 Kurt Granroth * Copyright (c) 2000-2002 Michael Haeckel * * This file is based on kmacctimap.coo by Michael Haeckel which was * based on kmacctexppop.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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "kmfolderimap.h" #include "kmfoldertree.h" #include "kmundostack.h" #include "kmfoldermgr.h" #include #include #include #include #include #include KMFolderImap::KMFolderImap(KMFolderDir* aParent, const QString& aName) : KMFolderImapInherited(aParent, aName) { mContentState = imapNoInformation; mSubfolderState = imapNoInformation; mAccount = 0; mIsSelected = FALSE; mLastUid = 0; mCheckFlags = TRUE; KConfig* config = kapp->config(); KConfigGroupSaver saver(config, "Folder-" + idString()); mUidValidity = config->readEntry("UidValidity"); if (mImapPath.isEmpty()) mImapPath = config->readEntry("ImapPath"); if (aName == "INBOX" && mImapPath == "/INBOX/") { mIsSystemFolder = TRUE; mLabel = i18n("inbox"); } mNoContent = config->readBoolEntry("NoContent", FALSE); mReadOnly = config->readBoolEntry("ReadOnly", FALSE); } KMFolderImap::~KMFolderImap() { KConfig* config = kapp->config(); KConfigGroupSaver saver(config, "Folder-" + idString()); config->writeEntry("UidValidity", mUidValidity); config->writeEntry("ImapPath", mImapPath); config->writeEntry("NoContent", mNoContent); config->writeEntry("ReadOnly", mReadOnly); if (kernel->undoStack()) kernel->undoStack()->folderDestroyed(this); } //----------------------------------------------------------------------------- void KMFolderImap::setAccount(KMAcctImap *aAccount) { mAccount = aAccount; if (!mChild) return; KMFolderNode* node; for (node = mChild->first(); node; node = mChild->next()) { if (!node->isDir()) static_cast(node)->setAccount(aAccount); } } //----------------------------------------------------------------------------- void KMFolderImap::readConfig() { KMFolderImapInherited::readConfig(); } //----------------------------------------------------------------------------- void KMFolderImap::writeConfig() { KMFolderImapInherited::writeConfig(); } //----------------------------------------------------------------------------- void KMFolderImap::removeOnServer() { KURL url = mAccount->getUrl(); url.setPath(imapPath()); if (!mAccount->makeConnection()) return; KIO::SimpleJob *job = KIO::file_delete(url, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); KMAcctImap::jobData jd; KMAcctImap::initJobData(jd); mAccount->mapJobData.insert(job, jd); mAccount->displayProgress(); connect(job, SIGNAL(result(KIO::Job *)), this, SLOT(slotRemoveFolderResult(KIO::Job *))); } //----------------------------------------------------------------------------- void KMFolderImap::slotRemoveFolderResult(KIO::Job *job) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; if (job->error()) { mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() ); } else { if (mAccount->slave()) mAccount->mapJobData.remove(it); mAccount->displayProgress(); kernel->imapFolderMgr()->remove(this); } } //----------------------------------------------------------------------------- void KMFolderImap::removeMsg(int idx, bool quiet) { if (idx < 0) return; KMMsgBase* mb = mMsgList[idx]; if (!quiet && !mb->isMessage()) readMsg(idx); if (!quiet) { KMMessage *msg = static_cast(mb); deleteMessage(msg); } mLastUid = 0; KMFolderImapInherited::removeMsg(idx); } void KMFolderImap::removeMsg(QPtrList msgList, bool quiet) { if (!quiet) deleteMessage(msgList); mLastUid = 0; KMFolderImapInherited::removeMsg(msgList); } //----------------------------------------------------------------------------- void KMFolderImap::addMsgQuiet(KMMessage* aMsg) { KMFolder *folder = aMsg->parent(); if (folder) { if (aMsg->enableUndo()) { kernel->undoStack()->pushAction( aMsg->getMsgSerNum(), folder, this ); } else { aMsg->setEnableUndo(true); } folder->take(folder->find(aMsg)); } delete aMsg; aMsg = 0; getFolder(); } //----------------------------------------------------------------------------- void KMFolderImap::addMsgQuiet(QPtrList msgList) { KMFolder *folder = msgList.first()->parent(); if (folder) { for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) { if (msg->enableUndo()) { kernel->undoStack()->pushAction( msg->getMsgSerNum(), folder, this ); } else { msg->setEnableUndo(true); } } folder->take(msgList); } msgList.setAutoDelete(true); msgList.clear(); getFolder(); } //----------------------------------------------------------------------------- int KMFolderImap::addMsg(KMMessage* aMsg, int* aIndex_ret) { QPtrList list; list.append(aMsg); return addMsg(list, aIndex_ret); } int KMFolderImap::addMsg(QPtrList& msgList, int* aIndex_ret) { KMMessage *aMsg = msgList.getFirst(); KMFolder *msgParent = aMsg->parent(); // 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); KMImapJob *imapJob = 0; if (msgParent) { if (msgParent->protocol() == "imap") { if (static_cast(msgParent)->account() == account()) { if (this == 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() ) { int idx = msgParent->find(msg); assert(idx != -1); msg = msgParent->getMsg(idx); imapJob = new KMImapJob(msg, KMImapJob::tPutMessage, this); connect(imapJob, SIGNAL(messageCopied(KMMessage*)), SLOT(addMsgQuiet(KMMessage*))); } } else { /* get the messages and the uids don't unGet the messages because we need to access the serial number in addMsgQuiet */ QValueList 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 temp_msgs = splitMessageList(*it, msgList); imapJob = new KMImapJob(temp_msgs, *it, KMImapJob::tMoveMessage, this); connect(imapJob, SIGNAL(messageCopied(QPtrList)), SLOT(addMsgQuiet(QPtrList))); } } if (aIndex_ret) *aIndex_ret = -1; return 0; } else { // different account, check if messages can be added QPtrListIterator it( msgList ); KMMessage *msg; while ( (msg = it.current()) != 0 ) { ++it; if (!canAddMsgNow(msg, aIndex_ret)) msgList.remove(msg); } } } // if imap } for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) { // transfer from local folders or other accounts if (msgParent && !msg->isMessage()) { int idx = msgParent->find(msg); assert(idx != -1); msg = msgParent->getMsg(idx); } imapJob = new KMImapJob(msg, KMImapJob::tPutMessage, this); connect(imapJob, SIGNAL(messageStored(KMMessage*)), SLOT(addMsgQuiet(KMMessage*))); } if (aIndex_ret) *aIndex_ret = -1; return 0; } //----------------------------------------------------------------------------- void KMFolderImap::copyMsg(QPtrList& msgList) { QValueList 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 temp_msgs = splitMessageList(*it, msgList); new KMImapJob(temp_msgs, *it, KMImapJob::tCopyMessage, this); } } //----------------------------------------------------------------------------- QPtrList KMFolderImap::splitMessageList(QString set, QPtrList& 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 QString last_uid = set.right(set.length() - last); QPtrList temp_msgs; QString uid; if (!last_uid.isEmpty()) { QPtrListIterator 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 = msg->headerField("X-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(mb); deleteMessage(msg); mLastUid = 0; return KMFolderImapInherited::take(idx); } void KMFolderImap::take(QPtrList msgList) { deleteMessage(msgList); mLastUid = 0; KMFolderImapInherited::take(msgList); } //----------------------------------------------------------------------------- // Originally from KMAcctImap //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool KMFolderImap::listDirectory(bool secondStep) { mSubfolderState = imapInProgress; KMAcctImap::jobData jd; jd.parent = this; jd.total = 1; jd.done = 0; jd.inboxOnly = !secondStep && mAccount->prefix() != "/" && imapPath() == mAccount->prefix(); KURL url = mAccount->getUrl(); url.setPath(((jd.inboxOnly) ? QString("/") : imapPath()) + ";TYPE=" + ((mAccount->onlySubscribedFolders()) ? "LSUB" : "LIST")); if (!mAccount->makeConnection()) return FALSE; if (!secondStep) mHasInbox = FALSE; mSubfolderNames.clear(); mSubfolderPaths.clear(); mSubfolderMimeTypes.clear(); KIO::SimpleJob *job = KIO::listDir(url, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); mAccount->mapJobData.insert(job, jd); connect(job, SIGNAL(result(KIO::Job *)), this, SLOT(slotListResult(KIO::Job *))); connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)), this, SLOT(slotListEntries(KIO::Job *, const KIO::UDSEntryList &))); mAccount->displayProgress(); return TRUE; } //----------------------------------------------------------------------------- void KMFolderImap::slotListResult(KIO::Job * job) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; assert(it != mAccount->mapJobData.end()); if (job->error()) { mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() ); } mSubfolderState = imapFinished; bool it_inboxOnly = (*it).inboxOnly; if (mAccount->slave()) mAccount->mapJobData.remove(it); if (!job->error()) { kernel->imapFolderMgr()->quiet(TRUE); if (it_inboxOnly) listDirectory(TRUE); else { if (mIsSystemFolder && mImapPath == "/INBOX/" && mAccount->prefix() == "/INBOX/") { mHasInbox = FALSE; mSubfolderNames.clear(); } createChildFolder(); KMFolderImap *folder; KMFolderNode *node = mChild->first(); while (node) { if (!node->isDir() && (node->name() != "INBOX" || !mHasInbox) && mSubfolderNames.findIndex(node->name()) == -1) { kdDebug(5006) << node->name() << " disappeared." << endl; kernel->imapFolderMgr()->remove(static_cast(node)); node = mChild->first(); } else node = mChild->next(); } if (mHasInbox) { for (node = mChild->first(); node; node = mChild->next()) if (!node->isDir() && node->name() == "INBOX") break; if (node) folder = static_cast(node); else folder = static_cast (mChild->createFolder("INBOX", TRUE)); folder->setAccount(mAccount); folder->setImapPath("/INBOX/"); folder->setLabel(i18n("inbox")); if (!node) folder->close(); folder->listDirectory(); kernel->imapFolderMgr()->contentsChanged(); } for (uint i = 0; i < mSubfolderNames.count(); i++) { for (node = mChild->first(); node; node = mChild->next()) if (!node->isDir() && node->name() == mSubfolderNames[i]) break; if (node) folder = static_cast(node); else { folder = static_cast (mChild->createFolder(mSubfolderNames[i])); if (folder) { folder->close(); kernel->imapFolderMgr()->contentsChanged(); } else { kdDebug(5006) << "can't create folder " << mSubfolderNames[i] << endl; } } if (folder) { folder->setAccount(mAccount); folder->setNoContent(mSubfolderMimeTypes[i] == "inode/directory"); folder->setImapPath(mSubfolderPaths[i]); if (mSubfolderMimeTypes[i] == "message/directory" || mSubfolderMimeTypes[i] == "inode/directory") folder->listDirectory(); } } } kernel->imapFolderMgr()->quiet(FALSE); } mAccount->displayProgress(); } //----------------------------------------------------------------------------- void KMFolderImap::slotListEntries(KIO::Job * job, const KIO::UDSEntryList & uds) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; assert(it != mAccount->mapJobData.end()); QString name; KURL url; QString mimeType; for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin(); udsIt != uds.end(); udsIt++) { mimeType = QString::null; 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_URL) url = KURL((*eIt).m_str, 106); // utf-8 else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE) mimeType = (*eIt).m_str; } if ((mimeType == "inode/directory" || mimeType == "message/digest" || mimeType == "message/directory") && name != ".." && (mAccount->hiddenFolders() || name.at(0) != '.') && (!(*it).inboxOnly || name == "INBOX")) { if (((*it).inboxOnly || url.path() == "/INBOX/") && name == "INBOX") mHasInbox = TRUE; else { // Some servers send _lots_ of duplicates if (mSubfolderNames.findIndex(name) == -1) { mSubfolderNames.append(name); mSubfolderPaths.append(url.path()); mSubfolderMimeTypes.append(mimeType); } } /* static_cast((*it).parent->listView()) ->addImapChildFolder((*it).parent, name, url.path(), mimeType, (*it).inboxOnly); */ } } // static_cast((*it).parent->listView())->delayedUpdate(); } //----------------------------------------------------------------------------- void KMFolderImap::checkValidity() { kdDebug(5006) << "KMFolderImap::checkValidity" << endl; KMAcctImap::jobData jd; jd.parent = this; jd.total = 1; jd.done = 0; KURL url = mAccount->getUrl(); url.setPath(imapPath() + ";UID=0:0"); if (!mAccount->makeConnection()) { emit folderComplete(this, FALSE); return; } KIO::SimpleJob *job = KIO::get(url, FALSE, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); mAccount->mapJobData.insert(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 &))); mAccount->displayProgress(); } //----------------------------------------------------------------------------- ulong KMFolderImap::lastUid() { if (mLastUid) return mLastUid; open(); if (count() > 0) { bool unget = !isMessage(count() - 1); KMMessage *msg = getMsg(count() - 1); mLastUid = msg->headerField("X-UID").toULong(); if (unget) unGetMsg(count() - 1); } close(); return mLastUid; } //----------------------------------------------------------------------------- void KMFolderImap::slotCheckValidityResult(KIO::Job * job) { kdDebug(5006) << "KMFolderImap::slotCheckValidityResult" << endl; QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; if (job->error()) { mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() ); emit folderComplete(this, FALSE); mAccount->displayProgress(); } 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"; QString startUid; if (uidValidity() != uidv) { // uidValidity changed expunge(); mLastUid = 0; uidmap.clear(); } else { if (!mCheckFlags) startUid = QString::number(lastUid() + 1); } mAccount->mapJobData.remove(it); reallyGetFolder(startUid); } } //----------------------------------------------------------------------------- void KMFolderImap::getFolder(bool force) { mGuessedUnreadMsgs = -1; if (mNoContent) { mContentState = imapFinished; emit folderComplete(this, true); return; } mContentState = imapInProgress; if (force) { // force an update mCheckFlags = TRUE; } checkValidity(); } //----------------------------------------------------------------------------- void KMFolderImap::reallyGetFolder(const QString &startUid) { KMAcctImap::jobData jd; jd.parent = this; jd.total = 1; jd.done = 0; KURL url = mAccount->getUrl(); if (!mAccount->makeConnection()) { emit folderComplete(this, FALSE); return; } if (startUid.isEmpty()) { url.setPath(imapPath() + ";SECTION=UID FLAGS"); KIO::SimpleJob *job = KIO::listDir(url, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); mAccount->mapJobData.insert(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 { quiet(TRUE); url.setPath(imapPath() + ";UID=" + startUid + ":*;SECTION=ENVELOPE"); KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob); mAccount->mapJobData.insert(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 &))); } mAccount->displayProgress(); } //----------------------------------------------------------------------------- void KMFolderImap::slotListFolderResult(KIO::Job * job) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; assert(it != mAccount->mapJobData.end()); QString uids; KMAcctImap::jobData jd; jd.parent = (*it).parent; jd.done = 0; if (job->error()) { mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() ); emit folderComplete(this, FALSE); return; } mCheckFlags = FALSE; QStringList::Iterator uid; quiet(TRUE); // Check for already retrieved headers if (count()) { QCString cstr; int idx = 0, a, b, c, serverFlags; long int mailUid, serverUid; uid = (*it).items.begin(); while (idx < count() && uid != (*it).items.end()) { getMsgString(idx, cstr); a = cstr.find("X-UID: "); b = cstr.find("\n", a); if (a == -1 || b == -1) mailUid = -1; else mailUid = cstr.mid(a + 7, b - a - 7).toLong(); 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 (!mReadOnly) getMsgBase(idx)->setStatus(flagsToStatus(serverFlags, false)); idx++; uid = (*it).items.remove(uid); } else break; // happens only, if deleted mails reappear on the server } while (idx < count()) removeMsg(idx, TRUE); } for (uid = (*it).items.begin(); uid != (*it).items.end(); uid++) (*uid).truncate((*uid).find(",")); //jd.items = (*it).items; jd.total = (*it).items.count(); uid = (*it).items.begin(); if (jd.total == 0) { quiet(FALSE); mContentState = imapFinished; emit folderComplete(this, TRUE); mAccount->mapJobData.remove(it); mAccount->displayProgress(); return; } QStringList sets; if (jd.total == 1) sets.append(*uid + ":" + *uid); else sets = makeSets( (*it).items ); for (QStringList::Iterator i = sets.begin(); i != sets.end(); ++i) { KURL url = mAccount->getUrl(); url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE"); if (!mAccount->makeConnection()) { quiet(FALSE); emit folderComplete(this, FALSE); return; } KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob); mAccount->mapJobData.insert(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 &))); } mAccount->mapJobData.remove(it); } //----------------------------------------------------------------------------- void KMFolderImap::slotListFolderEntries(KIO::Job * job, const KIO::UDSEntryList & uds) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; assert(it != mAccount->mapJobData.end()); 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" && !(flags & 8)) (*it).items.append(name + "," + QString::number(flags)); } } //----------------------------------------------------------------------------- KMMsgStatus KMFolderImap::flagsToStatus(int flags, bool newMsg) { if (flags & 4) return KMMsgStatusFlag; if (flags & 2) return KMMsgStatusReplied; if (flags & 1) return KMMsgStatusOld; return (newMsg) ? KMMsgStatusNew : KMMsgStatusUnread; } //----------------------------------------------------------------------------- QCString KMFolderImap::statusToFlags(KMMsgStatus status) { QCString flags = ""; switch (status) { case KMMsgStatusNew: case KMMsgStatusUnread: break; case KMMsgStatusDeleted: flags = "\\DELETED"; break; case KMMsgStatusReplied: flags = "\\SEEN \\ANSWERED"; break; case KMMsgStatusFlag: flags = "\\SEEN \\FLAGGED"; break; default: flags = "\\SEEN"; } return flags; } //----------------------------------------------------------------------------- void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; assert(it != mAccount->mapJobData.end()); (*it).cdata += QCString(data, data.size() + 1); int pos = (*it).cdata.find("\r\n--IMAPDIGEST"); 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)); (*it).cdata.remove(0, pos); } pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1); int flags; while (pos >= 0) { KMMessage *msg = new KMMessage; msg->fromString((*it).cdata.mid(16, pos - 16)); flags = msg->headerField("X-Flags").toInt(); ulong uid = msg->headerField("X-UID").toULong(); if (flags & 8 || uid <= lastUid()) { delete msg; msg = 0; } else { msg->setStatus(flagsToStatus(flags)); if (uidmap.find(uid)) { // assign the sernum from the cache const ulong sernum = (ulong) uidmap[uid]; kdDebug() << "set sernum:" << sernum << " for " << uid << endl; msg->setMsgSerNum(sernum); // delete the entry uidmap.remove(uid); } open(); KMFolderImapInherited::addMsg(msg, 0); close(); if (count() > 1) unGetMsg(count() - 1); mLastUid = uid; /* if ((*it).total > 20 && ((*it).done + 1) * 5 / (*it).total > (*it).done * 5 / (*it).total) { quiet(FALSE); quiet(TRUE); } */ } (*it).cdata.remove(0, pos); (*it).done++; pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1); mAccount->displayProgress(); } } //----------------------------------------------------------------------------- void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; assert(it != mAccount->mapJobData.end()); if (job->error()) { mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() ); mContentState = imapNoInformation; emit folderComplete(this, FALSE); } else if (lastSet) mContentState = imapFinished; if (lastSet) quiet(FALSE); if (mAccount->slave()) mAccount->mapJobData.remove(it); mAccount->displayProgress(); if (!job->error() && lastSet) emit folderComplete(this, TRUE); } //----------------------------------------------------------------------------- void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job) { getMessagesResult(job, true); } //----------------------------------------------------------------------------- void KMFolderImap::slotGetMessagesResult(KIO::Job * job) { getMessagesResult(job, false); } //----------------------------------------------------------------------------- void KMFolderImap::createFolder(const QString &name) { KURL url = mAccount->getUrl(); url.setPath(imapPath() + name); if (!mAccount->makeConnection()) return; KIO::SimpleJob *job = KIO::mkdir(url); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); KMAcctImap::jobData jd; jd.parent = this; jd.items = name; jd.total = 1; jd.done = 0; mAccount->mapJobData.insert(job, jd); mAccount->displayProgress(); connect(job, SIGNAL(result(KIO::Job *)), this, SLOT(slotCreateFolderResult(KIO::Job *))); } //----------------------------------------------------------------------------- void KMFolderImap::slotCreateFolderResult(KIO::Job * job) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; if (job->error()) { mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() ); } else { listDirectory(); } if (mAccount->slave()) mAccount->mapJobData.remove(it); mAccount->displayProgress(); } //----------------------------------------------------------------------------- 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()); } //----------------------------------------------------------------------------- QValueList KMFolderImap::splitSets(QString uids) { QValueList 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; } //----------------------------------------------------------------------------- KMImapJob::KMImapJob(KMMessage *msg, JobType jt, KMFolderImap* folder) { mMsg = msg; QPtrList list; list.append(msg); init(jt, msg->headerField("X-UID"), folder, list); } //----------------------------------------------------------------------------- KMImapJob::KMImapJob(QPtrList& msgList, QString sets, JobType jt, KMFolderImap* folder) { mMsg = msgList.first(); init(jt, sets, folder, msgList); } //----------------------------------------------------------------------------- void KMImapJob::init(JobType jt, QString sets, KMFolderImap* folder, QPtrList& msgList) { assert(jt == tGetMessage || folder); KMMessage* msg = msgList.first(); mType = jt; mDestFolder = folder; KMFolderImap *msg_parent = static_cast(msg->parent()); KMAcctImap *account = (folder) ? folder->account() : msg_parent->account(); account->mJobList.append(this); if (jt == tPutMessage) { // transfers the complete message to the server KURL url = account->getUrl(); url.setPath(folder->imapPath() + ";SECTION=" + QString::fromLatin1(KMFolderImap::statusToFlags(msg->status()))); KMAcctImap::jobData jd; jd.parent = 0; jd.offset = 0; jd.total = 1; jd.done = 0; jd.msgList.append(msg); QCString cstr(msg->asString()); int a = cstr.find("\nX-UID: "); int b = cstr.find("\n", a); if (a != -1 && b != -1 && cstr.find("\n\n") > a) cstr.remove(a, b-a); mData.resize(cstr.length() + cstr.contains("\n")); unsigned int i = 0; for (char *ch = cstr.data(); *ch; ch++) { if (*ch == '\n') { mData.at(i) = '\r'; i++; } mData.at(i) = *ch; i++; } jd.data = mData; if (!account->makeConnection()) { account->mJobList.remove(this); delete this; return; } KIO::SimpleJob *simpleJob = KIO::put(url, 0, FALSE, FALSE, FALSE); KIO::Scheduler::assignJobToSlave(account->slave(), simpleJob); mJob = simpleJob; account->mapJobData.insert(mJob, jd); connect(mJob, SIGNAL(result(KIO::Job *)), SLOT(slotPutMessageResult(KIO::Job *))); connect(mJob, SIGNAL(dataReq(KIO::Job *, QByteArray &)), SLOT(slotPutMessageDataReq(KIO::Job *, QByteArray &))); connect(mJob, SIGNAL(infoMessage(KIO::Job *, const QString &)), SLOT(slotPutMessageInfoData(KIO::Job *, const QString &))); account->displayProgress(); } else if (jt == tCopyMessage || jt == tMoveMessage) { KURL url = account->getUrl(); KURL destUrl = account->getUrl(); destUrl.setPath(folder->imapPath()); url.setPath(msg_parent->imapPath() + ";UID=" + sets); KMAcctImap::jobData jd; jd.parent = 0; mOffset = 0; jd.total = 1; jd.done = 0; jd.msgList = msgList; QByteArray packedArgs; QDataStream stream( packedArgs, IO_WriteOnly); stream << (int) 'C' << url << destUrl; if (!account->makeConnection()) { account->mJobList.remove(this); delete this; return; } KIO::SimpleJob *simpleJob = KIO::special(url, packedArgs, FALSE); KIO::Scheduler::assignJobToSlave(account->slave(), simpleJob); mJob = simpleJob; account->mapJobData.insert(mJob, jd); connect(mJob, SIGNAL(result(KIO::Job *)), SLOT(slotCopyMessageResult(KIO::Job *))); if (jt == tMoveMessage) { connect(mJob, SIGNAL(infoMessage(KIO::Job *, const QString &)), SLOT(slotCopyMessageInfoData(KIO::Job *, const QString &))); } account->displayProgress(); } else { slotGetNextMessage(); } } //----------------------------------------------------------------------------- KMImapJob::~KMImapJob() { if (mDestFolder) { KMAcctImap *account = mDestFolder->account(); if (!account || !mJob) return; QMap::Iterator it = account->mapJobData.find(mJob); if (it == account->mapJobData.end()) return; if ( !(*it).msgList.isEmpty() ) { for ( KMMessage* msg = (*it).msgList.first(); msg; msg = (*it).msgList.next() ) msg->setTransferInProgress(false); } } emit finished(); } //----------------------------------------------------------------------------- void KMImapJob::slotGetNextMessage() { KMFolderImap *msgParent = static_cast(mMsg->parent()); KMAcctImap *account = msgParent->account(); if (mMsg->headerField("X-UID").isEmpty()) { emit messageRetrieved(mMsg); account->mJobList.remove(this); delete this; return; } KURL url = account->getUrl(); url.setPath(msgParent->imapPath() + ";UID=" + mMsg->headerField("X-UID")); KMAcctImap::jobData jd; jd.parent = 0; jd.total = 1; jd.done = 0; if (!account->makeConnection()) { account->mJobList.remove(this); delete this; return; } KIO::SimpleJob *simpleJob = KIO::get(url, FALSE, FALSE); KIO::Scheduler::assignJobToSlave(account->slave(), simpleJob); mJob = simpleJob; account->mapJobData.insert(mJob, jd); connect(mJob, SIGNAL(result(KIO::Job *)), this, SLOT(slotGetMessageResult(KIO::Job *))); connect(mJob, SIGNAL(data(KIO::Job *, const QByteArray &)), msgParent, SLOT(slotSimpleData(KIO::Job *, const QByteArray &))); account->displayProgress(); } //----------------------------------------------------------------------------- void KMImapJob::slotGetMessageResult(KIO::Job * job) { if( !mMsg || !mMsg->parent() ) return; KMAcctImap *account = static_cast(mMsg->parent())->account(); QMap::Iterator it = account->mapJobData.find(job); if (it == account->mapJobData.end()) return; if (job->error()) { account->slotSlaveError( account->slave(), job->error(), job->errorText() ); return; } else { if ((*it).data.size() > 0) { QString uid = mMsg->headerField("X-UID"); (*it).data.resize((*it).data.size() + 1); (*it).data[(*it).data.size() - 1] = '\0'; mMsg->fromString(QCString((*it).data)); mMsg->setHeaderField("X-UID",uid); mMsg->setComplete( TRUE ); emit messageRetrieved(mMsg); } else { emit messageRetrieved(0); } mMsg = 0; } if (account->slave()) account->mapJobData.remove(it); account->displayProgress(); if (account->slave()) account->mJobList.remove(this); delete this; } //----------------------------------------------------------------------------- void KMImapJob::slotPutMessageDataReq(KIO::Job *job, QByteArray &data) { KMAcctImap *account = mDestFolder->account(); QMap::Iterator it = account->mapJobData.find(job); if (it == account->mapJobData.end()) return; if ((*it).data.size() - (*it).offset > 0x8000) { data.duplicate((*it).data.data() + (*it).offset, 0x8000); (*it).offset += 0x8000; } else if ((*it).data.size() - (*it).offset > 0) { data.duplicate((*it).data.data() + (*it).offset, (*it).data.size() - (*it).offset); (*it).offset = (*it).data.size(); } else data.resize(0); } //----------------------------------------------------------------------------- void KMImapJob::slotPutMessageResult(KIO::Job *job) { KMAcctImap *account = mDestFolder->account(); QMap::Iterator it = account->mapJobData.find(job); if (it == account->mapJobData.end()) return; if (job->error()) { account->slotSlaveError( account->slave(), job->error(), job->errorText() ); return; } else { if ( !(*it).msgList.isEmpty() ) { emit messageStored((*it).msgList.last()); (*it).msgList.removeLast(); } else if (mMsg) { emit messageStored(mMsg); } mMsg = 0; } if (account->slave()) account->mapJobData.remove(it); account->displayProgress(); if (account->slave()) account->mJobList.remove(this); delete this; } //----------------------------------------------------------------------------- void KMImapJob::slotPutMessageInfoData(KIO::Job *job, const QString &data) { KMAcctImap *account = mDestFolder->account(); QMap::Iterator it = account->mapJobData.find(job); if (it == account->mapJobData.end()) return; if (data.find("UID") != -1) { int uid = (data.right(data.length()-4)).toInt(); if ( !(*it).msgList.isEmpty() ) { const ulong * sernum = (ulong *)(*it).msgList.last()->getMsgSerNum(); kdDebug() << "insert sernum " << (*it).msgList.last()->getMsgSerNum() << " for " << uid << endl; mDestFolder->insertUidSerNumEntry(uid, sernum); } else if (mMsg) { const ulong * sernum = (ulong *)mMsg->getMsgSerNum(); kdDebug() << "insert sernum " << mMsg->getMsgSerNum() << " for " << uid << endl; mDestFolder->insertUidSerNumEntry(uid, sernum); } } } //----------------------------------------------------------------------------- void KMImapJob::slotCopyMessageResult(KIO::Job *job) { KMAcctImap *account = mDestFolder->account(); QMap::Iterator it = account->mapJobData.find(job); if (it == account->mapJobData.end()) return; if (job->error()) { account->slotSlaveError( account->slave(), job->error(), job->errorText() ); return; } else { if ( !(*it).msgList.isEmpty() ) { emit messageCopied((*it).msgList); } else if (mMsg) { emit messageCopied(mMsg); mMsg = 0; } } if (account->slave()) account->mapJobData.remove(it); account->displayProgress(); if (account->slave()) account->mJobList.remove(this); delete this; } //----------------------------------------------------------------------------- void KMImapJob::slotCopyMessageInfoData(KIO::Job * job, const QString & data) { KMAcctImap *account = mDestFolder->account(); QMap::Iterator it = account->mapJobData.find(job); if (it == account->mapJobData.end()) return; if (data.find("UID") != -1) { // split QString oldUid = data.section(" ", 1, 1); QString newUid = data.section(" ", 2, 2); // get lists of uids QValueList olduids = KMFolderImap::splitSets(oldUid); QValueList newuids = KMFolderImap::splitSets(newUid); int index = -1; if ( !(*it).msgList.isEmpty() ) { KMMessage * msg; for ( msg = (*it).msgList.first(); msg; msg = (*it).msgList.next() ) { uint uid = msg->headerField("X-UID").toInt(); index = olduids.findIndex(uid); if (index > -1) { // found, get the new uid const ulong * sernum = (ulong *)msg->getMsgSerNum(); mDestFolder->insertUidSerNumEntry(newuids[index], sernum); } } } else if (mMsg) { uint uid = mMsg->headerField("X-UID").toInt(); index = olduids.findIndex(uid); if (index > -1) { // found, get the new uid const ulong * sernum = (ulong *)mMsg->getMsgSerNum(); mDestFolder->insertUidSerNumEntry(newuids[index], sernum); } } } } //----------------------------------------------------------------------------- void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; assert(it != mAccount->mapJobData.end()); QBuffer buff((*it).data); buff.open(IO_WriteOnly | IO_Append); buff.writeBlock(data.data(), data.size()); buff.close(); } //----------------------------------------------------------------------------- void KMImapJob::ignoreJobsForMessage(KMMessage *msg) { if (!msg || msg->transferInProgress()) return; KMAcctImap *account; if (!msg->parent() || !(account = static_cast(msg->parent()) ->account())) return; for (KMImapJob *it = account->mJobList.first(); it; it = account->mJobList.next()) { if ((*it).mMsg == msg) { account->mapJobData.remove( (*it).mJob ); account->mJobList.remove( it ); delete it; break; } } } //----------------------------------------------------------------------------- void KMFolderImap::deleteMessage(KMMessage * msg) { KURL url = mAccount->getUrl(); KMFolderImap *msg_parent = static_cast(msg->parent()); url.setPath(msg_parent->imapPath() + ";UID=" + msg->headerField("X-UID")); if (!mAccount->makeConnection()) return; KIO::SimpleJob *job = KIO::file_delete(url, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); KMAcctImap::jobData jd; jd.total = 1; jd.done = 0; jd.parent = 0; jd.quiet = FALSE; mAccount->mapJobData.insert(job, jd); connect(job, SIGNAL(result(KIO::Job *)), mAccount, SLOT(slotSimpleResult(KIO::Job *))); mAccount->displayProgress(); } void KMFolderImap::deleteMessage(QPtrList msgList) { QValueList uids; getUids(msgList, uids); QStringList sets = makeSets(uids); KURL url = mAccount->getUrl(); KMFolderImap *msg_parent = static_cast(msgList.first()->parent()); for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) { url.setPath(msg_parent->imapPath() + ";UID=" + *it); if (!mAccount->makeConnection()) return; KIO::SimpleJob *job = KIO::file_delete(url, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); KMAcctImap::jobData jd; jd.total = 1; jd.done = 0; jd.parent = 0; jd.quiet = FALSE; mAccount->mapJobData.insert(job, jd); connect(job, SIGNAL(result(KIO::Job *)), mAccount, SLOT(slotSimpleResult(KIO::Job *))); } mAccount->displayProgress(); } //----------------------------------------------------------------------------- void KMFolderImap::setStatus(int idx, KMMsgStatus status) { QValueList ids; ids.append(idx); setStatus(ids, status); } void KMFolderImap::setStatus(QValueList& ids, KMMsgStatus status) { KMFolder::setStatus(ids, status); if (mReadOnly) return; // get the uids QValueList uids; getUids(ids, uids); // get the flags QCString flags = statusToFlags(status); // get the sets of ranges.. QStringList sets = makeSets(uids); // ..and pass them to the server for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) { setImapStatus(imapPath() + ";UID=" + *it, flags); } mAccount->displayProgress(); } //----------------------------------------------------------------------------- QStringList KMFolderImap::makeSets(QStringList& uids, bool sort) { QValueList tmp; for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it ) tmp.append( (*it).toInt() ); return makeSets(tmp, sort); } QStringList KMFolderImap::makeSets(QValueList& uids, bool sort) { QStringList sets; QString set; if (uids.size() == 1) { sets.append(QString::number(uids.first())); return sets; } if (sort) qHeapSort(uids); int 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::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& ids, QValueList& uids) { KMMessage *msg = 0; // get the uids for ( QValueList::Iterator it = ids.begin(); it != ids.end(); ++it ) { bool unget = !isMessage(*it); msg = getMsg(*it); if (!msg) continue; uids.append(msg->headerField("X-UID").toInt()); if (unget) unGetMsg(*it); } } void KMFolderImap::getUids(QPtrList& msgList, QValueList& uids, KMFolder* msgParent) { KMMessage *msg = 0; if (!msgParent) msgParent = msgList.first()->parent(); if (!msgParent) return; for ( msg = msgList.first(); msg; msg = msgList.next() ) { if ( !msg->headerField("X-UID").isEmpty() ) uids.append(msg->headerField("X-UID").toInt()); } } //----------------------------------------------------------------------------- void KMFolderImap::setImapStatus(QString path, QCString flags) { // set the status on the server, the uids are integrated in the path kdDebug(5006) << "setImapStatus path=" << path << endl; KURL url = mAccount->getUrl(); url.setPath(path); QByteArray packedArgs; QDataStream stream( packedArgs, IO_WriteOnly); stream << (int) 'S' << url << flags; if (!mAccount->makeConnection()) return; KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); KMAcctImap::jobData jd; jd.total = 1; jd.done = 0; jd.parent = 0; mAccount->mapJobData.insert(job, jd); connect(job, SIGNAL(result(KIO::Job *)), SLOT(slotSetStatusResult(KIO::Job *))); } //----------------------------------------------------------------------------- void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet) { aFolder->setNeedsCompacting(FALSE); KURL url = mAccount->getUrl(); url.setPath(aFolder->imapPath() + ";UID=*"); if (!mAccount->makeConnection()) return; KIO::SimpleJob *job = KIO::file_delete(url, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); KMAcctImap::jobData jd; jd.parent = 0; jd.quiet = quiet; jd.total = 1; jd.done = 0; mAccount->mapJobData.insert(job, jd); connect(job, SIGNAL(result(KIO::Job *)), mAccount, SLOT(slotSimpleResult(KIO::Job *))); mAccount->displayProgress(); } //----------------------------------------------------------------------------- void KMFolderImap::slotSetStatusResult(KIO::Job * job) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; mAccount->mapJobData.remove(it); if (job->error() && job->error() != KIO::ERR_CANNOT_OPEN_FOR_WRITING) { mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() ); } mAccount->displayProgress(); } //----------------------------------------------------------------------------- void KMFolderImap::processNewMail(bool) { KURL url = mAccount->getUrl(); if (mReadOnly) url.setPath(imapPath() + ";SECTION=UIDNEXT"); else url.setPath(imapPath() + ";SECTION=UNSEEN"); if (!mAccount->makeConnection()) return; KIO::SimpleJob *job = KIO::stat(url, FALSE); KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); KMAcctImap::jobData jd; KMAcctImap::initJobData(jd); mAccount->mapJobData.insert(job, jd); connect(job, SIGNAL(result(KIO::Job *)), SLOT(slotStatResult(KIO::Job *))); mAccount->displayProgress(); } //----------------------------------------------------------------------------- void KMFolderImap::slotStatResult(KIO::Job * job) { QMap::Iterator it = mAccount->mapJobData.find(job); if (it == mAccount->mapJobData.end()) return; mAccount->mapJobData.remove(it); if (job->error()) { mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() ); } else { KIO::UDSEntry uds = static_cast(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; } emit numUnreadMsgsChanged( this ); } } } mAccount->displayProgress(); } #include "kmfolderimap.moc"