message-parser.c revision 0e50c91bdf1a470e016a7800020ad061ffbca427
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2007 Dovecot authors, see the included COPYING file */
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen/* RFC-2046 requires boundaries are max. 70 chars + "--" prefix + "--" suffix.
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen We'll add a bit more just in case. */
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen struct message_header_parser_ctx *hdr_parser_ctx;
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen int (*parse_next_block)(struct message_parser_ctx *ctx,
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainenmessage_part_header_callback_t *null_message_part_header_callback = NULL;
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainenstatic int parse_next_header_init(struct message_parser_ctx *ctx,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic int parse_next_body_to_boundary(struct message_parser_ctx *ctx,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic int parse_next_body_to_eof(struct message_parser_ctx *ctx,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenboundary_find(struct message_boundary *boundaries,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* As MIME spec says: search from latest one to oldest one so that we
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen don't break if the same boundary is used in nested parts. Also the
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen full message line doesn't have to match the boundary, only the
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen beginning. */
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen memcmp(boundaries->boundary, data, boundaries->len) == 0)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic void parse_body_add_block(struct message_parser_ctx *ctx,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen unsigned int missing_cr_count = 0;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen ctx->part->flags |= MESSAGE_PART_FLAG_HAS_NULS;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen ctx->part->body_size.physical_size += block->size;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen ctx->part->body_size.virtual_size += block->size + missing_cr_count;
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainenstatic int message_parser_read_more(struct message_parser_ctx *ctx,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ret = i_stream_read_data(ctx->input, &block_r->data,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainenmessage_part_append(pool_t pool, struct message_part *parent)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* set child position */
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainenstatic void parse_next_body_multipart_init(struct message_parser_ctx *ctx)
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen b = p_new(ctx->parser_pool, struct message_boundary, 1);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic void parse_next_body_message_rfc822_init(struct message_parser_ctx *ctx)
360123b1b41b7aa8af6c4a91c39046be646cd349Timo Sirainen ctx->part = message_part_append(ctx->part_pool, ctx->part);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenboundary_line_find(struct message_parser_ctx *ctx,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const unsigned char *data, size_t size, bool full,
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk /* not a boundary, just skip this line */
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk /* need to find the end of line */
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen /* no LF found */
0bf25546c91ccafff9e2cc93368d2d25acb5c39eTimo Sirainen *boundary_r = boundary_find(ctx->boundaries, data, size);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen memcmp(data + (*boundary_r)->len, "--", 2) == 0;
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainenstatic int parse_next_body_skip_boundary_line(struct message_parser_ctx *ctx,
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen if ((ret = message_parser_read_more(ctx, block_r)) <= 0)
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen /* found the LF */
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen /* a new MIME part begins */
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen ctx->part = message_part_append(ctx->part_pool, ctx->part);
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen ctx->part->flags |= MESSAGE_PART_FLAG_IS_MIME;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen ctx->parse_next_block = parse_next_header_init;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainenstatic int parse_part_finish(struct message_parser_ctx *ctx,
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen struct message_block *block_r, bool first_line)
0bf25546c91ccafff9e2cc93368d2d25acb5c39eTimo Sirainen /* message ended unexpectedly */
0bf25546c91ccafff9e2cc93368d2d25acb5c39eTimo Sirainen /* get back to parent MIME part, summing the child MIME part sizes
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen into parent's body sizes */
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen for (part = ctx->part; part != boundary->part; part = part->parent) {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen message_size_add(&part->parent->body_size, &part->body_size);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen message_size_add(&part->parent->body_size, &part->header_size);
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen /* this boundary isn't needed anymore */
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen ctx->parse_next_block = parse_next_body_to_boundary;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen ctx->parse_next_block = parse_next_body_to_eof;
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen /* forget about the boundaries we possibly skipped */
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen /* the boundary itself should already be in buffer. add that. */
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen block_r->data = i_stream_get_data(ctx->input, &block_r->size);
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen i_assert(block_r->size >= ctx->skip + 2 + boundary->len +
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen /* [\n]--<boundary> */
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen block_r->size = (first_line ? 0 : 1) + 2 + boundary->len;
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen ctx->parse_next_block = parse_next_body_skip_boundary_line;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainenstatic int parse_next_body_to_boundary(struct message_parser_ctx *ctx,
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen const unsigned char *data;
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen if ((ret = message_parser_read_more(ctx, block_r)) == 0 ||
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen /* handle boundary in first line of message. alternatively
360123b1b41b7aa8af6c4a91c39046be646cd349Timo Sirainen it's an empty line. */
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen return parse_part_finish(ctx, boundary, block_r, TRUE);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen for (i = boundary_start = 0; i < block_r->size; i++) {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen /* skip to beginning of the next line. the first line was
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen handled already. */
0bf25546c91ccafff9e2cc93368d2d25acb5c39eTimo Sirainen /* we can skip the first lines. input buffer can't be
0bf25546c91ccafff9e2cc93368d2d25acb5c39eTimo Sirainen full anymore. */
0bf25546c91ccafff9e2cc93368d2d25acb5c39eTimo Sirainen /* no linefeeds in this block. we can just skip it. */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen ret = boundary_line_find(ctx, block_r->data + next_line_idx,
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* found / need more data */
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen /* the boundary wasn't found from this data block,
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen we'll need more data. */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen ctx->want_count = (block_r->size - boundary_start) + 1;
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* leave CR+LF + last line to buffer */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen parse_part_finish(ctx, boundary, block_r, FALSE);
0bf25546c91ccafff9e2cc93368d2d25acb5c39eTimo Sirainenstatic int parse_next_body_to_eof(struct message_parser_ctx *ctx,
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if ((ret = message_parser_read_more(ctx, block_r)) <= 0)
d508ab8db2b0f74b5e225d199b4aaa5293342746Timo Sirainenstatic void parse_content_type(struct message_parser_ctx *ctx,
d508ab8db2b0f74b5e225d199b4aaa5293342746Timo Sirainen rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen if (rfc822_parse_content_type(&parser, content_type) < 0)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen if (strcasecmp(str_c(content_type), "message/rfc822") == 0)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ctx->part->flags |= MESSAGE_PART_FLAG_MESSAGE_RFC822;
d508ab8db2b0f74b5e225d199b4aaa5293342746Timo Sirainen else if (strncasecmp(str_c(content_type), "text", 4) == 0 &&
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen else if (strncasecmp(str_c(content_type), "multipart/", 10) == 0) {
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ctx->part->flags |= MESSAGE_PART_FLAG_MULTIPART;
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen if (strcasecmp(str_c(content_type)+10, "digest") == 0)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ctx->part->flags |= MESSAGE_PART_FLAG_MULTIPART_DIGEST;
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen if ((ctx->part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen while (rfc822_parse_content_param(&parser, &key, &value) > 0) {
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ctx->last_boundary = p_strdup(ctx->parser_pool, value);
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen (MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_MULTIPART)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainenstatic int parse_next_header(struct message_parser_ctx *ctx,
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr);
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0))
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen else if (strcasecmp(hdr->name, "Mime-Version") == 0) {
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen /* it's MIME. Content-* headers are valid */
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen } else if (strcasecmp(hdr->name, "Content-Type") == 0) {
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen MESSAGE_PARSER_FLAG_MIME_VERSION_STRICT) == 0)
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen /* end of headers */
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0 &&
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen /* multipart type but no message boundary */
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen if ((part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) {
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen /* It's not MIME. Reset everything we found from
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen Content-Type. */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen (part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) {
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen /* when there's no content-type specified and we're
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen content-type */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen part->flags |= MESSAGE_PART_FLAG_MESSAGE_RFC822;
e58f291e777b25c1286965b80e142ada6fdacb03Timo Sirainen /* otherwise we default to text/plain */
e58f291e777b25c1286965b80e142ada6fdacb03Timo Sirainen if (message_parse_header_has_nuls(ctx->hdr_parser_ctx))
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen message_parse_header_deinit(&ctx->hdr_parser_ctx);
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen i_assert((part->flags & MUTEX_FLAGS) != MUTEX_FLAGS);
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen ctx->parse_next_block = parse_next_body_to_boundary;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen } else if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) {
08ed4ab71fd2a4e800d9025a736f0f46b771ea90Timo Sirainen ctx->parse_next_block = parse_next_header_init;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen ctx->parse_next_block = parse_next_body_to_boundary;
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen ctx->parse_next_block = parse_next_body_to_eof;
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen /* return empty block as end of headers */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic int parse_next_header_init(struct message_parser_ctx *ctx,
e4cb3bfcd42f1f2c9e676ece6f7f53803f5c6a16Timo Sirainen message_parse_header_init(ctx->input, &ctx->part->header_size,
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainenstatic int preparsed_parse_eof(struct message_parser_ctx *ctx ATTR_UNUSED,
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainenstatic void preparsed_skip_to_next(struct message_parser_ctx *ctx)
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen ctx->parse_next_block = preparsed_parse_next_header_init;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic int preparsed_parse_body_finish(struct message_parser_ctx *ctx,
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainenstatic int preparsed_parse_body_more(struct message_parser_ctx *ctx,
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen if (ctx->input->v_offset + block_r->size >= end_offset) {
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen block_r->size = end_offset - ctx->input->v_offset;
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ctx->parse_next_block = preparsed_parse_body_finish;
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainenstatic int preparsed_parse_body_init(struct message_parser_ctx *ctx,
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen i_stream_skip(ctx->input, offset - ctx->input->v_offset);
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ctx->parse_next_block = preparsed_parse_body_more;
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen return preparsed_parse_body_more(ctx, block_r);
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainenstatic int preparsed_parse_finish_header(struct message_parser_ctx *ctx,
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen ctx->parse_next_block = preparsed_parse_next_header_init;
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen } else if ((ctx->flags & MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK) == 0) {
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen ctx->parse_next_block = preparsed_parse_body_init;
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainenstatic int preparsed_parse_next_header(struct message_parser_ctx *ctx,
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr);
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0))
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen message_parse_header_deinit(&ctx->hdr_parser_ctx);
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen ctx->parse_next_block = preparsed_parse_finish_header;
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen /* return empty block as end of headers */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic int preparsed_parse_next_header_init(struct message_parser_ctx *ctx,
9b00ecffbe74fd864d0d72e6112ec53b86f619baTimo Sirainen i_assert(ctx->part->physical_pos >= ctx->input->v_offset);
9b00ecffbe74fd864d0d72e6112ec53b86f619baTimo Sirainen i_stream_skip(ctx->input, ctx->part->physical_pos -
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen message_parse_header_init(ctx->input, NULL, ctx->hdr_flags);
0bd259973f98837cf0e41fdee3e2a578e51ad09eTimo Sirainen ctx->parse_next_block = preparsed_parse_next_header;
0bd259973f98837cf0e41fdee3e2a578e51ad09eTimo Sirainen return preparsed_parse_next_header(ctx, block_r);
e4cb3bfcd42f1f2c9e676ece6f7f53803f5c6a16Timo Sirainenmessage_parser_init(pool_t part_pool, struct istream *input,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen pool = pool_alloconly_create("Message Parser", 1024);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen ctx = p_new(pool, struct message_parser_ctx, 1);
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen ctx->parts = ctx->part = part_pool == NULL ? NULL :
69af83d4e6c2c5c825a17edd7a41a4fb014caa8fTimo Sirainen ctx->parse_next_block = parse_next_header_init;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenmessage_parser_init_from_parts(struct message_part *parts,
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ctx = message_parser_init(NULL, input, hdr_flags, flags);
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ctx->parse_next_block = preparsed_parse_next_header_init;
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainenstruct message_part *message_parser_deinit(struct message_parser_ctx **_ctx)
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenint message_parser_parse_next_block(struct message_parser_ctx *ctx,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen while ((ret = ctx->parse_next_block(ctx, block_r)) == 0) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen message_size_add(&ctx->part->parent->body_size,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen message_size_add(&ctx->part->parent->body_size,
c58906589cafc32df4c04ffbef933baadd3f2276Timo Sirainenvoid message_parser_parse_header(struct message_parser_ctx *ctx,
6dd77763f5451269ace733579cf58f2f3b18bca4Timo Sirainen while ((ret = message_parser_parse_next_block(ctx, &block)) > 0) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen /* well, can't return error so fake end of headers */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid message_parser_parse_body(struct message_parser_ctx *ctx,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen while ((ret = message_parser_parse_next_block(ctx, &block)) > 0) {