1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2
3 #include "message.h"
4
5 #include "mailbox.h"
6 #include "address.h"
7 #include "bodypart.h"
8 #include "mimefields.h"
9 #include "configuration.h"
10 #include "annotation.h"
11 #include "allocator.h"
12 #include "entropy.h"
13 #include "codec.h"
14 #include "date.h"
15 #include "dict.h"
16 #include "flag.h"
17 #include "md5.h"
18
19
20 static const char * crlf = "\015\012";
21
22
23 class MessageData
24 : public Garbage
25 {
26 public:
MessageData()27 MessageData()
28 : databaseId( 0 ), threadId( 0 ),
29 wrapped( false ), rfc822Size( 0 ), internalDate( 0 ),
30 hasHeaders( false ), hasAddresses( false ), hasBodies( false ),
31 hasTrivia( false ), hasBytesAndLines( false ), hasPGPsignedPart( false )
32 {}
33
34 EString error;
35
36 uint databaseId;
37 uint threadId;
38 bool wrapped;
39
40 uint rfc822Size;
41 uint internalDate;
42
43 bool hasHeaders: 1;
44 bool hasAddresses: 1;
45 bool hasBodies: 1;
46 bool hasTrivia : 1;
47 bool hasBytesAndLines : 1;
48 bool hasPGPsignedPart : 1;
49 EString rawSignedMessageBody;
50 };
51
52
53 /*! \class Message message.h
54 The Message class is the top-level RFC 822 message parser and generator.
55
56 Its core is an email message, and its two duties are conversion to
57 and from RFC 822 format.
58
59 I'm writing it towards this: It can parse messages, with the aid of
60 Header and HeaderField, and split them into MIME bodyparts. It can
61 verify the validity of any single message.
62
63 This may not be what we really need. In particular, constructing
64 Messages (partially) from Cache needs consideration. We'll see.
65
66 This class also provides the utility function baseSubject(), which
67 strips extras such as "Re:" and "(fwd)" off a string to find the
68 presumed base subject of the message.
69 */
70
71
72 /*! Constructs an empty Message. */
73
Message()74 Message::Message()
75 : d( new MessageData )
76 {
77 setHeader( new Header( Header::Rfc2822 ) );
78 }
79
80
81 /*! Wipes out old message content and replaces it with a parse tree
82 based on \a rfc2822.
83 */
84
parse(const EString & rfc2822)85 void Message::parse( const EString & rfc2822 )
86 {
87 uint i = 0;
88
89 children()->clear();
90
91 setHeader( parseHeader( i, rfc2822.length(), rfc2822, Header::Rfc2822 ) );
92 header()->repair();
93 header()->repair( this, rfc2822.mid( i ) );
94
95 setRawSignedMessageBody( rfc2822.mid( i, rfc2822.length() - i ) );
96 uint rawLength = rfc2822.length() - i;
97 ContentType * ct = header()->contentType();
98 if ( ct && ct->type() == "multipart" ) {
99 if ( ct->subtype() == "signed" )
100 setPGPsignedPart( true );
101 Bodypart::parseMultipart( i, rfc2822.length(), rfc2822,
102 ct->parameter( "boundary" ),
103 ct->subtype() == "digest",
104 children(), this );
105 }
106 else {
107 Bodypart * bp = Bodypart::parseBodypart( i, rfc2822.length(), rfc2822,
108 header(), this );
109 children()->append( bp );
110 }
111
112 fix8BitHeaderFields();
113 header()->simplify();
114
115 EString e = d->error;
116 recomputeError();
117 if ( d->error.isEmpty() )
118 d->error = e;
119
120 if ( !d->error.isEmpty() )
121 return;
122 setAddressesFetched();
123 setHeadersFetched();
124 setBodiesFetched();
125
126 // throw away raw body text, if we are not signed
127 if ( !hasPGPsignedPart() ) {
128 d->rawSignedMessageBody.truncate(0);
129 } else { // add raw body as first bodypart
130 Bodypart * bpt = new Bodypart( 0, this );
131 bpt->setData( d->rawSignedMessageBody );
132 bpt->setNumBytes( rawLength );
133 bpt->setParent( this );
134 children()->prepend( bpt );
135 }
136 }
137
138
139 /*! Asks each Header and Bodypart for error information, and sets a
140 suitable error() message for the entire Message. Clears error() if
141 no Header or Bodypart has an error.
142 */
143
recomputeError()144 void Message::recomputeError()
145 {
146 d->error.truncate();
147 if ( !header()->valid() ) {
148 d->error = header()->error();
149 return;
150 }
151
152 List<Bodypart>::Iterator b( allBodyparts() );
153 while ( b && d->error.isEmpty() ) {
154 if ( b->header() && b->header() != header() ) {
155 if ( !b->header()->error().isEmpty() ) {
156 d->error = "In header of bodypart " + partNumber( b ) + ": " +
157 b->header()->error();
158 }
159 List<HeaderField>::Iterator it( b->header()->fields() );
160 while ( it && d->error.isEmpty() ) {
161 if ( !it->valid() )
162 d->error = "In bodypart " + partNumber( b ) +
163 ": Unable to parse header field " + it->name();
164 ++it;
165 }
166 if ( b->message() && b->message()->header() ) {
167 if ( !b->message()->header()->error().isEmpty() )
168 d->error = "In header of bodypart " + partNumber( b ) +
169 ".1: " + b->message()->header()->error();
170 it = b->message()->header()->fields()->first();
171 while ( it && d->error.isEmpty() ) {
172 if ( !it->valid() )
173 d->error = "In bodypart " + partNumber( b ) +
174 ".1: Unable to parse header field " +
175 it->name();
176 ++it;
177 }
178 }
179 }
180 if ( d->error.isEmpty() && !b->error().isEmpty() )
181 d->error = "In bodypart " + partNumber( b ) + ": " + b->error();
182 ++b;
183 }
184
185 // do this at the very end, so we prefer to give error messages
186 // about anything else
187 List<HeaderField>::Iterator it( header()->fields() );
188 while ( it && d->error.isEmpty() ) {
189 if ( !it->valid() )
190 d->error = "Unable to parse header field " + it->name();
191 ++it;
192 }
193 }
194
195
196 /*! Creates and returns a Header in mode \a m by parsing the part of
197 \a rfc2822 from index \a i to index \a end, not including \a
198 end. \a i is changed to the index of the first unparsed character.
199
200 If there is a leading From-space line, parseHeader() skips it and
201 discards its content. Skipping is fine, but should we discard?
202
203 Some messages copied from Courier start with a line like " Feb 12
204 12:12:12 2012". This code skips that, too.
205 */
206
parseHeader(uint & i,uint end,const EString & rfc2822,Header::Mode m)207 Header * Message::parseHeader( uint & i, uint end,
208 const EString & rfc2822,
209 Header::Mode m )
210 {
211 Header * h = new Header( m );
212 bool done = false;
213 while ( !done ) {
214 if ( i >= end )
215 done = true;
216 if ( rfc2822[i] == 0xEF &&
217 rfc2822[i+1] == 0xBB &&
218 rfc2822[i+2] == 0xBF )
219 i += 3;
220 uint j = i;
221 while ( rfc2822[j] >= 33 &&
222 rfc2822[j] <= 127 &&
223 rfc2822[j] != ':' )
224 j++;
225 if ( j == i + 4 && m == Header::Rfc2822 &&
226 rfc2822.mid( i, j-i+1 ).lower() == "from " ) {
227 while ( i < end && rfc2822[i] != '\r' && rfc2822[i] != '\n' )
228 i++;
229 while ( rfc2822[i] == '\r' )
230 i++;
231 if ( rfc2822[i] == '\n' )
232 i++;
233 }
234 else if ( j > i && rfc2822[j] == ':' ) {
235 EString name = rfc2822.mid( i, j-i );
236 i = j;
237 i++;
238 while ( rfc2822[i] == ' ' || rfc2822[i] == '\t' )
239 i++;
240 j = i;
241 // this isn't at all pretty, is it...
242 while ( j < rfc2822.length() &&
243 ( rfc2822[j] != '\n' ||
244 ( rfc2822[j] == '\n' &&
245 ( rfc2822[j+1] == ' ' || rfc2822[j+1] == '\t' ) ) ) )
246 j++;
247 if ( j && rfc2822[j-1] == '\r' )
248 j--;
249 EString value = rfc2822.mid( i, j-i );
250 if ( !value.simplified().isEmpty() ||
251 name.lower().startsWith( "x-" ) ) {
252 HeaderField * f = HeaderField::create( name, value );
253 h->add( f );
254 }
255 i = j;
256 if ( rfc2822[i] == '\r' && rfc2822[i+1] == '\n' )
257 i++;
258 i++;
259 }
260 else {
261 done = true;
262 }
263 }
264 return h;
265 }
266
267
268 /*! Returns true if this message is a valid RFC 2822 message, and false
269 if it has known/detected errors. Returns true if the message is
270 known to be incomplete.
271 */
272
valid() const273 bool Message::valid() const
274 {
275 return d->error.isEmpty();
276 }
277
278
279 /*! Returns a message describing the first detected syntax error in
280 this message, or an empty string if no error has been detected.
281 */
282
error() const283 EString Message::error() const
284 {
285 return d->error;
286 }
287
288
289 /*! Returns the message formatted in RFC 822 (actually 2822) format.
290 The return value is a canonical expression of the message, not
291 whatever was parsed.
292
293 If \a avoidUtf8 is true, this function loses information rather
294 than including UTF-8 in the result.
295 */
296
rfc822(bool avoidUtf8) const297 EString Message::rfc822( bool avoidUtf8 ) const
298 {
299 EString r;
300 if ( d->rfc822Size )
301 r.reserve( d->rfc822Size );
302 else
303 r.reserve( 50000 );
304
305 r.append( header()->asText( avoidUtf8 ) );
306 r.append( crlf );
307 r.append( body( avoidUtf8 ) );
308
309 return r;
310 }
311
312
313 /*! Returns the text representation of the body of this message. */
314
body(bool avoidUtf8) const315 EString Message::body( bool avoidUtf8 ) const
316 {
317 EString r;
318
319 ContentType *ct = header()->contentType();
320 if ( ct && ct->type() == "multipart" ) {
321 appendMultipart( r, avoidUtf8 );
322 }
323 else {
324 // XXX: Is this the right place to restore this linkage?
325 Bodypart * firstChild = children()->first();
326 if ( firstChild ) {
327 firstChild->setHeader( header() );
328 appendAnyPart( r, firstChild, ct, avoidUtf8 );
329 }
330 }
331
332 return r;
333 }
334
335
appendChildren(List<Bodypart> * l,Bodypart * bp)336 static void appendChildren(List<Bodypart> *l, Bodypart *bp )
337 {
338 l->append( bp );
339 List<Bodypart>::Iterator it( bp->children() );
340 while ( it ) {
341 appendChildren( l, it );
342 ++it;
343 }
344 }
345
346
347 /*! Returns a list of all Bodypart objects within this Message. The
348 returned pointer is never null, but may point to an empty list.
349
350 The Injector relies on children()->first() being first in the list.
351 */
352
allBodyparts() const353 List<Bodypart> *Message::allBodyparts() const
354 {
355 List< Bodypart > * l = new List< Bodypart >;
356 List<Bodypart>::Iterator it( children() );
357 while ( it ) {
358 appendChildren( l, it );
359 ++it;
360 }
361 return l;
362 }
363
364
365 /*! Returns a pointer to the Bodypart whose IMAP part number is \a s
366 and possibly create it. Creates Bodypart objects if \a create is
367 true. Returns null pointer if \a s is not valid and \a create is
368 false.
369 */
370
bodypart(const EString & s,bool create)371 class Bodypart * Message::bodypart( const EString & s, bool create )
372 {
373 uint b = 0;
374 Bodypart * bp = 0;
375
376 if ( s == "raw-pgp-signed" ) {
377 if ( create ) {
378 bp = new Bodypart( 0, this ); // hgu TODO: correct number ?
379 this->setPGPsignedPart( true );
380 children()->prepend( bp );
381 } else
382 bp = children()->first();
383 } else {
384 while ( b < s.length() ) {
385 uint e = b;
386 while ( s[e] >= '0' && s[e] <= '9' )
387 e++;
388 if ( e < s.length() && s[e] != '.' ) {
389 return 0;
390 }
391 bool inrange = false;
392 uint n = s.mid( b, e-b ).number( &inrange );
393 b = e + 1;
394 if ( !inrange || n == 0 ) {
395 return 0;
396 }
397 List<Bodypart> * c = children();
398 if ( bp )
399 c = bp->children();
400 List<Bodypart>::Iterator i( c );
401 while ( i && i->number() < n )
402 ++i;
403 if ( i && i->number() == n ) {
404 if ( n == 1 && !i->header() ) {
405 // it's possible that i doesn't have a header of its
406 // own, and that the parent message's header functions
407 // as such. link it in if that's the case.
408 Header * h = header();
409 if ( bp && bp->message() )
410 h = bp->message()->header();
411 if ( h && ( !h->contentType() ||
412 h->contentType()->type() != "multipart" ) )
413 i->setHeader( h );
414 }
415 bp = i;
416 }
417 else if ( create ) {
418 Bodypart * child = 0;
419 if ( bp )
420 child = new Bodypart( n, bp );
421 else
422 child = new Bodypart( n, this );
423 c->insert( i, child );
424 bp = child;
425 }
426 else {
427 return 0;
428 }
429 }
430 }
431 return bp;
432 }
433
434
435 /*! Returns the IMAP part number of \a bp, which must be a part of this
436 Multipart.
437 */
438
partNumber(Bodypart * bp) const439 EString Message::partNumber( Bodypart * bp ) const
440 {
441 Multipart * m = bp;
442
443 EString r;
444 while ( m && m->isBodypart() ) {
445 if ( !r.isEmpty() )
446 r = "." + r;
447 Multipart * parent = m->parent();
448 List<Bodypart>::Iterator i;
449 if ( parent )
450 i = parent->children()->first();
451 else
452 i = children()->first();
453 uint n = 1;
454 while ( i && i != m ) {
455 ++i;
456 ++n;
457 }
458 if ( !i )
459 return "";
460 r = fn( n ) + r;
461 m = parent;
462 }
463 return r;
464 }
465
466
467 /*! Notifies this Message that its internaldate is \a id. The Message
468 will remember \a id and internalDate() will return it.
469 */
470
setInternalDate(uint id)471 void Message::setInternalDate( uint id )
472 {
473 d->internalDate = id;
474 }
475
476
477 /*! Returns the message's internaldate mb, which is meant to be the
478 time when Archiveopteryx first saw it, although it actually is
479 whatever was set using setInternalDate().
480
481 If the messages comes from the database, this function's return
482 value is valid only if hasTrivia();
483 */
484
internalDate() const485 uint Message::internalDate() const
486 {
487 return d->internalDate;
488 }
489
490
491 /*! Notifies the Message that its size is \a s bytes. The Message will
492 believe and report this.
493 */
494
setRfc822Size(uint s)495 void Message::setRfc822Size( uint s )
496 {
497 d->rfc822Size = s;
498 }
499
500
501 /*! Reports the Message's size, as set using setRfc822Size() or the
502 constructor. Valid only if hasTrivia();
503 */
504
rfc822Size() const505 uint Message::rfc822Size() const
506 {
507 return d->rfc822Size;
508 }
509
510
511 /*! Returns true if this message has read its headers from the
512 database, and false it it has not.
513 */
514
hasHeaders() const515 bool Message::hasHeaders() const
516 {
517 return d->hasHeaders;
518 }
519
520
521 /*! Returns true if this message has read its bodyparts from the
522 database, and false if it has not.
523 */
524
hasBodies() const525 bool Message::hasBodies() const
526 {
527 return d->hasBodies;
528 }
529
530
531 /*! Records that all the bodies in this Message have been fetched. */
532
533
setHeadersFetched()534 void Message::setHeadersFetched()
535 {
536 d->hasHeaders = true;
537 }
538
539
540 /*! Records that all the bodies in this Message have been fetched. */
541
setBodiesFetched()542 void Message::setBodiesFetched()
543 {
544 setBytesAndLinesFetched();
545 d->hasBodies = true;
546 }
547
548
549 /*! Returns true if this message knows its internalDate() and
550 rfc822Size(), and false if not.
551 */
552
hasTrivia() const553 bool Message::hasTrivia() const
554 {
555 return d->hasTrivia;
556 }
557
558
559 /*! Records that the message now has correct values for internalDate()
560 and rfc822Size() if \a ok is true, and that it doesn't if \a ok is
561 false.
562 */
563
setTriviaFetched(bool ok)564 void Message::setTriviaFetched( bool ok )
565 {
566 d->hasTrivia = ok;
567 }
568
569
570 /*! Tries to remove the prefixes and suffixes used by MUAs from \a subject
571 to find a base subject that can be used to tie threads together
572 linearly.
573 */
574
baseSubject(const UString & subject)575 UString Message::baseSubject( const UString & subject )
576 {
577 // Comments and syntax mostly quoted on RFC 5256.
578
579 // The basic algorithm here is: Loop for (only) as long as the
580 // string grows shorter.
581
582 // (1) Convert any RFC 2047 encoded-words in the subject to
583 // [UTF-8] as described in "Internationalization
584 // Considerations". Convert all tabs and continuations to
585 // space. Convert all multiple spaces to a single space.
586
587 // We also convert other space characters than SP to space, and
588 // convert to titlecase here.
589
590 UString s( subject.simplified().titlecased() );
591
592 // step 5 starts here
593 uint l6 = UINT_MAX;
594 do {
595 l6 = s.length();
596
597 // from this point on, s must be simplified at the end of each
598 // step.
599
600 // (2) Remove all trailing text of the subject that matches
601 // the subj-trailer ABNF; repeat until no more matches are
602 // possible.
603
604 // subj-trailer = "(fwd)" / WSP
605
606 while ( s.endsWith( "(FWD)" ) )
607 s = s.mid( 0, s.length() - 5 ).simplified();
608
609 // step 5 starts here.
610 uint l5 = UINT_MAX;
611 do {
612 l5 = s.length();
613
614 // (3) Remove all prefix text of the subject that matches
615 // the subj-leader ABNF.
616
617 // subj-refwd = ("re" / ("fw" ["d"])) *WSP [subj-blob] ":"
618 // subj-blob = "[" *BLOBCHAR "]" *WSP
619 // subj-leader = (*subj-blob subj-refwd) / WSP
620
621 uint l3 = UINT_MAX;
622 while ( s.length() < l3 ) {
623 l3 = s.length();
624 uint i = 0;
625 bool blob = true;
626 while ( blob && s[i] == '[' ) {
627 uint j = i+1;
628 while ( j < s.length() && s[j] != '[' && s[j] != ']' )
629 j++;
630 if ( s[j] == ']' ) {
631 j++;
632 if ( s[j] == ' ' )
633 j++;
634 }
635 else {
636 blob = false;
637 }
638 if ( blob )
639 i = j;
640 }
641 if ( s[i] == 'R' && s[i+1] == 'E' ) {
642 i += 2;
643 }
644 else if ( s[i] == 'F' && s[i+1] == 'W' ) {
645 i += 2;
646 if ( s[i] == 'D' )
647 i++;
648 }
649 else {
650 i = 0;
651 }
652 if ( i ) {
653 if ( s[i] == ' ' )
654 i++;
655 blob = true;
656 while ( blob && s[i] == '[' ) {
657 uint j = i+1;
658 while ( j < s.length() && s[j] != '[' && s[j] != ']' )
659 j++;
660 if ( s[j] == ']' ) {
661 j++;
662 if ( s[j] == ' ' )
663 j++;
664 }
665 else {
666 blob = false;
667 }
668 if ( blob )
669 i = j;
670 }
671 if ( s[i] == ':' )
672 s = s.mid( i+1 ).simplified();
673 }
674 }
675
676 // (4) If there is prefix text of the subject that matches
677 // the subj-blob ABNF, and removing that prefix leaves
678 // a non-empty subj-base, then remove the prefix text.
679
680 // subj-blob = "[" *BLOBCHAR "]" *WSP
681
682 uint i = 0;
683 if ( s[0] == '[' ) {
684 i++;
685 while ( i < s.length() && s[i] != '[' && s[i] != ']' )
686 i++;
687 if ( s[i] == ']' ) {
688 i++;
689 if ( s[i] == ' ' )
690 i++;
691 }
692 else {
693 i = 0;
694 }
695 }
696 if ( i ) {
697 UString rest = s.mid( i ).simplified();
698 if ( !rest.isEmpty() )
699 s = rest;
700 }
701
702 // (5) Repeat (3) and (4) until no matches remain.
703 } while ( s.length() < l5 );
704
705 // (6) If the resulting text begins with the subj-fwd-hdr ABNF
706 // and ends with the subj-fwd-trl ABNF, remove the
707 // subj-fwd-hdr and subj-fwd-trl and repeat from step (2).
708
709 // subj-fwd-hdr = "[fwd:"
710 // subj-fwd-trl = "]"
711
712 if ( s.startsWith( "[FWD:" ) && s.endsWith( "]" ) )
713 s = s.mid( 5, s.length() - 6 ).simplified();
714 else
715 l6 = 0;
716 } while ( s.length() < l6 );
717
718 return s;
719 }
720
721
722 /*! Returns true. */
723
isMessage() const724 bool Message::isMessage() const
725 {
726 return true;
727 }
728
729
badFields(Header * h)730 static EString badFields( Header * h )
731 {
732 EStringList bad;
733 List<HeaderField>::Iterator hf( h->fields() );
734 while ( hf ) {
735 if ( !hf->valid() )
736 bad.append( hf->unparsedValue() );
737 ++hf;
738 }
739 return bad.join( "\n" );
740 }
741
742
743 /*! Tries to handle unlabelled 8-bit content in header fields, in
744 cooperation with Header::fix8BitFields().
745
746 The idea is that if we know which encodings are used for the text
747 bodies, and all bodies agree, then any unlabelled header fields
748 probably use that encoding, too. At least if they're legal
749 according to the relevant codec.
750
751 If we can't get charset information from any body, we try to see
752 if a single codec can encode the entire header, and if so, use
753 that.
754 */
755
fix8BitHeaderFields()756 void Message::fix8BitHeaderFields()
757 {
758 EString charset;
759 EString fallback = "us-ascii";
760 bool conflict = false;
761 List<Bodypart>::Iterator i( allBodyparts() );
762 while ( i ) {
763 ContentType * ct = 0;
764 if ( i->header() )
765 ct = i->header()->contentType();
766 if ( ct && ct->type() == "text" ) {
767 EString cs = ct->parameter( "charset" ).lower();
768 if ( cs == "windows-1252" )
769 cs = "iso-8859-1";
770 if ( cs.isEmpty() )
771 ; // no conclusion from this part
772 else if ( charset.isEmpty() )
773 charset = cs; // use this charset...?
774 else if ( cs != charset )
775 conflict = true;
776 if ( ct && ct->subtype() == "html" )
777 fallback = "iso-8859-1";
778 }
779 ++i;
780 }
781 Codec * c = 0;
782 if ( !charset.isEmpty() )
783 c = Codec::byName( charset );
784 else
785 c = Codec::byString( badFields( header() ) );
786 if ( !c )
787 c = Codec::byName( fallback );
788 if ( conflict || !c )
789 c = new AsciiCodec;
790
791 header()->fix8BitFields( c );
792 i = allBodyparts()->first();
793 while ( i ) {
794 if ( i->header() )
795 i->header()->fix8BitFields( c );
796 if ( i->message() && i->message()->header() )
797 i->message()->header()->fix8BitFields( c );
798 ++i;
799 }
800 }
801
802
803 /*! Returns a short string, e.g. "c", which can be used as a mime
804 boundary surrounding \a parts without causing problems.
805
806 \a parts may be one bodypart, or several separated by CRLF. The
807 important thing is that all the lines which might conflict with
808 the boundary are lines in \a parts.
809 */
810
acceptableBoundary(const EString & parts)811 EString Message::acceptableBoundary( const EString & parts )
812 {
813 uint i = 0;
814 uint boundaries = 0;
815 static char boundaryChars[33] = "0123456789abcdefghijklmnopqrstuv";
816 while ( i < parts.length() ) {
817 if ( parts[i] == '-' && parts[i+1] == '-' ) {
818 uint j = 0;
819 while ( j < 32 && boundaryChars[j] != parts[i+2] )
820 j++;
821 if ( j < 32 )
822 boundaries |= ( 1 << j );
823 }
824 while ( i < parts.length() && parts[i] != 10 )
825 i++;
826 while ( i < parts.length() && ( parts[i] == 13 || parts[i] == 10 ) )
827 i++;
828 }
829
830 i = 0;
831 while ( i < 32 && ( boundaries & ( 1 << i ) ) != 0 )
832 i++;
833 if ( i < 32 ) {
834 EString r;
835 r.append( boundaryChars[i] );
836 return r;
837 }
838
839 // in the all too likely case that some unfriendly soul tries
840 // to attack us, we'd better have some alternative plan,
841 // e.g. a string containing eight random base64 characters.
842 EString r = Entropy::asString( 6 ).e64();
843 while ( parts.contains( r ) )
844 // if at first you don't succeed, try again with a bigger hammer!
845 r = Entropy::asString( 36 ).e64();
846 return r;
847 }
848
849
850 /*! Returns true if this message has read its headers fields from the
851 database, and false it it has not.
852 */
853
hasAddresses() const854 bool Message::hasAddresses() const
855 {
856 return d->hasAddresses;
857 }
858
859
860 /*! Notifies this message that it knows what addresses its address
861 fields contain.
862 */
863
setAddressesFetched()864 void Message::setAddressesFetched()
865 {
866 d->hasAddresses = true;
867 }
868
869
870 /*! Returns true if setBytesAndLinesFetched() has been called, false
871 otherwise.
872 */
873
hasBytesAndLines() const874 bool Message::hasBytesAndLines() const
875 {
876 return d->hasBytesAndLines;
877 }
878
879
880 /*! Notifies this message that its Bodypart objects know their
881 Bodypart::numEncodedBytes() and Bodypart::numEncodedLines().
882 */
883
setBytesAndLinesFetched()884 void Message::setBytesAndLinesFetched()
885 {
886 d->hasBytesAndLines = true;
887 }
888
889
890 /*! Adds a message-id header unless this message already has one. The
891 message-id is based on the contents of the message and \a domain, so
892 if possible, addMessageId() should be called late.
893 */
894
addMessageId(const EString & domain)895 void Message::addMessageId( const EString & domain )
896 {
897 if ( header()->field( HeaderField::MessageId ) )
898 return;
899
900 MD5 x;
901 x.add( rfc822( false ) );
902 header()->add( "Message-Id",
903 "<" + x.hash().e64().mid( 0, 21 ) + ".md5@" +
904 domain + ">" );
905 }
906
907
908 /*! Records that this message's database ID is \a id. This corresponds
909 to the id column in messages.
910 */
911
setDatabaseId(uint id)912 void Message::setDatabaseId( uint id )
913 {
914 d->databaseId = id;
915 }
916
917
918 /*! Records what setDatabaseId() recorded, or 0 if setDatabaseId() has
919 not been called for this object.
920 */
921
databaseId() const922 uint Message::databaseId() const
923 {
924 return d->databaseId;
925 }
926
927
928 /*! Records that this message's database ID is \a id. This corresponds
929 to the thread_root column in messages.
930 */
931
setThreadId(uint id)932 void Message::setThreadId( uint id )
933 {
934 d->threadId = id;
935 }
936
937
938 /*! Records what setThreadId() recorded, or 0 if setThreadId() has
939 not been called for this object.
940 */
941
threadId() const942 uint Message::threadId() const
943 {
944 return d->threadId;
945 }
946
947
948 /*! Records that this message is a wrapper message if \a w is true,
949 and that it it's an ordinary message if not. Wrapper message (in
950 this context) are those which wrap an unparsable message.
951
952 The initial value is false, of course.
953 */
954
setWrapped(bool w) const955 void Message::setWrapped( bool w ) const
956 {
957 d->wrapped = w;
958 }
959
960
961 /*! Returns what setWrapped() set. */
962
isWrapped() const963 bool Message::isWrapped() const
964 {
965 return d->wrapped;
966 }
967
968 /*! Records that this message has a PGP-signed part
969 */
970
setPGPsignedPart(bool p)971 void Message::setPGPsignedPart( bool p )
972 {
973 d->hasPGPsignedPart = p;
974 }
975
976 /*! Returns whether a PGP-signed part has been found in this message. */
977
hasPGPsignedPart() const978 bool Message::hasPGPsignedPart() const
979 {
980 return d->hasPGPsignedPart;
981 }
982
setRawSignedMessageBody(const EString & s)983 void Message::setRawSignedMessageBody( const EString & s )
984 {
985 d->rawSignedMessageBody = s;
986 }
987