kmail

kmmsgbase.cpp

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