kmail

kmmsgbase.cpp
1 // kmmsgbase.cpp
2 
3 #include <config.h>
4 
5 #include "globalsettings.h"
6 #include "kmmsgbase.h"
7 
8 #include "kmfolderindex.h"
9 #include "kmfolder.h"
10 #include "kmheaders.h"
11 #include "kmmsgdict.h"
12 #include "messageproperty.h"
13 using KMail::MessageProperty;
14 
15 #include <kdebug.h>
16 #include <tdeglobal.h>
17 #include <kcharsets.h>
18 #include <kmdcodec.h>
19 #include <krfcdate.h>
20 
21 #include <mimelib/mimepp.h>
22 #include <kmime_codecs.h>
23 
24 #include <tqglobal.h>
25 #include <tqtextcodec.h>
26 #include <tqdeepcopy.h>
27 #include <tqregexp.h>
28 
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 
33 #ifdef HAVE_BYTESWAP_H
34 #include <byteswap.h>
35 #endif
36 
37 // We define functions as kmail_swap_NN so that we don't get compile errors
38 // on platforms where bswap_NN happens to be a function instead of a define.
39 
40 /* Swap bytes in 16 bit value. */
41 #ifdef bswap_16
42 #define kmail_swap_16(x) bswap_16(x)
43 #else
44 #define kmail_swap_16(x) \
45  ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
46 #endif
47 
48 /* Swap bytes in 32 bit value. */
49 #ifdef bswap_32
50 #define kmail_swap_32(x) bswap_32(x)
51 #else
52 #define kmail_swap_32(x) \
53  ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
54  (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
55 #endif
56 
57 /* Swap bytes in 64 bit value. */
58 #ifdef bswap_64
59 #define kmail_swap_64(x) bswap_64(x)
60 #else
61 #define kmail_swap_64(x) \
62  ((((x) & 0xff00000000000000ull) >> 56) \
63  | (((x) & 0x00ff000000000000ull) >> 40) \
64  | (((x) & 0x0000ff0000000000ull) >> 24) \
65  | (((x) & 0x000000ff00000000ull) >> 8) \
66  | (((x) & 0x00000000ff000000ull) << 8) \
67  | (((x) & 0x0000000000ff0000ull) << 24) \
68  | (((x) & 0x000000000000ff00ull) << 40) \
69  | (((x) & 0x00000000000000ffull) << 56))
70 #endif
71 
72 //-----------------------------------------------------------------------------
73 KMMsgBase::KMMsgBase(KMFolder* aParentFolder)
74  : mParent( aParentFolder ), mIndexOffset( 0 ),
75  mIndexLength( 0 ), mDirty( false ), mEnableUndo( false ), mStatus( KMMsgStatusUnknown )
76 {
77 }
78 
79 
80 //-----------------------------------------------------------------------------
81 KMMsgBase::~KMMsgBase()
82 {
83  MessageProperty::forget( this );
84 }
85 
86 KMFolderIndex* KMMsgBase::storage() const
87 {
88  // TODO: How did this ever work? What about KMFolderSearch that does
89  // not inherit KMFolderIndex?
90  if( mParent )
91  return static_cast<KMFolderIndex*>( mParent->storage() );
92  return 0;
93 }
94 
95 //-----------------------------------------------------------------------------
96 void KMMsgBase::assign(const KMMsgBase* other)
97 {
98  mParent = other->mParent;
99  mDirty = other->mDirty;
100  mIndexOffset = other->mIndexOffset;
101  mIndexLength = other->mIndexLength;
102 }
103 
104 //-----------------------------------------------------------------------------
105 KMMsgBase& KMMsgBase::operator=(const KMMsgBase& other)
106 {
107  assign(&other);
108  return *this;
109 }
110 
111 
112 //----------------------------------------------------------------------------
113 KMMsgBase::KMMsgBase( const KMMsgBase& other )
114 {
115  assign( &other );
116 }
117 
118 //-----------------------------------------------------------------------------
119 bool KMMsgBase::isMessage(void) const
120 {
121  return false;
122 }
123 //-----------------------------------------------------------------------------
124 void KMMsgBase::toggleStatus(const KMMsgStatus aStatus, int idx)
125 {
126  mDirty = true;
127  KMMsgStatus oldStatus = status();
128  if ( status() & aStatus ) {
129  mStatus &= ~aStatus;
130  } else {
131  mStatus |= aStatus;
132  // Ignored and Watched are toggleable, yet mutually exclusive.
133  // That is an arbitrary restriction on my part. HAR HAR HAR :) -till
134  if (aStatus == KMMsgStatusWatched)
135  mStatus &= ~KMMsgStatusIgnored;
136  if (aStatus == KMMsgStatusIgnored)
137  mStatus &= ~KMMsgStatusWatched;
138  if (aStatus == KMMsgStatusSpam)
139  mStatus &= ~KMMsgStatusHam;
140  if (aStatus == KMMsgStatusHam)
141  mStatus &= ~KMMsgStatusSpam;
142  }
143  if (storage()) {
144  if (idx < 0)
145  idx = storage()->find( this );
146  storage()->msgStatusChanged( oldStatus, status(), idx );
147  storage()->headerOfMsgChanged(this, idx);
148  }
149 
150 }
151 
152 //-----------------------------------------------------------------------------
153 void KMMsgBase::setStatus(const KMMsgStatus aStatus, int idx)
154 {
155  mDirty = true;
156  KMMsgStatus oldStatus = status();
157  switch (aStatus) {
158  case KMMsgStatusRead:
159  // Unset unread and new, set read
160  mStatus &= ~KMMsgStatusUnread;
161  mStatus &= ~KMMsgStatusNew;
162  mStatus |= KMMsgStatusRead;
163  break;
164 
165  case KMMsgStatusUnread:
166  // unread overrides read
167  mStatus &= ~KMMsgStatusOld;
168  mStatus &= ~KMMsgStatusRead;
169  mStatus &= ~KMMsgStatusNew;
170  mStatus |= KMMsgStatusUnread;
171  break;
172 
173  case KMMsgStatusOld:
174  // old can't be new or unread
175  mStatus &= ~KMMsgStatusNew;
176  mStatus &= ~KMMsgStatusUnread;
177  mStatus |= KMMsgStatusOld;
178  break;
179 
180  case KMMsgStatusNew:
181  // new overrides old and read
182  mStatus &= ~KMMsgStatusOld;
183  mStatus &= ~KMMsgStatusRead;
184  mStatus &= ~KMMsgStatusUnread;
185  mStatus |= KMMsgStatusNew;
186  break;
187 
188  case KMMsgStatusDeleted:
189  mStatus |= KMMsgStatusDeleted;
190  break;
191 
192  case KMMsgStatusReplied:
193  mStatus |= KMMsgStatusReplied;
194  break;
195 
196  case KMMsgStatusForwarded:
197  mStatus |= KMMsgStatusForwarded;
198  break;
199 
200  case KMMsgStatusQueued:
201  mStatus |= KMMsgStatusQueued;
202  break;
203 
204  case KMMsgStatusTodo:
205  mStatus |= KMMsgStatusTodo;
206  break;
207 
208  case KMMsgStatusSent:
209  mStatus &= ~KMMsgStatusQueued;
210  mStatus &= ~KMMsgStatusUnread;
211  mStatus &= ~KMMsgStatusNew;
212  mStatus |= KMMsgStatusSent;
213  break;
214 
215  case KMMsgStatusFlag:
216  mStatus |= KMMsgStatusFlag;
217  break;
218 
219  // Watched and ignored are mutually exclusive
220  case KMMsgStatusWatched:
221  mStatus &= ~KMMsgStatusIgnored;
222  mStatus |= KMMsgStatusWatched;
223  break;
224 
225  case KMMsgStatusIgnored:
226  mStatus &= ~KMMsgStatusWatched;
227  mStatus |= KMMsgStatusIgnored;
228  break;
229  // as are ham and spam
230  case KMMsgStatusSpam:
231  mStatus &= ~KMMsgStatusHam;
232  mStatus |= KMMsgStatusSpam;
233  break;
234  case KMMsgStatusHam:
235  mStatus &= ~KMMsgStatusSpam;
236  mStatus |= KMMsgStatusHam;
237  break;
238  case KMMsgStatusHasAttach:
239  mStatus &= ~KMMsgStatusHasNoAttach;
240  mStatus |= KMMsgStatusHasAttach;
241  break;
242  case KMMsgStatusHasNoAttach:
243  mStatus &= ~KMMsgStatusHasAttach;
244  mStatus |= KMMsgStatusHasNoAttach;
245  break;
246  case KMMsgStatusHasInvitation:
247  mStatus &= ~KMMsgStatusHasNoInvitation;
248  mStatus |= KMMsgStatusHasInvitation;
249  break;
250  case KMMsgStatusHasNoInvitation:
251  mStatus &= ~KMMsgStatusHasInvitation;
252  mStatus |= KMMsgStatusHasNoInvitation;
253  break;
254  default:
255  mStatus = aStatus;
256  break;
257  }
258 
259  if ( oldStatus != mStatus && storage() ) {
260  if (idx < 0)
261  idx = storage()->find( this );
262  storage()->msgStatusChanged( oldStatus, status(), idx );
263  storage()->headerOfMsgChanged( this, idx );
264  }
265 }
266 
267 
268 
269 //-----------------------------------------------------------------------------
270 void KMMsgBase::setStatus(const char* aStatusStr, const char* aXStatusStr)
271 {
272  // first try to find status from "X-Status" field if given
273  if (aXStatusStr) {
274  if (strchr(aXStatusStr, 'N')) setStatus(KMMsgStatusNew);
275  if (strchr(aXStatusStr, 'U')) setStatus(KMMsgStatusUnread);
276  if (strchr(aXStatusStr, 'O')) setStatus(KMMsgStatusOld);
277  if (strchr(aXStatusStr, 'R')) setStatus(KMMsgStatusRead);
278  if (strchr(aXStatusStr, 'D')) setStatus(KMMsgStatusDeleted);
279  if (strchr(aXStatusStr, 'A')) setStatus(KMMsgStatusReplied);
280  if (strchr(aXStatusStr, 'F')) setStatus(KMMsgStatusForwarded);
281  if (strchr(aXStatusStr, 'Q')) setStatus(KMMsgStatusQueued);
282  if (strchr(aXStatusStr, 'K')) setStatus(KMMsgStatusTodo);
283  if (strchr(aXStatusStr, 'S')) setStatus(KMMsgStatusSent);
284  if (strchr(aXStatusStr, 'G')) setStatus(KMMsgStatusFlag);
285  if (strchr(aXStatusStr, 'P')) setStatus(KMMsgStatusSpam);
286  if (strchr(aXStatusStr, 'H')) setStatus(KMMsgStatusHam);
287  if (strchr(aXStatusStr, 'T')) setStatus(KMMsgStatusHasAttach);
288  if (strchr(aXStatusStr, 'C')) setStatus(KMMsgStatusHasNoAttach);
289  }
290 
291  // Merge the contents of the "Status" field
292  if (aStatusStr) {
293  if ((aStatusStr[0]== 'R' && aStatusStr[1]== 'O') ||
294  (aStatusStr[0]== 'O' && aStatusStr[1]== 'R')) {
295  setStatus( KMMsgStatusOld );
296  setStatus( KMMsgStatusRead );
297  }
298  else if (aStatusStr[0] == 'R')
299  setStatus(KMMsgStatusRead);
300  else if (aStatusStr[0] == 'D')
301  setStatus(KMMsgStatusDeleted);
302  else
303  setStatus(KMMsgStatusNew);
304  }
305 }
306 
307 
308 void KMMsgBase::setEncryptionState( const KMMsgEncryptionState /*status*/, int idx )
309 {
310  //kdDebug(5006) << "***setEncryptionState1( " << status << " )" << endl;
311  mDirty = true;
312  if (storage())
313  storage()->headerOfMsgChanged(this, idx);
314 }
315 
316 void KMMsgBase::setEncryptionStateChar( TQChar status, int idx )
317 {
318  //kdDebug(5006) << "***setEncryptionState2( " << (status.isNull() ? '?' : status.latin1()) << " )" << endl;
319 
320  if( status.latin1() == (char)KMMsgEncryptionStateUnknown )
321  setEncryptionState( KMMsgEncryptionStateUnknown, idx );
322  else if( status.latin1() == (char)KMMsgNotEncrypted )
323  setEncryptionState( KMMsgNotEncrypted, idx );
324  else if( status.latin1() == (char)KMMsgPartiallyEncrypted )
325  setEncryptionState( KMMsgPartiallyEncrypted, idx );
326  else if( status.latin1() == (char)KMMsgFullyEncrypted )
327  setEncryptionState( KMMsgFullyEncrypted, idx );
328  else
329  setEncryptionState( KMMsgEncryptionStateUnknown, idx );
330 }
331 
332 
333 void KMMsgBase::setSignatureState( const KMMsgSignatureState /*status*/, int idx )
334 {
335  //kdDebug(5006) << "***setSignatureState1( " << status << " )" << endl;
336  mDirty = true;
337  if (storage())
338  storage()->headerOfMsgChanged(this, idx);
339 }
340 
341 void KMMsgBase::setMDNSentState( KMMsgMDNSentState, int idx ) {
342  mDirty = true;
343  if ( storage() )
344  storage()->headerOfMsgChanged(this, idx);
345 }
346 
347 void KMMsgBase::setSignatureStateChar( TQChar status, int idx )
348 {
349  //kdDebug(5006) << "***setSignatureState2( " << (status.isNull() ? '?' : status.latin1()) << " )" << endl;
350 
351  if( status.latin1() == (char)KMMsgSignatureStateUnknown )
352  setSignatureState( KMMsgSignatureStateUnknown, idx );
353  else if( status.latin1() == (char)KMMsgNotSigned )
354  setSignatureState( KMMsgNotSigned, idx );
355  else if( status.latin1() == (char)KMMsgPartiallySigned )
356  setSignatureState( KMMsgPartiallySigned,idx );
357  else if( status.latin1() == (char)KMMsgFullySigned )
358  setSignatureState( KMMsgFullySigned, idx );
359  else
360  setSignatureState( KMMsgSignatureStateUnknown, idx );
361 }
362 
363 //-----------------------------------------------------------------------------
364 bool KMMsgBase::isUnread(void) const
365 {
366  KMMsgStatus st = status();
367  return (st & KMMsgStatusUnread && !(st & KMMsgStatusIgnored));
368 }
369 
370 //-----------------------------------------------------------------------------
371 bool KMMsgBase::isNew(void) const
372 {
373  KMMsgStatus st = status();
374  return (st & KMMsgStatusNew && !(st & KMMsgStatusIgnored));
375 }
376 
377 //-----------------------------------------------------------------------------
378 bool KMMsgBase::isOfUnknownStatus(void) const
379 {
380  KMMsgStatus st = status();
381  return (st == KMMsgStatusUnknown);
382 }
383 
384 //-----------------------------------------------------------------------------
385 bool KMMsgBase::isOld(void) const
386 {
387  KMMsgStatus st = status();
388  return (st & KMMsgStatusOld);
389 }
390 
391 //-----------------------------------------------------------------------------
392 bool KMMsgBase::isRead(void) const
393 {
394  KMMsgStatus st = status();
395  return (st & KMMsgStatusRead || st & KMMsgStatusIgnored);
396 }
397 
398 //-----------------------------------------------------------------------------
399 bool KMMsgBase::isDeleted(void) const
400 {
401  KMMsgStatus st = status();
402  return (st & KMMsgStatusDeleted);
403 }
404 
405 //-----------------------------------------------------------------------------
406 bool KMMsgBase::isReplied(void) const
407 {
408  KMMsgStatus st = status();
409  return (st & KMMsgStatusReplied);
410 }
411 
412 //-----------------------------------------------------------------------------
413 bool KMMsgBase::isForwarded(void) const
414 {
415  KMMsgStatus st = status();
416  return (st & KMMsgStatusForwarded);
417 }
418 
419 //-----------------------------------------------------------------------------
420 bool KMMsgBase::isQueued(void) const
421 {
422  KMMsgStatus st = status();
423  return (st & KMMsgStatusQueued);
424 }
425 
426 //-----------------------------------------------------------------------------
427 bool KMMsgBase::isTodo(void) const
428 {
429  KMMsgStatus st = status();
430  return (st & KMMsgStatusTodo);
431 }
432 
433 //-----------------------------------------------------------------------------
434 bool KMMsgBase::isSent(void) const
435 {
436  KMMsgStatus st = status();
437  return (st & KMMsgStatusSent);
438 }
439 
440 //-----------------------------------------------------------------------------
441 bool KMMsgBase::isImportant(void) const
442 {
443  KMMsgStatus st = status();
444  return (st & KMMsgStatusFlag);
445 }
446 
447 //-----------------------------------------------------------------------------
448 bool KMMsgBase::isWatched(void) const
449 {
450  KMMsgStatus st = status();
451  return (st & KMMsgStatusWatched);
452 }
453 
454 //-----------------------------------------------------------------------------
455 bool KMMsgBase::isIgnored(void) const
456 {
457  KMMsgStatus st = status();
458  return (st & KMMsgStatusIgnored);
459 }
460 
461 //-----------------------------------------------------------------------------
462 bool KMMsgBase::isSpam(void) const
463 {
464  KMMsgStatus st = status();
465  return (st & KMMsgStatusSpam);
466 }
467 
468 //-----------------------------------------------------------------------------
469 bool KMMsgBase::isHam(void) const
470 {
471  KMMsgStatus st = status();
472  return (st & KMMsgStatusHam);
473 }
474 
475 //-----------------------------------------------------------------------------
476 TQCString KMMsgBase::statusToStr(const KMMsgStatus status)
477 {
478  TQCString sstr;
479  if (status & KMMsgStatusNew) sstr += 'N';
480  if (status & KMMsgStatusUnread) sstr += 'U';
481  if (status & KMMsgStatusOld) sstr += 'O';
482  if (status & KMMsgStatusRead) sstr += 'R';
483  if (status & KMMsgStatusDeleted) sstr += 'D';
484  if (status & KMMsgStatusReplied) sstr += 'A';
485  if (status & KMMsgStatusForwarded) sstr += 'F';
486  if (status & KMMsgStatusQueued) sstr += 'Q';
487  if (status & KMMsgStatusTodo) sstr += 'K';
488  if (status & KMMsgStatusSent) sstr += 'S';
489  if (status & KMMsgStatusFlag) sstr += 'G';
490  if (status & KMMsgStatusWatched) sstr += 'W';
491  if (status & KMMsgStatusIgnored) sstr += 'I';
492  if (status & KMMsgStatusSpam) sstr += 'P';
493  if (status & KMMsgStatusHam) sstr += 'H';
494  if (status & KMMsgStatusHasAttach) sstr += 'T';
495  if (status & KMMsgStatusHasNoAttach) sstr += 'C';
496 
497  return sstr;
498 }
499 
500 //-----------------------------------------------------------------------------
501 TQString KMMsgBase::statusToSortRank()
502 {
503  TQString sstr = "bcbbbbbbbb";
504 
505  // put watched ones first, then normal ones, ignored ones last
506  if (status() & KMMsgStatusWatched) sstr[0] = 'a';
507  if (status() & KMMsgStatusIgnored) sstr[0] = 'c';
508 
509  // Second level. One of new, old, read, unread
510  if (status() & KMMsgStatusNew) sstr[1] = 'a';
511  if (status() & KMMsgStatusUnread) sstr[1] = 'b';
512  //if (status() & KMMsgStatusOld) sstr[1] = 'c';
513  //if (status() & KMMsgStatusRead) sstr[1] = 'c';
514 
515  // Third level. In somewhat arbitrary order.
516  if (status() & KMMsgStatusDeleted) sstr[2] = 'a';
517  if (status() & KMMsgStatusFlag) sstr[3] = 'a';
518  if (status() & KMMsgStatusReplied) sstr[4] = 'a';
519  if (status() & KMMsgStatusForwarded) sstr[5] = 'a';
520  if (status() & KMMsgStatusQueued) sstr[6] = 'a';
521  if (status() & KMMsgStatusSent) sstr[7] = 'a';
522  if (status() & KMMsgStatusHam) sstr[8] = 'a';
523  if (status() & KMMsgStatusSpam) sstr[8] = 'c';
524  if (status() & KMMsgStatusTodo) sstr[9] = 'a';
525 
526  return sstr;
527 }
528 
529 
530 //-----------------------------------------------------------------------------
531 void KMMsgBase::setDate(const TQCString& aDateStr)
532 {
533  setDate( KRFCDate::parseDate( aDateStr ) );
534 }
535 
536 
537 //-----------------------------------------------------------------------------
538 TQString KMMsgBase::dateStr(void) const
539 {
540  time_t d = date();
541  return KMime::DateFormatter::formatDate(KMime::DateFormatter::Fancy, d);
542 }
543 
544 
545 //-----------------------------------------------------------------------------
546 TQString KMMsgBase::skipKeyword(const TQString& aStr, TQChar sepChar,
547  bool* hasKeyword)
548 {
549  unsigned int i = 0, maxChars = 3;
550  TQString str = aStr;
551 
552  while (str[0] == ' ') str.remove(0,1);
553  if (hasKeyword) *hasKeyword=false;
554 
555  unsigned int strLength(str.length());
556  for (i=0; i < strLength && i < maxChars; i++)
557  {
558  if (str[i] < 'A' || str[i] == sepChar) break;
559  }
560 
561  if (str[i] == sepChar) // skip following spaces too
562  {
563  do {
564  i++;
565  } while (str[i] == ' ');
566  if (hasKeyword) *hasKeyword=true;
567  return str.mid(i);
568  }
569  return str;
570 }
571 
572 
573 //-----------------------------------------------------------------------------
574 const TQTextCodec* KMMsgBase::codecForName(const TQCString& _str)
575 {
576  if (_str.isEmpty()) return 0;
577  TQCString codec = _str;
578  kasciitolower(codec.data());
579  return TDEGlobal::charsets()->codecForName(codec);
580 }
581 
582 
583 //-----------------------------------------------------------------------------
584 TQCString KMMsgBase::toUsAscii(const TQString& _str, bool *ok)
585 {
586  bool all_ok =true;
587  TQString result = _str;
588  int len = result.length();
589  for (int i = 0; i < len; i++)
590  if (result.at(i).unicode() >= 128) {
591  result.at(i) = '?';
592  all_ok = false;
593  }
594  if (ok)
595  *ok = all_ok;
596  return result.latin1();
597 }
598 
599 
600 //-----------------------------------------------------------------------------
601 TQStringList KMMsgBase::supportedEncodings(bool usAscii)
602 {
603  TQStringList encodingNames = TDEGlobal::charsets()->availableEncodingNames();
604  TQStringList encodings;
605  TQMap<TQString,bool> mimeNames;
606  for (TQStringList::Iterator it = encodingNames.begin();
607  it != encodingNames.end(); it++)
608  {
609  TQTextCodec *codec = TDEGlobal::charsets()->codecForName(*it);
610  TQString mimeName = (codec) ? TQString(codec->mimeName()).lower() : (*it);
611  if (mimeNames.find(mimeName) == mimeNames.end())
612  {
613  encodings.append(TDEGlobal::charsets()->languageForEncoding(*it)
614  + " ( " + mimeName + " )");
615  mimeNames.insert(mimeName, true);
616  }
617  }
618  encodings.sort();
619  if (usAscii) encodings.prepend(TDEGlobal::charsets()
620  ->languageForEncoding("us-ascii") + " ( us-ascii )");
621  return encodings;
622 }
623 
624 namespace {
625  // don't rely on isblank(), which is a GNU extension in
626  // <cctype>. But if someone wants to write a configure test for
627  // isblank(), we can then rename this function to isblank and #ifdef
628  // it's definition...
629  inline bool isBlank( char ch ) { return ch == ' ' || ch == '\t' ; }
630 
631  TQCString unfold( const TQCString & header ) {
632  if ( header.isEmpty() )
633  return TQCString();
634 
635  TQCString result( header.size() ); // size() >= length()+1 and size() is O(1)
636  char * d = result.data();
637 
638  for ( const char * s = header.data() ; *s ; )
639  if ( *s == '\r' ) { // ignore
640  ++s;
641  continue;
642  } else if ( *s == '\n' ) { // unfold
643  while ( isBlank( *++s ) )
644  ;
645  *d++ = ' ';
646  } else
647  *d++ = *s++;
648 
649  *d++ = '\0';
650 
651  result.truncate( d - result.data() );
652  return result;
653  }
654 }
655 
656 
657 //-----------------------------------------------------------------------------
658 TQString KMMsgBase::decodeRFC2047String(const TQCString& aStr, TQCString prefCharset)
659 {
660  if ( aStr.isEmpty() )
661  return TQString();
662 
663  const TQCString str = unfold( aStr );
664 
665  if ( str.isEmpty() )
666  return TQString();
667 
668  if ( str.find( "=?" ) < 0 ) {
669  if ( !prefCharset.isEmpty() &&
670  kmkernel->isCodecAsciiCompatible( KMMsgBase::codecForName( prefCharset ) ) ) {
671  if ( prefCharset == "us-ascii" ) {
672  // isn`t this foolproof?
673  return KMMsgBase::codecForName( "utf-8" )->toUnicode( str );
674  } else {
675  return KMMsgBase::codecForName( prefCharset )->toUnicode( str );
676  }
677  } else {
678  if ( kmkernel->isCodecAsciiCompatible( KMMsgBase::codecForName(
679  GlobalSettings::self()->fallbackCharacterEncoding().latin1() ) ) ) {
680  return KMMsgBase::codecForName( GlobalSettings::self()->
681  fallbackCharacterEncoding().latin1() )->toUnicode( str );
682  }
683  }
684 
685  // Not RFC2047 encoded, and codec not ascii-compatible -> interpret as ascii
686  return TQString::fromAscii( str );
687  }
688 
689  TQString result;
690  TQCString LWSP_buffer;
691  bool lastWasEncodedWord = false;
692 
693  for ( const char * pos = str.data() ; *pos ; ++pos ) {
694  // collect LWSP after encoded-words,
695  // because we might need to throw it out
696  // (when the next word is an encoded-word)
697  if ( lastWasEncodedWord && isBlank( pos[0] ) ) {
698  LWSP_buffer += pos[0];
699  continue;
700  }
701  // verbatimly copy normal text
702  if (pos[0]!='=' || pos[1]!='?') {
703  result += LWSP_buffer + pos[0];
704  LWSP_buffer = 0;
705  lastWasEncodedWord = false;
706  continue;
707  }
708  // found possible encoded-word
709  const char * const beg = pos;
710  {
711  // parse charset name
712  TQCString charset;
713  int i = 2;
714  pos += 2;
715  for ( ; *pos != '?' && ( *pos==' ' || ispunct(*pos) || isalnum(*pos) );
716  ++i, ++pos ) {
717  charset += *pos;
718  }
719  if ( *pos!='?' || i<4 )
720  goto invalid_encoded_word;
721 
722  // get encoding and check delimiting question marks
723  const char encoding[2] = { pos[1], '\0' };
724  if (pos[2]!='?' || (encoding[0]!='Q' && encoding[0]!='q' &&
725  encoding[0]!='B' && encoding[0]!='b'))
726  goto invalid_encoded_word;
727  pos+=3; i+=3; // skip ?x?
728  const char * enc_start = pos;
729  // search for end of encoded part
730  while ( *pos && !(*pos=='?' && *(pos+1)=='=') ) {
731  i++;
732  pos++;
733  }
734  if ( !*pos )
735  goto invalid_encoded_word;
736 
737  // valid encoding: decode and throw away separating LWSP
738  const KMime::Codec * c = KMime::Codec::codecForName( encoding );
739  kdFatal( !c, 5006 ) << "No \"" << encoding << "\" codec!?" << endl;
740 
741  TQByteArray in; in.setRawData( enc_start, pos - enc_start );
742  const TQByteArray enc = c->decode( in );
743  in.resetRawData( enc_start, pos - enc_start );
744 
745  const TQTextCodec * codec = codecForName(charset);
746  if (!codec) codec = kmkernel->networkCodec();
747  result += codec->toUnicode(enc);
748  lastWasEncodedWord = true;
749 
750  ++pos; // eat '?' (for loop eats '=')
751  LWSP_buffer = 0;
752  }
753  continue;
754  invalid_encoded_word:
755  // invalid encoding, keep separating LWSP.
756  pos = beg;
757  if ( !LWSP_buffer.isNull() )
758  result += LWSP_buffer;
759  result += "=?";
760  lastWasEncodedWord = false;
761  ++pos; // eat '?' (for loop eats '=')
762  LWSP_buffer = 0;
763  }
764  return result;
765 }
766 
767 
768 //-----------------------------------------------------------------------------
769 static const TQCString especials = "()<>@,;:\"/[]?.= \033";
770 
771 TQCString KMMsgBase::encodeRFC2047Quoted( const TQCString & s, bool base64 ) {
772  const char * codecName = base64 ? "b" : "q" ;
773  const KMime::Codec * codec = KMime::Codec::codecForName( codecName );
774  kdFatal( !codec, 5006 ) << "No \"" << codecName << "\" found!?" << endl;
775  TQByteArray in; in.setRawData( s.data(), s.length() );
776  const TQByteArray result = codec->encode( in );
777  in.resetRawData( s.data(), s.length() );
778  return TQCString( result.data(), result.size() + 1 );
779 }
780 
781 TQCString KMMsgBase::encodeRFC2047String(const TQString& _str,
782  const TQCString& charset)
783 {
784  static const TQString dontQuote = "\"()<>,@";
785 
786  if (_str.isEmpty()) return TQCString();
787  if (charset == "us-ascii") return toUsAscii(_str);
788 
789  TQCString cset;
790  if (charset.isEmpty())
791  {
792  cset = kmkernel->networkCodec()->mimeName();
793  kasciitolower(cset.data());
794  }
795  else cset = charset;
796 
797  const TQTextCodec *codec = codecForName(cset);
798  if (!codec) codec = kmkernel->networkCodec();
799 
800  unsigned int nonAscii = 0;
801  unsigned int strLength(_str.length());
802  for (unsigned int i = 0; i < strLength; i++)
803  if (_str.at(i).unicode() >= 128) nonAscii++;
804  bool useBase64 = (nonAscii * 6 > strLength);
805 
806  unsigned int start, stop, p, pos = 0, encLength;
807  TQCString result;
808  bool breakLine = false;
809  const unsigned int maxLen = 75 - 7 - cset.length();
810 
811  while (pos < strLength)
812  {
813  start = pos; p = pos;
814  while (p < strLength)
815  {
816  if (!breakLine && (_str.at(p) == ' ' || dontQuote.find(_str.at(p)) != -1))
817  start = p + 1;
818  if (_str.at(p).unicode() >= 128 || _str.at(p).unicode() < 32)
819  break;
820  p++;
821  }
822  if (breakLine || p < strLength)
823  {
824  while (dontQuote.find(_str.at(start)) != -1) start++;
825  stop = start;
826  while (stop < strLength && dontQuote.find(_str.at(stop)) == -1)
827  stop++;
828  result += _str.mid(pos, start - pos).latin1();
829  encLength = encodeRFC2047Quoted(codec->fromUnicode(_str.
830  mid(start, stop - start)), useBase64).length();
831  breakLine = (encLength > maxLen);
832  if (breakLine)
833  {
834  int dif = (stop - start) / 2;
835  int step = dif;
836  while (abs(step) > 1)
837  {
838  encLength = encodeRFC2047Quoted(codec->fromUnicode(_str.
839  mid(start, dif)), useBase64).length();
840  step = (encLength > maxLen) ? (-abs(step) / 2) : (abs(step) / 2);
841  dif += step;
842  }
843  stop = start + dif;
844  }
845  p = stop;
846  while (p > start && _str.at(p) != ' ') p--;
847  if (p > start) stop = p;
848  if (result.right(3) == "?= ") start--;
849  if (result.right(5) == "?=\n ") {
850  start--; result.truncate(result.length() - 1);
851  }
852  int lastNewLine = result.findRev("\n ");
853  if (!result.mid(lastNewLine).stripWhiteSpace().isEmpty()
854  && result.length() - lastNewLine + encLength + 2 > maxLen)
855  result += "\n ";
856  result += "=?";
857  result += cset;
858  result += (useBase64) ? "?b?" : "?q?";
859  result += encodeRFC2047Quoted(codec->fromUnicode(_str.mid(start,
860  stop - start)), useBase64);
861  result += "?=";
862  if (breakLine) result += "\n ";
863  pos = stop;
864  } else {
865  result += _str.mid(pos).latin1();
866  break;
867  }
868  }
869  return result;
870 }
871 
872 
873 //-----------------------------------------------------------------------------
874 TQCString KMMsgBase::encodeRFC2231String( const TQString& _str,
875  const TQCString& charset )
876 {
877  if ( _str.isEmpty() )
878  return TQCString();
879 
880  TQCString cset;
881  if ( charset.isEmpty() )
882  {
883  cset = kmkernel->networkCodec()->mimeName();
884  kasciitolower( cset.data() );
885  }
886  else
887  cset = charset;
888  const TQTextCodec *codec = codecForName( cset );
889  TQCString latin;
890  if ( charset == "us-ascii" )
891  latin = toUsAscii( _str );
892  else if ( codec )
893  latin = codec->fromUnicode( _str );
894  else
895  latin = _str.local8Bit();
896 
897  char *l;
898  for ( l = latin.data(); *l; ++l ) {
899  if ( ( ( *l & 0xE0 ) == 0 ) || ( *l & 0x80 ) )
900  // *l is control character or 8-bit char
901  break;
902  }
903  if ( !*l )
904  return latin;
905 
906  TQCString result = cset + "''";
907  for ( l = latin.data(); *l; ++l ) {
908  bool needsQuoting = ( *l & 0x80 );
909  if( !needsQuoting ) {
910  int len = especials.length();
911  for ( int i = 0; i < len; i++ )
912  if ( *l == especials[i] ) {
913  needsQuoting = true;
914  break;
915  }
916  }
917  if ( needsQuoting ) {
918  result += '%';
919  unsigned char hexcode;
920  hexcode = ( ( *l & 0xF0 ) >> 4 ) + 48;
921  if ( hexcode >= 58 )
922  hexcode += 7;
923  result += hexcode;
924  hexcode = ( *l & 0x0F ) + 48;
925  if ( hexcode >= 58 )
926  hexcode += 7;
927  result += hexcode;
928  } else {
929  result += *l;
930  }
931  }
932  return result;
933 }
934 
935 //-----------------------------------------------------------------------------
936 TQCString KMMsgBase::encodeRFC2231StringAutoDetectCharset( const TQString &str,
937  const TQCString &defaultCharset )
938 {
939  TQCString encoding = KMMsgBase::autoDetectCharset( defaultCharset,
941  if ( encoding.isEmpty() )
942  encoding = "utf-8";
943  return KMMsgBase::encodeRFC2231String( str, encoding );
944 }
945 
946 //-----------------------------------------------------------------------------
947 TQString KMMsgBase::decodeRFC2231String(const TQCString& _str)
948 {
949  int p = _str.find('\'');
950  if (p < 0) return kmkernel->networkCodec()->toUnicode(_str);
951 
952  TQCString charset = _str.left(p);
953 
954  TQCString st = _str.mid(_str.findRev('\'') + 1);
955  char ch, ch2;
956  p = 0;
957  while (p < (int)st.length())
958  {
959  if (st.at(p) == 37)
960  {
961  ch = st.at(p+1) - 48;
962  if (ch > 16) ch -= 7;
963  ch2 = st.at(p+2) - 48;
964  if (ch2 > 16) ch2 -= 7;
965  st.at(p) = ch * 16 + ch2;
966  st.remove( p+1, 2 );
967  }
968  p++;
969  }
970  TQString result;
971  const TQTextCodec * codec = codecForName( charset );
972  if ( !codec )
973  codec = kmkernel->networkCodec();
974  return codec->toUnicode( st );
975 }
976 
977 TQCString KMMsgBase::extractRFC2231HeaderField( const TQCString &aStr, const TQCString &field )
978 {
979  int n=-1;
980  TQCString str;
981  bool found = false;
982  while ( n<=0 || found ) {
983  TQString pattern( field );
984  pattern += "[*]"; // match a literal * after the fieldname, as defined by RFC 2231
985  if ( n>=0 ) { // If n<0, check for fieldname*=..., otherwise for fieldname*n=
986  pattern += TQString::number(n) + "[*]?";
987  }
988  pattern += "=";
989 
990  TQRegExp fnamePart( pattern, false );
991  int startPart = fnamePart.search( aStr );
992  int endPart;
993  found = ( startPart >= 0 );
994  if ( found ) {
995  startPart += fnamePart.matchedLength();
996  // Quoted values end at the ending quote
997  if ( aStr[startPart] == '"' ) {
998  startPart++; // the double quote isn't part of the filename
999  endPart = aStr.find('"', startPart) - 1;
1000  }
1001  else {
1002  endPart = aStr.find(';', startPart) - 1;
1003  }
1004  if (endPart < 0)
1005  endPart = 32767;
1006  str += aStr.mid( startPart, endPart-startPart+1).stripWhiteSpace();
1007  }
1008  n++;
1009  }
1010  return str;
1011 }
1012 
1013 TQString KMMsgBase::base64EncodedMD5( const TQString & s, bool utf8 ) {
1014  if (s.stripWhiteSpace().isEmpty()) return "";
1015  if ( utf8 )
1016  return base64EncodedMD5( s.stripWhiteSpace().utf8() ); // TQCString overload
1017  else
1018  return base64EncodedMD5( s.stripWhiteSpace().latin1() ); // const char * overload
1019 }
1020 
1021 TQString KMMsgBase::base64EncodedMD5( const TQCString & s ) {
1022  if (s.stripWhiteSpace().isEmpty()) return "";
1023  return base64EncodedMD5( s.stripWhiteSpace().data() );
1024 }
1025 
1026 TQString KMMsgBase::base64EncodedMD5( const char * s, int len ) {
1027  if (!s || !len) return "";
1028  static const int Base64EncodedMD5Len = 22;
1029  KMD5 md5( s, len );
1030  return md5.base64Digest().left( Base64EncodedMD5Len );
1031 }
1032 
1033 
1034 //-----------------------------------------------------------------------------
1035 TQCString KMMsgBase::autoDetectCharset(const TQCString &_encoding, const TQStringList &encodingList, const TQString &text)
1036 {
1037  TQStringList charsets = encodingList;
1038  if (!_encoding.isEmpty())
1039  {
1040  TQString currentCharset = TQString::fromLatin1(_encoding);
1041  charsets.remove(currentCharset);
1042  charsets.prepend(currentCharset);
1043  }
1044 
1045  TQStringList::ConstIterator it = charsets.begin();
1046  for (; it != charsets.end(); ++it)
1047  {
1048  TQCString encoding = (*it).latin1();
1049  if (encoding == "locale")
1050  {
1051  encoding = kmkernel->networkCodec()->mimeName();
1052  kasciitolower(encoding.data());
1053  }
1054  if (text.isEmpty())
1055  return encoding;
1056  if (encoding == "us-ascii") {
1057  bool ok;
1058  (void) KMMsgBase::toUsAscii(text, &ok);
1059  if (ok)
1060  return encoding;
1061  }
1062  else
1063  {
1064  const TQTextCodec *codec = KMMsgBase::codecForName(encoding);
1065  if (!codec) {
1066  kdDebug(5006) << "Auto-Charset: Something is wrong and I can not get a codec. [" << encoding << "]" << endl;
1067  } else {
1068  if (codec->canEncode(text))
1069  return encoding;
1070  }
1071  }
1072  }
1073  return 0;
1074 }
1075 
1076 
1077 //-----------------------------------------------------------------------------
1078 unsigned long KMMsgBase::getMsgSerNum() const
1079 {
1080  unsigned long msn = MessageProperty::serialCache( this );
1081  if (msn)
1082  return msn;
1083  if (mParent) {
1084  int index = mParent->find((KMMsgBase*)this);
1085  msn = KMMsgDict::instance()->getMsgSerNum(mParent, index);
1086  if (msn)
1087  MessageProperty::setSerialCache( this, msn );
1088  }
1089  return msn;
1090 }
1091 
1092 
1093 //-----------------------------------------------------------------------------
1094 KMMsgAttachmentState KMMsgBase::attachmentState() const
1095 {
1096  KMMsgStatus st = status();
1097  if (st & KMMsgStatusHasAttach)
1098  return KMMsgHasAttachment;
1099  else if (st & KMMsgStatusHasNoAttach)
1100  return KMMsgHasNoAttachment;
1101  else
1102  return KMMsgAttachmentUnknown;
1103 }
1104 
1105 
1106 KMMsgInvitationState KMMsgBase::invitationState() const
1107 {
1108  KMMsgStatus st = status();
1109  if (st & KMMsgStatusHasInvitation)
1110  return KMMsgHasInvitation;
1111  else if (st & KMMsgStatusHasNoInvitation)
1112  return KMMsgHasNoInvitation;
1113  else
1114  return KMMsgInvitationUnknown;
1115 }
1116 
1117 //-----------------------------------------------------------------------------
1118 static void swapEndian(TQString &str)
1119 {
1120  uint len = str.length();
1121  str = TQDeepCopy<TQString>(str);
1122  TQChar *unicode = const_cast<TQChar*>( str.unicode() );
1123  for (uint i = 0; i < len; i++)
1124  unicode[i] = kmail_swap_16(unicode[i].unicode());
1125 }
1126 
1127 //-----------------------------------------------------------------------------
1128 static int g_chunk_length = 0, g_chunk_offset=0;
1129 static uchar *g_chunk = 0;
1130 
1131 namespace {
1132  template < typename T > void copy_from_stream( T & x ) {
1133  if( g_chunk_offset + int(sizeof(T)) > g_chunk_length ) {
1134  g_chunk_offset = g_chunk_length;
1135  kdDebug( 5006 ) << "This should never happen.. "
1136  << __FILE__ << ":" << __LINE__ << endl;
1137  x = 0;
1138  } else {
1139  // the memcpy is optimized out by the compiler for the values
1140  // of sizeof(T) that is called with
1141  memcpy( &x, g_chunk + g_chunk_offset, sizeof(T) );
1142  g_chunk_offset += sizeof(T);
1143  }
1144  }
1145 }
1146 
1147 //-----------------------------------------------------------------------------
1148 TQString KMMsgBase::getStringPart(MsgPartType t) const
1149 {
1150 retry:
1151  TQString ret;
1152 
1153  g_chunk_offset = 0;
1154  bool using_mmap = false;
1155  bool swapByteOrder = storage()->indexSwapByteOrder();
1156  if (storage()->indexStreamBasePtr()) {
1157  if (g_chunk)
1158  free(g_chunk);
1159  using_mmap = true;
1160  g_chunk = storage()->indexStreamBasePtr() + mIndexOffset;
1161  g_chunk_length = mIndexLength;
1162  } else {
1163  if(!storage()->mIndexStream)
1164  return ret;
1165  if (g_chunk_length < mIndexLength)
1166  g_chunk = (uchar *)realloc(g_chunk, g_chunk_length = mIndexLength);
1167  off_t first_off=ftell(storage()->mIndexStream);
1168  fseek(storage()->mIndexStream, mIndexOffset, SEEK_SET);
1169  fread( g_chunk, mIndexLength, 1, storage()->mIndexStream);
1170  fseek(storage()->mIndexStream, first_off, SEEK_SET);
1171  }
1172 
1173  MsgPartType type;
1174  TQ_UINT16 l;
1175  while(g_chunk_offset < mIndexLength) {
1176  TQ_UINT32 tmp;
1177  copy_from_stream(tmp);
1178  copy_from_stream(l);
1179  if (swapByteOrder)
1180  {
1181  tmp = kmail_swap_32(tmp);
1182  l = kmail_swap_16(l);
1183  }
1184  type = (MsgPartType) tmp;
1185  if(g_chunk_offset + l > mIndexLength) {
1186  kdDebug(5006) << "This should never happen.. " << __FILE__ << ":" << __LINE__ << endl;
1187  if(using_mmap) {
1188  g_chunk_length = 0;
1189  g_chunk = 0;
1190  }
1191  storage()->recreateIndex();
1192  goto retry;
1193  }
1194  if(type == t) {
1195  // This works because the TQString constructor does a memcpy.
1196  // Otherwise we would need to be concerned about the alignment.
1197  if(l)
1198  ret = TQString((TQChar *)(g_chunk + g_chunk_offset), l/2);
1199  break;
1200  }
1201  g_chunk_offset += l;
1202  }
1203  if(using_mmap) {
1204  g_chunk_length = 0;
1205  g_chunk = 0;
1206  }
1207  // Normally we need to swap the byte order because the TQStrings are written
1208  // in the style of TQt2 (MSB -> network ordered).
1209  // TQStrings in TQt3 expect host ordering.
1210  // On e.g. Intel host ordering is LSB, on e.g. Sparc it is MSB.
1211 
1212 #ifndef WORDS_BIGENDIAN
1213  // #warning Byte order is little endian (swap is true)
1214  swapEndian(ret);
1215 #else
1216  // #warning Byte order is big endian (swap is false)
1217 #endif
1218 
1219  return ret;
1220 }
1221 
1222 //-----------------------------------------------------------------------------
1223 off_t KMMsgBase::getLongPart(MsgPartType t) const
1224 {
1225 retry:
1226  off_t ret = 0;
1227 
1228  g_chunk_offset = 0;
1229  bool using_mmap = false;
1230  int sizeOfLong = storage()->indexSizeOfLong();
1231  bool swapByteOrder = storage()->indexSwapByteOrder();
1232  if (storage()->indexStreamBasePtr()) {
1233  if (g_chunk)
1234  free(g_chunk);
1235  using_mmap = true;
1236  g_chunk = storage()->indexStreamBasePtr() + mIndexOffset;
1237  g_chunk_length = mIndexLength;
1238  } else {
1239  if (!storage()->mIndexStream)
1240  return ret;
1241  assert(mIndexLength >= 0);
1242  if (g_chunk_length < mIndexLength)
1243  g_chunk = (uchar *)realloc(g_chunk, g_chunk_length = mIndexLength);
1244  off_t first_off=ftell(storage()->mIndexStream);
1245  fseek(storage()->mIndexStream, mIndexOffset, SEEK_SET);
1246  fread( g_chunk, mIndexLength, 1, storage()->mIndexStream);
1247  fseek(storage()->mIndexStream, first_off, SEEK_SET);
1248  }
1249 
1250  MsgPartType type;
1251  TQ_UINT16 l;
1252  while (g_chunk_offset < mIndexLength) {
1253  TQ_UINT32 tmp;
1254  copy_from_stream(tmp);
1255  copy_from_stream(l);
1256  if (swapByteOrder)
1257  {
1258  tmp = kmail_swap_32(tmp);
1259  l = kmail_swap_16(l);
1260  }
1261  type = (MsgPartType) tmp;
1262 
1263  if (g_chunk_offset + l > mIndexLength) {
1264  kdDebug(5006) << "This should never happen.. " << __FILE__ << ":" << __LINE__ << endl;
1265  if(using_mmap) {
1266  g_chunk_length = 0;
1267  g_chunk = 0;
1268  }
1269  storage()->recreateIndex();
1270  goto retry;
1271  }
1272  if(type == t) {
1273  assert(sizeOfLong == l);
1274  if (sizeOfLong == sizeof(ret))
1275  {
1276  copy_from_stream(ret);
1277  if (swapByteOrder)
1278  {
1279  if (sizeof(ret) == 4)
1280  ret = kmail_swap_32(ret);
1281  else
1282  ret = kmail_swap_64(ret);
1283  }
1284  }
1285  else if (sizeOfLong == 4)
1286  {
1287  // Long is stored as 4 bytes in index file, sizeof(long) = 8
1288  TQ_UINT32 ret_32;
1289  copy_from_stream(ret_32);
1290  if (swapByteOrder)
1291  ret_32 = kmail_swap_32(ret_32);
1292  ret = ret_32;
1293  }
1294  else if (sizeOfLong == 8)
1295  {
1296  // Long is stored as 8 bytes in index file, sizeof(long) = 4
1297  TQ_UINT32 ret_1;
1298  TQ_UINT32 ret_2;
1299  copy_from_stream(ret_1);
1300  copy_from_stream(ret_2);
1301  if (!swapByteOrder)
1302  {
1303  // Index file order is the same as the order of this CPU.
1304 #ifndef WORDS_BIGENDIAN
1305  // Index file order is little endian
1306  ret = ret_1; // We drop the 4 most significant bytes
1307 #else
1308  // Index file order is big endian
1309  ret = ret_2; // We drop the 4 most significant bytes
1310 #endif
1311  }
1312  else
1313  {
1314  // Index file order is different from this CPU.
1315 #ifndef WORDS_BIGENDIAN
1316  // Index file order is big endian
1317  ret = ret_2; // We drop the 4 most significant bytes
1318 #else
1319  // Index file order is little endian
1320  ret = ret_1; // We drop the 4 most significant bytes
1321 #endif
1322  // We swap the result to host order.
1323  ret = kmail_swap_32(ret);
1324  }
1325 
1326  }
1327  break;
1328  }
1329  g_chunk_offset += l;
1330  }
1331  if(using_mmap) {
1332  g_chunk_length = 0;
1333  g_chunk = 0;
1334  }
1335  return ret;
1336 }
1337 
1338 #ifndef WORDS_BIGENDIAN
1339 // We need to use swab to swap bytes to network byte order
1340 #define memcpy_networkorder(to, from, len) swab((char *)(from), (char *)(to), len)
1341 #else
1342 // We're already in network byte order
1343 #define memcpy_networkorder(to, from, len) memcpy(to, from, len)
1344 #endif
1345 
1346 #define STORE_DATA_LEN(type, x, len, network_order) do { \
1347  int len2 = (len > 256) ? 256 : len; \
1348  if(csize < (length + (len2 + sizeof(short) + sizeof(MsgPartType)))) \
1349  ret = (uchar *)realloc(ret, csize += len2+sizeof(short)+sizeof(MsgPartType)); \
1350  TQ_UINT32 t = (TQ_UINT32) type; memcpy(ret+length, &t, sizeof(t)); \
1351  TQ_UINT16 l = len2; memcpy(ret+length+sizeof(t), &l, sizeof(l)); \
1352  if (network_order) \
1353  memcpy_networkorder(ret+length+sizeof(t)+sizeof(l), x, len2); \
1354  else \
1355  memcpy(ret+length+sizeof(t)+sizeof(l), x, len2); \
1356  length += len2+sizeof(t)+sizeof(l); \
1357  } while(0)
1358 #define STORE_DATA(type, x) STORE_DATA_LEN(type, &x, sizeof(x), false)
1359 
1360 //-----------------------------------------------------------------------------
1361 const uchar *KMMsgBase::asIndexString(int &length) const
1362 {
1363  unsigned int csize = 256;
1364  static uchar *ret = 0; //different static buffer here for we may use the other buffer in the functions below
1365  if(!ret)
1366  ret = (uchar *)malloc(csize);
1367  length = 0;
1368 
1369  unsigned long tmp;
1370  TQString tmp_str;
1371 
1372  //these is at the beginning because it is queried quite often
1373  tmp_str = msgIdMD5().stripWhiteSpace();
1374  STORE_DATA_LEN(MsgIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
1375  tmp = mLegacyStatus;
1376  STORE_DATA(MsgLegacyStatusPart, tmp);
1377 
1378  //these are completely arbitrary order
1379  tmp_str = fromStrip().stripWhiteSpace();
1380  STORE_DATA_LEN(MsgFromStripPart, tmp_str.unicode(), tmp_str.length() * 2, true);
1381  tmp_str = subject().stripWhiteSpace();
1382  STORE_DATA_LEN(MsgSubjectPart, tmp_str.unicode(), tmp_str.length() * 2, true);
1383  tmp_str = toStrip().stripWhiteSpace();
1384  STORE_DATA_LEN(MsgToStripPart, tmp_str.unicode(), tmp_str.length() * 2, true);
1385  tmp_str = replyToIdMD5().stripWhiteSpace();
1386  STORE_DATA_LEN(MsgReplyToIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
1387  tmp_str = xmark().stripWhiteSpace();
1388  STORE_DATA_LEN(MsgXMarkPart, tmp_str.unicode(), tmp_str.length() * 2, true);
1389  tmp_str = fileName().stripWhiteSpace();
1390  STORE_DATA_LEN(MsgFilePart, tmp_str.unicode(), tmp_str.length() * 2, true);
1391  tmp = msgSize();
1392  STORE_DATA(MsgSizePart, tmp);
1393  tmp = folderOffset();
1394  STORE_DATA(MsgOffsetPart, tmp);
1395  tmp = date();
1396  STORE_DATA(MsgDatePart, tmp);
1397  tmp = (signatureState() << 16) | encryptionState();
1398  STORE_DATA(MsgCryptoStatePart, tmp);
1399  tmp = mdnSentState();
1400  STORE_DATA(MsgMDNSentPart, tmp);
1401 
1402  tmp_str = replyToAuxIdMD5().stripWhiteSpace();
1403  STORE_DATA_LEN(MsgReplyToAuxIdMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
1404 
1405  tmp_str = strippedSubjectMD5().stripWhiteSpace();
1406  STORE_DATA_LEN(MsgStrippedSubjectMD5Part, tmp_str.unicode(), tmp_str.length() * 2, true);
1407 
1408  tmp = status();
1409  STORE_DATA(MsgStatusPart, tmp);
1410 
1411  tmp = msgSizeServer();
1412  STORE_DATA(MsgSizeServerPart, tmp);
1413  tmp = UID();
1414  STORE_DATA(MsgUIDPart, tmp);
1415 
1416  tmp_str = from();
1417  STORE_DATA_LEN( MsgFromPart, tmp_str.unicode(), tmp_str.length() * 2, true );
1418 
1419  tmp_str = to();
1420  STORE_DATA_LEN( MsgToPart, tmp_str.unicode(), tmp_str.length() * 2, true );
1421 
1422  return ret;
1423 }
1424 #undef STORE_DATA_LEN
1425 #undef STORE_DATA
1426 
1427 bool KMMsgBase::syncIndexString() const
1428 {
1429  if(!dirty())
1430  return true;
1431  int len;
1432  const uchar *buffer = asIndexString(len);
1433  if (len == mIndexLength) {
1434  Q_ASSERT(storage()->mIndexStream);
1435  fseek(storage()->mIndexStream, mIndexOffset, SEEK_SET);
1436  assert( mIndexOffset > 0 );
1437  fwrite( buffer, len, 1, storage()->mIndexStream);
1438  return true;
1439  }
1440  return false;
1441 }
1442 
1443 static TQStringList sReplySubjPrefixes, sForwardSubjPrefixes;
1444 static bool sReplaceSubjPrefix, sReplaceForwSubjPrefix;
1445 
1446 //-----------------------------------------------------------------------------
1447 void KMMsgBase::readConfig()
1448 {
1449  TDEConfigGroup composerGroup( KMKernel::config(), "Composer" );
1450  sReplySubjPrefixes = composerGroup.readListEntry("reply-prefixes", ',');
1451  if (sReplySubjPrefixes.isEmpty())
1452  sReplySubjPrefixes << "Re\\s*:" << "Re\\[\\d+\\]:" << "Re\\d+:";
1453  sReplaceSubjPrefix = composerGroup.readBoolEntry("replace-reply-prefix", true);
1454  sForwardSubjPrefixes = composerGroup.readListEntry("forward-prefixes", ',');
1455  if (sForwardSubjPrefixes.isEmpty())
1456  sForwardSubjPrefixes << "Fwd:" << "FW:";
1457  sReplaceForwSubjPrefix = composerGroup.readBoolEntry("replace-forward-prefix", true);
1458 }
1459 
1460 //-----------------------------------------------------------------------------
1461 // static
1462 TQString KMMsgBase::stripOffPrefixes( const TQString& str )
1463 {
1464  return replacePrefixes( str, sReplySubjPrefixes + sForwardSubjPrefixes,
1465  true, TQString() ).stripWhiteSpace();
1466 }
1467 
1468 //-----------------------------------------------------------------------------
1469 // static
1470 TQString KMMsgBase::replacePrefixes( const TQString& str,
1471  const TQStringList& prefixRegExps,
1472  bool replace,
1473  const TQString& newPrefix )
1474 {
1475  bool recognized = false;
1476  // construct a big regexp that
1477  // 1. is anchored to the beginning of str (sans whitespace)
1478  // 2. matches at least one of the part regexps in prefixRegExps
1479  TQString bigRegExp = TQString::fromLatin1("^(?:\\s+|(?:%1))+\\s*")
1480  .arg( prefixRegExps.join(")|(?:") );
1481  TQRegExp rx( bigRegExp, false /*case insens.*/ );
1482  if ( !rx.isValid() ) {
1483  kdWarning(5006) << "KMMessage::replacePrefixes(): bigRegExp = \""
1484  << bigRegExp << "\"\n"
1485  << "prefix regexp is invalid!" << endl;
1486  // try good ole Re/Fwd:
1487  recognized = str.startsWith( newPrefix );
1488  } else { // valid rx
1489  TQString tmp = str;
1490  if ( rx.search( tmp ) == 0 ) {
1491  recognized = true;
1492  if ( replace )
1493  return tmp.replace( 0, rx.matchedLength(), newPrefix + ' ' );
1494  }
1495  }
1496  if ( !recognized )
1497  return newPrefix + ' ' + str;
1498  else
1499  return str;
1500 }
1501 
1502 //-----------------------------------------------------------------------------
1503 TQString KMMsgBase::cleanSubject() const
1504 {
1505  return cleanSubject( sReplySubjPrefixes + sForwardSubjPrefixes,
1506  true, TQString() ).stripWhiteSpace();
1507 }
1508 
1509 //-----------------------------------------------------------------------------
1510 TQString KMMsgBase::cleanSubject( const TQStringList & prefixRegExps,
1511  bool replace,
1512  const TQString & newPrefix ) const
1513 {
1514  return KMMsgBase::replacePrefixes( subject(), prefixRegExps, replace,
1515  newPrefix );
1516 }
1517 
1518 //-----------------------------------------------------------------------------
1519 TQString KMMsgBase::forwardSubject() const {
1520  return cleanSubject( sForwardSubjPrefixes, sReplaceForwSubjPrefix, "Fwd:" );
1521 }
1522 
1523 //-----------------------------------------------------------------------------
1524 TQString KMMsgBase::replySubject() const {
1525  return cleanSubject( sReplySubjPrefixes, sReplaceSubjPrefix, "Re:" );
1526 }
bool mDirty
if the index is dirty it will be recreated upon close()
A FolderStorage with an index for faster access to often used message properties.
Definition: kmfolderindex.h:38
Mail folder.
Definition: kmfolder.h:69
static const TQStringList & preferredCharsets()
Get a list of preferred message charsets.
Definition: kmmessage.cpp:4092
unsigned long getMsgSerNum(KMFolder *folder, int index) const
Find the message serial number for the message located at index index in folder folder.
Definition: kmmsgdict.cpp:345
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167