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