kmail

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