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"
13using 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//-----------------------------------------------------------------------------
73KMMsgBase::KMMsgBase(KMFolder* aParentFolder)
74 : mParent( aParentFolder ), mIndexOffset( 0 ),
75 mIndexLength( 0 ), mDirty( false ), mEnableUndo( false ), mStatus( KMMsgStatusUnknown )
76{
77}
78
79
80//-----------------------------------------------------------------------------
81KMMsgBase::~KMMsgBase()
82{
83 MessageProperty::forget( this );
84}
85
86KMFolderIndex* 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//-----------------------------------------------------------------------------
96void 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//-----------------------------------------------------------------------------
105KMMsgBase& KMMsgBase::operator=(const KMMsgBase& other)
106{
107 assign(&other);
108 return *this;
109}
110
111
112//----------------------------------------------------------------------------
113KMMsgBase::KMMsgBase( const KMMsgBase& other )
114{
115 assign( &other );
116}
117
118//-----------------------------------------------------------------------------
119bool KMMsgBase::isMessage(void) const
120{
121 return false;
122}
123//-----------------------------------------------------------------------------
124void 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//-----------------------------------------------------------------------------
153void 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//-----------------------------------------------------------------------------
270void 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
308void 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
316void 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
333void 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
341void KMMsgBase::setMDNSentState( KMMsgMDNSentState, int idx ) {
342 mDirty = true;
343 if ( storage() )
344 storage()->headerOfMsgChanged(this, idx);
345}
346
347void 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//-----------------------------------------------------------------------------
364bool KMMsgBase::isUnread(void) const
365{
366 KMMsgStatus st = status();
367 return (st & KMMsgStatusUnread && !(st & KMMsgStatusIgnored));
368}
369
370//-----------------------------------------------------------------------------
371bool KMMsgBase::isNew(void) const
372{
373 KMMsgStatus st = status();
374 return (st & KMMsgStatusNew && !(st & KMMsgStatusIgnored));
375}
376
377//-----------------------------------------------------------------------------
378bool KMMsgBase::isOfUnknownStatus(void) const
379{
380 KMMsgStatus st = status();
381 return (st == KMMsgStatusUnknown);
382}
383
384//-----------------------------------------------------------------------------
385bool KMMsgBase::isOld(void) const
386{
387 KMMsgStatus st = status();
388 return (st & KMMsgStatusOld);
389}
390
391//-----------------------------------------------------------------------------
392bool KMMsgBase::isRead(void) const
393{
394 KMMsgStatus st = status();
395 return (st & KMMsgStatusRead || st & KMMsgStatusIgnored);
396}
397
398//-----------------------------------------------------------------------------
399bool KMMsgBase::isDeleted(void) const
400{
401 KMMsgStatus st = status();
402 return (st & KMMsgStatusDeleted);
403}
404
405//-----------------------------------------------------------------------------
406bool KMMsgBase::isReplied(void) const
407{
408 KMMsgStatus st = status();
409 return (st & KMMsgStatusReplied);
410}
411
412//-----------------------------------------------------------------------------
413bool KMMsgBase::isForwarded(void) const
414{
415 KMMsgStatus st = status();
416 return (st & KMMsgStatusForwarded);
417}
418
419//-----------------------------------------------------------------------------
420bool KMMsgBase::isQueued(void) const
421{
422 KMMsgStatus st = status();
423 return (st & KMMsgStatusQueued);
424}
425
426//-----------------------------------------------------------------------------
427bool KMMsgBase::isTodo(void) const
428{
429 KMMsgStatus st = status();
430 return (st & KMMsgStatusTodo);
431}
432
433//-----------------------------------------------------------------------------
434bool KMMsgBase::isSent(void) const
435{
436 KMMsgStatus st = status();
437 return (st & KMMsgStatusSent);
438}
439
440//-----------------------------------------------------------------------------
441bool KMMsgBase::isImportant(void) const
442{
443 KMMsgStatus st = status();
444 return (st & KMMsgStatusFlag);
445}
446
447//-----------------------------------------------------------------------------
448bool KMMsgBase::isWatched(void) const
449{
450 KMMsgStatus st = status();
451 return (st & KMMsgStatusWatched);
452}
453
454//-----------------------------------------------------------------------------
455bool KMMsgBase::isIgnored(void) const
456{
457 KMMsgStatus st = status();
458 return (st & KMMsgStatusIgnored);
459}
460
461//-----------------------------------------------------------------------------
462bool KMMsgBase::isSpam(void) const
463{
464 KMMsgStatus st = status();
465 return (st & KMMsgStatusSpam);
466}
467
468//-----------------------------------------------------------------------------
469bool KMMsgBase::isHam(void) const
470{
471 KMMsgStatus st = status();
472 return (st & KMMsgStatusHam);
473}
474
475//-----------------------------------------------------------------------------
476TQCString 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//-----------------------------------------------------------------------------
501TQString 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//-----------------------------------------------------------------------------
531void KMMsgBase::setDate(const TQCString& aDateStr)
532{
533 setDate( KRFCDate::parseDate( aDateStr ) );
534}
535
536
537//-----------------------------------------------------------------------------
538TQString KMMsgBase::dateStr(void) const
539{
540 time_t d = date();
541 return KMime::DateFormatter::formatDate(KMime::DateFormatter::Fancy, d);
542}
543
544
545//-----------------------------------------------------------------------------
546TQString 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//-----------------------------------------------------------------------------
574const 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//-----------------------------------------------------------------------------
584TQCString 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//-----------------------------------------------------------------------------
601TQStringList 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
624namespace {
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//-----------------------------------------------------------------------------
658TQString 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//-----------------------------------------------------------------------------
769static const TQCString especials = "()<>@,;:\"/[]?.= \033";
770
771TQCString 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
781TQCString 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//-----------------------------------------------------------------------------
874TQCString 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//-----------------------------------------------------------------------------
936TQCString 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//-----------------------------------------------------------------------------
947TQString 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
977TQCString 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
1013TQString 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
1021TQString KMMsgBase::base64EncodedMD5( const TQCString & s ) {
1022 if (s.stripWhiteSpace().isEmpty()) return "";
1023 return base64EncodedMD5( s.stripWhiteSpace().data() );
1024}
1025
1026TQString 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//-----------------------------------------------------------------------------
1035TQCString 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//-----------------------------------------------------------------------------
1078unsigned 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//-----------------------------------------------------------------------------
1094KMMsgAttachmentState 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
1106KMMsgInvitationState 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//-----------------------------------------------------------------------------
1118static 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//-----------------------------------------------------------------------------
1128static int g_chunk_length = 0, g_chunk_offset=0;
1129static uchar *g_chunk = 0;
1130
1131namespace {
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//-----------------------------------------------------------------------------
1148TQString KMMsgBase::getStringPart(MsgPartType t) const
1149{
1150retry:
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//-----------------------------------------------------------------------------
1223off_t KMMsgBase::getLongPart(MsgPartType t) const
1224{
1225retry:
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//-----------------------------------------------------------------------------
1361const 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
1427bool 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
1443static TQStringList sReplySubjPrefixes, sForwardSubjPrefixes;
1444static bool sReplaceSubjPrefix, sReplaceForwSubjPrefix;
1445
1446//-----------------------------------------------------------------------------
1447void 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
1462TQString KMMsgBase::stripOffPrefixes( const TQString& str )
1463{
1464 return replacePrefixes( str, sReplySubjPrefixes + sForwardSubjPrefixes,
1465 true, TQString() ).stripWhiteSpace();
1466}
1467
1468//-----------------------------------------------------------------------------
1469// static
1470TQString 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//-----------------------------------------------------------------------------
1503TQString KMMsgBase::cleanSubject() const
1504{
1505 return cleanSubject( sReplySubjPrefixes + sForwardSubjPrefixes,
1506 true, TQString() ).stripWhiteSpace();
1507}
1508
1509//-----------------------------------------------------------------------------
1510TQString 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//-----------------------------------------------------------------------------
1519TQString KMMsgBase::forwardSubject() const {
1520 return cleanSubject( sForwardSubjPrefixes, sReplaceForwSubjPrefix, "Fwd:" );
1521}
1522
1523//-----------------------------------------------------------------------------
1524TQString 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