Ejemplo n.º 1
0
void Multipart::appendAnyPart( EString &r, const Bodypart * bp,
                               ContentType * ct, bool avoidUtf8 ) const
{
    ContentType * childct = bp->header()->contentType();
    EString::Encoding e = EString::Binary;
    ContentTransferEncoding * cte
        = bp->header()->contentTransferEncoding();
    if ( cte )
        e = cte->encoding();

    if ( ( childct && childct->type() == "message" ) ||
         ( ct && ct->type() == "multipart" && ct->subtype() == "digest" &&
           !childct ) )
    {
        if ( childct && childct->subtype() != "rfc822" )
            appendTextPart( r, bp, childct );
        else
            r.append( bp->message()->rfc822( avoidUtf8 ) );
    }
    else if ( !childct || childct->type().lower() == "text" ) {
        appendTextPart( r, bp, childct );
    }
    else if ( childct->type() == "multipart" ) {
        bp->appendMultipart( r, avoidUtf8 );
    }
    else {
        r.append( bp->data().encoded( e, 72 ) );
    }
}
Ejemplo n.º 2
0
bool MediaPlayer::load(const URL& url, const ContentType& contentType, const String& keySystem)
{
    m_contentMIMEType = contentType.type().lower();
    m_contentTypeCodecs = contentType.parameter(codecs());
    m_url = url;
    m_keySystem = keySystem.lower();
    m_contentMIMETypeWasInferredFromExtension = false;

#if ENABLE(MEDIA_SOURCE)
    m_mediaSource = 0;
#endif

    // If the MIME type is missing or is not meaningful, try to figure it out from the URL.
    if (m_contentMIMEType.isEmpty() || m_contentMIMEType == applicationOctetStream() || m_contentMIMEType == textPlain()) {
        if (m_url.protocolIsData())
            m_contentMIMEType = mimeTypeFromDataURL(m_url.string());
        else {
            String lastPathComponent = url.lastPathComponent();
            size_t pos = lastPathComponent.reverseFind('.');
            if (pos != notFound) {
                String extension = lastPathComponent.substring(pos + 1);
                String mediaType = MIMETypeRegistry::getMediaMIMETypeForExtension(extension);
                if (!mediaType.isEmpty()) {
                    m_contentMIMEType = mediaType;
                    m_contentMIMETypeWasInferredFromExtension = true;
                }
            }
        }
    }

    loadWithNextMediaEngine(0);
    return m_currentMediaEngine;
}
Ejemplo n.º 3
0
void substituer(MimeEntity * mimeEntity, const string url, const string affectations, const string affectations_html) {
    // Substitue _URL_ et _AFFECTATIONS_ dans toutes les parties text/plain et text/html
    ContentType contentType = mimeEntity->header().contentType();
    if (
            contentType.type() == "text" and (
                contentType.subtype() == "plain" or
                contentType.subtype() == "html")
            )
    {
        Body& body = mimeEntity->body();
        string::size_type position;
        while((position = body.find("_URL_")) != string::npos) {
            body.replace(position, sizeof("_URL_") - 1, url);
        }
        while((position = body.find("_AFFECTATIONS_")) != string::npos) {
            body.replace(position, sizeof("_AFFECTATIONS_") - 1, (contentType.subtype() == "html" ? affectations_html : affectations));
        }
    } else {
        MimeEntityList::iterator sous_partie_iterator = mimeEntity->body().parts().begin(),
                        fin = mimeEntity->body().parts().end();
        for(;sous_partie_iterator!=fin;++sous_partie_iterator) {
            substituer(*sous_partie_iterator, url, affectations, affectations_html);
        }
    }
}
Ejemplo n.º 4
0
void MediaPlayer::load(const String& url, const ContentType& contentType)
{
    String type = contentType.type().lower();
    String typeCodecs = contentType.parameter(codecs());

    // If the MIME type is missing or is not meaningful, try to figure it out from the URL.
    if (type.isEmpty() || type == applicationOctetStream() || type == textPlain()) {
        if (protocolIs(url, "data"))
            type = mimeTypeFromDataURL(url);
        else {
            size_t pos = url.reverseFind('.');
            if (pos != notFound) {
                String extension = url.substring(pos + 1);
                String mediaType = MIMETypeRegistry::getMediaMIMETypeForExtension(extension);
                if (!mediaType.isEmpty())
                    type = mediaType;
            }
        }
    }

    m_url = url;
    m_contentMIMEType = type;
    m_contentTypeCodecs = typeCodecs;
    loadWithNextMediaEngine(0);
}
Ejemplo n.º 5
0
ContentType ContentType::fromString(const QString & value)
{
    ContentType contentType;

    contentType.parse(value);

    return contentType;
}
Ejemplo n.º 6
0
MediaPlayer::SupportsType MediaPlayer::supportsType(ContentType contentType)
{
    String type = contentType.type();
    String codecs = contentType.parameter("codecs");
    MediaPlayerFactory* engine = chooseBestEngineForTypeAndCodecs(type, codecs);

    if (!engine)
        return IsNotSupported;

    return engine->supportsTypeAndCodecs(type, codecs);
}
Ejemplo n.º 7
0
bool MediaPlayer::load(const URL& url, const ContentType& contentType, MediaSourcePrivateClient* mediaSource)
{
    ASSERT(mediaSource);
    m_mediaSource = mediaSource;
    m_contentMIMEType = contentType.type().lower();
    m_contentTypeCodecs = contentType.parameter(codecs());
    m_url = url;
    m_keySystem = "";
    m_contentMIMETypeWasInferredFromExtension = false;
    loadWithNextMediaEngine(0);
    return m_currentMediaEngine;
}
Ejemplo n.º 8
0
EString::Encoding Bodypart::contentTransferEncoding() const
{
    ContentTransferEncoding * cte = header()->contentTransferEncoding();
    if ( !cte && parent() ) {
        ContentType * ct = parent()->header()->contentType();
        if ( !ct || ( ct->type() != "multipart" &&
                      ct->type() != "message" ) )
            cte = parent()->header()->contentTransferEncoding();
    }
    if ( cte )
        return cte->encoding();
    return EString::Binary;
}
MediaSourcePrivate::AddStatus MockMediaSourcePrivate::addSourceBuffer(const ContentType& contentType, RefPtr<SourceBufferPrivate>& outPrivate)
{
    MediaEngineSupportParameters parameters;
    parameters.isMediaSource = true;
    parameters.type = contentType.type();
    parameters.codecs = contentType.parameter(ASCIILiteral("codecs"));
    if (MockMediaPlayerMediaSource::supportsType(parameters) == MediaPlayer::IsNotSupported)
        return NotSupported;

    m_sourceBuffers.append(MockSourceBufferPrivate::create(this));
    outPrivate = m_sourceBuffers.last();

    return Ok;
}
Ejemplo n.º 10
0
bool MediaPlayer::load(const URL& url, const ContentType& contentType, MediaSourcePrivateClient* mediaSource)
{
    ASSERT(!m_reloadTimer.isActive());
    ASSERT(mediaSource);

    m_mediaSource = mediaSource;
    m_contentMIMEType = contentType.type().convertToASCIILowercase();
    m_contentTypeCodecs = contentType.parameter(codecs());
    m_url = url;
    m_keySystem = emptyString();
    m_contentMIMETypeWasInferredFromExtension = false;
    loadWithNextMediaEngine(0);
    return m_currentMediaEngine;
}
Ejemplo n.º 11
0
MediaPlayer::SupportsType MediaPlayer::supportsType(const ContentType& contentType)
{
    String type = contentType.type().lower();
    String typeCodecs = contentType.parameter(codecs());

    // 4.8.10.3 MIME types - The canPlayType(type) method must return the empty string if type is a type that the
    // user agent knows it cannot render or is the type "application/octet-stream"
    if (type == applicationOctetStream())
        return IsNotSupported;

    MediaPlayerFactory* engine = bestMediaEngineForTypeAndCodecs(type, typeCodecs);
    if (!engine)
        return IsNotSupported;

    return engine->supportsTypeAndCodecs(type, typeCodecs);
}
Ejemplo n.º 12
0
void Message::fix8BitHeaderFields()
{
    EString charset;
    EString fallback = "us-ascii";
    bool conflict = false;
    List<Bodypart>::Iterator i( allBodyparts() );
    while ( i ) {
        ContentType * ct = 0;
        if ( i->header() )
            ct = i->header()->contentType();
        if ( ct && ct->type() == "text" ) {
            EString cs = ct->parameter( "charset" ).lower();
            if ( cs == "windows-1252" )
                cs = "iso-8859-1";
            if ( cs.isEmpty() )
                ; // no conclusion from this part
            else if ( charset.isEmpty() )
                charset = cs; // use this charset...?
            else if ( cs != charset )
                conflict = true;
            if ( ct && ct->subtype() == "html" )
                fallback = "iso-8859-1";
        }
        ++i;
    }
    Codec * c = 0;
    if ( !charset.isEmpty() )
        c = Codec::byName( charset );
    else
        c = Codec::byString( badFields( header() ) );
    if ( !c )
        c = Codec::byName( fallback );
    if ( conflict || !c )
        c = new AsciiCodec;

    header()->fix8BitFields( c );
    i = allBodyparts()->first();
    while ( i ) {
        if ( i->header() )
            i->header()->fix8BitFields( c );
        if ( i->message() && i->message()->header() )
            i->message()->header()->fix8BitFields( c );
        ++i;
    }
}
Ejemplo n.º 13
0
Archivo: cpim.cpp Proyecto: crubia/wt
	int contentType() 
	{
		ContentType* type = ContentType::Parse("text/plain ; charset = utf-8");

		//Check it is parsed corretly
		if (!type)
			return Error("ContentType not parsed correctly\n");

		Debug("ContentType: \"%s\"\n" , type->ToString().c_str());

		//Clone
		ContentType* cloned = type->Clone();

		Debug("ContentType: \"%s\"\n" , cloned->ToString().c_str());

		delete cloned;	
		delete type;
	}
Ejemplo n.º 14
0
ContentType * Bodypart::contentType() const
{
    ContentType * ct = header()->contentType();
    if ( ct )
        return ct;
    if ( !parent() )
        return 0;
    ct = parent()->header()->contentType();
    if ( ct ) {
        if ( ct->type() == "multipart" ) {
            ct = 0;
        }
        else if ( ct->type() == "message" && ct->subtype() == "rfc822" ) {
            Bodypart * bp = parent()->children()->firstElement();
            ct = bp->header()->contentType();
        }
    }
    return ct;
}
Ejemplo n.º 15
0
EString Message::body() const
{
    EString r;

    ContentType *ct = header()->contentType();
    if ( ct && ct->type() == "multipart" ) {
        appendMultipart( r );
    }
    else {
        // XXX: Is this the right place to restore this linkage?
        Bodypart * firstChild = children()->first();
        if ( firstChild ) {
            firstChild->setHeader( header() );
            appendAnyPart( r, firstChild, ct );
        }
    }

    return r;
}
Ejemplo n.º 16
0
void MediaPlayer::load(const String& url, const ContentType& contentType)
{
    String type = contentType.type();
    String codecs = contentType.parameter("codecs");

    // if we don't know the MIME type, see if the extension can help
    if (type.isEmpty() || type == "application/octet-stream" || type == "text/plain") {
        int pos = url.reverseFind('.');
        if (pos >= 0) {
            String extension = url.substring(pos + 1);
            String mediaType = MIMETypeRegistry::getMediaMIMETypeForExtension(extension);
            if (!mediaType.isEmpty())
                type = mediaType;
        }
    }

    MediaPlayerFactory* engine = 0;
    if (!type.isEmpty())
        engine = chooseBestEngineForTypeAndCodecs(type, codecs);

    // if we didn't find an engine that claims the MIME type, just use the first engine
    if (!engine && !installedMediaEngines().isEmpty())
        engine = installedMediaEngines()[0];
    
    // don't delete and recreate the player unless it comes from a different engine
    if (engine && m_currentMediaEngine != engine) {
        m_currentMediaEngine = engine;
        m_private.clear();
        m_private.set(engine->constructor(this));
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
        m_private->setMediaPlayerProxy(m_playerProxy);
#endif
        m_private->setAutobuffer(autobuffer());
        m_private->setPreservesPitch(preservesPitch());
    }

    if (m_private)
        m_private->load(url);
    else
        m_private.set(createNullMediaPlayer(this));
}    
Ejemplo n.º 17
0
EString Bodypart::asText( bool avoidUtf8 ) const
{
    EString r;
    Codec *c = 0;

    ContentType *ct = header()->contentType();
    if ( ct && !ct->parameter( "charset" ).isEmpty() )
        c = Codec::byName( ct->parameter( "charset" ) );
    if ( !c )
        c = new AsciiCodec;

    if ( !children()->isEmpty() )
        appendMultipart( r, avoidUtf8 );
    else if ( !header()->contentType() ||
              header()->contentType()->type() == "text" )
        r = c->fromUnicode( text() );
    else
        r = d->data.e64( 72 );

    return r;
}
Ejemplo n.º 18
0
void Multipart::appendMultipart( EString &r, bool avoidUtf8 ) const
{
    ContentType * ct = header()->contentType();
    EString delim = ct->parameter( "boundary" );
    if ( ! this )
        ::log( "Fetch::bodyStructure - FATAL, cannnot determine message", Log::Error );
    else {
        if ( this->parent() && this->parent()->isMessage() ) {
            Message *msg = (Message *)this->parent();
            if ( msg->hasPGPsignedPart() ) {
                appendAnyPart( r, children()->first(), ct, avoidUtf8 );
                return;
            }
        } else if ( this->isMessage() ) {
            Message *msg = (Message *)this;
            if ( msg->hasPGPsignedPart() ) {
                appendAnyPart( r, children()->first(), ct, avoidUtf8 );
                return;
            }
        }
    }
    List<Bodypart>::Iterator it( children() );
    r.append( "--" + delim );
    while ( it ) {
        r.append( crlf );

        Bodypart * bp = it;
        ++it;

        r.append( bp->header()->asText( avoidUtf8 ) );
        r.append( crlf );
        appendAnyPart( r, bp, ct, avoidUtf8 );
        r.append( crlf );
        r.append( "--" );
        r.append( delim );
    }
    r.append( "--" );
    r.append( crlf );
}
Ejemplo n.º 19
0
static void headerSummary( Header * h, int n )
{
    EStringList l;

    ContentType * ct = h->contentType();
    if ( ct )
        l.append( ct->type() + "/" + ct->subtype() );

    ContentTransferEncoding * cte = h->contentTransferEncoding();
    if ( cte ) {
        EString s;
        switch ( cte->encoding() ) {
        case EString::QP:
            s = "quoted-printable";
            break;
        case EString::Base64:
            s = "base64";
            break;
        case EString::Uuencode:
            s = "x-uuencode";
            break;
        case EString::Binary:
            s = "7bit";
            break;
        }
        l.append( s );
    }

    HeaderField * cd = h->field( HeaderField::ContentDescription );
    if ( cd )
        l.append( cd->rfc822( false ) );

    if ( !l.isEmpty() ) {
        spaces( n );
        fprintf( stderr, "%s\n", l.join( ";" ).cstr() );
    }
}
Ejemplo n.º 20
0
void Message::parse( const EString & rfc2822 )
{
    uint i = 0;

    children()->clear();

    setHeader( parseHeader( i, rfc2822.length(), rfc2822, Header::Rfc2822 ) );
    header()->repair();
    header()->repair( this, rfc2822.mid( i ) );

    ContentType * ct = header()->contentType();
    if ( ct && ct->type() == "multipart" ) {
        Bodypart::parseMultipart( i, rfc2822.length(), rfc2822,
                                  ct->parameter( "boundary" ),
                                  ct->subtype() == "digest",
                                  children(), this );
    }
    else {
        Bodypart * bp = Bodypart::parseBodypart( i, rfc2822.length(), rfc2822,
                                                 header(), this );
        children()->append( bp );
    }

    fix8BitHeaderFields();
    header()->simplify();

    EString e = d->error;
    recomputeError();
    if ( d->error.isEmpty() )
        d->error = e;

    if ( !d->error.isEmpty() )
        return;
    setAddressesFetched();
    setHeadersFetched();
    setBodiesFetched();
}
Ejemplo n.º 21
0
Bodypart * Bodypart::parseBodypart( uint start, uint end,
                                    const EString & rfc2822,
                                    Header * h, Multipart * parent )
{
    if ( rfc2822[start] == 13 )
        start++;
    if ( rfc2822[start] == 10 )
        start++;

    Bodypart * bp = new Bodypart;
    bp->setParent( parent );
    bp->setHeader( h );

    EString body;
    if ( end > start )
        body = rfc2822.mid( start, end-start );
    if ( !body.contains( '=' ) ) {
        // sometimes people send c-t-e: q-p _and_ c-t-e: 7bit or 8bit.
        // if they are equivalent we can accept it.
        uint i = 0;
        bool any = false;
        HeaderField * f = 0;
        while ( (f=h->field(HeaderField::ContentTransferEncoding,i)) != 0 ) {
            if ( ((ContentTransferEncoding*)f)->encoding() == EString::QP )
                any = true;
            i++;
        }
        if ( any && i > 1 )
            h->removeField( HeaderField::ContentTransferEncoding );
    }

    EString::Encoding e = EString::Binary;
    ContentTransferEncoding * cte = h->contentTransferEncoding();
    if ( cte )
        e = cte->encoding();
    if ( !body.isEmpty() ) {
        if ( e == EString::Base64 || e == EString::Uuencode )
            body = body.decoded( e );
        else
            body = body.crlf().decoded( e );
    }

    ContentType * ct = h->contentType();
    if ( !ct ) {
        switch ( h->defaultType() ) {
        case Header::TextPlain:
            h->add( "Content-Type", "text/plain" );
            break;
        case Header::MessageRfc822:
            h->add( "Content-Type", "message/rfc822" );
            break;
        }
        ct = h->contentType();
    }
    if ( ct->type() == "text" ) {
        bool specified = false;
        bool unknown = false;
        Codec * c = 0;

        if ( ct ) {
            EString csn = ct->parameter( "charset" );
            if ( csn.lower() == "default" )
                csn = "";
            if ( !csn.isEmpty() )
                specified = true;
            c = Codec::byName( csn );
            if ( !c )
                unknown = true;
            if ( c && c->name().lower() == "us-ascii" ) {
                // Some MTAs appear to say this in case there is no
                // Content-Type field - without checking whether the
                // body actually is ASCII. If it isn't, we'd better
                // call our charset guesser.
                (void)c->toUnicode( body );
                if ( !c->valid() )
                    specified = false;
                // Not pretty.
            }
        }

        if ( !c )
            c = new AsciiCodec;

        bp->d->hasText = true;
        bp->d->text = c->toUnicode( body.crlf() );

        if ( c->name() == "GB2312" || c->name() == "ISO-2022-JP" ||
             c->name() == "KS_C_5601-1987" ) {
            // undefined code point usage in GB2312 spam is much too
            // common. (GB2312 spam is much too common, but that's
            // another matter.) Gb2312Codec turns all undefined code
            // points into U+FFFD, so here, we can take the unicode
            // form and say it's the canonical form. when a client
            // later reads the message, it gets the text in unicode,
            // including U+FFFD.

            bool bad = !c->valid();

            // the header may contain some unencoded gb2312. we bang
            // it by hand, ignoring errors.
            List<HeaderField>::Iterator hf( h->fields() );
            while ( hf ) {
                if ( !hf->valid() &&
                     hf->type() == HeaderField::Subject ) {
                    // is it right to bang only Subject?
                    c->reset();
                    hf->setValue( c->toUnicode( hf->unparsedValue() ) );
                }
                ++hf;
            }

            // if the body was bad, we prefer the (unicode) in
            // bp->d->text and pretend it arrived as UTF-8:
            if ( bad ) {
                c = new Utf8Codec;
                body = c->fromUnicode( bp->d->text );
            }
        }

        if ( ( !specified && ( !c->wellformed() ||
                               ct->subtype() == "html" ) ) ||
             ( specified &&  ( !c->valid() ) ) ) {
            Codec * g = 0;
            if ( ct->subtype() == "html" )
                g = guessHtmlCodec( body );
            else
                g = guessTextCodec( body );
            UString guessed;
            if ( g )
                guessed = g->toUnicode( body.crlf() );
            if ( !g ) {
                // if we couldn't guess anything, keep what we had if
                // it's valid or explicitly specified, else use
                // unknown-8bit.
                if ( !specified && !c->valid() ) {
                    c = new Unknown8BitCodec;
                    bp->d->text = c->toUnicode( body.crlf() );
                }
            }
            else {
                // if we could guess something, is our guess better
                // than what we had?
                if ( g->wellformed() && !c->wellformed() ) {
                    c = g;
                    bp->d->text = guessed;
                }
            }
        }

        if ( specified && c->state() == Codec::Invalid ) {
            // the codec was specified, and the specified codec
            // resulted in an error, but did not abort conversion. we
            // respond by forgetting the error, using the conversion
            // result (probably including one or more U+FFFD) and
            // labelling the message as UTF-8.
            c = new Utf8Codec;
            body = c->fromUnicode( bp->d->text );
        }
        else if ( !specified && c->state() == Codec::Invalid ) {
            // the codec was not specified, and we couldn't find
            // anything. we call it unknown-8bit.
            c = new Unknown8BitCodec;
            bp->d->text = c->toUnicode( body );
        }

        // if we ended up using a 16-bit codec and were using q-p, we
        // need to reevaluate without any trailing CRLF
        if ( e == EString::QP && c->name().startsWith( "UTF-16" ) )
            bp->d->text = c->toUnicode( body.stripCRLF() );

        if ( !c->valid() && bp->d->error.isEmpty() ) {
            bp->d->error = "Could not convert body to Unicode";
            if ( specified ) {
                EString cs;
                if ( ct )
                    cs = ct->parameter( "charset" );
                if ( cs.isEmpty() )
                    cs = c->name();
                bp->d->error.append( " from " + cs );
            }
            if ( specified && unknown )
                bp->d->error.append( ": Character set not implemented" );
            else if ( !c->error().isEmpty() )
                bp->d->error.append( ": " + c->error() );
        }

        if ( c->name().lower() != "us-ascii" )
            ct->addParameter( "charset", c->name().lower() );
        else if ( ct )
            ct->removeParameter( "charset" );

        body = c->fromUnicode( bp->d->text );
        bool qp = body.needsQP();

        if ( cte ) {
            if ( !qp ) {
                h->removeField( HeaderField::ContentTransferEncoding );
                cte = 0;
            }
            else if ( cte->encoding() != EString::QP ) {
                cte->setEncoding( EString::QP );
            }
        }
        else if ( qp ) {
            h->add( "Content-Transfer-Encoding", "quoted-printable" );
            cte = h->contentTransferEncoding();
        }
    }
    else {
        bp->d->data = body;
        if ( ct->type() != "multipart" && ct->type() != "message" ) {
            e = EString::Base64;
            // there may be exceptions. cases where some format really
            // needs another content-transfer-encoding:
            if ( ct->type() == "application" &&
                 ct->subtype().startsWith( "pgp-" ) &&
                 !body.needsQP() ) {
                // seems some PGP things need "Version: 1" unencoded
                e = EString::Binary;
            }
            else if ( ct->type() == "application" &&
                      ct->subtype() == "octet-stream" &&
                      body.contains( "BEGIN PGP MESSAGE" ) ) {
                // mutt cannot handle PGP in base64 (what a crock)
                e = EString::Binary;
            }
            // change c-t-e to match the encoding decided above
            if ( e == EString::Binary ) {
                h->removeField( HeaderField::ContentTransferEncoding );
                cte = 0;
            }
            else if ( cte ) {
                cte->setEncoding( e );
            }
            else {
                h->add( "Content-Transfer-Encoding", "base64" );
                cte = h->contentTransferEncoding();
            }
        }
    }

    if ( ct->type() == "multipart" ) {
        parseMultipart( start, end, rfc2822,
                        ct->parameter( "boundary" ),
                        ct->subtype() == "digest",
                        bp->children(), bp, false );
    }
    else if ( ct->type() == "message" && ct->subtype() == "rfc822" ) {
        // There are sometimes blank lines before the message.
        while ( rfc2822[start] == 13 || rfc2822[start] == 10 )
            start++;
        Message * m = new Message;
        m->setParent( bp );
        m->parse( rfc2822.mid( start, end-start ) );
        List<Bodypart>::Iterator it( m->children() );
        while ( it ) {
            bp->children()->append( it );
            it->setParent( bp );
            ++it;
        }
        bp->setMessage( m );
        body = m->rfc822( false );
    }

    bp->d->numBytes = body.length();
    if ( cte )
        body = body.encoded( cte->encoding(), 72 );
    bp->d->numEncodedBytes = body.length();
    if ( bp->d->hasText ||
         ( ct->type() == "message" && ct->subtype() == "rfc822" ) ) {
        uint n = 0;
        uint i = 0;
        uint l = body.length();
        while ( i < l ) {
            if ( body[i] == '\n' )
                n++;
            i++;
        }
        if ( l && body[l-1] != '\n' )
            n++;
        bp->setNumEncodedLines( n );
    }

    h->simplify();

    return bp;
}