diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch) | |
tree | 67208f7c145782a7e90b123b982ca78d88cc2c87 /kmail/kmmsgdict.cpp | |
download | tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kmail/kmmsgdict.cpp')
-rw-r--r-- | kmail/kmmsgdict.cpp | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/kmail/kmmsgdict.cpp b/kmail/kmmsgdict.cpp new file mode 100644 index 000000000..6f834e02d --- /dev/null +++ b/kmail/kmmsgdict.cpp @@ -0,0 +1,651 @@ +/* kmail message dictionary */ +/* Author: Ronen Tzur <rtzur@shani.net> */ + +#include "kmfolderindex.h" +#include "kmfolder.h" +#include "kmmsgdict.h" +#include "kmdict.h" +#include "globalsettings.h" +#include "folderstorage.h" + +#include <qfileinfo.h> + +#include <kdebug.h> +#include <kstaticdeleter.h> + +#include <stdio.h> +#include <unistd.h> + +#include <string.h> +#include <errno.h> + +#include <config.h> + +#ifdef HAVE_BYTESWAP_H +#include <byteswap.h> +#endif + +// We define functions as kmail_swap_NN so that we don't get compile errors +// on platforms where bswap_NN happens to be a function instead of a define. + +/* Swap bytes in 32 bit value. */ +#ifdef bswap_32 +#define kmail_swap_32(x) bswap_32(x) +#else +#define kmail_swap_32(x) \ + ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) +#endif + + +//----------------------------------------------------------------------------- + +// Current version of the .index.ids files +#define IDS_VERSION 1002 + +// The asterisk at the end is important +#define IDS_HEADER "# KMail-Index-IDs V%d\n*" + +/** + * @short an entry in the global message dictionary consisting of a pointer + * to a folder and the index of a message in the folder. + */ +class KMMsgDictEntry : public KMDictItem +{ +public: + KMMsgDictEntry(const KMFolder *aFolder, int aIndex) + : folder( aFolder ), index( aIndex ) + {} + + const KMFolder *folder; + int index; +}; + +/** + * @short A "reverse entry", consisting of an array of DictEntry pointers. + * + * Each folder (storage) holds such an entry. That's useful for looking up the + * serial number of a message at a certain index in the folder, since that is the + * key of these entries. + */ +class KMMsgDictREntry +{ +public: + KMMsgDictREntry(int size = 0) + { + array.resize(size); + memset(array.data(), 0, array.size() * sizeof(KMMsgDictEntry *)); // faster than a loop + fp = 0; + swapByteOrder = false; + baseOffset = 0; + } + + ~KMMsgDictREntry() + { + array.resize(0); + if (fp) + fclose(fp); + } + + void set(int index, KMMsgDictEntry *entry) + { + if (index >= 0) { + int size = array.size(); + if (index >= size) { + int newsize = QMAX(size + 25, index + 1); + array.resize(newsize); + for (int j = size; j < newsize; j++) + array.at(j) = 0; + } + array.at(index) = entry; + } + } + + KMMsgDictEntry *get(int index) + { + if (index >= 0 && (unsigned)index < array.size()) + return array.at(index); + return 0; + } + + ulong getMsn(int index) + { + KMMsgDictEntry *entry = get(index); + if (entry) + return entry->key; + return 0; + } + + int getRealSize() + { + int count = array.size() - 1; + while (count >= 0) { + if (array.at(count)) + break; + count--; + } + return count + 1; + } + + void sync() + { + fflush(fp); + } + +public: + QMemArray<KMMsgDictEntry *> array; + FILE *fp; + bool swapByteOrder; + off_t baseOffset; +}; + + +static KStaticDeleter<KMMsgDict> msgDict_sd; +KMMsgDict * KMMsgDict::m_self = 0; + +//----------------------------------------------------------------------------- + +KMMsgDict::KMMsgDict() +{ + int lastSizeOfDict = GlobalSettings::self()->msgDictSizeHint(); + lastSizeOfDict = ( lastSizeOfDict * 11 ) / 10; + GlobalSettings::self()->setMsgDictSizeHint( 0 ); + dict = new KMDict( lastSizeOfDict ); + nextMsgSerNum = 1; + m_self = this; +} + +//----------------------------------------------------------------------------- + +KMMsgDict::~KMMsgDict() +{ + delete dict; +} + +//----------------------------------------------------------------------------- + +const KMMsgDict* KMMsgDict::instance() +{ + if ( !m_self ) { + msgDict_sd.setObject( m_self, new KMMsgDict() ); + } + return m_self; +} + +KMMsgDict* KMMsgDict::mutableInstance() +{ + if ( !m_self ) { + msgDict_sd.setObject( m_self, new KMMsgDict() ); + } + return m_self; +} + +//----------------------------------------------------------------------------- + +unsigned long KMMsgDict::getNextMsgSerNum() { + unsigned long msn = nextMsgSerNum; + nextMsgSerNum++; + return msn; +} + +void KMMsgDict::deleteRentry(KMMsgDictREntry *entry) +{ + delete entry; +} + +unsigned long KMMsgDict::insert(unsigned long msgSerNum, + const KMMsgBase *msg, int index) +{ + unsigned long msn = msgSerNum; + if (!msn) { + msn = getNextMsgSerNum(); + } else { + if (msn >= nextMsgSerNum) + nextMsgSerNum = msn + 1; + } + + KMFolderIndex* folder = static_cast<KMFolderIndex*>( msg->storage() ); + if ( !folder ) { + kdDebug(5006) << "KMMsgDict::insert: Cannot insert the message, " + << "null pointer to storage. Requested serial: " << msgSerNum + << endl; + kdDebug(5006) << " Message info: Subject: " << msg->subject() << ", To: " + << msg->toStrip() << ", Date: " << msg->dateStr() << endl; + return 0; + } + + if (index == -1) + index = folder->find(msg); + + // Should not happen, indicates id file corruption + while (dict->find((long)msn)) { + msn = getNextMsgSerNum(); + folder->setDirty( true ); // rewrite id file + } + + // Insert into the dict. Don't use dict->replace() as we _know_ + // there is no entry with the same msn, we just made sure. + KMMsgDictEntry *entry = new KMMsgDictEntry(folder->folder(), index); + dict->insert((long)msn, entry); + + KMMsgDictREntry *rentry = folder->rDict(); + if (!rentry) { + rentry = new KMMsgDictREntry(); + folder->setRDict(rentry); + } + rentry->set(index, entry); + + return msn; +} + +unsigned long KMMsgDict::insert(const KMMsgBase *msg, int index) +{ + unsigned long msn = msg->getMsgSerNum(); + return insert(msn, msg, index); +} + +//----------------------------------------------------------------------------- + +void KMMsgDict::replace(unsigned long msgSerNum, + const KMMsgBase *msg, int index) +{ + KMFolderIndex* folder = static_cast<KMFolderIndex*>( msg->storage() ); + if ( !folder ) { + kdDebug(5006) << "KMMsgDict::replace: Cannot replace the message serial " + << "number, null pointer to storage. Requested serial: " << msgSerNum + << endl; + kdDebug(5006) << " Message info: Subject: " << msg->subject() << ", To: " + << msg->toStrip() << ", Date: " << msg->dateStr() << endl; + return; + } + + if ( index == -1 ) + index = folder->find( msg ); + + remove( msgSerNum ); + KMMsgDictEntry *entry = new KMMsgDictEntry( folder->folder(), index ); + dict->insert( (long)msgSerNum, entry ); + + KMMsgDictREntry *rentry = folder->rDict(); + if (!rentry) { + rentry = new KMMsgDictREntry(); + folder->setRDict(rentry); + } + rentry->set(index, entry); +} + +//----------------------------------------------------------------------------- + +void KMMsgDict::remove(unsigned long msgSerNum) +{ + long key = (long)msgSerNum; + KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find(key); + if (!entry) + return; + + if (entry->folder) { + KMMsgDictREntry *rentry = entry->folder->storage()->rDict(); + if (rentry) + rentry->set(entry->index, 0); + } + + dict->remove((long)key); +} + +unsigned long KMMsgDict::remove(const KMMsgBase *msg) +{ + unsigned long msn = msg->getMsgSerNum(); + remove(msn); + return msn; +} + +//----------------------------------------------------------------------------- + +void KMMsgDict::update(const KMMsgBase *msg, int index, int newIndex) +{ + KMMsgDictREntry *rentry = msg->parent()->storage()->rDict(); + if (rentry) { + KMMsgDictEntry *entry = rentry->get(index); + if (entry) { + entry->index = newIndex; + rentry->set(index, 0); + rentry->set(newIndex, entry); + } + } +} + +//----------------------------------------------------------------------------- + +void KMMsgDict::getLocation(unsigned long key, + KMFolder **retFolder, int *retIndex) const +{ + KMMsgDictEntry *entry = (KMMsgDictEntry *)dict->find((long)key); + if (entry) { + *retFolder = (KMFolder *)entry->folder; + *retIndex = entry->index; + } else { + *retFolder = 0; + *retIndex = -1; + } +} + +void KMMsgDict::getLocation(const KMMsgBase *msg, + KMFolder **retFolder, int *retIndex) const +{ + getLocation(msg->getMsgSerNum(), retFolder, retIndex); +} + +void KMMsgDict::getLocation( const KMMessage * msg, KMFolder * *retFolder, int * retIndex ) const +{ + getLocation( msg->toMsgBase().getMsgSerNum(), retFolder, retIndex ); +} + +//----------------------------------------------------------------------------- + +unsigned long KMMsgDict::getMsgSerNum(KMFolder *folder, int index) const +{ + unsigned long msn = 0; + if ( folder ) { + KMMsgDictREntry *rentry = folder->storage()->rDict(); + if (rentry) + msn = rentry->getMsn(index); + } + return msn; +} + +//----------------------------------------------------------------------------- + +//static +QValueList<unsigned long> KMMsgDict::serNumList(QPtrList<KMMsgBase> msgList) +{ + QValueList<unsigned long> ret; + for ( unsigned int i = 0; i < msgList.count(); i++ ) { + unsigned long serNum = msgList.at(i)->getMsgSerNum(); + assert( serNum ); + ret.append( serNum ); + } + return ret; +} + +//----------------------------------------------------------------------------- + +QString KMMsgDict::getFolderIdsLocation( const FolderStorage &storage ) +{ + return storage.indexLocation() + ".ids"; +} + +//----------------------------------------------------------------------------- + +bool KMMsgDict::isFolderIdsOutdated( const FolderStorage &storage ) +{ + bool outdated = false; + + QFileInfo indexInfo( storage.indexLocation() ); + QFileInfo idsInfo( getFolderIdsLocation( storage ) ); + + if (!indexInfo.exists() || !idsInfo.exists()) + outdated = true; + if (indexInfo.lastModified() > idsInfo.lastModified()) + outdated = true; + + return outdated; +} + +//----------------------------------------------------------------------------- + +int KMMsgDict::readFolderIds( FolderStorage& storage ) +{ + if ( isFolderIdsOutdated( storage ) ) + return -1; + + QString filename = getFolderIdsLocation( storage ); + FILE *fp = fopen(QFile::encodeName(filename), "r+"); + if (!fp) + return -1; + + int version = 0; + fscanf(fp, IDS_HEADER, &version); + if (version != IDS_VERSION) { + fclose(fp); + return -1; + } + + bool swapByteOrder; + Q_UINT32 byte_order; + if (!fread(&byte_order, sizeof(byte_order), 1, fp)) { + fclose(fp); + return -1; + } + swapByteOrder = (byte_order == 0x78563412); + + Q_UINT32 count; + if (!fread(&count, sizeof(count), 1, fp)) { + fclose(fp); + return -1; + } + if (swapByteOrder) + count = kmail_swap_32(count); + + // quick consistency check to avoid allocating huge amount of memory + // due to reading corrupt file (#71549) + long pos = ftell(fp); // store current position + fseek(fp, 0, SEEK_END); + long fileSize = ftell(fp); // how large is the file ? + fseek(fp, pos, SEEK_SET); // back to previous position + + // the file must at least contain what we try to read below + if ( (fileSize - pos) < (long)(count * sizeof(Q_UINT32)) ) { + fclose(fp); + return -1; + } + + KMMsgDictREntry *rentry = new KMMsgDictREntry(count); + + for (unsigned int index = 0; index < count; index++) { + Q_UINT32 msn; + + bool readOk = fread(&msn, sizeof(msn), 1, fp); + if (swapByteOrder) + msn = kmail_swap_32(msn); + + if (!readOk || dict->find(msn)) { + for (unsigned int i = 0; i < index; i++) { + msn = rentry->getMsn(i); + dict->remove((long)msn); + } + delete rentry; + fclose(fp); + return -1; + } + + //if (!msn) + //kdDebug(5006) << "Dict found zero serial number in folder " << folder->label() << endl; + + // Insert into the dict. Don't use dict->replace() as we _know_ + // there is no entry with the same msn, we just made sure. + KMMsgDictEntry *entry = new KMMsgDictEntry( storage.folder(), index); + dict->insert((long)msn, entry); + if (msn >= nextMsgSerNum) + nextMsgSerNum = msn + 1; + + rentry->set(index, entry); + } + // Remember how many items we put into the dict this time so we can create + // it with an appropriate size next time. + GlobalSettings::setMsgDictSizeHint( GlobalSettings::msgDictSizeHint() + count ); + + fclose(fp); + storage.setRDict(rentry); + + return 0; +} + +//----------------------------------------------------------------------------- + +KMMsgDictREntry *KMMsgDict::openFolderIds( const FolderStorage& storage, bool truncate) +{ + KMMsgDictREntry *rentry = storage.rDict(); + if (!rentry) { + rentry = new KMMsgDictREntry(); + storage.setRDict(rentry); + } + + if (!rentry->fp) { + QString filename = getFolderIdsLocation( storage ); + FILE *fp = truncate ? 0 : fopen(QFile::encodeName(filename), "r+"); + if (fp) + { + int version = 0; + fscanf(fp, IDS_HEADER, &version); + if (version == IDS_VERSION) + { + Q_UINT32 byte_order = 0; + fread(&byte_order, sizeof(byte_order), 1, fp); + rentry->swapByteOrder = (byte_order == 0x78563412); + } + else + { + fclose(fp); + fp = 0; + } + } + + if (!fp) + { + fp = fopen(QFile::encodeName(filename), "w+"); + if (!fp) + { + kdDebug(5006) << "Dict '" << filename + << "' cannot open with folder " << storage.label() << ": " + << strerror(errno) << " (" << errno << ")" << endl; + delete rentry; + rentry = 0; + return 0; + } + fprintf(fp, IDS_HEADER, IDS_VERSION); + Q_UINT32 byteOrder = 0x12345678; + fwrite(&byteOrder, sizeof(byteOrder), 1, fp); + rentry->swapByteOrder = false; + } + rentry->baseOffset = ftell(fp); + rentry->fp = fp; + } + + return rentry; +} + +//----------------------------------------------------------------------------- + +int KMMsgDict::writeFolderIds( const FolderStorage &storage ) +{ + KMMsgDictREntry *rentry = openFolderIds( storage, true ); + if (!rentry) + return 0; + FILE *fp = rentry->fp; + + fseek(fp, rentry->baseOffset, SEEK_SET); + // kdDebug(5006) << "Dict writing for folder " << storage.label() << endl; + Q_UINT32 count = rentry->getRealSize(); + if (!fwrite(&count, sizeof(count), 1, fp)) { + kdDebug(5006) << "Dict cannot write count with folder " << storage.label() << ": " + << strerror(errno) << " (" << errno << ")" << endl; + return -1; + } + + for (unsigned int index = 0; index < count; index++) { + Q_UINT32 msn = rentry->getMsn(index); + if (!fwrite(&msn, sizeof(msn), 1, fp)) + return -1; + } + + rentry->sync(); + + off_t eof = ftell(fp); + QString filename = getFolderIdsLocation( storage ); + truncate(QFile::encodeName(filename), eof); + fclose(rentry->fp); + rentry->fp = 0; + + return 0; +} + +//----------------------------------------------------------------------------- + +int KMMsgDict::touchFolderIds( const FolderStorage &storage ) +{ + KMMsgDictREntry *rentry = openFolderIds( storage, false); + if (rentry) { + rentry->sync(); + fclose(rentry->fp); + rentry->fp = 0; + } + return 0; +} + +//----------------------------------------------------------------------------- + +int KMMsgDict::appendToFolderIds( FolderStorage& storage, int index) +{ + KMMsgDictREntry *rentry = openFolderIds( storage, false); + if (!rentry) + return 0; + FILE *fp = rentry->fp; + +// kdDebug(5006) << "Dict appending for folder " << storage.label() << endl; + + fseek(fp, rentry->baseOffset, SEEK_SET); + Q_UINT32 count; + if (!fread(&count, sizeof(count), 1, fp)) { + kdDebug(5006) << "Dict cannot read count for folder " << storage.label() << ": " + << strerror(errno) << " (" << errno << ")" << endl; + return 0; + } + if (rentry->swapByteOrder) + count = kmail_swap_32(count); + + count++; + + if (rentry->swapByteOrder) + count = kmail_swap_32(count); + fseek(fp, rentry->baseOffset, SEEK_SET); + if (!fwrite(&count, sizeof(count), 1, fp)) { + kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": " + << strerror(errno) << " (" << errno << ")" << endl; + return 0; + } + + long ofs = (count - 1) * sizeof(ulong); + if (ofs > 0) + fseek(fp, ofs, SEEK_CUR); + + Q_UINT32 msn = rentry->getMsn(index); + if (rentry->swapByteOrder) + msn = kmail_swap_32(msn); + if (!fwrite(&msn, sizeof(msn), 1, fp)) { + kdDebug(5006) << "Dict cannot write count for folder " << storage.label() << ": " + << strerror(errno) << " (" << errno << ")" << endl; + return 0; + } + + rentry->sync(); + fclose(rentry->fp); + rentry->fp = 0; + + return 0; +} + +//----------------------------------------------------------------------------- + +bool KMMsgDict::hasFolderIds( const FolderStorage& storage ) +{ + return storage.rDict() != 0; +} + +//----------------------------------------------------------------------------- + +bool KMMsgDict::removeFolderIds( FolderStorage& storage ) +{ + storage.setRDict( 0 ); + QString filename = getFolderIdsLocation( storage ); + return unlink( QFile::encodeName( filename) ); +} |