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