diff options
Diffstat (limited to 'kmail/folderstorage.cpp')
-rw-r--r-- | kmail/folderstorage.cpp | 1176 |
1 files changed, 1176 insertions, 0 deletions
diff --git a/kmail/folderstorage.cpp b/kmail/folderstorage.cpp new file mode 100644 index 000000000..89fc20db9 --- /dev/null +++ b/kmail/folderstorage.cpp @@ -0,0 +1,1176 @@ +/* + Virtual base class for mail storage. + + This file is part of KMail. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include <config.h> + +#include "folderstorage.h" +#include "kmfolder.h" +#include "kmkernel.h" + +#include "kmfolderimap.h" //for the nasty imap hacks, FIXME +#include "undostack.h" +#include "kmmsgdict.h" +#include "kmfoldermgr.h" +#include "kmcommands.h" +#include "listjob.h" +using KMail::ListJob; +#include "kmsearchpattern.h" +#include "globalsettings.h" + +#include <klocale.h> +#include <kconfig.h> +#include <kdebug.h> + +#include <qfile.h> +#include <qregexp.h> + +#include <mimelib/mimepp.h> +#include <errno.h> + +//----------------------------------------------------------------------------- + +FolderStorage::FolderStorage( KMFolder* folder, const char* aName ) + : QObject( folder, aName ), mFolder( folder ), mEmitChangedTimer( 0L ) +{ + mOpenCount = 0; + mQuiet = 0; + mChanged = false; + mAutoCreateIndex = true; + mExportsSernums = false; + mDirty = false; + mUnreadMsgs = -1; + mGuessedUnreadMsgs = -1; + mTotalMsgs = -1; + mSize = -1; + needsCompact = false; + mConvertToUtf8 = false; + mCompactable = true; + mNoContent = false; + mNoChildren = false; + mRDict = 0; + mDirtyTimer = new QTimer(this, "mDirtyTimer"); + connect(mDirtyTimer, SIGNAL(timeout()), + this, SLOT(updateIndex())); + + mHasChildren = HasNoChildren; + mContentsType = KMail::ContentsTypeMail; + + connect(this, SIGNAL(closed(KMFolder*)), mFolder, SIGNAL(closed())); +} + +//----------------------------------------------------------------------------- +FolderStorage::~FolderStorage() +{ + mJobList.setAutoDelete( true ); + QObject::disconnect( SIGNAL(destroyed(QObject*)), this, 0 ); + mJobList.clear(); + KMMsgDict::deleteRentry(mRDict); +} + + +void FolderStorage::close( const char* owner, bool aForced ) +{ + if (mOpenCount <= 0) return; + if (mOpenCount > 0) mOpenCount--; + if (mOpenCount > 0 && !aForced) return; + + // kdWarning() << "Really closing: " << folder()->prettyURL() << kdBacktrace() << endl; + reallyDoClose(owner); +} + +//----------------------------------------------------------------------------- +QString FolderStorage::dotEscape(const QString& aStr) +{ + if (aStr[0] != '.') return aStr; + return aStr.left(aStr.find(QRegExp("[^\\.]"))) + aStr; +} + +void FolderStorage::addJob( FolderJob* job ) const +{ + QObject::connect( job, SIGNAL(destroyed(QObject*)), + SLOT(removeJob(QObject*)) ); + mJobList.append( job ); +} + +void FolderStorage::removeJob( QObject* job ) +{ + mJobList.remove( static_cast<FolderJob*>( job ) ); +} + + +//----------------------------------------------------------------------------- +QString FolderStorage::location() const +{ + QString sLocation(const_cast<FolderStorage*>(this)->folder()->path()); + + if (!sLocation.isEmpty()) sLocation += '/'; + sLocation += dotEscape(fileName()); + + return sLocation; +} + +QString FolderStorage::fileName() const +{ + return mFolder->name(); +} + + + +//----------------------------------------------------------------------------- +void FolderStorage::setAutoCreateIndex(bool autoIndex) +{ + mAutoCreateIndex = autoIndex; +} + +//----------------------------------------------------------------------------- +void FolderStorage::setDirty(bool f) +{ + mDirty = f; + if (mDirty && mAutoCreateIndex) + mDirtyTimer->changeInterval( mDirtyTimerInterval ); + else + mDirtyTimer->stop(); +} + +//----------------------------------------------------------------------------- +void FolderStorage::markNewAsUnread() +{ + KMMsgBase* msgBase; + int i; + + for (i=0; i< count(); ++i) + { + if (!(msgBase = getMsgBase(i))) continue; + if (msgBase->isNew()) + { + msgBase->setStatus(KMMsgStatusUnread); + msgBase->setDirty(true); + } + } +} + +void FolderStorage::markUnreadAsRead() +{ + KMMsgBase* msgBase; + SerNumList serNums; + + for (int i=count()-1; i>=0; --i) + { + msgBase = getMsgBase(i); + assert(msgBase); + if (msgBase->isNew() || msgBase->isUnread()) + { + serNums.append( msgBase->getMsgSerNum() ); + } + } + if (serNums.empty()) + return; + + KMCommand *command = new KMSetStatusCommand( KMMsgStatusRead, serNums ); + command->start(); +} + +//----------------------------------------------------------------------------- +void FolderStorage::quiet(bool beQuiet) +{ + + if (beQuiet) + { + /* Allocate the timer here to don't have one timer for each folder. BTW, + * a timer is created when a folder is checked + */ + if ( !mEmitChangedTimer) { + mEmitChangedTimer= new QTimer( this, "mEmitChangedTimer" ); + connect( mEmitChangedTimer, SIGNAL( timeout() ), + this, SLOT( slotEmitChangedTimer() ) ); + } + mQuiet++; + } else { + mQuiet--; + if (mQuiet <= 0) + { + delete mEmitChangedTimer; + mEmitChangedTimer=0L; + + mQuiet = 0; + if (mChanged) { + emit changed(); + // Don't hurt emit this if the mUnreadMsg really don't change + // We emit it here, because this signal is delayed if mQuiet >0 + emit numUnreadMsgsChanged( folder() ); + } + mChanged = false; + } + } +} + +//----------------------------------------------------------------------------- + +/** Compare message's date. This is useful for message sorting */ +int operator<( KMMsgBase & m1, KMMsgBase & m2 ) +{ + return (m1.date() < m2.date()); +} + +/** Compare message's date. This is useful for message sorting */ +int operator==( KMMsgBase & m1, KMMsgBase & m2 ) +{ + return (m1.date() == m2.date()); +} + + +//----------------------------------------------------------------------------- +int FolderStorage::expungeOldMsg(int days) +{ + int i, msgnb=0; + time_t msgTime, maxTime; + const KMMsgBase* mb; + QValueList<int> rmvMsgList; + + maxTime = time(0) - days * 3600 * 24; + + for (i=count()-1; i>=0; i--) { + mb = getMsgBase(i); + assert(mb); + msgTime = mb->date(); + + if (msgTime < maxTime) { + //kdDebug(5006) << "deleting msg " << i << " : " << mb->subject() << " - " << mb->dateStr(); // << endl; + removeMsg( i ); + msgnb++; + } + } + return msgnb; +} + +//------------------------------------------ +void FolderStorage::slotEmitChangedTimer() +{ + emit changed(); + mChanged=false; +} +//----------------------------------------------------------------------------- +void FolderStorage::emitMsgAddedSignals(int idx) +{ + Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder() , idx ); + if (!mQuiet) { + emit msgAdded(idx); + } else { + /** Restart always the timer or not. BTW we get a kmheaders refresh + * each 3 seg.?*/ + if ( !mEmitChangedTimer->isActive() ) { + mEmitChangedTimer->start( 3000 ); + } + mChanged=true; + } + emit msgAdded( folder(), serNum ); +} + +//----------------------------------------------------------------------------- +bool FolderStorage::canAddMsgNow(KMMessage* aMsg, int* aIndex_ret) +{ + if (aIndex_ret) *aIndex_ret = -1; + KMFolder *msgParent = aMsg->parent(); + // If the message has a parent and is in transfer, bail out. If it does not + // have a parent we want to be able to add it even if it is in transfer. + if (aMsg->transferInProgress() && msgParent) + return false; + if (!aMsg->isComplete() && msgParent && msgParent->folderType() == KMFolderTypeImap) + { + FolderJob *job = msgParent->createJob(aMsg); + connect(job, SIGNAL(messageRetrieved(KMMessage*)), + SLOT(reallyAddMsg(KMMessage*))); + job->start(); + aMsg->setTransferInProgress( true ); + return false; + } + return true; +} + + +//----------------------------------------------------------------------------- +void FolderStorage::reallyAddMsg(KMMessage* aMsg) +{ + if (!aMsg) // the signal that is connected can call with aMsg=0 + return; + aMsg->setTransferInProgress( false ); + aMsg->setComplete( true ); + KMFolder *aFolder = aMsg->parent(); + int index; + ulong serNum = aMsg->getMsgSerNum(); + bool undo = aMsg->enableUndo(); + addMsg(aMsg, &index); + if (index < 0) return; + unGetMsg(index); + if (undo) + { + kmkernel->undoStack()->pushSingleAction( serNum, aFolder, folder() ); + } +} + + +//----------------------------------------------------------------------------- +void FolderStorage::reallyAddCopyOfMsg(KMMessage* aMsg) +{ + if ( !aMsg ) return; // messageRetrieved(0) is always possible + aMsg->setParent( 0 ); + aMsg->setTransferInProgress( false ); + addMsg( aMsg ); + unGetMsg( count() - 1 ); +} + +int FolderStorage::find( const KMMessage * msg ) const { + return find( &msg->toMsgBase() ); +} + +//----------------------------------------------------------------------------- +void FolderStorage::removeMsg(const QPtrList<KMMsgBase>& msgList, bool imapQuiet) +{ + for( QPtrListIterator<KMMsgBase> it( msgList ); *it; ++it ) + { + int idx = find(it.current()); + assert( idx != -1); + removeMsg(idx, imapQuiet); + } +} + +//----------------------------------------------------------------------------- +void FolderStorage::removeMsg(const QPtrList<KMMessage>& msgList, bool imapQuiet) +{ + for( QPtrListIterator<KMMessage> it( msgList ); *it; ++it ) + { + int idx = find(it.current()); + assert( idx != -1); + removeMsg(idx, imapQuiet); + } +} + +//----------------------------------------------------------------------------- +void FolderStorage::removeMsg(int idx, bool) +{ + //assert(idx>=0); + if(idx < 0) + { + kdDebug(5006) << "FolderStorage::removeMsg() : idx < 0\n" << endl; + return; + } + + KMMsgBase* mb = getMsgBase(idx); + + Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), idx ); + if (serNum != 0) + emit msgRemoved( folder(), serNum ); + mb = takeIndexEntry( idx ); + + setDirty( true ); + needsCompact=true; // message is taken from here - needs to be compacted + + if (mb->isUnread() || mb->isNew() || + (folder() == kmkernel->outboxFolder())) { + --mUnreadMsgs; + if ( !mQuiet ) { +// kdDebug( 5006 ) << "FolderStorage::msgStatusChanged" << endl; + emit numUnreadMsgsChanged( folder() ); + }else{ + if ( !mEmitChangedTimer->isActive() ) { +// kdDebug( 5006 )<< "EmitChangedTimer started" << endl; + mEmitChangedTimer->start( 3000 ); + } + mChanged = true; + } + } + --mTotalMsgs; + + mSize = -1; + QString msgIdMD5 = mb->msgIdMD5(); + emit msgRemoved( idx, msgIdMD5 ); + emit msgRemoved( folder() ); +} + + +//----------------------------------------------------------------------------- +KMMessage* FolderStorage::take(int idx) +{ + KMMsgBase* mb; + KMMessage* msg; + + assert(idx>=0 && idx<=count()); + + mb = getMsgBase(idx); + if (!mb) return 0; + if (!mb->isMessage()) readMsg(idx); + Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), idx ); + emit msgRemoved( folder(), serNum ); + + msg = (KMMessage*)takeIndexEntry(idx); + + if (msg->isUnread() || msg->isNew() || + ( folder() == kmkernel->outboxFolder() )) { + --mUnreadMsgs; + if ( !mQuiet ) { + emit numUnreadMsgsChanged( folder() ); + }else{ + if ( !mEmitChangedTimer->isActive() ) { + mEmitChangedTimer->start( 3000 ); + } + mChanged = true; + } + } + --mTotalMsgs; + msg->setParent(0); + setDirty( true ); + mSize = -1; + needsCompact=true; // message is taken from here - needs to be compacted + QString msgIdMD5 = msg->msgIdMD5(); + emit msgRemoved( idx, msgIdMD5 ); + emit msgRemoved( folder() ); + + return msg; +} + +void FolderStorage::take(QPtrList<KMMessage> msgList) +{ + for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) + { + if (msg->parent()) + { + int idx = msg->parent()->find(msg); + if ( idx >= 0 ) + take(idx); + } + } +} + + +//----------------------------------------------------------------------------- +KMMessage* FolderStorage::getMsg(int idx) +{ + if ( mOpenCount <= 0 ) { + kdWarning(5006) << "FolderStorage::getMsg was called on a closed folder: " << folder()->prettyURL() << endl; + return 0; + } + if ( idx < 0 || idx >= count() ) { + kdWarning(5006) << "FolderStorage::getMsg was asked for an invalid index. idx =" << idx << " count()=" << count() << endl; + return 0; + } + + KMMsgBase* mb = getMsgBase(idx); + if (!mb) { + kdWarning(5006) << "FolderStorage::getMsg, getMsgBase failed for index: " << idx << endl; + return 0; + } + + KMMessage *msg = 0; + bool undo = mb->enableUndo(); + if (mb->isMessage()) { + msg = ((KMMessage*)mb); + } else { + QString mbSubject = mb->subject(); + msg = readMsg(idx); + // sanity check + if (mCompactable && (!msg || (msg->subject().isEmpty() != mbSubject.isEmpty()))) { + kdDebug(5006) << "Error: " << location() << + " Index file is inconsistent with folder file. This should never happen." << endl; + mCompactable = false; // Don't compact + writeConfig(); + } + + } + // Either isMessage and we had a sernum, or readMsg gives us one + // (via insertion into mMsgList). sernum == 0 may still occur due to + // an outdated or corrupt IMAP cache. + if ( msg->getMsgSerNum() == 0 ) { + kdWarning(5006) << "FolderStorage::getMsg, message has no sernum, index: " << idx << endl; + return 0; + } + msg->setEnableUndo(undo); + msg->setComplete( true ); + return msg; +} + +//----------------------------------------------------------------------------- +KMMessage* FolderStorage::readTemporaryMsg(int idx) +{ + if(!(idx >= 0 && idx <= count())) + return 0; + + KMMsgBase* mb = getMsgBase(idx); + if (!mb) return 0; + + unsigned long sernum = mb->getMsgSerNum(); + + KMMessage *msg = 0; + bool undo = mb->enableUndo(); + if (mb->isMessage()) { + // the caller will delete it, so we must make a copy it + msg = new KMMessage(*(KMMessage*)mb); + msg->setMsgSerNum(sernum); + msg->setComplete( true ); + } else { + // ## Those two lines need to be moved to a virtual method for KMFolderSearch, like readMsg + msg = new KMMessage(*(KMMsgInfo*)mb); + msg->setMsgSerNum(sernum); // before fromDwString so that readyToShow uses the right sernum + msg->setComplete( true ); + msg->fromDwString(getDwString(idx)); + } + msg->setEnableUndo(undo); + return msg; +} + + +//----------------------------------------------------------------------------- +KMMsgInfo* FolderStorage::unGetMsg(int idx) +{ + KMMsgBase* mb; + + if(!(idx >= 0 && idx <= count())) + return 0; + + mb = getMsgBase(idx); + if (!mb) return 0; + + + if (mb->isMessage()) { + // Remove this message from all jobs' list it might still be on. + // setIndexEntry deletes the message. + KMMessage *msg = static_cast<KMMessage*>(mb); + if ( msg->transferInProgress() ) return 0; + ignoreJobsForMessage( msg ); + return setIndexEntry( idx, msg ); + } + + return 0; +} + + +//----------------------------------------------------------------------------- +bool FolderStorage::isMessage(int idx) +{ + KMMsgBase* mb; + if (!(idx >= 0 && idx <= count())) return false; + mb = getMsgBase(idx); + return (mb && mb->isMessage()); +} + +//----------------------------------------------------------------------------- +FolderJob* FolderStorage::createJob( KMMessage *msg, FolderJob::JobType jt, + KMFolder *folder, QString partSpecifier, + const AttachmentStrategy *as ) const +{ + FolderJob * job = doCreateJob( msg, jt, folder, partSpecifier, as ); + if ( job ) + addJob( job ); + return job; +} + +//----------------------------------------------------------------------------- +FolderJob* FolderStorage::createJob( QPtrList<KMMessage>& msgList, const QString& sets, + FolderJob::JobType jt, KMFolder *folder ) const +{ + FolderJob * job = doCreateJob( msgList, sets, jt, folder ); + if ( job ) + addJob( job ); + return job; +} + +//----------------------------------------------------------------------------- +int FolderStorage::moveMsg(KMMessage* aMsg, int* aIndex_ret) +{ + assert(aMsg != 0); + KMFolder* msgParent = aMsg->parent(); + + if (msgParent) + msgParent->open("moveMsgSrc"); + + open("moveMsgDest"); + int rc = addMsg(aMsg, aIndex_ret); + close("moveMsgDest"); + + if (msgParent) + msgParent->close("moveMsgSrc"); + + return rc; +} + +//----------------------------------------------------------------------------- +int FolderStorage::moveMsg(QPtrList<KMMessage> msglist, int* aIndex_ret) +{ + KMMessage* aMsg = msglist.first(); + assert(aMsg != 0); + KMFolder* msgParent = aMsg->parent(); + + if (msgParent) + msgParent->open("foldermovemsg"); + + QValueList<int> index; + open("moveMsg"); + int rc = addMsg(msglist, index); + close("moveMsg"); + // FIXME: we want to have a QValueList to pass it back, so change this method + if ( !index.isEmpty() ) + aIndex_ret = &index.first(); + + if (msgParent) + msgParent->close("foldermovemsg"); + + return rc; +} + + +//----------------------------------------------------------------------------- +int FolderStorage::rename(const QString& newName, KMFolderDir *newParent) +{ + QString oldLoc, oldIndexLoc, oldIdsLoc, newLoc, newIndexLoc, newIdsLoc; + QString oldSubDirLoc, newSubDirLoc; + QString oldName; + int rc=0; + KMFolderDir *oldParent; + + assert(!newName.isEmpty()); + + oldLoc = location(); + oldIndexLoc = indexLocation(); + oldSubDirLoc = folder()->subdirLocation(); + oldIdsLoc = KMMsgDict::instance()->getFolderIdsLocation( *this ); + QString oldConfigString = "Folder-" + folder()->idString(); + + close("rename", true); + + oldName = folder()->fileName(); + oldParent = folder()->parent(); + if (newParent) + folder()->setParent( newParent ); + + folder()->setName(newName); + newLoc = location(); + newIndexLoc = indexLocation(); + newSubDirLoc = folder()->subdirLocation(); + newIdsLoc = KMMsgDict::instance()->getFolderIdsLocation( *this ); + + if (::rename(QFile::encodeName(oldLoc), QFile::encodeName(newLoc))) { + folder()->setName(oldName); + folder()->setParent(oldParent); + rc = errno; + } + else { + // rename/move index file and index.sorted file + if (!oldIndexLoc.isEmpty()) { + ::rename(QFile::encodeName(oldIndexLoc), QFile::encodeName(newIndexLoc)); + ::rename(QFile::encodeName(oldIndexLoc) + ".sorted", + QFile::encodeName(newIndexLoc) + ".sorted"); + } + + // rename/move serial number file + if (!oldIdsLoc.isEmpty()) + ::rename(QFile::encodeName(oldIdsLoc), QFile::encodeName(newIdsLoc)); + + // rename/move the subfolder directory + KMFolderDir* child = 0; + if( folder() ) + child = folder()->child(); + + if (!::rename(QFile::encodeName(oldSubDirLoc), QFile::encodeName(newSubDirLoc) )) { + // now that the subfolder directory has been renamed and/or moved also + // change the name that is stored in the corresponding KMFolderNode + // (provide that the name actually changed) + if( child && ( oldName != newName ) ) { + child->setName( "." + QFile::encodeName(newName) + ".directory" ); + } + } + + // if the folder is being moved then move its node and, if necessary, also + // the associated subfolder directory node to the new parent + if (newParent) { + if (oldParent->findRef( folder() ) != -1) + oldParent->take(); + newParent->inSort( folder() ); + if ( child ) { + if ( child->parent()->findRef( child ) != -1 ) + child->parent()->take(); + newParent->inSort( child ); + child->setParent( newParent ); + } + } + } + + writeConfig(); + + // delete the old entry as we get two entries with the same ID otherwise + if ( oldConfigString != "Folder-" + folder()->idString() ) + KMKernel::config()->deleteGroup( oldConfigString ); + + emit locationChanged( oldLoc, newLoc ); + emit nameChanged(); + kmkernel->folderMgr()->contentsChanged(); + emit closed(folder()); // let the ticket owners regain + return rc; +} + + +//----------------------------------------------------------------------------- +void FolderStorage::remove() +{ + assert(!folder()->name().isEmpty()); + + clearIndex( true, mExportsSernums ); // delete and remove from dict if necessary + close("remove", true); + + if ( mExportsSernums ) { + KMMsgDict::mutableInstance()->removeFolderIds( *this ); + mExportsSernums = false; // do not writeFolderIds after removal + } + unlink(QFile::encodeName(indexLocation()) + ".sorted"); + unlink(QFile::encodeName(indexLocation())); + + int rc = removeContents(); + + needsCompact = false; //we are dead - no need to compact us + + // Erase settings, otherwise they might interfer when recreating the folder + KConfig* config = KMKernel::config(); + config->deleteGroup( "Folder-" + folder()->idString() ); + + emit closed(folder()); + emit removed(folder(), (rc ? false : true)); +} + + +//----------------------------------------------------------------------------- +int FolderStorage::expunge() +{ + assert(!folder()->name().isEmpty()); + + clearIndex( true, mExportsSernums ); // delete and remove from dict, if needed + close( "expunge", true ); + + if ( mExportsSernums ) + KMMsgDict::mutableInstance()->removeFolderIds( *this ); + if ( mAutoCreateIndex ) + truncateIndex(); + else unlink(QFile::encodeName(indexLocation())); + + int rc = expungeContents(); + if (rc) return rc; + + mDirty = false; + needsCompact = false; //we're cleared and truncated no need to compact + + mUnreadMsgs = 0; + mTotalMsgs = 0; + mSize = 0; + emit numUnreadMsgsChanged( folder() ); + if ( mAutoCreateIndex ) // FIXME Heh? - Till + writeConfig(); + emit changed(); + emit expunged( folder() ); + + return 0; +} + +//----------------------------------------------------------------------------- +QString FolderStorage::label() const +{ + return folder()->label(); +} + +int FolderStorage::count(bool cache) const +{ + if (cache && mTotalMsgs != -1) + return mTotalMsgs; + else + return -1; +} + +//----------------------------------------------------------------------------- +int FolderStorage::countUnread() +{ + if (mGuessedUnreadMsgs > -1) + return mGuessedUnreadMsgs; + if (mUnreadMsgs > -1) + return mUnreadMsgs; + + readConfig(); + + if (mUnreadMsgs > -1) + return mUnreadMsgs; + + open("countunread"); // will update unreadMsgs + int unread = mUnreadMsgs; + close("countunread"); + return (unread > 0) ? unread : 0; +} + +Q_INT64 FolderStorage::folderSize() const +{ + if ( mSize != -1 ) { + return mSize; + } else { + return doFolderSize(); + } +} + + +/*virtual*/ +bool FolderStorage::isCloseToQuota() const +{ + return false; +} + +//----------------------------------------------------------------------------- +void FolderStorage::msgStatusChanged(const KMMsgStatus oldStatus, + const KMMsgStatus newStatus, int idx) +{ + int oldUnread = 0; + int newUnread = 0; + + if (((oldStatus & KMMsgStatusUnread || oldStatus & KMMsgStatusNew) && + !(oldStatus & KMMsgStatusIgnored)) || + (folder() == kmkernel->outboxFolder())) + oldUnread = 1; + if (((newStatus & KMMsgStatusUnread || newStatus & KMMsgStatusNew) && + !(newStatus & KMMsgStatusIgnored)) || + (folder() == kmkernel->outboxFolder())) + newUnread = 1; + int deltaUnread = newUnread - oldUnread; + + mDirtyTimer->changeInterval(mDirtyTimerInterval); + if (deltaUnread != 0) { + if (mUnreadMsgs < 0) mUnreadMsgs = 0; + mUnreadMsgs += deltaUnread; + if ( !mQuiet ) { + emit numUnreadMsgsChanged( folder() ); + }else{ + if ( !mEmitChangedTimer->isActive() ) { + mEmitChangedTimer->start( 3000 ); + } + mChanged = true; + } + Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum(folder(), idx); + emit msgChanged( folder(), serNum, deltaUnread ); + } +} + +//----------------------------------------------------------------------------- +void FolderStorage::headerOfMsgChanged(const KMMsgBase* aMsg, int idx) +{ + if (idx < 0) + idx = aMsg->parent()->find( aMsg ); + + if (idx >= 0 ) + { + if ( !mQuiet ) + emit msgHeaderChanged(folder(), idx); + else{ + if ( !mEmitChangedTimer->isActive() ) { + mEmitChangedTimer->start( 3000 ); + } + mChanged = true; + } + } else + mChanged = true; +} + +//----------------------------------------------------------------------------- +void FolderStorage::readConfig() +{ + //kdDebug(5006)<<"#### READING CONFIG = "<< name() <<endl; + KConfig* config = KMKernel::config(); + KConfigGroupSaver saver(config, "Folder-" + folder()->idString()); + if (mUnreadMsgs == -1) + mUnreadMsgs = config->readNumEntry("UnreadMsgs", -1); + if (mTotalMsgs == -1) + mTotalMsgs = config->readNumEntry("TotalMsgs", -1); + mCompactable = config->readBoolEntry("Compactable", true); + if ( mSize == -1 ) + mSize = config->readNum64Entry("FolderSize", -1); + + int type = config->readNumEntry( "ContentsType", 0 ); + if ( type < 0 || type > KMail::ContentsTypeLast ) type = 0; + setContentsType( static_cast<KMail::FolderContentsType>( type ) ); + + if( folder() ) folder()->readConfig( config ); +} + +//----------------------------------------------------------------------------- +void FolderStorage::writeConfig() +{ + KConfig* config = KMKernel::config(); + KConfigGroupSaver saver(config, "Folder-" + folder()->idString()); + config->writeEntry("UnreadMsgs", + mGuessedUnreadMsgs == -1 ? mUnreadMsgs : mGuessedUnreadMsgs); + config->writeEntry("TotalMsgs", mTotalMsgs); + config->writeEntry("Compactable", mCompactable); + config->writeEntry("ContentsType", mContentsType); + config->writeEntry("FolderSize", mSize); + + // Write the KMFolder parts + if( folder() ) folder()->writeConfig( config ); + + GlobalSettings::self()->requestSync(); +} + +//----------------------------------------------------------------------------- +void FolderStorage::correctUnreadMsgsCount() +{ + open("countunreadmsg"); + close("countunreadmsg"); + emit numUnreadMsgsChanged( folder() ); +} + +void FolderStorage::registerWithMessageDict() +{ + mExportsSernums = true; + readFolderIdsFile(); +} + +void FolderStorage::deregisterFromMessageDict() +{ + writeFolderIdsFile(); + mExportsSernums = false; +} + +void FolderStorage::readFolderIdsFile() +{ + if ( !mExportsSernums ) return; + if ( KMMsgDict::mutableInstance()->readFolderIds( *this ) == -1 ) { + invalidateFolder(); + } + if ( !KMMsgDict::mutableInstance()->hasFolderIds( *this ) ) { + invalidateFolder(); + } +} + +void FolderStorage::invalidateFolder() +{ + if ( !mExportsSernums ) return; + unlink(QFile::encodeName( indexLocation()) + ".sorted"); + unlink(QFile::encodeName( indexLocation()) + ".ids"); + fillMessageDict(); + KMMsgDict::mutableInstance()->writeFolderIds( *this ); + emit invalidated( folder() ); +} + + +//----------------------------------------------------------------------------- +int FolderStorage::writeFolderIdsFile() const +{ + if ( !mExportsSernums ) return -1; + return KMMsgDict::mutableInstance()->writeFolderIds( *this ); +} + +//----------------------------------------------------------------------------- +int FolderStorage::touchFolderIdsFile() +{ + if ( !mExportsSernums ) return -1; + return KMMsgDict::mutableInstance()->touchFolderIds( *this ); +} + +//----------------------------------------------------------------------------- +int FolderStorage::appendToFolderIdsFile( int idx ) +{ + if ( !mExportsSernums ) return -1; + int ret = 0; + if ( count() == 1 ) { + ret = KMMsgDict::mutableInstance()->writeFolderIds( *this ); + } else { + ret = KMMsgDict::mutableInstance()->appendToFolderIds( *this, idx ); + } + return ret; +} + +void FolderStorage::replaceMsgSerNum( unsigned long sernum, KMMsgBase* msg, int idx ) +{ + if ( !mExportsSernums ) return; + KMMsgDict::mutableInstance()->replace( sernum, msg, idx ); +} + +void FolderStorage::setRDict( KMMsgDictREntry *rentry ) const +{ + if ( ! mExportsSernums ) + kdDebug(5006) << "WTF, this FolderStorage should be invisible to the msgdict, who is calling us?" << kdBacktrace() << endl; + assert( mExportsSernums ); // otherwise things are very wrong + if ( rentry == mRDict ) + return; + KMMsgDict::deleteRentry( mRDict ); + mRDict = rentry; +} + +//----------------------------------------------------------------------------- +void FolderStorage::setStatus(int idx, KMMsgStatus status, bool toggle) +{ + KMMsgBase *msg = getMsgBase(idx); + if ( msg ) { + if (toggle) + msg->toggleStatus(status, idx); + else + msg->setStatus(status, idx); + } +} + + +//----------------------------------------------------------------------------- +void FolderStorage::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle) +{ + for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) + { + FolderStorage::setStatus(*it, status, toggle); + } +} + +void FolderStorage::ignoreJobsForMessage( KMMessage *msg ) +{ + if ( !msg || msg->transferInProgress() ) + return; + + QPtrListIterator<FolderJob> it( mJobList ); + while ( it.current() ) + { + //FIXME: the questions is : should we iterate through all + //messages in jobs? I don't think so, because it would + //mean canceling the jobs that work with other messages + if ( it.current()->msgList().first() == msg ) + { + FolderJob* job = it.current(); + mJobList.remove( job ); + delete job; + } else + ++it; + } +} + +//----------------------------------------------------------------------------- +void FolderStorage::removeJobs() +{ + mJobList.setAutoDelete( true ); + mJobList.clear(); + mJobList.setAutoDelete( false ); +} + + + +//----------------------------------------------------------------------------- +void FolderStorage::updateChildrenState() +{ + if ( folder() && folder()->child() ) + { + if ( kmkernel->folderMgr()->folderCount( folder()->child() ) > 0 ) + setHasChildren( HasChildren ); + else + setHasChildren( HasNoChildren ); + } +} + +//----------------------------------------------------------------------------- +void FolderStorage::setNoChildren( bool aNoChildren ) +{ + mNoChildren = aNoChildren; + if ( aNoChildren ) + setHasChildren( HasNoChildren ); +} + +//----------------------------------------------------------------------------- +void FolderStorage::setContentsType( KMail::FolderContentsType type, bool quiet ) +{ + if ( type != mContentsType ) { + mContentsType = type; + if ( !quiet ) + emit contentsTypeChanged( type ); + } +} + +//----------------------------------------------------------------------------- +void FolderStorage::search( const KMSearchPattern* pattern ) +{ + mSearchPattern = pattern; + mCurrentSearchedMsg = 0; + if ( pattern ) + slotProcessNextSearchBatch(); +} + +void FolderStorage::slotProcessNextSearchBatch() +{ + if ( !mSearchPattern ) + return; + QValueList<Q_UINT32> matchingSerNums; + const int end = QMIN( mCurrentSearchedMsg + 15, count() ); + for ( int i = mCurrentSearchedMsg; i < end; ++i ) + { + Q_UINT32 serNum = KMMsgDict::instance()->getMsgSerNum( folder(), i ); + if ( mSearchPattern->matches( serNum ) ) + matchingSerNums.append( serNum ); + } + mCurrentSearchedMsg = end; + bool complete = ( end >= count() ); + emit searchResult( folder(), matchingSerNums, mSearchPattern, complete ); + if ( !complete ) + QTimer::singleShot( 0, this, SLOT(slotProcessNextSearchBatch()) ); +} + +//----------------------------------------------------------------------------- +void FolderStorage::search( const KMSearchPattern* pattern, Q_UINT32 serNum ) +{ + bool matches = pattern && pattern->matches( serNum ); + + emit searchDone( folder(), serNum, pattern, matches ); +} + +//----------------------------------------------------------------------------- +int FolderStorage::addMsg( QPtrList<KMMessage>& msgList, QValueList<int>& index_ret ) +{ + int ret = 0; + int index; + for ( QPtrListIterator<KMMessage> it( msgList ); *it; ++it ) + { + int aret = addMsg( *it, &index ); + index_ret << index; + if ( aret != 0 ) // error condition + ret = aret; + } + return ret; +} + +//----------------------------------------------------------------------------- +bool FolderStorage::isMoveable() const +{ + return ( folder()->isSystemFolder() ) ? false : true; +} + + +/*virtual*/ +KMAccount* FolderStorage::account() const +{ + return 0; +} + +#include "folderstorage.moc" |