bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenmessage_parse_header_init(struct istream *input, struct message_size *hdr_size,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen ctx = i_new(struct message_header_parser_ctx, 1);
1ff487015234b23c32cf8bb4c9f8c02922535b8eTimo Sirainen ctx->value_buf = buffer_create_dynamic(default_pool, 4096);
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenvoid message_parse_header_deinit(struct message_header_parser_ctx **_ctx)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen struct message_header_parser_ctx *ctx = *_ctx;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenint message_parse_header_next(struct message_header_parser_ctx *ctx,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen struct message_header_line *line = &ctx->line;
a556e29bb9e48968b88c783148f3308bf90c7912Timo Sirainen size_t i, size, startpos, colon_pos, parse_size, skip = 0;
c2f2a6cdf7dd55a4aabe72495cabb4deaa59cffcTimo Sirainen bool continued, continues, last_no_newline, last_crlf;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* new header line */
651f981ca065d2365bf2ea07b04318a5402f047aTimo Sirainen ret = i_stream_read_bytes(ctx->input, &msg, &size, startpos+2);
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* we want to know one byte in advance to find out
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if it's multiline header */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* header ended unexpectedly. */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* error / EOF with no bytes */
01ca85e6e763cfa0d146166fdd60654f04f51199Timo Sirainen if (size > 0 && !ctx->skip_line && !continued &&
cc3da53f3a49304a251bfae88f814505326ac210Timo Sirainen (msg[0] == '\r' && size > 1 && msg[1] == '\n'))) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* end of headers - this mostly happens just
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen with mbox where headers are read separately
cc3da53f3a49304a251bfae88f814505326ac210Timo Sirainen /* stream is nonblocking - need more data */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* a) line is larger than input buffer
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen b) header ended unexpectedly */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* go back to last LWSP if found. */
6cb3c4f4276531258be706821e034f1f0a8cd276Timo Sirainen /* we may or may not have a full header,
6cb3c4f4276531258be706821e034f1f0a8cd276Timo Sirainen but we don't know until we get the
6cb3c4f4276531258be706821e034f1f0a8cd276Timo Sirainen next character. leave out the
6cb3c4f4276531258be706821e034f1f0a8cd276Timo Sirainen linefeed and finish the header on
6cb3c4f4276531258be706821e034f1f0a8cd276Timo Sirainen the next run. */
01ca85e6e763cfa0d146166fdd60654f04f51199Timo Sirainen /* the buffer really has to be more than 2 to
01ca85e6e763cfa0d146166fdd60654f04f51199Timo Sirainen avoid CRLF looping forever */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* find ':' */
064ccad9cea2a75a0d6caa2c679b6cd22be58174Timo Sirainen /* end of headers, or error */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* find '\n' */
064ccad9cea2a75a0d6caa2c679b6cd22be58174Timo Sirainen for (; i < parse_size; i++) {
6cb3c4f4276531258be706821e034f1f0a8cd276Timo Sirainen if (i < parse_size && i+1 == size && ret == -2) {
6cb3c4f4276531258be706821e034f1f0a8cd276Timo Sirainen /* we don't know if the line continues. */
6cb3c4f4276531258be706821e034f1f0a8cd276Timo Sirainen } else if (i < parse_size) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* got a line */
f16712057c1b82c6d2a3a4267c4521d357cd4b4cTimo Sirainen /* skipping a line with a huge header name */
51c331377beb4a2acb81aee4d12bc8ef6c496625Timo Sirainen /* missing CR */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* missing CR */
675b2b1c4587a79478062b05725da61afa5d8045Timo Sirainen (ctx->flags & MESSAGE_HEADER_PARSER_FLAG_DROP_CR) == 0;
675b2b1c4587a79478062b05725da61afa5d8045Timo Sirainen (ctx->flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) != 0;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* end of headers */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen line->name_len = line->value_len = line->full_value_len = 0;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen line->name = ""; line->value = line->full_value = NULL;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* missing ':', assume the whole line is name */
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if ((ctx->flags & MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP) != 0) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* get value. skip all LWSP after ':'. Note that
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen RFC2822 doesn't say we should, but history behind
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen Exception to this is if the value consists only of
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen LWSP, then skip only the one LWSP after ':'. */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* everything was LWSP */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* get name, skip LWSP before ':' */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen while (colon_pos > 0 && IS_LWSP(msg[colon_pos-1]))
200500ebd3c93a5771e7ae2f442659b9acb16eb6Timo Sirainen /* use buffer_append() so the name won't be truncated if there
f3aff3c6bf32d5bb0a61993b76a3fbe5ad798c09Timo Sirainen /* keep middle stored also in ctx->name so it's available
f3aff3c6bf32d5bb0a61993b76a3fbe5ad798c09Timo Sirainen with use_full_value */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen line->middle_len = (size_t)(line->value - line->middle);
f3aff3c6bf32d5bb0a61993b76a3fbe5ad798c09Timo Sirainen str_append_n(ctx->name, line->middle, line->middle_len);
f3aff3c6bf32d5bb0a61993b76a3fbe5ad798c09Timo Sirainen line->middle = str_data(ctx->name) + line->name_len + 1;
1ff487015234b23c32cf8bb4c9f8c02922535b8eTimo Sirainen /* first header line. make a copy of the line since we can't
1ff487015234b23c32cf8bb4c9f8c02922535b8eTimo Sirainen really trust input stream not to lose it. */
1ff487015234b23c32cf8bb4c9f8c02922535b8eTimo Sirainen buffer_append(ctx->value_buf, line->value, line->value_len);
1ff487015234b23c32cf8bb4c9f8c02922535b8eTimo Sirainen line->value = line->full_value = ctx->value_buf->data;
15362cdf9df29fef8795e865957e17ec027a9ebfTimo Sirainen /* continue saving the full value. */
15362cdf9df29fef8795e865957e17ec027a9ebfTimo Sirainen /* line is longer than fit into our buffer, so we
15362cdf9df29fef8795e865957e17ec027a9ebfTimo Sirainen were forced to break it into multiple
15362cdf9df29fef8795e865957e17ec027a9ebfTimo Sirainen message_header_lines */
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if ((ctx->flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) != 0 &&
15362cdf9df29fef8795e865957e17ec027a9ebfTimo Sirainen line->value_len > 0 && line->value[0] != ' ' &&
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* we didn't want full_value, and this is a continued line. */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* always reset it */
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainenbool message_parse_header_has_nuls(const struct message_header_parser_ctx *ctx)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenvoid message_parse_header(struct istream *input, struct message_size *hdr_size,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen message_header_callback_t *callback, void *context)
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen hdr_ctx = message_parse_header_init(input, hdr_size, flags);
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* call after the final skipping */
d45a4c7d1df04dd93d57aa8a29b76b55d4905341Timo Sirainenvoid message_header_line_write(buffer_t *output,
d45a4c7d1df04dd93d57aa8a29b76b55d4905341Timo Sirainen buffer_append(output, hdr->name, strlen(hdr->name));
d45a4c7d1df04dd93d57aa8a29b76b55d4905341Timo Sirainen buffer_append(output, hdr->middle, hdr->middle_len);