bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "lib.h"
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen#include "str.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "istream.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-parser.h"
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch#include "http-date.h"
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen#include "http-message-parser.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-response-parser.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include <ctype.h>
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschenum http_response_parser_state {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_INIT = 0,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_VERSION,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_SP1,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_STATUS,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_SP2,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_REASON,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_CR,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_LF,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_RESPONSE_PARSE_STATE_HEADER
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch};
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_response_parser {
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen struct http_message_parser parser;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch enum http_response_parser_state state;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch unsigned int response_status;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch const char *response_reason;
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch uoff_t response_offset;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch};
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Boschstruct http_response_parser *
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Boschhttp_response_parser_init(struct istream *input,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch const struct http_header_limits *hdr_limits,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch enum http_response_parse_flags flags)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_response_parser *parser;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch enum http_message_parse_flags msg_flags = 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* FIXME: implement status line limit */
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if ((flags & HTTP_RESPONSE_PARSE_FLAG_STRICT) != 0)
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch msg_flags |= HTTP_MESSAGE_PARSE_FLAG_STRICT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser = i_new(struct http_response_parser, 1);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch http_message_parser_init(&parser->parser,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch input, hdr_limits, 0, msg_flags);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return parser;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_response_parser_deinit(struct http_response_parser **_parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_response_parser *parser = *_parser;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
da300472555d9afdb0bcb767456f731cf5c2f6aaStephan Bosch *_parser = NULL;
f9d2a1f21ad65262bc630f0834d7eead06a1bac3Timo Sirainen
f9d2a1f21ad65262bc630f0834d7eead06a1bac3Timo Sirainen http_message_parser_deinit(&parser->parser);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_free(parser);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic void
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_response_parser_restart(struct http_response_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
6dad0888fcec8372f230941c70d8940b8c203b32Stephan Bosch http_message_parser_restart(&parser->parser, NULL);
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch parser->response_status = 0;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch parser->response_reason = NULL;
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch parser->response_offset = (uoff_t)-1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_response_parse_status(struct http_response_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen const unsigned char *p = parser->parser.cur;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen const size_t size = parser->parser.end - parser->parser.cur;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* status-code = 3DIGIT
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen if (size < 3)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen if (!i_isdigit(p[0]) || !i_isdigit(p[1]) || !i_isdigit(p[2]))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch parser->response_status =
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0');
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch if (parser->response_status < 100 ||
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch parser->response_status >= 600)
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch return -1;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen parser->parser.cur += 3;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_response_parse_reason(struct http_response_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen const unsigned char *p = parser->parser.cur;
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch pool_t pool;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* reason-phrase = *( HTAB / SP / VCHAR / obs-text )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch // FIXME: limit length
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen while (p < parser->parser.end && http_char_is_text(*p))
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen p++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (p == parser->parser.end)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch pool = http_message_parser_get_pool(&parser->parser);
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch parser->response_reason =
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch p_strdup_until(pool, parser->parser.cur, p);
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen parser->parser.cur = p;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainenstatic const char *_reply_sanitize(struct http_message_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen string_t *str = t_str_new(32);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen const unsigned char *p;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen unsigned int i;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen bool quote_open = FALSE;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen i_assert(parser->cur < parser->end);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen for (p = parser->cur, i = 0; p < parser->end && i < 20; p++, i++) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (*p >= 0x20 && *p < 0x7F) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (!quote_open) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen str_append_c(str, '`');
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen quote_open = TRUE;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen str_append_c(str, *p);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen } else {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (quote_open) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen str_append_c(str, '\'');
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen quote_open = FALSE;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (*p == 0x0a)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen str_append(str, "<LF>");
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen else if (*p == 0x0d)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen str_append(str, "<CR>");
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen else
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen str_printfa(str, "<0x%02x>", *p);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (quote_open)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen str_append_c(str, '\'');
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen return str_c(str);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschstatic int http_response_parse(struct http_response_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen struct http_message_parser *_parser = &parser->parser;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.1.2: Status Line
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch status-line = HTTP-version SP status-code SP reason-phrase CRLF
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch status-code = 3DIGIT
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch reason-phrase = *( HTAB / SP / VCHAR / obs-text )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch switch (parser->state) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_INIT:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_VERSION;
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch parser->response_offset = _parser->input->v_offset +
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch (_parser->cur - _parser->begin);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_VERSION:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret=http_message_parse_version(_parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (ret < 0)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen _parser->error = t_strdup_printf(
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen "Invalid HTTP version in response: %s",
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen _reply_sanitize(_parser));
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return ret;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_SP1;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (_parser->cur == _parser->end)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return 0;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_SP1:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (*_parser->cur != ' ') {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->error = t_strdup_printf
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ("Expected ' ' after response version, but found %s",
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen _reply_sanitize(_parser));
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return -1;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->cur++;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_STATUS;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (_parser->cur >= _parser->end)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return 0;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_STATUS:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret=http_response_parse_status(parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (ret < 0)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->error = "Invalid HTTP status code in response";
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return ret;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_SP2;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (_parser->cur == _parser->end)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return 0;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_SP2:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (*_parser->cur != ' ') {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->error = t_strdup_printf
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ("Expected ' ' after response status code, but found %s",
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen _reply_sanitize(_parser));
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return -1;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->cur++;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_REASON;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (_parser->cur >= _parser->end)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return 0;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_REASON:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret=http_response_parse_reason(parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_assert(ret == 0);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return 0;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_CR;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (_parser->cur == _parser->end)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return 0;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_CR:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (*_parser->cur == '\r')
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen _parser->cur++;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_LF;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (_parser->cur == _parser->end)
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return 0;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* fall through */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_LF:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (*_parser->cur != '\n') {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->error = t_strdup_printf
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ("Expected line end after response, but found %s",
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen _reply_sanitize(_parser));
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->cur++;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_HEADER;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return 1;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch case HTTP_RESPONSE_PARSE_STATE_HEADER:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch default:
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch break;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_unreached();
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschstatic int
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Boschhttp_response_parse_status_line(struct http_response_parser *parser)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen struct http_message_parser *_parser = &parser->parser;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen const unsigned char *begin;
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen size_t size, old_bytes = 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
651f981ca065d2365bf2ea07b04318a5402f047aTimo Sirainen while ((ret = i_stream_read_bytes(_parser->input, &begin, &size,
651f981ca065d2365bf2ea07b04318a5402f047aTimo Sirainen old_bytes + 1)) > 0) {
8f985b8241246b321db52b9f63d2e6a4a83d4707Stephan Bosch _parser->begin = _parser->cur = begin;
8f985b8241246b321db52b9f63d2e6a4a83d4707Stephan Bosch _parser->end = _parser->begin + size;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret = http_response_parse(parser)) < 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen i_stream_skip(_parser->input, _parser->cur - begin);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (ret > 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen old_bytes = i_stream_get_data_size(_parser->input);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (ret == -2) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch _parser->error = "HTTP status line is too long";
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return -1;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (ret < 0) {
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (_parser->input->eof &&
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen parser->state == HTTP_RESPONSE_PARSE_STATE_INIT)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
2a8454fe986d4a881d3612eebb39101be1d6db94Timo Sirainen _parser->error = t_strdup_printf("Stream error: %s",
2a8454fe986d4a881d3612eebb39101be1d6db94Timo Sirainen i_stream_get_error(_parser->input));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Boschstatic int
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Boschhttp_response_parse_retry_after(const char *hdrval, time_t resp_time,
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch time_t *retry_after_r)
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch{
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch time_t delta;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7231, Section 7.1.3: Retry-After
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch The value of this field can be either an HTTP-date or a number of
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch seconds to delay after the response is received.
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch Retry-After = HTTP-date / delta-seconds
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch A delay-seconds value is a non-negative decimal integer, representing
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch time in seconds.
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch delta-seconds = 1*DIGIT
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch */
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if (str_to_time(hdrval, &delta) >= 0) {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if (resp_time == (time_t)-1) {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch return -1;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch }
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch *retry_after_r = resp_time + delta;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch return 0;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch }
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch return (http_date_parse
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch ((unsigned char *)hdrval, strlen(hdrval), retry_after_r) ? 0 : -1);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch}
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Boschuoff_t http_response_parser_get_last_offset(struct http_response_parser *parser)
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch{
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch return parser->response_offset;
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch}
be90d5399e65511d3161fa5f2063b048a2ee8264Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschint http_response_parse_next(struct http_response_parser *parser,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch enum http_response_payload_type payload_type,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct http_response *response, const char **error_r)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch const char *hdrval;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch time_t retry_after = (time_t)-1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
88ead42eefcad4af0313f55275de196f96e8f002Stephan Bosch i_zero(response);
88ead42eefcad4af0313f55275de196f96e8f002Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* make sure we finished streaming payload from previous response
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch before we continue. */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret = http_message_parse_finish_payload(&parser->parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch *error_r = parser->parser.error;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen return ret;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
c177bc7c153dd4d00dfa719f8a93c81129c4235eStephan Bosch if (parser->state == HTTP_RESPONSE_PARSE_STATE_INIT)
c177bc7c153dd4d00dfa719f8a93c81129c4235eStephan Bosch http_response_parser_restart(parser);
c177bc7c153dd4d00dfa719f8a93c81129c4235eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3:
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch HTTP-message = start-line
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch *( header-field CRLF )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch CRLF
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch [ message-body ]
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (parser->state != HTTP_RESPONSE_PARSE_STATE_HEADER) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret = http_response_parse_status_line(parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch *error_r = parser->parser.error;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return ret;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if ((ret = http_message_parse_headers(&parser->parser)) <= 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch *error_r = parser->parser.error;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return ret;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.3.2: Content-Length
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch A server MUST NOT send a Content-Length header field in any response
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch with a status code of 1xx (Informational) or 204 (No Content).
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch if ((parser->response_status / 100 == 1 || parser->response_status == 204) &&
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen parser->parser.msg.content_length > 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch *error_r = t_strdup_printf(
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Unexpected Content-Length header field for %u response "
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch "(length=%"PRIuUOFF_T")", parser->response_status,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen parser->parser.msg.content_length);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.3.3: Message Body Length
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch 1. Any response to a HEAD request and any response with a 1xx
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch (Informational), 204 (No Content), or 304 (Not Modified) status
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch code is always terminated by the first empty line after the
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch header fields, regardless of the header fields present in the
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch message, and thus cannot contain a message body.
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch if (parser->response_status / 100 == 1 || parser->response_status == 204
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch || parser->response_status == 304) { // HEAD is handled in caller
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if ((payload_type == HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED) ||
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch (payload_type == HTTP_RESPONSE_PAYLOAD_TYPE_ONLY_UNSUCCESSFUL &&
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch parser->response_status / 100 != 2)) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* [ message-body ] */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (http_message_parse_body(&parser->parser, FALSE) < 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch *error_r = parser->parser.error;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return -1;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7231, Section 7.1.3: Retry-After
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch Servers send the "Retry-After" header field to indicate how long the
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch user agent ought to wait before making a follow-up request. When
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch sent with a 503 (Service Unavailable) response, Retry-After indicates
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch how long the service is expected to be unavailable to the client.
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch When sent with any 3xx (Redirection) response, Retry-After indicates
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch the minimum time that the user agent is asked to wait before issuing
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch the redirected request.
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch */
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if (parser->response_status == 503 || (parser->response_status / 100) == 3) {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch hdrval = http_header_field_get(parser->parser.msg.header, "Retry-After");
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch if (hdrval != NULL) {
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch (void)http_response_parse_retry_after
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch (hdrval, parser->parser.msg.date, &retry_after);
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch /* broken Retry-After header is ignored */
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch }
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch }
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch parser->state = HTTP_RESPONSE_PARSE_STATE_INIT;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->status = parser->response_status;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->reason = parser->response_reason;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->version_major = parser->parser.msg.version_major;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->version_minor = parser->parser.msg.version_minor;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->location = parser->parser.msg.location;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->date = parser->parser.msg.date;
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch response->retry_after = retry_after;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->payload = parser->parser.payload;
e8f1e510df3ab051a816715c2056f0d10aee929eStephan Bosch response->header = parser->parser.msg.header;
9e7bf91667639a2390207ab4d90bf88e2afcec2aStephan Bosch response->connection_options = parser->parser.msg.connection_options;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch response->connection_close = parser->parser.msg.connection_close;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}