summaryrefslogtreecommitdiffstats
path: root/kmail/kmmsgbase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmail/kmmsgbase.cpp')
-rw-r--r--kmail/kmmsgbase.cpp1483
1 files changed, 1483 insertions, 0 deletions
diff --git a/kmail/kmmsgbase.cpp b/kmail/kmmsgbase.cpp
new file mode 100644
index 000000000..00c7e5761
--- /dev/null
+++ b/kmail/kmmsgbase.cpp
@@ -0,0 +1,1483 @@
+// kmmsgbase.cpp
+
+#include <config.h>
+
+#include "globalsettings.h"
+#include "kmmsgbase.h"
+
+#include "kmfolderindex.h"
+#include "kmfolder.h"
+#include "kmheaders.h"
+#include "kmmsgdict.h"
+#include "messageproperty.h"
+using KMail::MessageProperty;
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+#include <kasciistringtools.h>
+#include <kmdcodec.h>
+#include <krfcdate.h>
+
+#include <mimelib/mimepp.h>
+#include <kmime_codecs.h>
+
+#include <qtextcodec.h>
+#include <qdeepcopy.h>
+#include <qregexp.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.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 16 bit value. */
+#ifdef bswap_16
+#define kmail_swap_16(x) bswap_16(x)
+#else
+#define kmail_swap_16(x) \
+ ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
+#endif
+
+/* 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
+
+/* Swap bytes in 64 bit value. */
+#ifdef bswap_64
+#define kmail_swap_64(x) bswap_64(x)
+#else
+#define kmail_swap_64(x) \
+ ((((x) & 0xff00000000000000ull) >> 56) \
+ | (((x) & 0x00ff000000000000ull) >> 40) \
+ | (((x) & 0x0000ff0000000000ull) >> 24) \
+ | (((x) & 0x000000ff00000000ull) >> 8) \
+ | (((x) & 0x00000000ff000000ull) << 8) \
+ | (((x) & 0x0000000000ff0000ull) << 24) \
+ | (((x) & 0x000000000000ff00ull) << 40) \
+ | (((x) & 0x00000000000000ffull) << 56))
+#endif
+
+//-----------------------------------------------------------------------------
+KMMsgBase::KMMsgBase(KMFolder* aParentFolder)
+ : mParent( aParentFolder ), mIndexOffset( 0 ),
+ mIndexLength( 0 ), mDirty( false ), mEnableUndo( false ), mStatus( KMMsgStatusUnknown )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+KMMsgBase::~KMMsgBase()
+{
+ MessageProperty::forget( this );
+}
+
+KMFolderIndex* KMMsgBase::storage() const
+{
+ // TODO: How did this ever work? What about KMFolderSearch that does
+ // not inherit KMFolderIndex?
+ if( mParent )
+ return static_cast<KMFolderIndex*>( mParent->storage() );
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+void KMMsgBase::assign(const KMMsgBase* other)
+{
+ mParent = other->mParent;
+ mDirty = other->mDirty;
+ mIndexOffset = other->mIndexOffset;
+ mIndexLength = other->mIndexLength;
+}
+
+//-----------------------------------------------------------------------------
+KMMsgBase& KMMsgBase::operator=(const KMMsgBase& other)
+{
+ assign(&other);
+ return *this;
+}
+
+
+//----------------------------------------------------------------------------
+KMMsgBase::KMMsgBase( const KMMsgBase& other )
+{
+ assign( &other );
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isMessage(void) const
+{
+ return false;
+}
+//-----------------------------------------------------------------------------
+void KMMsgBase::toggleStatus(const KMMsgStatus aStatus, int idx)
+{
+ mDirty = true;
+ KMMsgStatus oldStatus = status();
+ if ( status() & aStatus ) {
+ mStatus &= ~aStatus;
+ } else {
+ mStatus |= aStatus;
+ // Ignored and Watched are toggleable, yet mutually exclusive.
+ // That is an arbitrary restriction on my part. HAR HAR HAR :) -till
+ if (aStatus == KMMsgStatusWatched)
+ mStatus &= ~KMMsgStatusIgnored;
+ if (aStatus == KMMsgStatusIgnored)
+ mStatus &= ~KMMsgStatusWatched;
+ if (aStatus == KMMsgStatusSpam)
+ mStatus &= ~KMMsgStatusHam;
+ if (aStatus == KMMsgStatusHam)
+ mStatus &= ~KMMsgStatusSpam;
+ }
+ if (storage()) {
+ if (idx < 0)
+ idx = storage()->find( this );
+ storage()->msgStatusChanged( oldStatus, status(), idx );
+ storage()->headerOfMsgChanged(this, idx);
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+void KMMsgBase::setStatus(const KMMsgStatus aStatus, int idx)
+{
+ mDirty = true;
+ KMMsgStatus oldStatus = status();
+ switch (aStatus) {
+ case KMMsgStatusRead:
+ // Unset unread and new, set read
+ mStatus &= ~KMMsgStatusUnread;
+ mStatus &= ~KMMsgStatusNew;
+ mStatus |= KMMsgStatusRead;
+ break;
+
+ case KMMsgStatusUnread:
+ // unread overrides read
+ mStatus &= ~KMMsgStatusOld;
+ mStatus &= ~KMMsgStatusRead;
+ mStatus &= ~KMMsgStatusNew;
+ mStatus |= KMMsgStatusUnread;
+ break;
+
+ case KMMsgStatusOld:
+ // old can't be new or unread
+ mStatus &= ~KMMsgStatusNew;
+ mStatus &= ~KMMsgStatusUnread;
+ mStatus |= KMMsgStatusOld;
+ break;
+
+ case KMMsgStatusNew:
+ // new overrides old and read
+ mStatus &= ~KMMsgStatusOld;
+ mStatus &= ~KMMsgStatusRead;
+ mStatus &= ~KMMsgStatusUnread;
+ mStatus |= KMMsgStatusNew;
+ break;
+
+ case KMMsgStatusDeleted:
+ mStatus |= KMMsgStatusDeleted;
+ break;
+
+ case KMMsgStatusReplied:
+ mStatus |= KMMsgStatusReplied;
+ break;
+
+ case KMMsgStatusForwarded:
+ mStatus |= KMMsgStatusForwarded;
+ break;
+
+ case KMMsgStatusQueued:
+ mStatus |= KMMsgStatusQueued;
+ break;
+
+ case KMMsgStatusTodo:
+ mStatus |= KMMsgStatusTodo;
+ break;
+
+ case KMMsgStatusSent:
+ mStatus &= ~KMMsgStatusQueued;
+ mStatus &= ~KMMsgStatusUnread;
+ mStatus &= ~KMMsgStatusNew;
+ mStatus |= KMMsgStatusSent;
+ break;
+
+ case KMMsgStatusFlag:
+ mStatus |= KMMsgStatusFlag;
+ break;
+
+ // Watched and ignored are mutually exclusive
+ case KMMsgStatusWatched:
+ mStatus &= ~KMMsgStatusIgnored;
+ mStatus |= KMMsgStatusWatched;
+ break;
+
+ case KMMsgStatusIgnored:
+ mStatus &= ~KMMsgStatusWatched;
+ mStatus |= KMMsgStatusIgnored;
+ break;
+ // as are ham and spam
+ case KMMsgStatusSpam:
+ mStatus &= ~KMMsgStatusHam;
+ mStatus |= KMMsgStatusSpam;
+ break;
+ case KMMsgStatusHam:
+ mStatus &= ~KMMsgStatusSpam;
+ mStatus |= KMMsgStatusHam;
+ break;
+ case KMMsgStatusHasAttach:
+ mStatus &= ~KMMsgStatusHasNoAttach;
+ mStatus |= KMMsgStatusHasAttach;
+ break;
+ case KMMsgStatusHasNoAttach:
+ mStatus &= ~KMMsgStatusHasAttach;
+ mStatus |= KMMsgStatusHasNoAttach;
+ break;
+ default:
+ mStatus = aStatus;
+ break;
+ }
+
+ if ( oldStatus != mStatus && storage() ) {
+ if (idx < 0)
+ idx = storage()->find( this );
+ storage()->msgStatusChanged( oldStatus, status(), idx );
+ storage()->headerOfMsgChanged( this, idx );
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+void KMMsgBase::setStatus(const char* aStatusStr, const char* aXStatusStr)
+{
+ // first try to find status from "X-Status" field if given
+ if (aXStatusStr) {
+ if (strchr(aXStatusStr, 'N')) setStatus(KMMsgStatusNew);
+ if (strchr(aXStatusStr, 'U')) setStatus(KMMsgStatusUnread);
+ if (strchr(aXStatusStr, 'O')) setStatus(KMMsgStatusOld);
+ if (strchr(aXStatusStr, 'R')) setStatus(KMMsgStatusRead);
+ if (strchr(aXStatusStr, 'D')) setStatus(KMMsgStatusDeleted);
+ if (strchr(aXStatusStr, 'A')) setStatus(KMMsgStatusReplied);
+ if (strchr(aXStatusStr, 'F')) setStatus(KMMsgStatusForwarded);
+ if (strchr(aXStatusStr, 'Q')) setStatus(KMMsgStatusQueued);
+ if (strchr(aXStatusStr, 'K')) setStatus(KMMsgStatusTodo);
+ if (strchr(aXStatusStr, 'S')) setStatus(KMMsgStatusSent);
+ if (strchr(aXStatusStr, 'G')) setStatus(KMMsgStatusFlag);
+ if (strchr(aXStatusStr, 'P')) setStatus(KMMsgStatusSpam);
+ if (strchr(aXStatusStr, 'H')) setStatus(KMMsgStatusHam);
+ if (strchr(aXStatusStr, 'T')) setStatus(KMMsgStatusHasAttach);
+ if (strchr(aXStatusStr, 'C')) setStatus(KMMsgStatusHasNoAttach);
+ }
+
+ // Merge the contents of the "Status" field
+ if (aStatusStr) {
+ if ((aStatusStr[0]== 'R' && aStatusStr[1]== 'O') ||
+ (aStatusStr[0]== 'O' && aStatusStr[1]== 'R')) {
+ setStatus( KMMsgStatusOld );
+ setStatus( KMMsgStatusRead );
+ }
+ else if (aStatusStr[0] == 'R')
+ setStatus(KMMsgStatusRead);
+ else if (aStatusStr[0] == 'D')
+ setStatus(KMMsgStatusDeleted);
+ else
+ setStatus(KMMsgStatusNew);
+ }
+}
+
+
+void KMMsgBase::setEncryptionState( const KMMsgEncryptionState /*status*/, int idx )
+{
+ //kdDebug(5006) << "***setEncryptionState1( " << status << " )" << endl;
+ mDirty = true;
+ if (storage())
+ storage()->headerOfMsgChanged(this, idx);
+}
+
+void KMMsgBase::setEncryptionStateChar( QChar status, int idx )
+{
+ //kdDebug(5006) << "***setEncryptionState2( " << (status.isNull() ? '?' : status.latin1()) << " )" << endl;
+
+ if( status.latin1() == (char)KMMsgEncryptionStateUnknown )
+ setEncryptionState( KMMsgEncryptionStateUnknown, idx );
+ else if( status.latin1() == (char)KMMsgNotEncrypted )
+ setEncryptionState( KMMsgNotEncrypted, idx );
+ else if( status.latin1() == (char)KMMsgPartiallyEncrypted )
+ setEncryptionState( KMMsgPartiallyEncrypted, idx );
+ else if( status.latin1() == (char)KMMsgFullyEncrypted )
+ setEncryptionState( KMMsgFullyEncrypted, idx );
+ else
+ setEncryptionState( KMMsgEncryptionStateUnknown, idx );
+}
+
+
+void KMMsgBase::setSignatureState( const KMMsgSignatureState /*status*/, int idx )
+{
+ //kdDebug(5006) << "***setSignatureState1( " << status << " )" << endl;
+ mDirty = true;
+ if (storage())
+ storage()->headerOfMsgChanged(this, idx);
+}
+
+void KMMsgBase::setMDNSentState( KMMsgMDNSentState, int idx ) {
+ mDirty = true;
+ if ( storage() )
+ storage()->headerOfMsgChanged(this, idx);
+}
+
+void KMMsgBase::setSignatureStateChar( QChar status, int idx )
+{
+ //kdDebug(5006) << "***setSignatureState2( " << (status.isNull() ? '?' : status.latin1()) << " )" << endl;
+
+ if( status.latin1() == (char)KMMsgSignatureStateUnknown )
+ setSignatureState( KMMsgSignatureStateUnknown, idx );
+ else if( status.latin1() == (char)KMMsgNotSigned )
+ setSignatureState( KMMsgNotSigned, idx );
+ else if( status.latin1() == (char)KMMsgPartiallySigned )
+ setSignatureState( KMMsgPartiallySigned,idx );
+ else if( status.latin1() == (char)KMMsgFullySigned )
+ setSignatureState( KMMsgFullySigned, idx );
+ else
+ setSignatureState( KMMsgSignatureStateUnknown, idx );
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isUnread(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusUnread && !(st & KMMsgStatusIgnored));
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isNew(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusNew && !(st & KMMsgStatusIgnored));
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isOfUnknownStatus(void) const
+{
+ KMMsgStatus st = status();
+ return (st == KMMsgStatusUnknown);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isOld(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusOld);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isRead(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusRead || st & KMMsgStatusIgnored);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isDeleted(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusDeleted);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isReplied(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusReplied);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isForwarded(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusForwarded);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isQueued(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusQueued);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isTodo(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusTodo);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isSent(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusSent);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isImportant(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusFlag);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isWatched(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusWatched);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isIgnored(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusIgnored);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isSpam(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusSpam);
+}
+
+//-----------------------------------------------------------------------------
+bool KMMsgBase::isHam(void) const
+{
+ KMMsgStatus st = status();
+ return (st & KMMsgStatusHam);
+}
+
+//-----------------------------------------------------------------------------
+QCString KMMsgBase::statusToStr(const KMMsgStatus status)
+{
+ QCString sstr;
+ if (status & KMMsgStatusNew) sstr += 'N';
+ if (status & KMMsgStatusUnread) sstr += 'U';
+ if (status & KMMsgStatusOld) sstr += 'O';
+ if (status & KMMsgStatusRead) sstr += 'R';
+ if (status & KMMsgStatusDeleted) sstr += 'D';
+ if (status & KMMsgStatusReplied) sstr += 'A';
+ if (status & KMMsgStatusForwarded) sstr += 'F';
+ if (status & KMMsgStatusQueued) sstr += 'Q';
+ if (status & KMMsgStatusTodo) sstr += 'K';
+ if (status & KMMsgStatusSent) sstr += 'S';
+ if (status & KMMsgStatusFlag) sstr += 'G';
+ if (status & KMMsgStatusWatched) sstr += 'W';
+ if (status & KMMsgStatusIgnored) sstr += 'I';
+ if (status & KMMsgStatusSpam) sstr += 'P';
+ if (status & KMMsgStatusHam) sstr += 'H';
+ if (status & KMMsgStatusHasAttach) sstr += 'T';
+ if (status & KMMsgStatusHasNoAttach) sstr += 'C';
+
+ return sstr;
+}
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::statusToSortRank()
+{
+ QString sstr = "bcbbbbbbbb";
+
+ // put watched ones first, then normal ones, ignored ones last
+ if (status() & KMMsgStatusWatched) sstr[0] = 'a';
+ if (status() & KMMsgStatusIgnored) sstr[0] = 'c';
+
+ // Second level. One of new, old, read, unread
+ if (status() & KMMsgStatusNew) sstr[1] = 'a';
+ if (status() & KMMsgStatusUnread) sstr[1] = 'b';
+ //if (status() & KMMsgStatusOld) sstr[1] = 'c';
+ //if (status() & KMMsgStatusRead) sstr[1] = 'c';
+
+ // Third level. In somewhat arbitrary order.
+ if (status() & KMMsgStatusDeleted) sstr[2] = 'a';
+ if (status() & KMMsgStatusFlag) sstr[3] = 'a';
+ if (status() & KMMsgStatusReplied) sstr[4] = 'a';
+ if (status() & KMMsgStatusForwarded) sstr[5] = 'a';
+ if (status() & KMMsgStatusQueued) sstr[6] = 'a';
+ if (status() & KMMsgStatusSent) sstr[7] = 'a';
+ if (status() & KMMsgStatusHam) sstr[8] = 'a';
+ if (status() & KMMsgStatusSpam) sstr[8] = 'c';
+ if (status() & KMMsgStatusTodo) sstr[9] = 'a';
+
+ return sstr;
+}
+
+
+//-----------------------------------------------------------------------------
+void KMMsgBase::setDate(const QCString& aDateStr)
+{
+ setDate( KRFCDate::parseDate( aDateStr ) );
+}
+
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::dateStr(void) const
+{
+ time_t d = date();
+ return KMime::DateFormatter::formatDate(KMime::DateFormatter::Fancy, d);
+}
+
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::skipKeyword(const QString& aStr, QChar sepChar,
+ bool* hasKeyword)
+{
+ unsigned int i = 0, maxChars = 3;
+ QString str = aStr;
+
+ while (str[0] == ' ') str.remove(0,1);
+ if (hasKeyword) *hasKeyword=false;
+
+ unsigned int strLength(str.length());
+ for (i=0; i < strLength && i < maxChars; i++)
+ {
+ if (str[i] < 'A' || str[i] == sepChar) break;
+ }
+
+ if (str[i] == sepChar) // skip following spaces too
+ {
+ do {
+ i++;
+ } while (str[i] == ' ');
+ if (hasKeyword) *hasKeyword=true;
+ return str.mid(i);
+ }
+ return str;
+}
+
+
+//-----------------------------------------------------------------------------
+const QTextCodec* KMMsgBase::codecForName(const QCString& _str)
+{
+ if (_str.isEmpty()) return 0;
+ QCString codec = _str;
+ KPIM::kAsciiToLower(codec.data());
+ return KGlobal::charsets()->codecForName(codec);
+}
+
+
+//-----------------------------------------------------------------------------
+QCString KMMsgBase::toUsAscii(const QString& _str, bool *ok)
+{
+ bool all_ok =true;
+ QString result = _str;
+ int len = result.length();
+ for (int i = 0; i < len; i++)
+ if (result.at(i).unicode() >= 128) {
+ result.at(i) = '?';
+ all_ok = false;
+ }
+ if (ok)
+ *ok = all_ok;
+ return result.latin1();
+}
+
+
+//-----------------------------------------------------------------------------
+QStringList KMMsgBase::supportedEncodings(bool usAscii)
+{
+ QStringList encodingNames = KGlobal::charsets()->availableEncodingNames();
+ QStringList encodings;
+ QMap<QString,bool> mimeNames;
+ for (QStringList::Iterator it = encodingNames.begin();
+ it != encodingNames.end(); it++)
+ {
+ QTextCodec *codec = KGlobal::charsets()->codecForName(*it);
+ QString mimeName = (codec) ? QString(codec->mimeName()).lower() : (*it);
+ if (mimeNames.find(mimeName) == mimeNames.end())
+ {
+ encodings.append(KGlobal::charsets()->languageForEncoding(*it)
+ + " ( " + mimeName + " )");
+ mimeNames.insert(mimeName, true);
+ }
+ }
+ encodings.sort();
+ if (usAscii) encodings.prepend(KGlobal::charsets()
+ ->languageForEncoding("us-ascii") + " ( us-ascii )");
+ return encodings;
+}
+
+namespace {
+ // don't rely on isblank(), which is a GNU extension in
+ // <cctype>. But if someone wants to write a configure test for
+ // isblank(), we can then rename this function to isblank and #ifdef
+ // it's definition...
+ inline bool isBlank( char ch ) { return ch == ' ' || ch == '\t' ; }
+
+ QCString unfold( const QCString & header ) {
+ if ( header.isEmpty() )
+ return QCString();
+
+ QCString result( header.size() ); // size() >= length()+1 and size() is O(1)
+ char * d = result.data();
+
+ for ( const char * s = header.data() ; *s ; )
+ if ( *s == '\r' ) { // ignore
+ ++s;
+ continue;
+ } else if ( *s == '\n' ) { // unfold
+ while ( isBlank( *++s ) )
+ ;
+ *d++ = ' ';
+ } else
+ *d++ = *s++;
+
+ *d++ = '\0';
+
+ result.truncate( d - result.data() );
+ return result;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::decodeRFC2047String(const QCString& aStr, QCString prefCharset)
+{
+ if ( aStr.isEmpty() )
+ return QString::null;
+
+ const QCString str = unfold( aStr );
+
+ if ( str.isEmpty() )
+ return QString::null;
+
+ if ( str.find( "=?" ) < 0 ) {
+ if ( !prefCharset.isEmpty() ) {
+ if ( prefCharset == "us-ascii" ) {
+ // isn`t this foolproof?
+ return KMMsgBase::codecForName( "utf-8" )->toUnicode( str );
+ } else {
+ return KMMsgBase::codecForName( prefCharset )->toUnicode( str );
+ }
+ } else {
+ return KMMsgBase::codecForName( GlobalSettings::self()->
+ fallbackCharacterEncoding().latin1() )->toUnicode( str );
+ }
+ }
+
+ QString result;
+ QCString LWSP_buffer;
+ bool lastWasEncodedWord = false;
+
+ for ( const char * pos = str.data() ; *pos ; ++pos ) {
+ // collect LWSP after encoded-words,
+ // because we might need to throw it out
+ // (when the next word is an encoded-word)
+ if ( lastWasEncodedWord && isBlank( pos[0] ) ) {
+ LWSP_buffer += pos[0];
+ continue;
+ }
+ // verbatimly copy normal text
+ if (pos[0]!='=' || pos[1]!='?') {
+ result += LWSP_buffer + pos[0];
+ LWSP_buffer = 0;
+ lastWasEncodedWord = false;
+ continue;
+ }
+ // found possible encoded-word
+ const char * const beg = pos;
+ {
+ // parse charset name
+ QCString charset;
+ int i = 2;
+ pos += 2;
+ for ( ; *pos != '?' && ( *pos==' ' || ispunct(*pos) || isalnum(*pos) );
+ ++i, ++pos ) {
+ charset += *pos;
+ }
+ if ( *pos!='?' || i<4 )
+ goto invalid_encoded_word;
+
+ // get encoding and check delimiting question marks
+ const char encoding[2] = { pos[1], '\0' };
+ if (pos[2]!='?' || (encoding[0]!='Q' && encoding[0]!='q' &&
+ encoding[0]!='B' && encoding[0]!='b'))
+ goto invalid_encoded_word;
+ pos+=3; i+=3; // skip ?x?
+ const char * enc_start = pos;
+ // search for end of encoded part
+ while ( *pos && !(*pos=='?' && *(pos+1)=='=') ) {
+ i++;
+ pos++;
+ }
+ if ( !*pos )
+ goto invalid_encoded_word;
+
+ // valid encoding: decode and throw away separating LWSP
+ const KMime::Codec * c = KMime::Codec::codecForName( encoding );
+ kdFatal( !c, 5006 ) << "No \"" << encoding << "\" codec!?" << endl;
+
+ QByteArray in; in.setRawData( enc_start, pos - enc_start );
+ const QByteArray enc = c->decode( in );
+ in.resetRawData( enc_start, pos - enc_start );
+
+ const QTextCodec * codec = codecForName(charset);
+ if (!codec) codec = kmkernel->networkCodec();
+ result += codec->toUnicode(enc);
+ lastWasEncodedWord = true;
+
+ ++pos; // eat '?' (for loop eats '=')
+ LWSP_buffer = 0;
+ }
+ continue;
+ invalid_encoded_word:
+ // invalid encoding, keep separating LWSP.
+ pos = beg;
+ if ( !LWSP_buffer.isNull() )
+ result += LWSP_buffer;
+ result += "=?";
+ lastWasEncodedWord = false;
+ ++pos; // eat '?' (for loop eats '=')
+ LWSP_buffer = 0;
+ }
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+static const QCString especials = "()<>@,;:\"/[]?.= \033";
+
+QCString KMMsgBase::encodeRFC2047Quoted( const QCString & s, bool base64 ) {
+ const char * codecName = base64 ? "b" : "q" ;
+ const KMime::Codec * codec = KMime::Codec::codecForName( codecName );
+ kdFatal( !codec, 5006 ) << "No \"" << codecName << "\" found!?" << endl;
+ QByteArray in; in.setRawData( s.data(), s.length() );
+ const QByteArray result = codec->encode( in );
+ in.resetRawData( s.data(), s.length() );
+ return QCString( result.data(), result.size() + 1 );
+}
+
+QCString KMMsgBase::encodeRFC2047String(const QString& _str,
+ const QCString& charset)
+{
+ static const QString dontQuote = "\"()<>,@";
+
+ if (_str.isEmpty()) return QCString();
+ if (charset == "us-ascii") return toUsAscii(_str);
+
+ QCString cset;
+ if (charset.isEmpty())
+ {
+ cset = kmkernel->networkCodec()->mimeName();
+ KPIM::kAsciiToLower(cset.data());
+ }
+ else cset = charset;
+
+ const QTextCodec *codec = codecForName(cset);
+ if (!codec) codec = kmkernel->networkCodec();
+
+ unsigned int nonAscii = 0;
+ unsigned int strLength(_str.length());
+ for (unsigned int i = 0; i < strLength; i++)
+ if (_str.at(i).unicode() >= 128) nonAscii++;
+ bool useBase64 = (nonAscii * 6 > strLength);
+
+ unsigned int start, stop, p, pos = 0, encLength;
+ QCString result;
+ bool breakLine = false;
+ const unsigned int maxLen = 75 - 7 - cset.length();
+
+ while (pos < strLength)
+ {
+ start = pos; p = pos;
+ while (p < strLength)
+ {
+ if (!breakLine && (_str.at(p) == ' ' || dontQuote.find(_str.at(p)) != -1))
+ start = p + 1;
+ if (_str.at(p).unicode() >= 128 || _str.at(p).unicode() < 32)
+ break;
+ p++;
+ }
+ if (breakLine || p < strLength)
+ {
+ while (dontQuote.find(_str.at(start)) != -1) start++;
+ stop = start;
+ while (stop < strLength && dontQuote.find(_str.at(stop)) == -1)
+ stop++;
+ result += _str.mid(pos, start - pos).latin1();
+ encLength = encodeRFC2047Quoted(codec->fromUnicode(_str.
+ mid(start, stop - start)), useBase64).length();
+ breakLine = (encLength > maxLen);
+ if (breakLine)
+ {
+ int dif = (stop - start) / 2;
+ int step = dif;
+ while (abs(step) > 1)
+ {
+ encLength = encodeRFC2047Quoted(codec->fromUnicode(_str.
+ mid(start, dif)), useBase64).length();
+ step = (encLength > maxLen) ? (-abs(step) / 2) : (abs(step) / 2);
+ dif += step;
+ }
+ stop = start + dif;
+ }
+ p = stop;
+ while (p > start && _str.at(p) != ' ') p--;
+ if (p > start) stop = p;
+ if (result.right(3) == "?= ") start--;
+ if (result.right(5) == "?=\n ") {
+ start--; result.truncate(result.length() - 1);
+ }
+ int lastNewLine = result.findRev("\n ");
+ if (!result.mid(lastNewLine).stripWhiteSpace().isEmpty()
+ && result.length() - lastNewLine + encLength + 2 > maxLen)
+ result += "\n ";
+ result += "=?";
+ result += cset;
+ result += (useBase64) ? "?b?" : "?q?";
+ result += encodeRFC2047Quoted(codec->fromUnicode(_str.mid(start,
+ stop - start)), useBase64);
+ result += "?=";
+ if (breakLine) result += "\n ";
+ pos = stop;
+ } else {
+ result += _str.mid(pos).latin1();
+ break;
+ }
+ }
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+QCString KMMsgBase::encodeRFC2231String( const QString& _str,
+ const QCString& charset )
+{
+ if ( _str.isEmpty() )
+ return QCString();
+
+ QCString cset;
+ if ( charset.isEmpty() )
+ {
+ cset = kmkernel->networkCodec()->mimeName();
+ KPIM::kAsciiToLower( cset.data() );
+ }
+ else
+ cset = charset;
+ const QTextCodec *codec = codecForName( cset );
+ QCString latin;
+ if ( charset == "us-ascii" )
+ latin = toUsAscii( _str );
+ else if ( codec )
+ latin = codec->fromUnicode( _str );
+ else
+ latin = _str.local8Bit();
+
+ char *l;
+ for ( l = latin.data(); *l; ++l ) {
+ if ( ( ( *l & 0xE0 ) == 0 ) || ( *l & 0x80 ) )
+ // *l is control character or 8-bit char
+ break;
+ }
+ if ( !*l )
+ return latin;
+
+ QCString result = cset + "''";
+ for ( l = latin.data(); *l; ++l ) {
+ bool needsQuoting = ( *l & 0x80 );
+ if( !needsQuoting ) {
+ int len = especials.length();
+ for ( int i = 0; i < len; i++ )
+ if ( *l == especials[i] ) {
+ needsQuoting = true;
+ break;
+ }
+ }
+ if ( needsQuoting ) {
+ result += '%';
+ unsigned char hexcode;
+ hexcode = ( ( *l & 0xF0 ) >> 4 ) + 48;
+ if ( hexcode >= 58 )
+ hexcode += 7;
+ result += hexcode;
+ hexcode = ( *l & 0x0F ) + 48;
+ if ( hexcode >= 58 )
+ hexcode += 7;
+ result += hexcode;
+ } else {
+ result += *l;
+ }
+ }
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::decodeRFC2231String(const QCString& _str)
+{
+ int p = _str.find('\'');
+ if (p < 0) return kmkernel->networkCodec()->toUnicode(_str);
+
+ QCString charset = _str.left(p);
+
+ QCString st = _str.mid(_str.findRev('\'') + 1);
+ char ch, ch2;
+ p = 0;
+ while (p < (int)st.length())
+ {
+ if (st.at(p) == 37)
+ {
+ ch = st.at(p+1) - 48;
+ if (ch > 16) ch -= 7;
+ ch2 = st.at(p+2) - 48;
+ if (ch2 > 16) ch2 -= 7;
+ st.at(p) = ch * 16 + ch2;
+ st.remove( p+1, 2 );
+ }
+ p++;
+ }
+ QString result;
+ const QTextCodec * codec = codecForName( charset );
+ if ( !codec )
+ codec = kmkernel->networkCodec();
+ return codec->toUnicode( st );
+}
+
+QCString KMMsgBase::extractRFC2231HeaderField( const QCString &aStr, const QCString &field )
+{
+ int n=-1;
+ QCString str;
+ bool found = false;
+ while ( n<=0 || found ) {
+ QString pattern( field );
+ pattern += "[*]"; // match a literal * after the fieldname, as defined by RFC 2231
+ if ( n>=0 ) { // If n<0, check for fieldname*=..., otherwise for fieldname*n=
+ pattern += QString::number(n) + "[*]?";
+ }
+ pattern += "=";
+
+ QRegExp fnamePart( pattern, false );
+ int startPart = fnamePart.search( aStr );
+ int endPart;
+ found = ( startPart >= 0 );
+ if ( found ) {
+ startPart += fnamePart.matchedLength();
+ // Quoted values end at the ending quote
+ if ( aStr[startPart] == '"' ) {
+ startPart++; // the double quote isn't part of the filename
+ endPart = aStr.find('"', startPart) - 1;
+ }
+ else {
+ endPart = aStr.find(';', startPart) - 1;
+ }
+ if (endPart < 0)
+ endPart = 32767;
+ str += aStr.mid( startPart, endPart-startPart+1).stripWhiteSpace();
+ }
+ n++;
+ }
+ return str;
+}
+
+QString KMMsgBase::base64EncodedMD5( const QString & s, bool utf8 ) {
+ if (s.stripWhiteSpace().isEmpty()) return "";
+ if ( utf8 )
+ return base64EncodedMD5( s.stripWhiteSpace().utf8() ); // QCString overload
+ else
+ return base64EncodedMD5( s.stripWhiteSpace().latin1() ); // const char * overload
+}
+
+QString KMMsgBase::base64EncodedMD5( const QCString & s ) {
+ if (s.stripWhiteSpace().isEmpty()) return "";
+ return base64EncodedMD5( s.stripWhiteSpace().data() );
+}
+
+QString KMMsgBase::base64EncodedMD5( const char * s, int len ) {
+ if (!s || !len) return "";
+ static const int Base64EncodedMD5Len = 22;
+ KMD5 md5( s, len );
+ return md5.base64Digest().left( Base64EncodedMD5Len );
+}
+
+
+//-----------------------------------------------------------------------------
+QCString KMMsgBase::autoDetectCharset(const QCString &_encoding, const QStringList &encodingList, const QString &text)
+{
+ QStringList charsets = encodingList;
+ if (!_encoding.isEmpty())
+ {
+ QString currentCharset = QString::fromLatin1(_encoding);
+ charsets.remove(currentCharset);
+ charsets.prepend(currentCharset);
+ }
+
+ QStringList::ConstIterator it = charsets.begin();
+ for (; it != charsets.end(); ++it)
+ {
+ QCString encoding = (*it).latin1();
+ if (encoding == "locale")
+ {
+ encoding = kmkernel->networkCodec()->mimeName();
+ KPIM::kAsciiToLower(encoding.data());
+ }
+ if (text.isEmpty())
+ return encoding;
+ if (encoding == "us-ascii") {
+ bool ok;
+ (void) KMMsgBase::toUsAscii(text, &ok);
+ if (ok)
+ return encoding;
+ }
+ else
+ {
+ const QTextCodec *codec = KMMsgBase::codecForName(encoding);
+ if (!codec) {
+ kdDebug(5006) << "Auto-Charset: Something is wrong and I can not get a codec. [" << encoding << "]" << endl;
+ } else {
+ if (codec->canEncode(text))
+ return encoding;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+unsigned long KMMsgBase::getMsgSerNum() const
+{
+ unsigned long msn = MessageProperty::serialCache( this );
+ if (msn)
+ return msn;
+ if (mParent) {
+ int index = mParent->find((KMMsgBase*)this);
+ msn = KMMsgDict::instance()->getMsgSerNum(mParent, index);
+ if (msn)
+ MessageProperty::setSerialCache( this, msn );
+ }
+ return msn;
+}
+
+
+//-----------------------------------------------------------------------------
+KMMsgAttachmentState KMMsgBase::attachmentState() const
+{
+ KMMsgStatus st = status();
+ if (st & KMMsgStatusHasAttach)
+ return KMMsgHasAttachment;
+ else if (st & KMMsgStatusHasNoAttach)
+ return KMMsgHasNoAttachment;
+ else
+ return KMMsgAttachmentUnknown;
+}
+
+//-----------------------------------------------------------------------------
+static void swapEndian(QString &str)
+{
+ uint len = str.length();
+ str = QDeepCopy<QString>(str);
+ QChar *unicode = const_cast<QChar*>( str.unicode() );
+ for (uint i = 0; i < len; i++)
+ unicode[i] = kmail_swap_16(unicode[i].unicode());
+}
+
+//-----------------------------------------------------------------------------
+static int g_chunk_length = 0, g_chunk_offset=0;
+static uchar *g_chunk = 0;
+
+namespace {
+ template < typename T > void copy_from_stream( T & x ) {
+ if( g_chunk_offset + int(sizeof(T)) > g_chunk_length ) {
+ g_chunk_offset = g_chunk_length;
+ kdDebug( 5006 ) << "This should never happen.. "
+ << __FILE__ << ":" << __LINE__ << endl;
+ x = 0;
+ } else {
+ // the memcpy is optimized out by the compiler for the values
+ // of sizeof(T) that is called with
+ memcpy( &x, g_chunk + g_chunk_offset, sizeof(T) );
+ g_chunk_offset += sizeof(T);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::getStringPart(MsgPartType t) const
+{
+retry:
+ QString ret;
+
+ g_chunk_offset = 0;
+ bool using_mmap = false;
+ bool swapByteOrder = storage()->indexSwapByteOrder();
+ if (storage()->indexStreamBasePtr()) {
+ if (g_chunk)
+ free(g_chunk);
+ using_mmap = true;
+ g_chunk = storage()->indexStreamBasePtr() + mIndexOffset;
+ g_chunk_length = mIndexLength;
+ } else {
+ if(!storage()->mIndexStream)
+ return ret;
+ if (g_chunk_length < mIndexLength)
+ g_chunk = (uchar *)realloc(g_chunk, g_chunk_length = mIndexLength);
+ off_t first_off=ftell(storage()->mIndexStream);
+ fseek(storage()->mIndexStream, mIndexOffset, SEEK_SET);
+ fread( g_chunk, mIndexLength, 1, storage()->mIndexStream);
+ fseek(storage()->mIndexStream, first_off, SEEK_SET);
+ }
+
+ MsgPartType type;
+ Q_UINT16 l;
+ while(g_chunk_offset < mIndexLength) {
+ Q_UINT32 tmp;
+ copy_from_stream(tmp);
+ copy_from_stream(l);
+ if (swapByteOrder)
+ {
+ tmp = kmail_swap_32(tmp);
+ l = kmail_swap_16(l);
+ }
+ type = (MsgPartType) tmp;
+ if(g_chunk_offset + l > mIndexLength) {
+ kdDebug(5006) << "This should never happen.. " << __FILE__ << ":" << __LINE__ << endl;
+ if(using_mmap) {
+ g_chunk_length = 0;
+ g_chunk = 0;
+ }
+ storage()->recreateIndex();
+ goto retry;
+ }
+ if(type == t) {
+ // This works because the QString constructor does a memcpy.
+ // Otherwise we would need to be concerned about the alignment.
+ if(l)
+ ret = QString((QChar *)(g_chunk + g_chunk_offset), l/2);
+ break;
+ }
+ g_chunk_offset += l;
+ }
+ if(using_mmap) {
+ g_chunk_length = 0;
+ g_chunk = 0;
+ }
+ // Normally we need to swap the byte order because the QStrings are written
+ // in the style of Qt2 (MSB -> network ordered).
+ // QStrings in Qt3 expect host ordering.
+ // On e.g. Intel host ordering is LSB, on e.g. Sparc it is MSB.
+
+#ifndef WORDS_BIGENDIAN
+ // #warning Byte order is little endian (swap is true)
+ swapEndian(ret);
+#else
+ // #warning Byte order is big endian (swap is false)
+#endif
+
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+off_t KMMsgBase::getLongPart(MsgPartType t) const
+{
+retry:
+ off_t ret = 0;
+
+ g_chunk_offset = 0;
+ bool using_mmap = false;
+ int sizeOfLong = storage()->indexSizeOfLong();
+ bool swapByteOrder = storage()->indexSwapByteOrder();
+ if (storage()->indexStreamBasePtr()) {
+ if (g_chunk)
+ free(g_chunk);
+ using_mmap = true;
+ g_chunk = storage()->indexStreamBasePtr() + mIndexOffset;
+ g_chunk_length = mIndexLength;
+ } else {
+ if (!storage()->mIndexStream)
+ return ret;
+ assert(mIndexLength >= 0);
+ if (g_chunk_length < mIndexLength)
+ g_chunk = (uchar *)realloc(g_chunk, g_chunk_length = mIndexLength);
+ off_t first_off=ftell(storage()->mIndexStream);
+ fseek(storage()->mIndexStream, mIndexOffset, SEEK_SET);
+ fread( g_chunk, mIndexLength, 1, storage()->mIndexStream);
+ fseek(storage()->mIndexStream, first_off, SEEK_SET);
+ }
+
+ MsgPartType type;
+ Q_UINT16 l;
+ while (g_chunk_offset < mIndexLength) {
+ Q_UINT32 tmp;
+ copy_from_stream(tmp);
+ copy_from_stream(l);
+ if (swapByteOrder)
+ {
+ tmp = kmail_swap_32(tmp);
+ l = kmail_swap_16(l);
+ }
+ type = (MsgPartType) tmp;
+
+ if (g_chunk_offset + l > mIndexLength) {
+ kdDebug(5006) << "This should never happen.. " << __FILE__ << ":" << __LINE__ << endl;
+ if(using_mmap) {
+ g_chunk_length = 0;
+ g_chunk = 0;
+ }
+ storage()->recreateIndex();
+ goto retry;
+ }
+ if(type == t) {
+ assert(sizeOfLong == l);
+ if (sizeOfLong == sizeof(ret))
+ {
+ copy_from_stream(ret);
+ if (swapByteOrder)
+ {
+ if (sizeof(ret) == 4)
+ ret = kmail_swap_32(ret);
+ else
+ ret = kmail_swap_64(ret);
+ }
+ }
+ else if (sizeOfLong == 4)
+ {
+ // Long is stored as 4 bytes in index file, sizeof(long) = 8
+ Q_UINT32 ret_32;
+ copy_from_stream(ret_32);
+ if (swapByteOrder)
+ ret_32 = kmail_swap_32(ret_32);
+ ret = ret_32;
+ }
+ else if (sizeOfLong == 8)
+ {
+ // Long is stored as 8 bytes in index file, sizeof(long) = 4
+ Q_UINT32 ret_1;
+ Q_UINT32 ret_2;
+ copy_from_stream(ret_1);
+ copy_from_stream(ret_2);
+ if (!swapByteOrder)
+ {
+ // Index file order is the same as the order of this CPU.
+#ifndef WORDS_BIGENDIAN
+ // Index file order is little endian
+ ret = ret_1; // We drop the 4 most significant bytes
+#else
+ // Index file order is big endian
+ ret = ret_2; // We drop the 4 most significant bytes
+#endif
+ }
+ else
+ {
+ // Index file order is different from this CPU.
+#ifndef WORDS_BIGENDIAN
+ // Index file order is big endian
+ ret = ret_2; // We drop the 4 most significant bytes
+#else
+ // Index file order is little endian
+ ret = ret_1; // We drop the 4 most significant bytes
+#endif
+ // We swap the result to host order.
+ ret = kmail_swap_32(ret);
+ }
+
+ }
+ break;
+ }
+ g_chunk_offset += l;
+ }
+ if(using_mmap) {
+ g_chunk_length = 0;
+ g_chunk = 0;
+ }
+ return ret;
+}
+
+#ifndef WORDS_BIGENDIAN
+// We need to use swab to swap bytes to network byte order
+#define memcpy_networkorder(to, from, len) swab((char *)(from), (char *)(to), len)
+#else
+// We're already in network byte order
+#define memcpy_networkorder(to, from, len) memcpy(to, from, len)
+#endif
+
+#define STORE_DATA_LEN(type, x, len, network_order) do { \
+ int len2 = (len > 256) ? 256 : len; \
+ if(csize < (length + (len2 + sizeof(short) + sizeof(MsgPartType)))) \
+ ret = (uchar *)realloc(ret, csize += len2+sizeof(short)+sizeof(MsgPartType)); \
+ Q_UINT32 t = (Q_UINT32) type; memcpy(ret+length, &t, sizeof(t)); \
+ Q_UINT16 l = len2; memcpy(ret+length+sizeof(t), &l, sizeof(l)); \
+ if (network_order) \
+ memcpy_networkorder(ret+length+sizeof(t)+sizeof(l), x, len2); \
+ else \
+ memcpy(ret+length+sizeof(t)+sizeof(l), x, len2); \
+ length += len2+sizeof(t)+sizeof(l); \
+ } while(0)
+#define STORE_DATA(type, x) STORE_DATA_LEN(type, &x, sizeof(x), false)
+
+//-----------------------------------------------------------------------------
+const uchar *KMMsgBase::asIndexString(int &length) const
+{
+ unsigned int csize = 256;
+ static uchar *ret = 0; //different static buffer here for we may use the other buffer in the functions below
+ if(!ret)
+ ret = (uchar *)malloc(csize);
+ length = 0;
+
+ unsigned long tmp;
+ QString tmp_str;
+
+ //these is at the beginning because it is queried quite often
+ tmp_str = msgIdMD5().stripWhiteSpace();
+ STORE_DATA_LEN(MsgIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
+ tmp = mLegacyStatus;
+ STORE_DATA(MsgLegacyStatusPart, tmp);
+
+ //these are completely arbitrary order
+ tmp_str = fromStrip().stripWhiteSpace();
+ STORE_DATA_LEN(MsgFromPart, tmp_str.unicode(), tmp_str.length() * 2, true);
+ tmp_str = subject().stripWhiteSpace();
+ STORE_DATA_LEN(MsgSubjectPart, tmp_str.unicode(), tmp_str.length() * 2, true);
+ tmp_str = toStrip().stripWhiteSpace();
+ STORE_DATA_LEN(MsgToPart, tmp_str.unicode(), tmp_str.length() * 2, true);
+ tmp_str = replyToIdMD5().stripWhiteSpace();
+ STORE_DATA_LEN(MsgReplyToIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
+ tmp_str = xmark().stripWhiteSpace();
+ STORE_DATA_LEN(MsgXMarkPart, tmp_str.unicode(), tmp_str.length() * 2, true);
+ tmp_str = fileName().stripWhiteSpace();
+ STORE_DATA_LEN(MsgFilePart, tmp_str.unicode(), tmp_str.length() * 2, true);
+ tmp = msgSize();
+ STORE_DATA(MsgSizePart, tmp);
+ tmp = folderOffset();
+ STORE_DATA(MsgOffsetPart, tmp);
+ tmp = date();
+ STORE_DATA(MsgDatePart, tmp);
+ tmp = (signatureState() << 16) | encryptionState();
+ STORE_DATA(MsgCryptoStatePart, tmp);
+ tmp = mdnSentState();
+ STORE_DATA(MsgMDNSentPart, tmp);
+
+ tmp_str = replyToAuxIdMD5().stripWhiteSpace();
+ STORE_DATA_LEN(MsgReplyToAuxIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
+
+ tmp_str = strippedSubjectMD5().stripWhiteSpace();
+ STORE_DATA_LEN(MsgStrippedSubjectMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
+
+ tmp = status();
+ STORE_DATA(MsgStatusPart, tmp);
+
+ tmp = msgSizeServer();
+ STORE_DATA(MsgSizeServerPart, tmp);
+ tmp = UID();
+ STORE_DATA(MsgUIDPart, tmp);
+
+ return ret;
+}
+#undef STORE_DATA_LEN
+#undef STORE_DATA
+
+bool KMMsgBase::syncIndexString() const
+{
+ if(!dirty())
+ return true;
+ int len;
+ const uchar *buffer = asIndexString(len);
+ if (len == mIndexLength) {
+ Q_ASSERT(storage()->mIndexStream);
+ fseek(storage()->mIndexStream, mIndexOffset, SEEK_SET);
+ assert( mIndexOffset > 0 );
+ fwrite( buffer, len, 1, storage()->mIndexStream);
+ return true;
+ }
+ return false;
+}
+
+static QStringList sReplySubjPrefixes, sForwardSubjPrefixes;
+static bool sReplaceSubjPrefix, sReplaceForwSubjPrefix;
+
+//-----------------------------------------------------------------------------
+void KMMsgBase::readConfig()
+{
+ KConfigGroup composerGroup( KMKernel::config(), "Composer" );
+ sReplySubjPrefixes = composerGroup.readListEntry("reply-prefixes", ',');
+ if (sReplySubjPrefixes.isEmpty())
+ sReplySubjPrefixes << "Re\\s*:" << "Re\\[\\d+\\]:" << "Re\\d+:";
+ sReplaceSubjPrefix = composerGroup.readBoolEntry("replace-reply-prefix", true);
+ sForwardSubjPrefixes = composerGroup.readListEntry("forward-prefixes", ',');
+ if (sForwardSubjPrefixes.isEmpty())
+ sForwardSubjPrefixes << "Fwd:" << "FW:";
+ sReplaceForwSubjPrefix = composerGroup.readBoolEntry("replace-forward-prefix", true);
+}
+
+//-----------------------------------------------------------------------------
+// static
+QString KMMsgBase::stripOffPrefixes( const QString& str )
+{
+ return replacePrefixes( str, sReplySubjPrefixes + sForwardSubjPrefixes,
+ true, QString::null ).stripWhiteSpace();
+}
+
+//-----------------------------------------------------------------------------
+// static
+QString KMMsgBase::replacePrefixes( const QString& str,
+ const QStringList& prefixRegExps,
+ bool replace,
+ const QString& newPrefix )
+{
+ bool recognized = false;
+ // construct a big regexp that
+ // 1. is anchored to the beginning of str (sans whitespace)
+ // 2. matches at least one of the part regexps in prefixRegExps
+ QString bigRegExp = QString::fromLatin1("^(?:\\s+|(?:%1))+\\s*")
+ .arg( prefixRegExps.join(")|(?:") );
+ QRegExp rx( bigRegExp, false /*case insens.*/ );
+ if ( !rx.isValid() ) {
+ kdWarning(5006) << "KMMessage::replacePrefixes(): bigRegExp = \""
+ << bigRegExp << "\"\n"
+ << "prefix regexp is invalid!" << endl;
+ // try good ole Re/Fwd:
+ recognized = str.startsWith( newPrefix );
+ } else { // valid rx
+ QString tmp = str;
+ if ( rx.search( tmp ) == 0 ) {
+ recognized = true;
+ if ( replace )
+ return tmp.replace( 0, rx.matchedLength(), newPrefix + ' ' );
+ }
+ }
+ if ( !recognized )
+ return newPrefix + ' ' + str;
+ else
+ return str;
+}
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::cleanSubject() const
+{
+ return cleanSubject( sReplySubjPrefixes + sForwardSubjPrefixes,
+ true, QString::null ).stripWhiteSpace();
+}
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::cleanSubject( const QStringList & prefixRegExps,
+ bool replace,
+ const QString & newPrefix ) const
+{
+ return KMMsgBase::replacePrefixes( subject(), prefixRegExps, replace,
+ newPrefix );
+}
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::forwardSubject() const {
+ return cleanSubject( sForwardSubjPrefixes, sReplaceForwSubjPrefix, "Fwd:" );
+}
+
+//-----------------------------------------------------------------------------
+QString KMMsgBase::replySubject() const {
+ return cleanSubject( sReplySubjPrefixes, sReplaceSubjPrefix, "Re:" );
+}