kmail

kmmsgbase.cpp

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