You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1001 lines
27 KiB
1001 lines
27 KiB
// kmmsgbase.cpp |
|
|
|
#include <kdebug.h> |
|
#include <kglobal.h> |
|
#include <klocale.h> |
|
#include <kcharsets.h> |
|
|
|
#include <mimelib/mimepp.h> |
|
#include <qregexp.h> |
|
#include <qtextcodec.h> |
|
#include <qstringlist.h> |
|
#include <kmfolder.h> |
|
#include <kmheaders.h> |
|
#include <kmmsgdict.h> |
|
|
|
#include <ctype.h> |
|
#include <stdlib.h> |
|
|
|
#include <config.h> |
|
|
|
#if 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 |
|
|
|
static KMMsgStatus sStatusList[] = |
|
{ |
|
KMMsgStatusDeleted, KMMsgStatusNew, |
|
KMMsgStatusUnread, KMMsgStatusOld, |
|
KMMsgStatusRead, KMMsgStatusReplied, |
|
KMMsgStatusSent, KMMsgStatusQueued, |
|
KMMsgStatusFlag, |
|
KMMsgStatusUnknown /* "Unknown" must be at the *end* of the list */ |
|
}; |
|
static const int NUM_STATUSLIST = sizeof sStatusList / sizeof *sStatusList; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
KMMsgBase::KMMsgBase(KMFolder* aParent) |
|
{ |
|
mParent = aParent; |
|
mDirty = FALSE; |
|
mIndexOffset = mIndexLength = 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
KMMsgBase::~KMMsgBase() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
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; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMMsgBase::isMessage(void) const |
|
{ |
|
return FALSE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMMsgBase::setStatus(const KMMsgStatus aStatus, int idx) |
|
{ |
|
if (mParent) |
|
mParent->msgStatusChanged( status(), aStatus ); |
|
mDirty = TRUE; |
|
if (mParent) |
|
mParent->headerOfMsgChanged(this, idx); |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMMsgBase::setStatus(const char* aStatusStr, const char* aXStatusStr) |
|
{ |
|
setStatus(KMMsgStatusUnknown); |
|
// first try to find status from "X-Status" field if given |
|
if (aXStatusStr) { |
|
for (int i=0; i<NUM_STATUSLIST-1; i++) { |
|
if (strchr(aXStatusStr, (char)sStatusList[i])) { |
|
setStatus(sStatusList[i]); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
// if not successful then use the "Status" field |
|
if (status() == KMMsgStatusUnknown) |
|
{ |
|
if (aStatusStr && ((aStatusStr[0]==(char)KMMsgStatusRead && aStatusStr[1]==(char)KMMsgStatusOld) || |
|
(aStatusStr[0]==(char)KMMsgStatusOld && aStatusStr[1]==(char)KMMsgStatusRead))) |
|
setStatus(KMMsgStatusOld); |
|
else if (aStatusStr && aStatusStr[0]==(char)KMMsgStatusRead) |
|
setStatus(KMMsgStatusRead); |
|
else if (aStatusStr && aStatusStr[0]==(char)KMMsgStatusDeleted) |
|
setStatus(KMMsgStatusDeleted); |
|
else |
|
setStatus(KMMsgStatusNew); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMMsgBase::isUnread(void) const |
|
{ |
|
KMMsgStatus st = status(); |
|
return (st==KMMsgStatusNew || st==KMMsgStatusUnread); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMMsgBase::isNew(void) const |
|
{ |
|
KMMsgStatus st = status(); |
|
return (st==KMMsgStatusNew); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
const char* KMMsgBase::statusToStr(KMMsgStatus aStatus) |
|
{ |
|
static char sstr[2]; |
|
|
|
sstr[0] = (char)aStatus; |
|
sstr[1] = '\0'; |
|
|
|
return sstr; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMMsgBase::setDate(const QCString& aDateStr) |
|
{ |
|
DwDateTime dwDate; |
|
dwDate.FromString(aDateStr); |
|
dwDate.Parse(); |
|
setDate(dwDate.AsUnixTime()); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QString KMMsgBase::dateStr(void) const |
|
{ |
|
time_t d = date(); |
|
return KMHeaders::fancyDate(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; |
|
|
|
for (i=0; i < str.length() && 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; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QTextCodec* KMMsgBase::codecForName(const QCString& _str) |
|
{ |
|
if (_str.isEmpty()) return NULL; |
|
return KGlobal::charsets()->codecForName(_str.lower()); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
const 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; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QString KMMsgBase::decodeRFC2047String(const QCString& aStr) |
|
{ |
|
QString result; |
|
QCString charset; |
|
char *pos, *beg, *end, *mid=0; |
|
QCString str, cstr, LWSP_buffer; |
|
char encoding='Q', ch; |
|
bool valid, lastWasEncodedWord=FALSE; |
|
const int maxLen=200; |
|
int i; |
|
|
|
if (aStr.find("=?") < 0) |
|
{ |
|
QString str = kernel->networkCodec()->toUnicode(aStr); |
|
if (str.find('\n') == -1) return str; |
|
QString str2((QChar*)NULL, str.length()); |
|
uint i = 0; |
|
while (i < str.length()) |
|
{ |
|
if (str[i] == '\n') |
|
{ |
|
str2 += " "; |
|
i += 2; |
|
} else { |
|
str2 += str[i]; |
|
i++; |
|
} |
|
} |
|
return str2; |
|
} |
|
|
|
for (pos=aStr.data(); *pos; pos++) |
|
{ |
|
// line unfolding |
|
if ( pos[0] == '\r' && pos[1] == '\n' ) { |
|
pos++; |
|
continue; |
|
} |
|
if ( pos[0] == '\n' ) |
|
continue; |
|
// collect LWSP after encoded-words, |
|
// because we might need to throw it out |
|
// (when the next word is an encoded-word) |
|
if ( lastWasEncodedWord && ( pos[0] == ' ' || pos[0] == '\t' ) ) { |
|
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 |
|
beg = pos+2; |
|
end = beg; |
|
valid = TRUE; |
|
// parse charset name |
|
charset = ""; |
|
for (i=2,pos+=2; i<maxLen && (*pos!='?'&&(*pos==' '||ispunct(*pos)||isalnum(*pos))); i++) |
|
{ |
|
charset += *pos; |
|
pos++; |
|
} |
|
if (*pos!='?' || i<4 || i>=maxLen) valid = FALSE; |
|
else |
|
{ |
|
// get encoding and check delimiting question marks |
|
encoding = toupper(pos[1]); |
|
if (pos[2]!='?' || (encoding!='Q' && encoding!='B')) |
|
valid = FALSE; |
|
pos+=3; |
|
i+=3; |
|
} |
|
if (valid) |
|
{ |
|
mid = pos; |
|
// search for end of encoded part |
|
while (i<maxLen && *pos && !(*pos=='?' && *(pos+1)=='=')) |
|
{ |
|
i++; |
|
pos++; |
|
} |
|
end = pos+2;//end now points to the first char after the encoded string |
|
if (i>=maxLen || !*pos) valid = FALSE; |
|
} |
|
if (valid) |
|
{ |
|
// valid encoding: decode and throw away separating LWSP |
|
ch = *pos; |
|
*pos = '\0'; |
|
str = QCString(mid).left((int)(mid - pos - 1)); |
|
if (encoding == 'Q') |
|
{ |
|
// decode quoted printable text |
|
for (i=str.length()-1; i>=0; i--) |
|
if (str[i]=='_') str[i]=' '; |
|
cstr = decodeQuotedPrintable(str); |
|
} |
|
else |
|
{ |
|
// decode base64 text |
|
cstr = decodeBase64(str); |
|
} |
|
QTextCodec *codec = codecForName(charset); |
|
if (!codec) codec = kernel->networkCodec(); |
|
result += codec->toUnicode(cstr); |
|
lastWasEncodedWord = TRUE; |
|
|
|
*pos = ch; |
|
pos = end -1; |
|
} |
|
else |
|
{ |
|
// invalid encoding, keep separating LWSP. |
|
//result += "=?"; |
|
//pos = beg -1; // because pos gets increased shortly afterwards |
|
pos = beg - 2; |
|
result += LWSP_buffer; |
|
result += *pos++; |
|
result += *pos; |
|
lastWasEncodedWord = FALSE; |
|
} |
|
LWSP_buffer = 0; |
|
} |
|
return result; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
const QString especials = "()<>@,;:\"/[]?.= \033"; |
|
const QString dontQuote = "\"()<>,"; |
|
|
|
QCString KMMsgBase::encodeRFC2047Quoted(const QCString& aStr, bool base64) |
|
{ |
|
if (base64) |
|
return encodeBase64(aStr).replace(QRegExp("\n"),""); |
|
QCString result; |
|
unsigned char ch, hex; |
|
for (unsigned int i = 0; i < aStr.length(); i++) |
|
{ |
|
ch = aStr.at(i); |
|
if (ch >= 128 || ch == '_' || especials.find(aStr.at(i)) != -1) |
|
{ |
|
result += "="; |
|
hex = ((ch & 0xF0) >> 4) + 48; |
|
if (hex >= 58) hex += 7; |
|
result += hex; |
|
hex = (ch & 0x0F) + 48; |
|
if (hex >= 58) hex += 7; |
|
result += hex; |
|
} else { |
|
result += aStr.at(i); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
|
|
QCString KMMsgBase::encodeRFC2047String(const QString& _str, |
|
const QCString& charset) |
|
{ |
|
if (_str.isEmpty()) return QCString(); |
|
if (charset == "us-ascii") return toUsAscii(_str); |
|
|
|
QCString cset; |
|
if (charset.isEmpty()) cset = QCString(kernel->networkCodec()->mimeName()).lower(); |
|
else cset = charset; |
|
QTextCodec *codec = codecForName(cset); |
|
if (!codec) codec = kernel->networkCodec(); |
|
|
|
unsigned int nonAscii = 0; |
|
for (unsigned int i = 0; i < _str.length(); i++) |
|
if (_str.at(i).unicode() >= 128) nonAscii++; |
|
bool useBase64 = (nonAscii * 6 > _str.length()); |
|
|
|
unsigned int start, stop, p, pos = 0, encLength; |
|
QCString result; |
|
bool breakLine; |
|
const unsigned int maxLen = 75 - 7 - cset.length(); |
|
|
|
while (pos < _str.length()) |
|
{ |
|
start = pos; p = pos; |
|
while (p < _str.length()) |
|
{ |
|
if (_str.at(p) == ' ' || dontQuote.find(_str.at(p)) != -1) start = p + 1; |
|
if (_str.at(p).unicode() >= 128 || _str.at(p) < ' ') break; |
|
p++; |
|
} |
|
if (p < _str.length()) |
|
{ |
|
while (dontQuote.find(_str.at(start)) != -1) start++; |
|
stop = start; |
|
while (stop < _str.length() && 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 = QCString(kernel->networkCodec()->mimeName()).lower(); |
|
else cset = charset; |
|
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 = latin.data(); |
|
char hexcode; |
|
int i; |
|
bool quote; |
|
while (*l) |
|
{ |
|
if (*l < 32) break; |
|
l++; |
|
} |
|
if (!*l) return latin; |
|
QCString result = cset + "''"; |
|
l = latin.data(); |
|
while (*l) |
|
{ |
|
quote = *l < 0; |
|
for (i = 0; i < 17; i++) if (*l == especials[i]) quote = true; |
|
if (quote) |
|
{ |
|
result += "%"; |
|
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; |
|
} |
|
l++; |
|
} |
|
return result; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QString KMMsgBase::decodeRFC2231String(const QCString& _str) |
|
{ |
|
int p = _str.find("'"); |
|
if (p < 0) return kernel->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; |
|
QTextCodec *codec = codecForName(charset); |
|
if (!codec) codec = kernel->networkCodec(); |
|
if (codec) result = codec->toUnicode(st); |
|
else result = kernel->networkCodec()->toUnicode(st); |
|
|
|
return result; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
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 = QCString(kernel->networkCodec()->mimeName()).lower(); |
|
if (encoding == "us-ascii") { |
|
bool ok; |
|
(void) KMMsgBase::toUsAscii(text, &ok); |
|
if (ok) |
|
return encoding; |
|
} |
|
else |
|
{ |
|
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; |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QCString KMMsgBase::decodeQuotedPrintable(const QCString& aStr) |
|
{ |
|
QCString bStr = aStr; |
|
if (aStr.isNull()) |
|
bStr = ""; |
|
|
|
DwString dwsrc(bStr.data()); |
|
DwString dwdest; |
|
|
|
DwDecodeQuotedPrintable(dwsrc, dwdest); |
|
return dwdest.c_str(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QCString KMMsgBase::encodeQuotedPrintable(const QCString& aStr) |
|
{ |
|
QCString bStr = aStr; |
|
if (aStr.isNull()) |
|
bStr = ""; |
|
|
|
DwString dwsrc(bStr.data(), bStr.length()); |
|
DwString dwdest; |
|
QCString result; |
|
|
|
DwEncodeQuotedPrintable(dwsrc, dwdest); |
|
result = dwdest.c_str(); |
|
return result; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QCString KMMsgBase::decodeBase64(const QCString& aStr) |
|
{ |
|
QCString bStr = aStr; |
|
if (aStr.isNull()) |
|
bStr = ""; |
|
while (bStr.length() < 16) bStr += "="; |
|
|
|
DwString dwsrc(bStr.data(), bStr.length()); |
|
DwString dwdest; |
|
QCString result; |
|
|
|
DwDecodeBase64(dwsrc, dwdest); |
|
result = dwdest.c_str(); |
|
return result; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QCString KMMsgBase::encodeBase64(const QCString& aStr) |
|
{ |
|
QCString bStr = aStr; |
|
if (aStr.isNull()) |
|
bStr = ""; |
|
|
|
DwString dwsrc(bStr.data(), bStr.length()); |
|
DwString dwdest; |
|
QCString result; |
|
|
|
DwEncodeBase64(dwsrc, dwdest); |
|
result = dwdest.c_str(); |
|
return result; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
unsigned long KMMsgBase::getMsgSerNum() const |
|
{ |
|
unsigned long msn = 0; |
|
if (mParent) { |
|
int index = mParent->find((KMMsgBasePtr)this); |
|
msn = kernel->msgDict()->getMsgSerNum(mParent, index); |
|
} |
|
return msn; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void swapEndian(QString &str) |
|
{ |
|
ushort us; |
|
uint len = str.length(); |
|
for (uint i = 0; i < len; i++) |
|
{ |
|
us = str[i].unicode(); |
|
str[i] = QChar(kmail_swap_16(us)); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
static int g_chunk_length = 0, g_chunk_offset=0; |
|
static uchar *g_chunk = NULL; |
|
|
|
#define COPY_DATA(x, length) do { \ |
|
if(g_chunk_offset + ((int)length) > g_chunk_length) {\ |
|
g_chunk_offset = g_chunk_length; \ |
|
kdDebug(5006) << "This should never happen.. " << __FILE__ << ":" << __LINE__ << endl; \ |
|
memset(x, length, '\0'); \ |
|
} else { \ |
|
memcpy(x, g_chunk+g_chunk_offset, length); \ |
|
g_chunk_offset += length; \ |
|
} } while(0) |
|
#define COPY_HEADER_TYPE(x) Q_ASSERT(sizeof(x) == sizeof(Q_UINT32)); COPY_DATA(&x, sizeof(x)); |
|
#define COPY_HEADER_LEN(x) Q_ASSERT(sizeof(x) == sizeof(Q_UINT16)); COPY_DATA(&x, sizeof(x)); |
|
//----------------------------------------------------------------------------- |
|
QString KMMsgBase::getStringPart(MsgPartType t) const |
|
{ |
|
QString ret(""); |
|
|
|
g_chunk_offset = 0; |
|
bool using_mmap = FALSE; |
|
bool swapByteOrder = mParent->indexSwapByteOrder(); |
|
if (mParent->indexStreamBasePtr()) { |
|
if (g_chunk) |
|
free(g_chunk); |
|
using_mmap = TRUE; |
|
g_chunk = mParent->indexStreamBasePtr() + mIndexOffset; |
|
g_chunk_length = mIndexLength; |
|
} else { |
|
if(!mParent->mIndexStream) |
|
return ret; |
|
if (g_chunk_length < mIndexLength) |
|
g_chunk = (uchar *)realloc(g_chunk, g_chunk_length = mIndexLength); |
|
int first_off=ftell(mParent->mIndexStream); |
|
fseek(mParent->mIndexStream, mIndexOffset, SEEK_SET); |
|
fread( g_chunk, mIndexLength, 1, mParent->mIndexStream); |
|
fseek(mParent->mIndexStream, first_off, SEEK_SET); |
|
} |
|
|
|
MsgPartType type; |
|
Q_UINT16 l; |
|
while(g_chunk_offset < mIndexLength) { |
|
Q_UINT32 tmp; |
|
COPY_HEADER_TYPE(tmp); |
|
COPY_HEADER_LEN(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; |
|
break; |
|
} |
|
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 = NULL; |
|
} |
|
// 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; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
unsigned long KMMsgBase::getLongPart(MsgPartType t) const |
|
{ |
|
unsigned long ret = 0; |
|
|
|
g_chunk_offset = 0; |
|
bool using_mmap = FALSE; |
|
int sizeOfLong = mParent->indexSizeOfLong(); |
|
bool swapByteOrder = mParent->indexSwapByteOrder(); |
|
if (mParent->indexStreamBasePtr()) { |
|
if (g_chunk) |
|
free(g_chunk); |
|
using_mmap = TRUE; |
|
g_chunk = mParent->indexStreamBasePtr() + mIndexOffset; |
|
g_chunk_length = mIndexLength; |
|
} else { |
|
if (!mParent->mIndexStream) |
|
return ret; |
|
assert(mIndexLength >= 0); |
|
if (g_chunk_length < mIndexLength) |
|
g_chunk = (uchar *)realloc(g_chunk, g_chunk_length = mIndexLength); |
|
int first_off=ftell(mParent->mIndexStream); |
|
fseek(mParent->mIndexStream, mIndexOffset, SEEK_SET); |
|
fread( g_chunk, mIndexLength, 1, mParent->mIndexStream); |
|
fseek(mParent->mIndexStream, first_off, SEEK_SET); |
|
} |
|
|
|
MsgPartType type; |
|
Q_UINT16 l; |
|
while (g_chunk_offset < mIndexLength) { |
|
Q_UINT32 tmp; |
|
COPY_HEADER_TYPE(tmp); |
|
COPY_HEADER_LEN(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; |
|
break; |
|
} |
|
if(type == t) { |
|
assert(sizeOfLong == l); |
|
if (sizeOfLong == sizeof(ret)) |
|
{ |
|
COPY_DATA(&ret, sizeof(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_DATA(&ret_32, sizeof(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_DATA(&ret_1, sizeof(ret_1)); |
|
COPY_DATA(&ret_2, sizeof(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 = NULL; |
|
} |
|
return ret; |
|
} |
|
#undef COPY_DATA |
|
|
|
//----------------------------------------------------------------------------- |
|
const uchar *KMMsgBase::asIndexString(int &length) const |
|
{ |
|
unsigned int csize = 256; |
|
static uchar *ret = NULL; //different static buffer here for we may use the other buffer in the functions below |
|
if(!ret) |
|
ret = (uchar *)malloc(csize); |
|
length = 0; |
|
|
|
#define STORE_DATA_LEN(type, x, len) 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)); \ |
|
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)) |
|
#ifndef WORDS_BIGENDIAN |
|
// #warning Byte order is little endian (call swapEndian) |
|
#define SWAP_TO_NETWORK_ORDER(x) swapEndian(x) |
|
#else |
|
// #warning Byte order is big endian |
|
#define SWAP_TO_NETWORK_ORDER(x) |
|
#endif |
|
unsigned long tmp; |
|
QString tmp_str; |
|
|
|
//these is at the beginning because it is queried quite often |
|
tmp_str = msgIdMD5().stripWhiteSpace(); |
|
SWAP_TO_NETWORK_ORDER(tmp_str); |
|
STORE_DATA_LEN(MsgIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2); |
|
tmp = status(); |
|
STORE_DATA(MsgStatusPart, tmp); |
|
|
|
//these are completely arbitrary order |
|
tmp_str = fromStrip().stripWhiteSpace(); |
|
SWAP_TO_NETWORK_ORDER(tmp_str); |
|
STORE_DATA_LEN(MsgFromPart, tmp_str.unicode(), tmp_str.length() * 2); |
|
tmp_str = subject().stripWhiteSpace(); |
|
SWAP_TO_NETWORK_ORDER(tmp_str); |
|
STORE_DATA_LEN(MsgSubjectPart, tmp_str.unicode(), tmp_str.length() * 2); |
|
tmp_str = toStrip().stripWhiteSpace(); |
|
SWAP_TO_NETWORK_ORDER(tmp_str); |
|
STORE_DATA_LEN(MsgToPart, tmp_str.unicode(), tmp_str.length() * 2); |
|
tmp_str = replyToIdMD5().stripWhiteSpace(); |
|
SWAP_TO_NETWORK_ORDER(tmp_str); |
|
STORE_DATA_LEN(MsgReplyToIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2); |
|
tmp_str = xmark().stripWhiteSpace(); |
|
SWAP_TO_NETWORK_ORDER(tmp_str); |
|
STORE_DATA_LEN(MsgXMarkPart, tmp_str.unicode(), tmp_str.length() * 2); |
|
tmp_str = fileName().stripWhiteSpace(); |
|
SWAP_TO_NETWORK_ORDER(tmp_str); |
|
STORE_DATA_LEN(MsgFilePart, tmp_str.unicode(), tmp_str.length() * 2); |
|
tmp = msgSize(); |
|
STORE_DATA(MsgSizePart, tmp); |
|
tmp = folderOffset(); |
|
STORE_DATA(MsgOffsetPart, tmp); |
|
tmp = date(); |
|
STORE_DATA(MsgDatePart, tmp); |
|
#undef STORE_DATA_LEN |
|
return ret; |
|
} |
|
|
|
bool KMMsgBase::syncIndexString() const |
|
{ |
|
if(!dirty()) |
|
return TRUE; |
|
int len; |
|
const uchar *buffer = asIndexString(len); |
|
if (len == mIndexLength) { |
|
Q_ASSERT(mParent->mIndexStream); |
|
fseek(mParent->mIndexStream, mIndexOffset, SEEK_SET); |
|
fwrite( buffer, len, 1, mParent->mIndexStream); |
|
return TRUE; |
|
} |
|
return FALSE; |
|
} |
|
|
|
|