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:
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
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
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
82
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
91
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
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
114
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
143
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
162
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
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
182
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
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
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
217
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
227
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
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
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
260
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
280
EString Bodypart::asText() 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 );
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
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
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
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
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();
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
815
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
826
void Bodypart::setMessage( Message * m )
827
{
828
    d->message = m;
829
}
830
831
832
/*! Returns true. */
833
834
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
844
EString Bodypart::error() const
845
{
846
    return d->error;
847
}