1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2 
3 #include "bodypart.h"
4 
5 #include "cp.h"
6 #include "utf.h"
7 #include "codec.h"
8 #include "header.h"
9 #include "iso8859.h"
10 #include "ustring.h"
11 #include "message.h"
12 #include "unknown.h"
13 #include "iso2022jp.h"
14 #include "mimefields.h"
15 
16 
17 class BodypartData
18     : public Garbage
19 {
20 public:
BodypartData()21     BodypartData()
22         : id( 0 ), number( 1 ), message( 0 ),
23           numBytes( 0 ), numEncodedBytes(), numEncodedLines( 0 ),
24           hasText( false )
25     {}
26 
27     uint id;
28     uint number;
29 
30     Message * message;
31 
32     uint numBytes;
33     uint numEncodedBytes;
34     uint numEncodedLines;
35 
36     EString data;
37     UString text;
38     bool hasText;
39     EString error;
40 };
41 
42 
43 /*! \class Bodypart bodypart.h
44 
45     The Bodypart class models a single MIME body part. It is a subclass
46     of Multipart, and an adjunct to Message.
47 
48     Every Bodypart has a number(), and contains text(), data(), or a
49     message(), based on its contentType(). It knows how many numBytes(),
50     numEncodedBytes() and numEncodedLines() of data it contains, and can
51     present itself asText().
52 
53     This class is also responsible for parsing bodyparts in messages.
54 */
55 
56 /*! Constructs an empty Bodypart.
57     This is meant to be used only by parseBodypart().
58 */
59 
Bodypart()60 Bodypart::Bodypart()
61     : d ( new BodypartData )
62 {
63     setHeader( new Header( Header::Mime ) );
64 }
65 
66 
67 /*! Constructs a Bodypart with number \a n and parent \a p. */
68 
Bodypart(uint n,Multipart * p)69 Bodypart::Bodypart( uint n, Multipart * p )
70     : d( new BodypartData )
71 {
72     setHeader( new Header( Header::Mime ) );
73     d->number = n;
74     setParent( p );
75 }
76 
77 
78 /*! Returns a number that reflects this Bodypart's position within its
79     containing Multipart.
80 */
81 
number() const82 uint Bodypart::number() const
83 {
84     return d->number;
85 }
86 
87 
88 /*! Returns the id of this bodypart in the bodyparts table, or 0 if it
89     has not been stored there yet. */
90 
id() const91 uint Bodypart::id() const
92 {
93     return d->id;
94 }
95 
96 
97 /*! Sets the id of this bodypart to \a id. Meant for use only by the
98     Injector. */
99 
setId(uint id)100 void Bodypart::setId( uint id )
101 {
102     d->id = id;
103 }
104 
105 
106 /*! Returns the ContentType of this Bodypart, which may be a null
107     pointer in case the Content-Type is the default one. The default
108     is either text/plain or message/rfc822.
109 
110     The Bodypart cannot find the default alone, since it depends on
111     the surrounding type.
112 */
113 
contentType() const114 ContentType * Bodypart::contentType() const
115 {
116     ContentType * ct = header()->contentType();
117     if ( ct )
118         return ct;
119     if ( !parent() )
120         return 0;
121     ct = parent()->header()->contentType();
122     if ( ct ) {
123         if ( ct->type() == "multipart" ) {
124             ct = 0;
125         }
126         else if ( ct->type() == "message" && ct->subtype() == "rfc822" ) {
127             Bodypart * bp = parent()->children()->firstElement();
128             ct = bp->header()->contentType();
129         }
130     }
131     return ct;
132 }
133 
134 
135 /*! Returns the content transfer encoding of this Bodypart, which may
136     be any of EString::Binary, EString::QuotedPrintable and
137     EString::Base64.
138 
139     Note that data() and text() return the canonical representation of
140     the body, not encoded with this.
141 */
142 
contentTransferEncoding() const143 EString::Encoding Bodypart::contentTransferEncoding() const
144 {
145     ContentTransferEncoding * cte = header()->contentTransferEncoding();
146     if ( !cte && parent() ) {
147         ContentType * ct = parent()->header()->contentType();
148         if ( !ct || ( ct->type() != "multipart" &&
149                       ct->type() != "message" ) )
150             cte = parent()->header()->contentTransferEncoding();
151     }
152     if ( cte )
153         return cte->encoding();
154     return EString::Binary;
155 }
156 
157 
158 /*! Returns this Bodypart's content, provided it has an 8-bit type. If
159     this Bodypart is a text part, data() returns an empty string.
160 */
161 
data() const162 EString Bodypart::data() const
163 {
164     return d->data;
165 }
166 
167 
168 /*! Sets the data of this Bodypart to \a s. For use only by
169     MessageBodyFetcher for now.
170 */
171 
setData(const EString & s)172 void Bodypart::setData( const EString &s )
173 {
174     d->data = s;
175 }
176 
177 
178 /*! Returns the text of this Bodypart. MUST NOT be called for non-text
179     parts (whose contents are not known to be well-formed text).
180 */
181 
text() const182 UString Bodypart::text() const
183 {
184     if ( d->hasText )
185         return d->text;
186 
187     Utf8Codec c;
188     return c.toUnicode( d->data );
189 }
190 
191 
192 /*! Sets the text of this Bodypart to \a s. For use only by
193     MessageBodyFetcher for now.
194 */
195 
setText(const UString & s)196 void Bodypart::setText( const UString &s )
197 {
198     d->hasText = true;
199     d->text = s;
200 }
201 
202 
203 /*! Notifies this Bodypart that it contains \a n bytes of data().
204     The initial value is 0.
205 */
206 
setNumBytes(uint n)207 void Bodypart::setNumBytes( uint n )
208 {
209     d->numBytes = n;
210 }
211 
212 
213 /*! Returns the number of bytes in this body part, as set using
214     setNumBytes().
215 */
216 
numBytes() const217 uint Bodypart::numBytes() const
218 {
219     return d->numBytes;
220 }
221 
222 
223 /*! Returns the value set by setNumEncodedBytes(). Compare to
224     numBytes().
225 */
226 
numEncodedBytes() const227 uint Bodypart::numEncodedBytes() const
228 {
229     return d->numEncodedBytes;
230 }
231 
232 
233 /*! Notifies this Bodypart that it contains \a n bytes of asText()
234     when fully encoded using the current ContentTransferEncoding.  The
235     initial value is 0.
236 
237     Compare to numBytes(), which returns the raw number of bytes.
238 */
239 
setNumEncodedBytes(uint n)240 void Bodypart::setNumEncodedBytes( uint n )
241 {
242     d->numEncodedBytes = n;
243 }
244 
245 
246 /*! Notifies this Bodypart that it contains \a n lines of text() once
247     encoded some ContentTransferEncoding. The initial value is 0.
248 */
249 
setNumEncodedLines(uint n)250 void Bodypart::setNumEncodedLines( uint n )
251 {
252     d->numEncodedLines = n;
253 }
254 
255 
256 /*! Returns the number of lines in this body part, as set using
257     setNumEncodedLines().
258 */
259 
numEncodedLines() const260 uint Bodypart::numEncodedLines() const
261 {
262     return d->numEncodedLines;
263 }
264 
265 
266 /*! Returns the text representation of this Bodypart.
267 
268     Notes: This function seems uncomfortable. It returns just one of
269     many possible text representations, and the exact choice seems
270     arbitrary, and finally, it does rather overlap with text() and
271     data().
272 
273     We probably should transition away from this function.
274 
275     The exact representation returned uses base64 encoding for data
276     types and no ContentTransferEncoding. For text types, it encodes
277     the text according to the ContentType.
278 */
279 
asText(bool avoidUtf8) const280 EString Bodypart::asText( bool avoidUtf8 ) const
281 {
282     EString r;
283     Codec *c = 0;
284 
285     ContentType *ct = header()->contentType();
286     if ( ct && !ct->parameter( "charset" ).isEmpty() )
287         c = Codec::byName( ct->parameter( "charset" ) );
288     if ( !c )
289         c = new AsciiCodec;
290 
291     if ( !children()->isEmpty() )
292         appendMultipart( r, avoidUtf8 );
293     else if ( !header()->contentType() ||
294               header()->contentType()->type() == "text" )
295         r = c->fromUnicode( text() );
296     else
297         r = d->data.e64( 72 );
298 
299     return r;
300 }
301 
302 
303 
304 /*! Parses the part of \a rfc2822 from index \a i to (but not including)
305     \a end, dividing the part into bodyparts wherever the boundary \a
306     divider occurs and adding each bodypart to \a children, and setting
307     the correct \a parent. \a divider does not contain the leading or
308     trailing hyphens. \a digest is true for multipart/digest and false
309     for other types.
310 */
311 
parseMultipart(uint i,uint end,const EString & rfc2822,const EString & divider,bool digest,List<Bodypart> * children,Multipart * parent)312 void Bodypart::parseMultipart( uint i, uint end,
313                                const EString & rfc2822,
314                                const EString & divider,
315                                bool digest,
316                                List< Bodypart > * children,
317                                Multipart * parent )
318 {
319     uint start = 0;
320     bool last = false;
321     uint pn = 1;
322     while ( !last && i <= end ) {
323         if ( i >= end ||
324              ( rfc2822[i] == '-' && rfc2822[i+1] == '-' &&
325                ( i == 0 || rfc2822[i-1] == 13 || rfc2822[i-1] == 10 ) &&
326                rfc2822[i+2] == divider[0] &&
327                rfc2822.mid( i+2, divider.length() ) == divider ) )
328         {
329             uint j = i;
330             bool l = false;
331             if ( i >= end ) {
332                 l = true;
333             }
334             else {
335                 j = i + 2 + divider.length();
336                 if ( rfc2822[j] == '-' && rfc2822[j+1] == '-' ) {
337                     j += 2;
338                     l = true;
339                 }
340             }
341             while ( rfc2822[j] == ' ' || rfc2822[j] == '\t' )
342                 j++;
343             if ( rfc2822[j] == 13 || rfc2822[j] == 10 ||
344                  j >= rfc2822.length() ) {
345                 // finally. we accept that as a boundary line.
346                 if ( rfc2822[j] == 13 )
347                     j++;
348                 if ( rfc2822[j] == 10 )
349                     j++;
350                 if ( start > 0 ) {
351                     Header * h = Message::parseHeader( start, j,
352                                                        rfc2822,
353                                                        Header::Mime );
354                     if ( digest )
355                         h->setDefaultType( Header::MessageRfc822 );
356 
357                     h->repair();
358 
359                     // Strip the [CR]LF that belongs to the boundary.
360                     if ( rfc2822[i-1] == 10 ) {
361                         i--;
362                         if ( rfc2822[i-1] == 13 )
363                             i--;
364                     }
365 
366                     Bodypart * bp =
367                         parseBodypart( start, i, rfc2822, h, parent );
368                     bp->d->number = pn;
369                     children->append( bp );
370                     pn++;
371 
372                     h->repair( bp, "" );
373                 }
374                 last = l;
375                 start = j;
376                 i = j;
377             }
378         }
379         while ( i < end && rfc2822[i] != 13 && rfc2822[i] != 10 )
380             i++;
381         while ( i < end && ( rfc2822[i] == 13 || rfc2822[i] == 10 ) )
382             i++;
383     }
384 }
385 
386 
guessTextCodec(const EString & body)387 static Codec * guessTextCodec( const EString & body )
388 {
389     // step 1. try iso-2022-jp. this goes first because it's so
390     // restrictive, and because 2022 strings also match the ascii and
391     // utf-8 tests.
392     if ( body[0] == 0x1B &&
393          ( body[1] == '(' || body[1] == '$' ) &&
394          ( body[2] == 'B' || body[2] == 'J' || body[2] == '@' ) ) {
395         Codec * c = new Iso2022JpCodec;
396         c->toUnicode( body );
397         if ( c->wellformed() )
398             return c;
399     }
400 
401     // step 2. could it be pure ascii?
402     Codec * a = new AsciiCodec;
403     (void)a->toUnicode( body );
404     if ( a->wellformed() )
405         return a;
406 
407     // some multibyte encodings have to go before utf-8, or else utf-8
408     // will match. this applies at least to iso-2002-jp, but may also
409     // apply to other encodings that use octet values 0x01-0x07f
410     // exclusively.
411 
412     // step 3. does it look good as utf-8?
413     Codec * u = new Utf8Codec;
414     (void)u->toUnicode( body );
415     if ( u->wellformed() ) {
416         // if it's actually ascii, return that.
417         if ( a->valid() )
418             return a;
419         return u;
420     }
421 
422     // step 4. guess a codec based on the bodypart content.
423     Codec * g = Codec::byString( body );
424     if ( g ) {
425         // this probably isn't necessary... but it doesn't hurt to be sure.
426         (void)g->toUnicode( body );
427         if ( g->wellformed() )
428             return g;
429     }
430 
431     // step 5. is utf-8 at all plausible?
432     if ( u->valid() )
433         return u;
434     // should we use g here if valid()?
435 
436     return 0;
437 }
438 
439 
guessHtmlCodec(const EString & body)440 static Codec * guessHtmlCodec( const EString & body )
441 {
442     // Let's see if the general function has something for us.
443     Codec * guess = guessTextCodec( body );
444 
445     // HTML prescribes that 8859-1 is the default. Let's see if 8859-1
446     // works.
447     if ( !guess ) {
448         guess = new Iso88591Codec;
449         (void)guess->toUnicode( body );
450         if ( !guess->valid() )
451             guess = 0;
452     }
453 
454     if ( !guess ||
455          ( !guess->wellformed() &&
456            ( guess->name() == "ISO-8859-1" ||
457              guess->name() == "ISO-8859-15" ) ) ) {
458         // Some people believe that Windows codepage 1252 is
459         // ISO-8859-1. Let's see if that works.
460         Codec * windoze = new Cp1252Codec;
461         (void)windoze->toUnicode( body );
462         if ( windoze->wellformed() )
463             guess = windoze;
464     }
465 
466 
467     // Some user-agents add a <meta http-equiv="content-type"> instead
468     // of the Content-Type field. Maybe that exists? And if it exists,
469     // is it more likely to be correct than our guess above?
470 
471     EString b = body.lower().simplified();
472     int i = 0;
473     while ( i >= 0 ) {
474         EString tag( "<meta http-equiv=\"content-type\" content=\"" );
475         i = b.find( tag, i );
476         if ( i >= 0 ) {
477             i = i + tag.length();
478             int j = i;
479             while ( j < (int)b.length() && b[j] != '"' )
480                 j++;
481             HeaderField * hf
482                 = HeaderField::create( "Content-Type",
483                                        b.mid( i, j-i ) );
484             EString cs = ((MimeField*)hf)->parameter( "charset" );
485             Codec * meta = 0;
486             if ( !cs.isEmpty() )
487                 meta = Codec::byName( cs );
488             UString m;
489             if ( meta )
490                 m = meta->toUnicode( body );
491             UString g;
492             if ( guess )
493                 g = guess->toUnicode( body );
494             if ( meta &&
495                  ( ( !m.isEmpty() && m == g ) ||
496                    ( meta->wellformed() &&
497                      ( !guess || !guess->wellformed() ) ) ||
498                    ( meta->valid() && !guess ) ||
499                    ( meta->valid() && guess &&
500                      guess->name() == "ISO-8859-1" ) ||
501                    ( meta->valid() && guess && !guess->valid() ) ) &&
502                  meta->toUnicode( b ).ascii().contains( tag ) ) {
503                 guess = meta;
504             }
505         }
506     }
507 
508     return guess;
509 }
510 
511 
512 /*! Parses the part of \a rfc2822 from \a start to \a end (not
513     including \a end) as a single bodypart with MIME/RFC 822 header \a h.
514 
515     This removes the "charset" argument from the Content-Type field in \a h.
516 
517     The \a parent argument is provided so that nested message/rfc822
518     bodyparts without a Date field may be fixed with reference to the
519     Date field in the enclosing bodypart.
520 */
521 
parseBodypart(uint start,uint end,const EString & rfc2822,Header * h,Multipart * parent)522 Bodypart * Bodypart::parseBodypart( uint start, uint end,
523                                     const EString & rfc2822,
524                                     Header * h, Multipart * parent )
525 {
526     if ( rfc2822[start] == 13 )
527         start++;
528     if ( rfc2822[start] == 10 )
529         start++;
530 
531     Bodypart * bp = new Bodypart;
532     bp->setParent( parent );
533     bp->setHeader( h );
534 
535     EString body;
536     if ( end > start )
537         body = rfc2822.mid( start, end-start );
538     if ( !body.contains( '=' ) ) {
539         // sometimes people send c-t-e: q-p _and_ c-t-e: 7bit or 8bit.
540         // if they are equivalent we can accept it.
541         uint i = 0;
542         bool any = false;
543         HeaderField * f = 0;
544         while ( (f=h->field(HeaderField::ContentTransferEncoding,i)) != 0 ) {
545             if ( ((ContentTransferEncoding*)f)->encoding() == EString::QP )
546                 any = true;
547             i++;
548         }
549         if ( any && i > 1 )
550             h->removeField( HeaderField::ContentTransferEncoding );
551     }
552 
553     EString::Encoding e = EString::Binary;
554     ContentTransferEncoding * cte = h->contentTransferEncoding();
555     if ( cte )
556         e = cte->encoding();
557     if ( !body.isEmpty() ) {
558         if ( e == EString::Base64 || e == EString::Uuencode )
559             body = body.decoded( e );
560         else
561             body = body.crlf().decoded( e );
562     }
563 
564     ContentType * ct = h->contentType();
565     if ( !ct ) {
566         switch ( h->defaultType() ) {
567         case Header::TextPlain:
568             h->add( "Content-Type", "text/plain" );
569             break;
570         case Header::MessageRfc822:
571             h->add( "Content-Type", "message/rfc822" );
572             break;
573         }
574         ct = h->contentType();
575     }
576     if ( ct->type() == "text" ) {
577         bool specified = false;
578         bool unknown = false;
579         Codec * c = 0;
580 
581         if ( ct ) {
582             EString csn = ct->parameter( "charset" );
583             if ( csn.lower() == "default" )
584                 csn = "";
585             if ( !csn.isEmpty() )
586                 specified = true;
587             c = Codec::byName( csn );
588             if ( !c )
589                 unknown = true;
590             if ( c && c->name().lower() == "us-ascii" ) {
591                 // Some MTAs appear to say this in case there is no
592                 // Content-Type field - without checking whether the
593                 // body actually is ASCII. If it isn't, we'd better
594                 // call our charset guesser.
595                 (void)c->toUnicode( body );
596                 if ( !c->valid() )
597                     specified = false;
598                 // Not pretty.
599             }
600         }
601 
602         if ( !c )
603             c = new AsciiCodec;
604 
605         bp->d->hasText = true;
606         bp->d->text = c->toUnicode( body.crlf() );
607 
608         if ( c->name() == "GB2312" || c->name() == "ISO-2022-JP" ||
609              c->name() == "KS_C_5601-1987" ) {
610             // undefined code point usage in GB2312 spam is much too
611             // common. (GB2312 spam is much too common, but that's
612             // another matter.) Gb2312Codec turns all undefined code
613             // points into U+FFFD, so here, we can take the unicode
614             // form and say it's the canonical form. when a client
615             // later reads the message, it gets the text in unicode,
616             // including U+FFFD.
617 
618             bool bad = !c->valid();
619 
620             // the header may contain some unencoded gb2312. we bang
621             // it by hand, ignoring errors.
622             List<HeaderField>::Iterator hf( h->fields() );
623             while ( hf ) {
624                 if ( !hf->valid() &&
625                      hf->type() == HeaderField::Subject ) {
626                     // is it right to bang only Subject?
627                     c->reset();
628                     hf->setValue( c->toUnicode( hf->unparsedValue() ) );
629                 }
630                 ++hf;
631             }
632 
633             // if the body was bad, we prefer the (unicode) in
634             // bp->d->text and pretend it arrived as UTF-8:
635             if ( bad ) {
636                 c = new Utf8Codec;
637                 body = c->fromUnicode( bp->d->text );
638             }
639         }
640 
641         if ( ( !specified && ( !c->wellformed() ||
642                                ct->subtype() == "html" ) ) ||
643              ( specified &&  ( !c->valid() ) ) ) {
644             Codec * g = 0;
645             if ( ct->subtype() == "html" )
646                 g = guessHtmlCodec( body );
647             else
648                 g = guessTextCodec( body );
649             UString guessed;
650             if ( g )
651                 guessed = g->toUnicode( body.crlf() );
652             if ( !g ) {
653                 // if we couldn't guess anything, keep what we had if
654                 // it's valid or explicitly specified, else use
655                 // unknown-8bit.
656                 if ( !specified && !c->valid() ) {
657                     c = new Unknown8BitCodec;
658                     bp->d->text = c->toUnicode( body.crlf() );
659                 }
660             }
661             else {
662                 // if we could guess something, is our guess better
663                 // than what we had?
664                 if ( g->wellformed() && !c->wellformed() ) {
665                     c = g;
666                     bp->d->text = guessed;
667                 }
668             }
669         }
670 
671         if ( specified && c->state() == Codec::Invalid ) {
672             // the codec was specified, and the specified codec
673             // resulted in an error, but did not abort conversion. we
674             // respond by forgetting the error, using the conversion
675             // result (probably including one or more U+FFFD) and
676             // labelling the message as UTF-8.
677             c = new Utf8Codec;
678             body = c->fromUnicode( bp->d->text );
679         }
680         else if ( !specified && c->state() == Codec::Invalid ) {
681             // the codec was not specified, and we couldn't find
682             // anything. we call it unknown-8bit.
683             c = new Unknown8BitCodec;
684             bp->d->text = c->toUnicode( body );
685         }
686 
687         // if we ended up using a 16-bit codec and were using q-p, we
688         // need to reevaluate without any trailing CRLF
689         if ( e == EString::QP && c->name().startsWith( "UTF-16" ) )
690             bp->d->text = c->toUnicode( body.stripCRLF() );
691 
692         if ( !c->valid() && bp->d->error.isEmpty() ) {
693             bp->d->error = "Could not convert body to Unicode";
694             if ( specified ) {
695                 EString cs;
696                 if ( ct )
697                     cs = ct->parameter( "charset" );
698                 if ( cs.isEmpty() )
699                     cs = c->name();
700                 bp->d->error.append( " from " + cs );
701             }
702             if ( specified && unknown )
703                 bp->d->error.append( ": Character set not implemented" );
704             else if ( !c->error().isEmpty() )
705                 bp->d->error.append( ": " + c->error() );
706         }
707 
708         if ( c->name().lower() != "us-ascii" )
709             ct->addParameter( "charset", c->name().lower() );
710         else if ( ct )
711             ct->removeParameter( "charset" );
712 
713         body = c->fromUnicode( bp->d->text );
714         bool qp = body.needsQP();
715 
716         if ( cte ) {
717             if ( !qp ) {
718                 h->removeField( HeaderField::ContentTransferEncoding );
719                 cte = 0;
720             }
721             else if ( cte->encoding() != EString::QP ) {
722                 cte->setEncoding( EString::QP );
723             }
724         }
725         else if ( qp ) {
726             h->add( "Content-Transfer-Encoding", "quoted-printable" );
727             cte = h->contentTransferEncoding();
728         }
729     }
730     else {
731         bp->d->data = body;
732         if ( ct->type() != "multipart" && ct->type() != "message" ) {
733             e = EString::Base64;
734             // there may be exceptions. cases where some format really
735             // needs another content-transfer-encoding:
736             if ( ct->type() == "application" &&
737                  ct->subtype().startsWith( "pgp-" ) &&
738                  !body.needsQP() ) {
739                 // seems some PGP things need "Version: 1" unencoded
740                 e = EString::Binary;
741             }
742             else if ( ct->type() == "application" &&
743                       ct->subtype() == "octet-stream" &&
744                       body.contains( "BEGIN PGP MESSAGE" ) ) {
745                 // mutt cannot handle PGP in base64 (what a crock)
746                 e = EString::Binary;
747             }
748             // change c-t-e to match the encoding decided above
749             if ( e == EString::Binary ) {
750                 h->removeField( HeaderField::ContentTransferEncoding );
751                 cte = 0;
752             }
753             else if ( cte ) {
754                 cte->setEncoding( e );
755             }
756             else {
757                 h->add( "Content-Transfer-Encoding", "base64" );
758                 cte = h->contentTransferEncoding();
759             }
760         }
761     }
762 
763     if ( ct->type() == "multipart" ) {
764         parseMultipart( start, end, rfc2822,
765                         ct->parameter( "boundary" ),
766                         ct->subtype() == "digest",
767                         bp->children(), bp );
768     }
769     else if ( ct->type() == "message" && ct->subtype() == "rfc822" ) {
770         // There are sometimes blank lines before the message.
771         while ( rfc2822[start] == 13 || rfc2822[start] == 10 )
772             start++;
773         Message * m = new Message;
774         m->setParent( bp );
775         m->parse( rfc2822.mid( start, end-start ) );
776         List<Bodypart>::Iterator it( m->children() );
777         while ( it ) {
778             bp->children()->append( it );
779             it->setParent( bp );
780             ++it;
781         }
782         bp->setMessage( m );
783         body = m->rfc822( false );
784     }
785 
786     bp->d->numBytes = body.length();
787     if ( cte )
788         body = body.encoded( cte->encoding(), 72 );
789     bp->d->numEncodedBytes = body.length();
790     if ( bp->d->hasText ||
791          ( ct->type() == "message" && ct->subtype() == "rfc822" ) ) {
792         uint n = 0;
793         uint i = 0;
794         uint l = body.length();
795         while ( i < l ) {
796             if ( body[i] == '\n' )
797                 n++;
798             i++;
799         }
800         if ( l && body[l-1] != '\n' )
801             n++;
802         bp->setNumEncodedLines( n );
803     }
804 
805     h->simplify();
806 
807     return bp;
808 }
809 
810 
811 /*! Returns a pointer to the subsidiary message, provided this is a
812     message/rfc822 bodypart, or a null pointer in other cases.
813 */
814 
message() const815 Message * Bodypart::message() const
816 {
817     return d->message;
818 }
819 
820 
821 /*! Notifies this Bodypart that it has a subsidiary message \a m. This
822     function is only meaningful if the Bodypart has content-type
823     message/rfc822.
824 */
825 
setMessage(Message * m)826 void Bodypart::setMessage( Message * m )
827 {
828     d->message = m;
829 }
830 
831 
832 /*! Returns true. */
833 
isBodypart() const834 bool Bodypart::isBodypart() const
835 {
836     return true;
837 }
838 
839 
840 /*! Returns an error message describing why this bodypart is bad, or
841     an empty string if nothing seems to be the matter.
842 */
843 
error() const844 EString Bodypart::error() const
845 {
846     return d->error;
847 }
848