static void test_message_parser_small_blocks(void) { struct message_parser_ctx *parser; struct istream *input; struct message_part *parts, *parts2; struct message_block block; unsigned int i, end_of_headers_idx; pool_t pool; int ret; test_begin("message parser in small blocks"); pool = pool_alloconly_create("message parser", 10240); input = test_istream_create(test_msg); /* full parsing */ parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); /* parsing in small blocks */ i_stream_seek(input, 0); test_istream_set_allow_eof(input, FALSE); parser = message_parser_init(pool, input, 0, 0); for (i = 1; i <= TEST_MSG_LEN*2+1; i++) { test_istream_set_size(input, i/2); if (i > TEST_MSG_LEN*2) test_istream_set_allow_eof(input, TRUE); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert((ret == 0 && i <= TEST_MSG_LEN*2) || (ret < 0 && i > TEST_MSG_LEN*2)); } test_assert(message_parser_deinit(&parser, &parts2) == 0); test_assert(msg_parts_cmp(parts, parts2)); /* parsing in small blocks from preparsed parts */ i_stream_seek(input, 0); test_istream_set_allow_eof(input, FALSE); end_of_headers_idx = (strstr(test_msg, "\n-----") - test_msg); parser = message_parser_init_from_parts(parts, input, 0, MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK); for (i = 1; i <= TEST_MSG_LEN*2+1; i++) { test_istream_set_size(input, i/2); if (i > TEST_MSG_LEN*2) test_istream_set_allow_eof(input, TRUE); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; test_assert((ret == 0 && i/2 <= end_of_headers_idx) || (ret < 0 && i/2 > end_of_headers_idx)); } test_assert(message_parser_deinit(&parser, &parts2) == 0); test_assert(msg_parts_cmp(parts, parts2)); i_stream_unref(&input); pool_unref(&pool); test_end(); }
static struct message_part *msg_parse(pool_t pool, bool parse_bodystructure) { struct message_parser_ctx *parser; struct istream *input; struct message_block block; struct message_part *parts; int ret; input = i_stream_create_from_data(testmsg, sizeof(testmsg)-1); parser = message_parser_init(pool, input, MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP | MESSAGE_HEADER_PARSER_FLAG_DROP_CR, MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) { if (parse_bodystructure) { imap_bodystructure_parse_header(pool, block.part, block.hdr); } } test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); i_stream_unref(&input); return parts; }
int message_snippet_generate(struct istream *input, unsigned int max_snippet_chars, string_t *snippet) { struct message_parser_ctx *parser; struct message_part *parts; struct message_decoder_context *decoder; struct message_block raw_block, block; struct snippet_context ctx; pool_t pool; int ret; memset(&ctx, 0, sizeof(ctx)); pool = pool_alloconly_create("message snippet", 1024); ctx.snippet = snippet; ctx.chars_left = max_snippet_chars; parser = message_parser_init(pool_datastack_create(), input, 0, 0); decoder = message_decoder_init(NULL, 0); while ((ret = message_parser_parse_next_block(parser, &raw_block)) > 0) { if (!message_decoder_decode_next_block(decoder, &raw_block, &block)) continue; if (block.size == 0) { const char *ct; if (block.hdr != NULL) continue; /* end of headers - verify that we can use this Content-Type. we get here only once, because we always handle only one non-multipart MIME part. */ ct = message_decoder_current_content_type(decoder); if (ct == NULL) /* text/plain */ ; else if (mail_html2text_content_type_match(ct)) { ctx.html2text = mail_html2text_init(MAIL_HTML2TEXT_FLAG_SKIP_QUOTED); ctx.plain_output = buffer_create_dynamic(pool, 1024); } else if (strncasecmp(ct, "text/", 5) != 0) break; continue; } if (!snippet_generate(&ctx, block.data, block.size)) break; } i_assert(ret != 0); message_decoder_deinit(&decoder); message_parser_deinit(&parser, &parts); if (ctx.html2text != NULL) mail_html2text_deinit(&ctx.html2text); pool_unref(&pool); return input->stream_errno == 0 ? 0 : -1; }
void index_mail_cache_parse_continue(struct mail *_mail) { struct index_mail *mail = (struct index_mail *)_mail; struct message_block block; while (message_parser_parse_next_block(mail->data.parser_ctx, &block) > 0) { if (block.size != 0) continue; if (!mail->data.header_parsed) { index_mail_parse_header(block.part, block.hdr, mail); if (block.hdr == NULL) mail->data.header_parsed = TRUE; } else { imap_bodystructure_parse_header(mail->data_pool, block.part, block.hdr); } } }
static void test_message_part_idx(void) { struct message_parser_ctx *parser; struct istream *input; struct message_part *parts, *part, *prev_part; struct message_block block; unsigned int i, prev_idx = 0, part_idx; pool_t pool; int ret; test_begin("message part indexes"); pool = pool_alloconly_create("message parser", 10240); input = i_stream_create_from_data(test_msg, TEST_MSG_LEN); parser = message_parser_init(pool, input, 0, 0); while ((ret = message_parser_parse_next_block(parser, &block)) > 0) { part_idx = message_part_to_idx(block.part); test_assert(part_idx >= prev_idx); prev_idx = part_idx; } test_assert(ret < 0); test_assert(message_parser_deinit(&parser, &parts) == 0); part = message_part_by_idx(parts, 0); test_assert(part == parts); test_assert(message_part_by_idx(parts, 1) == parts->children); for (i = 1; i < 11; i++) { prev_part = part; part = message_part_by_idx(parts, i); test_assert(part != NULL); test_assert(part != NULL && message_part_to_idx(part) == i); test_assert(part != NULL && prev_part != NULL && prev_part->physical_pos < part->physical_pos); } test_assert(message_part_by_idx(parts, i) == NULL); i_stream_unref(&input); pool_unref(&pool); test_end(); }
static int message_search_msg_real(struct message_search_context *ctx, struct istream *input, struct message_part *parts, const char **error_r) { const enum message_header_parser_flags hdr_parser_flags = MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE; struct message_parser_ctx *parser_ctx; struct message_block raw_block; struct message_part *new_parts; int ret; message_search_reset(ctx); if (parts != NULL) { parser_ctx = message_parser_init_from_parts(parts, input, hdr_parser_flags, 0); } else { parser_ctx = message_parser_init(pool_datastack_create(), input, hdr_parser_flags, 0); } while ((ret = message_parser_parse_next_block(parser_ctx, &raw_block)) > 0) { if (message_search_more(ctx, &raw_block)) { ret = 1; break; } } i_assert(ret != 0); if (ret < 0 && input->stream_errno == 0) { /* normal exit */ ret = 0; } if (message_parser_deinit_from_parts(&parser_ctx, &new_parts, error_r) < 0) { /* broken parts */ ret = -1; } return ret; }
static int astream_read_next(struct attachment_istream *astream, bool *retry_r) { struct istream_private *stream = &astream->istream; struct message_block block; size_t old_size, new_size; const char *error; int ret; *retry_r = FALSE; if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream)) return -2; if (astream->failed) { stream->istream.stream_errno = EINVAL; return -1; } old_size = stream->pos - stream->skip; switch (message_parser_parse_next_block(astream->parser, &block)) { case -1: /* done / error */ ret = astream_end_of_part(astream, &error); if (ret > 0) { /* final data */ new_size = stream->pos - stream->skip; return new_size - old_size; } stream->istream.eof = TRUE; stream->istream.stream_errno = stream->parent->stream_errno; if (ret < 0) { io_stream_set_error(&stream->iostream, "%s", error); stream->istream.stream_errno = EINVAL; astream->failed = TRUE; } astream->cur_part = NULL; return -1; case 0: /* need more data */ return 0; default: break; } if (block.part != astream->cur_part && astream->cur_part != NULL) { /* end of a MIME part */ if (astream_end_of_part(astream, &error) < 0) { io_stream_set_error(&stream->iostream, "%s", error); stream->istream.stream_errno = EINVAL; astream->failed = TRUE; return -1; } } astream->cur_part = block.part; if (block.hdr != NULL) { /* parsing a header */ astream_parse_header(astream, block.hdr); } else if (block.size == 0) { /* end of headers */ if (astream_want_attachment(astream, block.part)) { astream->part.state = MAIL_ATTACHMENT_STATE_MAYBE; astream->part.start_offset = stream->parent->v_offset; } } else { astream_add_body(astream, &block); } new_size = stream->pos - stream->skip; *retry_r = new_size == old_size; return new_size - old_size; }