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