bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch parser->value_buf = buffer_create_dynamic(default_pool, 4096);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_header_parser_deinit(struct http_header_parser **_parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch //i_stream_skip(ctx->input, ctx->skip);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_header_parser_reset(struct http_header_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_header_parse_name(struct http_header_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* field-name = token
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch token = 1*tchar
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch while (parser->cur < parser->end && http_char_is_token(*parser->cur))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_append_n(parser->name, first, parser->cur-first);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_header_parse_ows(struct http_header_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* OWS = *( SP / HTAB )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ; "optional" whitespace
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (*parser->cur == ' ' || *parser->cur == '\t'))
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_header_parse_content(struct http_header_parser *parser)
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch field-vchar = VCHAR / obs-text
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch while (parser->cur < parser->end && http_char_is_text(*parser->cur)) {
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch buffer_append(parser->value_buf, first, parser->cur-first);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if ((parser->flags & HTTP_HEADER_PARSE_FLAG_STRICT) != 0)
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch /* We'll be lenient here to accommodate for some bad servers. We just
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch drop offending characters */
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch while (parser->cur < parser->end && !http_char_is_text(*parser->cur) &&
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch (*parser->cur != '\r' && *parser->cur != '\n'))
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch (*parser->cur != '\r' && *parser->cur != '\n'));
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic inline const char *_chr_sanitize(unsigned char c)
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_header_parse(struct http_header_parser *parser)
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.2: Header Fields
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch 'header' = *( header-field CRLF ) CRLF
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch ; Actually part of HTTP-message syntax
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen header-field = field-name ":" OWS field-value OWS
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch field-name = token
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch field-value = *( field-content / obs-fold )
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch field-vchar = VCHAR / obs-text
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch obs-fold = CRLF 1*( SP / HTAB )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ; obsolete line folding
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch ; see Section 3.2.4
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch /* last CRLF */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch /* last LF */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch /* next line */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch if ((ret=http_header_parse_name(parser)) <= 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_COLON;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ("Expected ':' after header field name '%s', but found %s",
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (++parser->field_count > parser->limits.max_fields) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->error = "Excessive number of header fields";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_CONTENT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_header_parse_content(parser)) <= 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch ("Invalid character %s in content of header field '%s'",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch ("Expected LF after CR at end of header field '%s', but found %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_HEADER_PARSE_STATE_NEW_LINE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (*parser->cur == ' ' || *parser->cur == '\t') {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* obs-fold */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch /* next header line */
b4d90cf5b73018ef7a64d60748dde8ec6f539d60Stephan Bosch ("Encountered stray CR at beginning of header line, followed by %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* header fully parsed */
7384b4e78eaab44693c985192276e31322155e32Stephan Boschint http_header_parse_next_field(struct http_header_parser *parser,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char **name_r, const unsigned char **data_r, size_t *size_r,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char **error_r)
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch const uoff_t max_size = parser->limits.max_size;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch const uoff_t max_field_size = parser->limits.max_field_size;
9184983183ae28fb543695c54c85bc5396c07e42Phil Carmody while ((ret=i_stream_read_more(parser->input, &parser->begin, &size)) > 0) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* check header size limits */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* don't parse beyond header size limits */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (size > (max_field_size - parser->field_size)) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch size = (size == 0 ? 1 : size); /* need to parse one more byte */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_stream_skip(parser->input, parser->cur - parser->begin);
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch parser->field_size += parser->cur - parser->begin;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (parser->state != HTTP_HEADER_PARSE_STATE_EOH) {
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen data = buffer_get_data(parser->value_buf, &size);
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen /* trim trailing OWS */
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen while (size > 0 &&
9a449fb205b61613586b905ce3ee537486d5bd7cTimo Sirainen (data[size-1] == ' ' || data[size-1] == '\t'))
2a8454fe986d4a881d3612eebb39101be1d6db94Timo Sirainen *error_r = t_strdup_printf("Stream error: %s",