00001
00002
00003 #include <config.h>
00004
00005 #include "kmmsgbase.h"
00006
00007 #include "kmfolderindex.h"
00008 #include "kmheaders.h"
00009 #include "kmmsgdict.h"
00010 #include "messageproperty.h"
00011 using KMail::MessageProperty;
00012
00013 #include <kdebug.h>
00014 #include <kglobal.h>
00015 #include <kcharsets.h>
00016 #include <kmdcodec.h>
00017 #include <krfcdate.h>
00018
00019 #include <mimelib/mimepp.h>
00020 #include <kmime_codecs.h>
00021
00022 #include <qtextcodec.h>
00023 #include <qdeepcopy.h>
00024
00025 #include <ctype.h>
00026 #include <stdlib.h>
00027 #include <unistd.h>
00028
00029 #ifdef HAVE_BYTESWAP_H
00030 #include <byteswap.h>
00031 #endif
00032
00033
00034
00035
00036
00037 #ifdef bswap_16
00038 #define kmail_swap_16(x) bswap_16(x)
00039 #else
00040 #define kmail_swap_16(x) \
00041 ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
00042 #endif
00043
00044
00045 #ifdef bswap_32
00046 #define kmail_swap_32(x) bswap_32(x)
00047 #else
00048 #define kmail_swap_32(x) \
00049 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
00050 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
00051 #endif
00052
00053
00054 #ifdef bswap_64
00055 #define kmail_swap_64(x) bswap_64(x)
00056 #else
00057 #define kmail_swap_64(x) \
00058 ((((x) & 0xff00000000000000ull) >> 56) \
00059 | (((x) & 0x00ff000000000000ull) >> 40) \
00060 | (((x) & 0x0000ff0000000000ull) >> 24) \
00061 | (((x) & 0x000000ff00000000ull) >> 8) \
00062 | (((x) & 0x00000000ff000000ull) << 8) \
00063 | (((x) & 0x0000000000ff0000ull) << 24) \
00064 | (((x) & 0x000000000000ff00ull) << 40) \
00065 | (((x) & 0x00000000000000ffull) << 56))
00066 #endif
00067
00068
00069 KMMsgBase::KMMsgBase(KMFolderIndex* aParent)
00070 {
00071 mParent = aParent;
00072 mDirty = FALSE;
00073 mIndexOffset = 0;
00074 mIndexLength = 0;
00075 mStatus = KMMsgStatusUnknown;
00076 mEnableUndo = false;
00077 }
00078
00079
00080
00081 KMMsgBase::~KMMsgBase()
00082 {
00083 MessageProperty::forget( this );
00084 }
00085
00086
00087
00088 void KMMsgBase::assign(const KMMsgBase* other)
00089 {
00090 mParent = other->mParent;
00091 mDirty = other->mDirty;
00092 mIndexOffset = other->mIndexOffset;
00093 mIndexLength = other->mIndexLength;
00094 MessageProperty::forget( this );
00095 bool otherTransfer = MessageProperty::transferInProgress( other );
00096 MessageProperty::setTransferInProgress( this, otherTransfer );
00097 }
00098
00099
00100
00101 KMMsgBase& KMMsgBase::operator=(const KMMsgBase& other)
00102 {
00103 assign(&other);
00104 return *this;
00105 }
00106
00107
00108
00109 KMMsgBase::KMMsgBase( const KMMsgBase& other )
00110 {
00111 assign( &other );
00112 }
00113
00114
00115
00116 bool KMMsgBase::isMessage(void) const
00117 {
00118 return FALSE;
00119 }
00120
00121 void KMMsgBase::toggleStatus(const KMMsgStatus aStatus, int idx)
00122 {
00123 mDirty = true;
00124 KMMsgStatus oldStatus = status();
00125 if ( status() & aStatus ) {
00126 mStatus &= ~aStatus;
00127 } else {
00128 mStatus |= aStatus;
00129
00130
00131 if (aStatus == KMMsgStatusWatched)
00132 mStatus &= ~KMMsgStatusIgnored;
00133 if (aStatus == KMMsgStatusIgnored) {
00134 mStatus &= ~KMMsgStatusWatched;
00135 setStatus(KMMsgStatusRead, idx);
00136 }
00137 if (aStatus == KMMsgStatusSpam)
00138 mStatus &= ~KMMsgStatusHam;
00139 if (aStatus == KMMsgStatusHam)
00140 mStatus &= ~KMMsgStatusSpam;
00141 }
00142 if (mParent) {
00143 if (idx < 0)
00144 idx = mParent->find( this );
00145 mParent->msgStatusChanged( oldStatus, status(), idx );
00146 mParent->headerOfMsgChanged(this, idx);
00147 }
00148
00149 }
00150
00151
00152 void KMMsgBase::setStatus(const KMMsgStatus aStatus, int idx)
00153 {
00154 mDirty = TRUE;
00155 KMMsgStatus oldStatus = status();
00156 switch (aStatus) {
00157 case KMMsgStatusRead:
00158
00159 mStatus &= ~KMMsgStatusUnread;
00160 mStatus &= ~KMMsgStatusNew;
00161 mStatus |= KMMsgStatusRead;
00162 break;
00163
00164 case KMMsgStatusUnread:
00165
00166 mStatus &= ~KMMsgStatusOld;
00167 mStatus &= ~KMMsgStatusRead;
00168 mStatus &= ~KMMsgStatusNew;
00169 mStatus |= KMMsgStatusUnread;
00170 break;
00171
00172 case KMMsgStatusOld:
00173
00174 mStatus &= ~KMMsgStatusNew;
00175 mStatus &= ~KMMsgStatusUnread;
00176 mStatus |= KMMsgStatusOld;
00177 break;
00178
00179 case KMMsgStatusNew:
00180
00181 mStatus &= ~KMMsgStatusOld;
00182 mStatus &= ~KMMsgStatusRead;
00183 mStatus &= ~KMMsgStatusUnread;
00184 mStatus |= KMMsgStatusNew;
00185 break;
00186
00187 case KMMsgStatusDeleted:
00188 mStatus |= KMMsgStatusDeleted;
00189 break;
00190
00191 case KMMsgStatusReplied:
00192 mStatus |= KMMsgStatusReplied;
00193 break;
00194
00195 case KMMsgStatusForwarded:
00196 mStatus |= KMMsgStatusForwarded;
00197 break;
00198
00199 case KMMsgStatusQueued:
00200 mStatus |= KMMsgStatusQueued;
00201 break;
00202
00203 case KMMsgStatusSent:
00204 mStatus &= ~KMMsgStatusQueued;
00205 mStatus &= ~KMMsgStatusUnread;
00206 mStatus &= ~KMMsgStatusNew;
00207 mStatus |= KMMsgStatusSent;
00208 break;
00209
00210 case KMMsgStatusFlag:
00211 mStatus |= KMMsgStatusFlag;
00212 break;
00213
00214
00215 case KMMsgStatusWatched:
00216 mStatus &= ~KMMsgStatusIgnored;
00217 mStatus |= KMMsgStatusWatched;
00218 break;
00219
00220 case KMMsgStatusIgnored:
00221 mStatus &= ~KMMsgStatusWatched;
00222 mStatus |= KMMsgStatusIgnored;
00223 break;
00224
00225 case KMMsgStatusSpam:
00226 mStatus &= ~KMMsgStatusHam;
00227 mStatus |= KMMsgStatusSpam;
00228 break;
00229 case KMMsgStatusHam:
00230 mStatus &= ~KMMsgStatusSpam;
00231 mStatus |= KMMsgStatusHam;
00232 break;
00233 default:
00234 mStatus = aStatus;
00235 break;
00236 }
00237
00238 if (mParent) {
00239 if (idx < 0)
00240 idx = mParent->find( this );
00241 mParent->msgStatusChanged( oldStatus, status(), idx );
00242 mParent->headerOfMsgChanged( this, idx );
00243 }
00244 }
00245
00246
00247
00248
00249 void KMMsgBase::setStatus(const char* aStatusStr, const char* aXStatusStr)
00250 {
00251
00252 if (aXStatusStr) {
00253 if (strchr(aXStatusStr, 'N')) setStatus(KMMsgStatusNew);
00254 if (strchr(aXStatusStr, 'U')) setStatus(KMMsgStatusUnread);
00255 if (strchr(aXStatusStr, 'O')) setStatus(KMMsgStatusOld);
00256 if (strchr(aXStatusStr, 'R')) setStatus(KMMsgStatusRead);
00257 if (strchr(aXStatusStr, 'D')) setStatus(KMMsgStatusDeleted);
00258 if (strchr(aXStatusStr, 'A')) setStatus(KMMsgStatusReplied);
00259 if (strchr(aXStatusStr, 'F')) setStatus(KMMsgStatusForwarded);
00260 if (strchr(aXStatusStr, 'Q')) setStatus(KMMsgStatusQueued);
00261 if (strchr(aXStatusStr, 'S')) setStatus(KMMsgStatusSent);
00262 if (strchr(aXStatusStr, 'G')) setStatus(KMMsgStatusFlag);
00263 if (strchr(aXStatusStr, 'P')) setStatus(KMMsgStatusSpam);
00264 if (strchr(aXStatusStr, 'H')) setStatus(KMMsgStatusHam);
00265 }
00266
00267
00268 if (aStatusStr) {
00269 if ((aStatusStr[0]== 'R' && aStatusStr[1]== 'O') ||
00270 (aStatusStr[0]== 'O' && aStatusStr[1]== 'R')) {
00271 setStatus( KMMsgStatusOld );
00272 setStatus( KMMsgStatusRead );
00273 }
00274 else if (aStatusStr[0] == 'R')
00275 setStatus(KMMsgStatusRead);
00276 else if (aStatusStr[0] == 'D')
00277 setStatus(KMMsgStatusDeleted);
00278 else
00279 setStatus(KMMsgStatusNew);
00280 }
00281 }
00282
00283
00284 void KMMsgBase::setEncryptionState( const KMMsgEncryptionState status, int idx )
00285 {
00286 kdDebug(5006) << "***setEncryptionState1( " << status << " )" << endl;
00287 mDirty = TRUE;
00288 if (mParent)
00289 mParent->headerOfMsgChanged(this, idx);
00290 }
00291
00292 void KMMsgBase::setEncryptionStateChar( QChar status, int idx )
00293 {
00294 kdDebug(5006) << "***setEncryptionState2( " << (status.isNull() ? '?' : status.latin1()) << " )" << endl;
00295
00296 if( status.latin1() == (char)KMMsgEncryptionStateUnknown )
00297 setEncryptionState( KMMsgEncryptionStateUnknown, idx );
00298 else if( status.latin1() == (char)KMMsgNotEncrypted )
00299 setEncryptionState( KMMsgNotEncrypted, idx );
00300 else if( status.latin1() == (char)KMMsgPartiallyEncrypted )
00301 setEncryptionState( KMMsgPartiallyEncrypted, idx );
00302 else if( status.latin1() == (char)KMMsgFullyEncrypted )
00303 setEncryptionState( KMMsgFullyEncrypted, idx );
00304 else
00305 setEncryptionState( KMMsgEncryptionStateUnknown, idx );
00306 }
00307
00308
00309 void KMMsgBase::setSignatureState( const KMMsgSignatureState status, int idx )
00310 {
00311 kdDebug(5006) << "***setSignatureState1( " << status << " )" << endl;
00312 mDirty = TRUE;
00313 if (mParent)
00314 mParent->headerOfMsgChanged(this, idx);
00315 }
00316
00317 void KMMsgBase::setMDNSentState( KMMsgMDNSentState, int idx ) {
00318 mDirty = true;
00319 if ( mParent )
00320 mParent->headerOfMsgChanged(this, idx);
00321 }
00322
00323 void KMMsgBase::setSignatureStateChar( QChar status, int idx )
00324 {
00325 kdDebug(5006) << "***setSignatureState2( " << (status.isNull() ? '?' : status.latin1()) << " )" << endl;
00326
00327 if( status.latin1() == (char)KMMsgSignatureStateUnknown )
00328 setSignatureState( KMMsgSignatureStateUnknown, idx );
00329 else if( status.latin1() == (char)KMMsgNotSigned )
00330 setSignatureState( KMMsgNotSigned, idx );
00331 else if( status.latin1() == (char)KMMsgPartiallySigned )
00332 setSignatureState( KMMsgPartiallySigned,idx );
00333 else if( status.latin1() == (char)KMMsgFullySigned )
00334 setSignatureState( KMMsgFullySigned, idx );
00335 else
00336 setSignatureState( KMMsgSignatureStateUnknown, idx );
00337 }
00338
00339
00340 bool KMMsgBase::isUnread(void) const
00341 {
00342 KMMsgStatus st = status();
00343 return (st & KMMsgStatusUnread);
00344 }
00345
00346
00347 bool KMMsgBase::isNew(void) const
00348 {
00349 KMMsgStatus st = status();
00350 return (st & KMMsgStatusNew);
00351 }
00352
00353
00354 bool KMMsgBase::isOfUnknownStatus(void) const
00355 {
00356 KMMsgStatus st = status();
00357 return (st == KMMsgStatusUnknown);
00358 }
00359
00360
00361 bool KMMsgBase::isOld(void) const
00362 {
00363 KMMsgStatus st = status();
00364 return (st & KMMsgStatusOld);
00365 }
00366
00367
00368 bool KMMsgBase::isRead(void) const
00369 {
00370 KMMsgStatus st = status();
00371 return (st & KMMsgStatusRead);
00372 }
00373
00374
00375 bool KMMsgBase::isDeleted(void) const
00376 {
00377 KMMsgStatus st = status();
00378 return (st & KMMsgStatusDeleted);
00379 }
00380
00381
00382 bool KMMsgBase::isReplied(void) const
00383 {
00384 KMMsgStatus st = status();
00385 return (st & KMMsgStatusReplied);
00386 }
00387
00388
00389 bool KMMsgBase::isForwarded(void) const
00390 {
00391 KMMsgStatus st = status();
00392 return (st & KMMsgStatusForwarded);
00393 }
00394
00395
00396 bool KMMsgBase::isQueued(void) const
00397 {
00398 KMMsgStatus st = status();
00399 return (st & KMMsgStatusQueued);
00400 }
00401
00402
00403 bool KMMsgBase::isSent(void) const
00404 {
00405 KMMsgStatus st = status();
00406 return (st & KMMsgStatusSent);
00407 }
00408
00409
00410 bool KMMsgBase::isFlag(void) const
00411 {
00412 KMMsgStatus st = status();
00413 return (st & KMMsgStatusFlag);
00414 }
00415
00416
00417 bool KMMsgBase::isWatched(void) const
00418 {
00419 KMMsgStatus st = status();
00420 return (st & KMMsgStatusWatched);
00421 }
00422
00423
00424 bool KMMsgBase::isIgnored(void) const
00425 {
00426 KMMsgStatus st = status();
00427 return (st & KMMsgStatusIgnored);
00428 }
00429
00430
00431 bool KMMsgBase::isSpam(void) const
00432 {
00433 KMMsgStatus st = status();
00434 return (st & KMMsgStatusSpam);
00435 }
00436
00437
00438 bool KMMsgBase::isHam(void) const
00439 {
00440 KMMsgStatus st = status();
00441 return (st & KMMsgStatusHam);
00442 }
00443
00444
00445 QCString KMMsgBase::statusToStr(const KMMsgStatus status)
00446 {
00447 QCString sstr;
00448 if (status & KMMsgStatusNew) sstr += 'N';
00449 if (status & KMMsgStatusUnread) sstr += 'U';
00450 if (status & KMMsgStatusOld) sstr += 'O';
00451 if (status & KMMsgStatusRead) sstr += 'R';
00452 if (status & KMMsgStatusDeleted) sstr += 'D';
00453 if (status & KMMsgStatusReplied) sstr += 'A';
00454 if (status & KMMsgStatusForwarded) sstr += 'F';
00455 if (status & KMMsgStatusQueued) sstr += 'Q';
00456 if (status & KMMsgStatusSent) sstr += 'S';
00457 if (status & KMMsgStatusFlag) sstr += 'G';
00458 if (status & KMMsgStatusWatched) sstr += 'W';
00459 if (status & KMMsgStatusIgnored) sstr += 'I';
00460 if (status & KMMsgStatusSpam) sstr += 'P';
00461 if (status & KMMsgStatusHam) sstr += 'H';
00462
00463 return sstr;
00464 }
00465
00466
00467 QString KMMsgBase::statusToSortRank()
00468 {
00469 QString sstr = "bcbbbbbbb";
00470
00471
00472 if (status() & KMMsgStatusWatched) sstr[0] = 'a';
00473 if (status() & KMMsgStatusIgnored) sstr[0] = 'c';
00474
00475
00476 if (status() & KMMsgStatusNew) sstr[1] = 'a';
00477 if (status() & KMMsgStatusUnread) sstr[1] = 'b';
00478
00479
00480
00481
00482 if (status() & KMMsgStatusDeleted) sstr[2] = 'a';
00483 if (status() & KMMsgStatusFlag) sstr[3] = 'a';
00484 if (status() & KMMsgStatusReplied) sstr[4] = 'a';
00485 if (status() & KMMsgStatusForwarded) sstr[5] = 'a';
00486 if (status() & KMMsgStatusQueued) sstr[6] = 'a';
00487 if (status() & KMMsgStatusSent) sstr[7] = 'a';
00488 if (status() & KMMsgStatusHam) sstr[8] = 'a';
00489 if (status() & KMMsgStatusSpam) sstr[8] = 'c';
00490
00491 return sstr;
00492 }
00493
00494
00495
00496 void KMMsgBase::setDate(const QCString& aDateStr)
00497 {
00498 setDate( KRFCDate::parseDate( aDateStr ) );
00499 }
00500
00501
00502
00503 QString KMMsgBase::dateStr(void) const
00504 {
00505 time_t d = date();
00506 return KMime::DateFormatter::formatDate(KMime::DateFormatter::Fancy, d);
00507 }
00508
00509
00510
00511 QString KMMsgBase::skipKeyword(const QString& aStr, QChar sepChar,
00512 bool* hasKeyword)
00513 {
00514 unsigned int i = 0, maxChars = 3;
00515 QString str = aStr;
00516
00517 while (str[0] == ' ') str.remove(0,1);
00518 if (hasKeyword) *hasKeyword=FALSE;
00519
00520 for (i=0; i < str.length() && i < maxChars; i++)
00521 {
00522 if (str[i] < 'A' || str[i] == sepChar) break;
00523 }
00524
00525 if (str[i] == sepChar)
00526 {
00527 do {
00528 i++;
00529 } while (str[i] == ' ');
00530 if (hasKeyword) *hasKeyword=TRUE;
00531 return str.mid(i);
00532 }
00533 return str;
00534 }
00535
00536
00537
00538 const QTextCodec* KMMsgBase::codecForName(const QCString& _str)
00539 {
00540 if (_str.isEmpty()) return 0;
00541 return KGlobal::charsets()->codecForName(_str.lower());
00542 }
00543
00544
00545
00546 QCString KMMsgBase::toUsAscii(const QString& _str, bool *ok)
00547 {
00548 bool all_ok =true;
00549 QString result = _str;
00550 int len = result.length();
00551 for (int i = 0; i < len; i++)
00552 if (result.at(i).unicode() >= 128) {
00553 result.at(i) = '?';
00554 all_ok = false;
00555 }
00556 if (ok)
00557 *ok = all_ok;
00558 return result.latin1();
00559 }
00560
00561
00562
00563 QStringList KMMsgBase::supportedEncodings(bool usAscii)
00564 {
00565 QStringList encodingNames = KGlobal::charsets()->availableEncodingNames();
00566 QStringList encodings;
00567 QMap<QString,bool> mimeNames;
00568 for (QStringList::Iterator it = encodingNames.begin();
00569 it != encodingNames.end(); it++)
00570 {
00571 QTextCodec *codec = KGlobal::charsets()->codecForName(*it);
00572 QString mimeName = (codec) ? QString(codec->mimeName()).lower() : (*it);
00573 if (mimeNames.find(mimeName) == mimeNames.end())
00574 {
00575 encodings.append(KGlobal::charsets()->languageForEncoding(*it)
00576 + " ( " + mimeName + " )");
00577 mimeNames.insert(mimeName, TRUE);
00578 }
00579 }
00580 encodings.sort();
00581 if (usAscii) encodings.prepend(KGlobal::charsets()
00582 ->languageForEncoding("us-ascii") + " ( us-ascii )");
00583 return encodings;
00584 }
00585
00586 namespace {
00587
00588
00589
00590
00591 inline bool isBlank( char ch ) { return ch == ' ' || ch == '\t' ; }
00592
00593 QCString unfold( const QCString & header ) {
00594 if ( header.isEmpty() )
00595 return QCString();
00596
00597 QCString result( header.size() );
00598 char * d = result.data();
00599
00600 for ( const char * s = header.data() ; *s ; )
00601 if ( *s == '\r' ) {
00602 ++s;
00603 continue;
00604 } else if ( *s == '\n' ) {
00605 while ( isBlank( *++s ) );
00606 *d++ = ' ';
00607 } else
00608 *d++ = *s++;
00609
00610 *d++ = '\0';
00611
00612 result.truncate( d - result.data() );
00613 return result;
00614 }
00615 }
00616
00617
00618
00619 QString KMMsgBase::decodeRFC2047String(const QCString& aStr)
00620 {
00621 if ( aStr.isEmpty() )
00622 return QString::null;
00623
00624 const QCString str = unfold( aStr );
00625
00626 if ( str.isEmpty() )
00627 return QString::null;
00628
00629 if ( str.find( "=?" ) < 0 )
00630 return kmkernel->networkCodec()->toUnicode( str );
00631
00632 QString result;
00633 QCString LWSP_buffer;
00634 bool lastWasEncodedWord = false;
00635 static const int maxLen = 200;
00636
00637 for ( const char * pos = str.data() ; *pos ; ++pos ) {
00638
00639
00640
00641 if ( lastWasEncodedWord && isBlank( pos[0] ) ) {
00642 LWSP_buffer += pos[0];
00643 continue;
00644 }
00645
00646 if (pos[0]!='=' || pos[1]!='?') {
00647 result += LWSP_buffer + pos[0];
00648 LWSP_buffer = 0;
00649 lastWasEncodedWord = FALSE;
00650 continue;
00651 }
00652
00653 const char * const beg = pos;
00654 {
00655
00656 QCString charset;
00657 int i = 2;
00658 for (pos+=2; i<maxLen && (*pos!='?'&&(*pos==' '||ispunct(*pos)||isalnum(*pos))); ++i) {
00659 charset += *pos;
00660 pos++;
00661 }
00662 if (*pos!='?' || i<4 || i>=maxLen)
00663 goto invalid_encoded_word;
00664
00665
00666 const char encoding[2] = { pos[1], '\0' };
00667 if (pos[2]!='?' || (encoding[0]!='Q' && encoding[0]!='q' &&
00668 encoding[0]!='B' && encoding[0]!='b'))
00669 goto invalid_encoded_word;
00670 pos+=3; i+=3;
00671 const char * enc_start = pos;
00672
00673 while (i<maxLen && *pos && !(*pos=='?' && *(pos+1)=='=')) {
00674 i++;
00675 pos++;
00676 }
00677 if (i>=maxLen || !*pos)
00678 goto invalid_encoded_word;
00679
00680
00681 const KMime::Codec * c = KMime::Codec::codecForName( encoding );
00682 kdFatal( !c, 5006 ) << "No \"" << encoding << "\" codec!?" << endl;
00683
00684 QByteArray in; in.setRawData( enc_start, pos - enc_start );
00685 const QByteArray enc = c->decode( in );
00686 in.resetRawData( enc_start, pos - enc_start );
00687
00688 const QTextCodec * codec = codecForName(charset);
00689 if (!codec) codec = kmkernel->networkCodec();
00690 result += codec->toUnicode(enc);
00691 lastWasEncodedWord = true;
00692
00693 ++pos;
00694 LWSP_buffer = 0;
00695 }
00696 continue;
00697 invalid_encoded_word:
00698
00699 pos = beg;
00700 result += LWSP_buffer;
00701 result += "=?";
00702 lastWasEncodedWord = false;
00703 LWSP_buffer = 0;
00704 }
00705 return result;
00706 }
00707
00708
00709
00710 static const QCString especials = "()<>@,;:\"/[]?.= \033";
00711
00712 QCString KMMsgBase::encodeRFC2047Quoted( const QCString & s, bool base64 ) {
00713 const char * codecName = base64 ? "b" : "q" ;
00714 const KMime::Codec * codec = KMime::Codec::codecForName( codecName );
00715 kdFatal( !codec, 5006 ) << "No \"" << codecName << "\" found!?" << endl;
00716 QByteArray in; in.setRawData( s.data(), s.length() );
00717 const QByteArray result = codec->encode( in );
00718 in.resetRawData( s.data(), s.length() );
00719 return QCString( result.data(), result.size() + 1 );
00720 }
00721
00722 QCString KMMsgBase::encodeRFC2047String(const QString& _str,
00723 const QCString& charset)
00724 {
00725 static const QString dontQuote = "\"()<>,@";
00726
00727 if (_str.isEmpty()) return QCString();
00728 if (charset == "us-ascii") return toUsAscii(_str);
00729
00730 QCString cset;
00731 if (charset.isEmpty()) cset = QCString(kmkernel->networkCodec()->mimeName()).lower();
00732 else cset = charset;
00733 const QTextCodec *codec = codecForName(cset);
00734 if (!codec) codec = kmkernel->networkCodec();
00735
00736 unsigned int nonAscii = 0;
00737 for (unsigned int i = 0; i < _str.length(); i++)
00738 if (_str.at(i).unicode() >= 128) nonAscii++;
00739 bool useBase64 = (nonAscii * 6 > _str.length());
00740
00741 unsigned int start, stop, p, pos = 0, encLength;
00742 QCString result;
00743 bool breakLine = FALSE;
00744 const unsigned int maxLen = 75 - 7 - cset.length();
00745
00746 while (pos < _str.length())
00747 {
00748 start = pos; p = pos;
00749 while (p < _str.length())
00750 {
00751 if (!breakLine && (_str.at(p) == ' ' || dontQuote.find(_str.at(p)) != -1))
00752 start = p + 1;
00753 if (_str.at(p).unicode() >= 128 || _str.at(p).unicode() < 32)
00754 break;
00755 p++;
00756 }
00757 if (breakLine || p < _str.length())
00758 {
00759 while (dontQuote.find(_str.at(start)) != -1) start++;
00760 stop = start;
00761 while (stop < _str.length() && dontQuote.find(_str.at(stop)) == -1)
00762 stop++;
00763 result += _str.mid(pos, start - pos).latin1();
00764 encLength = encodeRFC2047Quoted(codec->fromUnicode(_str.
00765 mid(start, stop - start)), useBase64).length();
00766 breakLine = (encLength > maxLen);
00767 if (breakLine)
00768 {
00769 int dif = (stop - start) / 2;
00770 int step = dif;
00771 while (abs(step) > 1)
00772 {
00773 encLength = encodeRFC2047Quoted(codec->fromUnicode(_str.
00774 mid(start, dif)), useBase64).length();
00775 step = (encLength > maxLen) ? (-abs(step) / 2) : (abs(step) / 2);
00776 dif += step;
00777 }
00778 stop = start + dif;
00779 }
00780 p = stop;
00781 while (p > start && _str.at(p) != ' ') p--;
00782 if (p > start) stop = p;
00783 if (result.right(3) == "?= ") start--;
00784 if (result.right(5) == "?=\n ") {
00785 start--; result.truncate(result.length() - 1);
00786 }
00787 int lastNewLine = result.findRev("\n ");
00788 if (!result.mid(lastNewLine).stripWhiteSpace().isEmpty()
00789 && result.length() - lastNewLine + encLength + 2 > maxLen)
00790 result += "\n ";
00791 result += "=?";
00792 result += cset;
00793 result += (useBase64) ? "?b?" : "?q?";
00794 result += encodeRFC2047Quoted(codec->fromUnicode(_str.mid(start,
00795 stop - start)), useBase64);
00796 result += "?=";
00797 if (breakLine) result += "\n ";
00798 pos = stop;
00799 } else {
00800 result += _str.mid(pos).latin1();
00801 break;
00802 }
00803 }
00804 return result;
00805 }
00806
00807
00808
00809 QCString KMMsgBase::encodeRFC2231String( const QString& _str,
00810 const QCString& charset )
00811 {
00812 if ( _str.isEmpty() )
00813 return QCString();
00814
00815 QCString cset;
00816 if ( charset.isEmpty() )
00817 cset = QCString( kmkernel->networkCodec()->mimeName() ).lower();
00818 else
00819 cset = charset;
00820 const QTextCodec *codec = codecForName( cset );
00821 QCString latin;
00822 if ( charset == "us-ascii" )
00823 latin = toUsAscii( _str );
00824 else if ( codec )
00825 latin = codec->fromUnicode( _str );
00826 else
00827 latin = _str.local8Bit();
00828
00829 char *l;
00830 for ( l = latin.data(); *l; ++l ) {
00831 if ( ( *l & 0xE0 == 0 ) || ( *l & 0x80 ) )
00832
00833 break;
00834 }
00835 if ( !*l )
00836 return latin;
00837
00838 QCString result = cset + "''";
00839 for ( l = latin.data(); *l; ++l ) {
00840 bool needsQuoting = ( *l & 0x80 );
00841 if( !needsQuoting ) {
00842 int len = especials.length();
00843 for ( int i = 0; i < len; i++ )
00844 if ( *l == especials[i] ) {
00845 needsQuoting = true;
00846 break;
00847 }
00848 }
00849 if ( needsQuoting ) {
00850 result += '%';
00851 unsigned char hexcode;
00852 hexcode = ( ( *l & 0xF0 ) >> 4 ) + 48;
00853 if ( hexcode >= 58 )
00854 hexcode += 7;
00855 result += hexcode;
00856 hexcode = ( *l & 0x0F ) + 48;
00857 if ( hexcode >= 58 )
00858 hexcode += 7;
00859 result += hexcode;
00860 } else {
00861 result += *l;
00862 }
00863 }
00864 return result;
00865 }
00866
00867
00868
00869 QString KMMsgBase::decodeRFC2231String(const QCString& _str)
00870 {
00871 int p = _str.find('\'');
00872 if (p < 0) return kmkernel->networkCodec()->toUnicode(_str);
00873
00874 QCString charset = _str.left(p);
00875
00876 QCString st = _str.mid(_str.findRev('\'') + 1);
00877 char ch, ch2;
00878 p = 0;
00879 while (p < (int)st.length())
00880 {
00881 if (st.at(p) == 37)
00882 {
00883 ch = st.at(p+1) - 48;
00884 if (ch > 16) ch -= 7;
00885 ch2 = st.at(p+2) - 48;
00886 if (ch2 > 16) ch2 -= 7;
00887 st.at(p) = ch * 16 + ch2;
00888 st.remove( p+1, 2 );
00889 }
00890 p++;
00891 }
00892 QString result;
00893 const QTextCodec * codec = codecForName( charset );
00894 if ( !codec )
00895 codec = kmkernel->networkCodec();
00896 return codec->toUnicode( st );
00897 }
00898
00899 QString KMMsgBase::base64EncodedMD5( const QString & s, bool utf8 ) {
00900 if (s.stripWhiteSpace().isEmpty()) return "";
00901 if ( utf8 )
00902 return base64EncodedMD5( s.stripWhiteSpace().utf8() );
00903 else
00904 return base64EncodedMD5( s.stripWhiteSpace().latin1() );
00905 }
00906
00907 QString KMMsgBase::base64EncodedMD5( const QCString & s ) {
00908 if (s.stripWhiteSpace().isEmpty()) return "";
00909 return base64EncodedMD5( s.stripWhiteSpace().data() );
00910 }
00911
00912 QString KMMsgBase::base64EncodedMD5( const char * s, int len ) {
00913 if (!s || !len) return "";
00914 static const int Base64EncodedMD5Len = 22;
00915 KMD5 md5( s, len );
00916 return md5.base64Digest().left( Base64EncodedMD5Len );
00917 }
00918
00919
00920
00921 QCString KMMsgBase::autoDetectCharset(const QCString &_encoding, const QStringList &encodingList, const QString &text)
00922 {
00923 QStringList charsets = encodingList;
00924 if (!_encoding.isEmpty())
00925 {
00926 QString currentCharset = QString::fromLatin1(_encoding);
00927 charsets.remove(currentCharset);
00928 charsets.prepend(currentCharset);
00929 }
00930
00931 QStringList::ConstIterator it = charsets.begin();
00932 for (; it != charsets.end(); ++it)
00933 {
00934 QCString encoding = (*it).latin1();
00935 if (encoding == "locale")
00936 encoding = QCString(kmkernel->networkCodec()->mimeName()).lower();
00937 if (text.isEmpty())
00938 return encoding;
00939 if (encoding == "us-ascii") {
00940 bool ok;
00941 (void) KMMsgBase::toUsAscii(text, &ok);
00942 if (ok)
00943 return encoding;
00944 }
00945 else
00946 {
00947 const QTextCodec *codec = KMMsgBase::codecForName(encoding);
00948 if (!codec) {
00949 kdDebug(5006) << "Auto-Charset: Something is wrong and I can not get a codec. [" << encoding << "]" << endl;
00950 } else {
00951 if (codec->canEncode(text))
00952 return encoding;
00953 }
00954 }
00955 }
00956 return 0;
00957 }
00958
00959
00960
00961 unsigned long KMMsgBase::getMsgSerNum() const
00962 {
00963 unsigned long msn = MessageProperty::serialCache( this );
00964 if (msn)
00965 return msn;
00966 if (mParent) {
00967 int index = mParent->find((KMMsgBase*)this);
00968 msn = kmkernel->msgDict()->getMsgSerNum(mParent, index);
00969 if (msn)
00970 MessageProperty::setSerialCache( this, msn );
00971 }
00972 return msn;
00973 }
00974
00975
00976
00977 bool KMMsgBase::isComplete()
00978 {
00979 return MessageProperty::complete( getMsgSerNum() );
00980 }
00981
00982
00983
00984 void KMMsgBase::setComplete(bool value)
00985 {
00986 MessageProperty::setComplete( getMsgSerNum(), value );
00987 if ( value )
00988 setReadyToShow( true );
00989 }
00990
00991
00992 bool KMMsgBase::readyToShow()
00993 {
00994 return MessageProperty::readyToShow( getMsgSerNum() );
00995 }
00996
00997
00998
00999 void KMMsgBase::setReadyToShow(bool value)
01000 {
01001 MessageProperty::setReadyToShow( getMsgSerNum(), value );
01002 }
01003
01004
01005
01006 bool KMMsgBase::transferInProgress()
01007 {
01008 return MessageProperty::transferInProgress( getMsgSerNum() );
01009 }
01010
01011
01012
01013 void KMMsgBase::setTransferInProgress(bool value, bool force)
01014 {
01015 MessageProperty::setTransferInProgress( getMsgSerNum(), value, force );
01016 }
01017
01018
01019
01020 static void swapEndian(QString &str)
01021 {
01022 uint len = str.length();
01023 str = QDeepCopy<QString>(str);
01024 QChar *unicode = const_cast<QChar*>( str.unicode() );
01025 for (uint i = 0; i < len; i++)
01026 unicode[i] = kmail_swap_16(unicode[i].unicode());
01027 }
01028
01029
01030 static int g_chunk_length = 0, g_chunk_offset=0;
01031 static uchar *g_chunk = 0;
01032
01033 namespace {
01034 template < typename T > void copy_from_stream( T & x ) {
01035 if( g_chunk_offset + int(sizeof(T)) > g_chunk_length ) {
01036 g_chunk_offset = g_chunk_length;
01037 kdDebug( 5006 ) << "This should never happen.. "
01038 << __FILE__ << ":" << __LINE__ << endl;
01039 x = 0;
01040 } else {
01041
01042
01043 memcpy( &x, g_chunk + g_chunk_offset, sizeof(T) );
01044 g_chunk_offset += sizeof(T);
01045 }
01046 }
01047 }
01048
01049
01050 QString KMMsgBase::getStringPart(MsgPartType t) const
01051 {
01052 QString ret;
01053
01054 g_chunk_offset = 0;
01055 bool using_mmap = FALSE;
01056 bool swapByteOrder = mParent->indexSwapByteOrder();
01057 if (mParent->indexStreamBasePtr()) {
01058 if (g_chunk)
01059 free(g_chunk);
01060 using_mmap = TRUE;
01061 g_chunk = mParent->indexStreamBasePtr() + mIndexOffset;
01062 g_chunk_length = mIndexLength;
01063 } else {
01064 if(!mParent->mIndexStream)
01065 return ret;
01066 if (g_chunk_length < mIndexLength)
01067 g_chunk = (uchar *)realloc(g_chunk, g_chunk_length = mIndexLength);
01068 off_t first_off=ftell(mParent->mIndexStream);
01069 fseek(mParent->mIndexStream, mIndexOffset, SEEK_SET);
01070 fread( g_chunk, mIndexLength, 1, mParent->mIndexStream);
01071 fseek(mParent->mIndexStream, first_off, SEEK_SET);
01072 }
01073
01074 MsgPartType type;
01075 Q_UINT16 l;
01076 while(g_chunk_offset < mIndexLength) {
01077 Q_UINT32 tmp;
01078 copy_from_stream(tmp);
01079 copy_from_stream(l);
01080 if (swapByteOrder)
01081 {
01082 tmp = kmail_swap_32(tmp);
01083 l = kmail_swap_16(l);
01084 }
01085 type = (MsgPartType) tmp;
01086 if(g_chunk_offset + l > mIndexLength) {
01087 kdDebug(5006) << "This should never happen.. " << __FILE__ << ":" << __LINE__ << endl;
01088 break;
01089 }
01090 if(type == t) {
01091
01092
01093 if(l)
01094 ret = QString((QChar *)(g_chunk + g_chunk_offset), l/2);
01095 break;
01096 }
01097 g_chunk_offset += l;
01098 }
01099 if(using_mmap) {
01100 g_chunk_length = 0;
01101 g_chunk = 0;
01102 }
01103
01104
01105
01106
01107
01108 #ifndef WORDS_BIGENDIAN
01109
01110 swapEndian(ret);
01111 #else
01112
01113 #endif
01114
01115 return ret;
01116 }
01117
01118
01119 off_t KMMsgBase::getLongPart(MsgPartType t) const
01120 {
01121 off_t ret = 0;
01122
01123 g_chunk_offset = 0;
01124 bool using_mmap = FALSE;
01125 int sizeOfLong = mParent->indexSizeOfLong();
01126 bool swapByteOrder = mParent->indexSwapByteOrder();
01127 if (mParent->indexStreamBasePtr()) {
01128 if (g_chunk)
01129 free(g_chunk);
01130 using_mmap = TRUE;
01131 g_chunk = mParent->indexStreamBasePtr() + mIndexOffset;
01132 g_chunk_length = mIndexLength;
01133 } else {
01134 if (!mParent->mIndexStream)
01135 return ret;
01136 assert(mIndexLength >= 0);
01137 if (g_chunk_length < mIndexLength)
01138 g_chunk = (uchar *)realloc(g_chunk, g_chunk_length = mIndexLength);
01139 off_t first_off=ftell(mParent->mIndexStream);
01140 fseek(mParent->mIndexStream, mIndexOffset, SEEK_SET);
01141 fread( g_chunk, mIndexLength, 1, mParent->mIndexStream);
01142 fseek(mParent->mIndexStream, first_off, SEEK_SET);
01143 }
01144
01145 MsgPartType type;
01146 Q_UINT16 l;
01147 while (g_chunk_offset < mIndexLength) {
01148 Q_UINT32 tmp;
01149 copy_from_stream(tmp);
01150 copy_from_stream(l);
01151 if (swapByteOrder)
01152 {
01153 tmp = kmail_swap_32(tmp);
01154 l = kmail_swap_16(l);
01155 }
01156 type = (MsgPartType) tmp;
01157
01158 if (g_chunk_offset + l > mIndexLength) {
01159 kdDebug(5006) << "This should never happen.. " << __FILE__ << ":" << __LINE__ << endl;
01160 break;
01161 }
01162 if(type == t) {
01163 assert(sizeOfLong == l);
01164 if (sizeOfLong == sizeof(ret))
01165 {
01166 copy_from_stream(ret);
01167 if (swapByteOrder)
01168 {
01169 if (sizeof(ret) == 4)
01170 ret = kmail_swap_32(ret);
01171 else
01172 ret = kmail_swap_64(ret);
01173 }
01174 }
01175 else if (sizeOfLong == 4)
01176 {
01177
01178 Q_UINT32 ret_32;
01179 copy_from_stream(ret_32);
01180 if (swapByteOrder)
01181 ret_32 = kmail_swap_32(ret_32);
01182 ret = ret_32;
01183 }
01184 else if (sizeOfLong == 8)
01185 {
01186
01187 Q_UINT32 ret_1;
01188 Q_UINT32 ret_2;
01189 copy_from_stream(ret_1);
01190 copy_from_stream(ret_2);
01191 if (!swapByteOrder)
01192 {
01193
01194 #ifndef WORDS_BIGENDIAN
01195
01196 ret = ret_1;
01197 #else
01198
01199 ret = ret_2;
01200 #endif
01201 }
01202 else
01203 {
01204
01205 #ifndef WORDS_BIGENDIAN
01206
01207 ret = ret_2;
01208 #else
01209
01210 ret = ret_1;
01211 #endif
01212
01213 ret = kmail_swap_32(ret);
01214 }
01215
01216 }
01217 break;
01218 }
01219 g_chunk_offset += l;
01220 }
01221 if(using_mmap) {
01222 g_chunk_length = 0;
01223 g_chunk = 0;
01224 }
01225 return ret;
01226 }
01227
01228 #ifndef WORDS_BIGENDIAN
01229
01230 #define memcpy_networkorder(to, from, len) swab((char *)(from), (char *)(to), len)
01231 #else
01232
01233 #define memcpy_networkorder(to, from, len) memcpy(to, from, len)
01234 #endif
01235
01236 #define STORE_DATA_LEN(type, x, len, network_order) do { \
01237 int len2 = (len > 256) ? 256 : len; \
01238 if(csize < (length + (len2 + sizeof(short) + sizeof(MsgPartType)))) \
01239 ret = (uchar *)realloc(ret, csize += len2+sizeof(short)+sizeof(MsgPartType)); \
01240 Q_UINT32 t = (Q_UINT32) type; memcpy(ret+length, &t, sizeof(t)); \
01241 Q_UINT16 l = len2; memcpy(ret+length+sizeof(t), &l, sizeof(l)); \
01242 if (network_order) \
01243 memcpy_networkorder(ret+length+sizeof(t)+sizeof(l), x, len2); \
01244 else \
01245 memcpy(ret+length+sizeof(t)+sizeof(l), x, len2); \
01246 length += len2+sizeof(t)+sizeof(l); \
01247 } while(0)
01248 #define STORE_DATA(type, x) STORE_DATA_LEN(type, &x, sizeof(x), false)
01249
01250
01251 const uchar *KMMsgBase::asIndexString(int &length) const
01252 {
01253 unsigned int csize = 256;
01254 static uchar *ret = 0;
01255 if(!ret)
01256 ret = (uchar *)malloc(csize);
01257 length = 0;
01258
01259 unsigned long tmp;
01260 QString tmp_str;
01261
01262
01263 tmp_str = msgIdMD5().stripWhiteSpace();
01264 STORE_DATA_LEN(MsgIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
01265 tmp = mLegacyStatus;
01266 STORE_DATA(MsgLegacyStatusPart, tmp);
01267
01268
01269 tmp_str = fromStrip().stripWhiteSpace();
01270 STORE_DATA_LEN(MsgFromPart, tmp_str.unicode(), tmp_str.length() * 2, true);
01271 tmp_str = subject().stripWhiteSpace();
01272 STORE_DATA_LEN(MsgSubjectPart, tmp_str.unicode(), tmp_str.length() * 2, true);
01273 tmp_str = toStrip().stripWhiteSpace();
01274 STORE_DATA_LEN(MsgToPart, tmp_str.unicode(), tmp_str.length() * 2, true);
01275 tmp_str = replyToIdMD5().stripWhiteSpace();
01276 STORE_DATA_LEN(MsgReplyToIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
01277 tmp_str = xmark().stripWhiteSpace();
01278 STORE_DATA_LEN(MsgXMarkPart, tmp_str.unicode(), tmp_str.length() * 2, true);
01279 tmp_str = fileName().stripWhiteSpace();
01280 STORE_DATA_LEN(MsgFilePart, tmp_str.unicode(), tmp_str.length() * 2, true);
01281 tmp = msgSize();
01282 STORE_DATA(MsgSizePart, tmp);
01283 tmp = folderOffset();
01284 STORE_DATA(MsgOffsetPart, tmp);
01285 tmp = date();
01286 STORE_DATA(MsgDatePart, tmp);
01287 tmp = (signatureState() << 16) | encryptionState();
01288 STORE_DATA(MsgCryptoStatePart, tmp);
01289 tmp = mdnSentState();
01290 STORE_DATA(MsgMDNSentPart, tmp);
01291
01292 tmp_str = replyToAuxIdMD5().stripWhiteSpace();
01293 STORE_DATA_LEN(MsgReplyToAuxIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
01294
01295 tmp_str = strippedSubjectMD5().stripWhiteSpace();
01296 STORE_DATA_LEN(MsgStrippedSubjectMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
01297
01298 tmp = status();
01299 STORE_DATA(MsgStatusPart, tmp);
01300
01301 return ret;
01302 }
01303 #undef STORE_DATA_LEN
01304 #undef STORE_DATA
01305
01306 bool KMMsgBase::syncIndexString() const
01307 {
01308 if(!dirty())
01309 return TRUE;
01310 int len;
01311 const uchar *buffer = asIndexString(len);
01312 if (len == mIndexLength) {
01313 Q_ASSERT(mParent->mIndexStream);
01314 fseek(mParent->mIndexStream, mIndexOffset, SEEK_SET);
01315 assert( mIndexOffset > 0 );
01316 fwrite( buffer, len, 1, mParent->mIndexStream);
01317 return TRUE;
01318 }
01319 return FALSE;
01320 }