static int http_header_parse_content(struct http_header_parser *parser) { const unsigned char *first; /* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] field-vchar = VCHAR / obs-text */ do { first = parser->cur; while (parser->cur < parser->end && http_char_is_text(*parser->cur)) { parser->cur++; } buffer_append(parser->value_buf, first, parser->cur-first); if (!parser->lenient) break; /* We'll be lenient here to accommodate for some bad servers. We just drop offending characters */ while (parser->cur < parser->end && !http_char_is_text(*parser->cur) && (*parser->cur != '\r' && *parser->cur != '\n')) parser->cur++; } while (parser->cur < parser->end && (*parser->cur != '\r' && *parser->cur != '\n')); if (parser->cur == parser->end) return 0; return 1; }
int http_parse_quoted_string(struct http_parser *parser, const char **str_r) { string_t *str; /* quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) obs-text = %x80-FF */ /* DQUOTE */ if (parser->cur >= parser->end || parser->cur[0] != '"') return 0; parser->cur++; /* *( qdtext / quoted-pair ) */ str = t_str_new(256); for (;;) { const unsigned char *first; /* *qdtext */ first = parser->cur; while (parser->cur < parser->end && http_char_is_qdtext(*parser->cur)) parser->cur++; if (parser->cur >= parser->end) return -1; str_append_data(str, first, parser->cur - first); /* DQUOTE */ if (*parser->cur == '"') { parser->cur++; break; /* "\" */ } else if (*parser->cur == '\\') { parser->cur++; if (parser->cur >= parser->end || !http_char_is_text(*parser->cur)) return -1; str_append_c(str, *parser->cur); parser->cur++; /* ERROR */ } else { return -1; } } *str_r = str_c(str); return 1; }
static int http_transfer_chunked_parse(struct http_transfer_chunked_istream *tcstream) { int ret; /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21; Section 4.1: chunked-body = *chunk last-chunk trailer-part CRLF chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF chunk-size = 1*HEXDIG last-chunk = 1*("0") [ chunk-ext ] CRLF chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) chunk-ext-name = token chunk-ext-val = token / quoted-str-nf chunk-data = 1*OCTET ; a sequence of chunk-size octets trailer-part = *( header-field CRLF ) quoted-str-nf = DQUOTE *( qdtext-nf / quoted-pair ) DQUOTE ; like quoted-string, but disallowing line folding qdtext-nf = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) */ for (;;) { switch (tcstream->state) { case HTTP_CHUNKED_PARSE_STATE_INIT: tcstream->chunk_size = 0; tcstream->chunk_pos = 0; tcstream->parsed_chars = 0; tcstream->state = HTTP_CHUNKED_PARSE_STATE_SIZE; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_SIZE: if ((ret=http_transfer_chunked_parse_size(tcstream)) <= 0) return ret; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT: if (*tcstream->cur != ';') { tcstream->state = HTTP_CHUNKED_PARSE_STATE_CR; break; } /* chunk-ext */ tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_NAME; if (tcstream->cur >= tcstream->end) return 0; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT_NAME: /* chunk-ext-name = token */ if ((ret=http_transfer_chunked_skip_token(tcstream)) <= 0) { if (ret < 0) tcstream->error = "Invalid chunked extension name"; return ret; } tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_EQ; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT_EQ: if (*tcstream->cur != '=') { tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT; break; } tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE; if (tcstream->cur >= tcstream->end) return 0; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE: /* chunk-ext-val = token / quoted-str-nf */ if (*tcstream->cur != '"') { tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN; break; } tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING; if (tcstream->cur >= tcstream->end) return 0; /* fall through */ case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING: for (;;) { if (*tcstream->cur == '"') { tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT; if (tcstream->cur >= tcstream->end) return 0; break; } else if ((ret=http_transfer_chunked_skip_qdtext(tcstream)) <= 0) { if (ret < 0) tcstream->error = "Invalid chunked extension value"; return ret; } else if (*tcstream->cur == '\\') { tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE; if (tcstream->cur >= tcstream->end) return 0; break; } else { tcstream->error = t_strdup_printf( "Invalid character %s in chunked extension value string", _chr_sanitize(*tcstream->cur)); return -1; } } break; case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE: /* ( HTAB / SP / VCHAR / obs-text ) */ if (!http_char_is_text(*tcstream->cur)) { tcstream->error = t_strdup_printf( "Escaped invalid character %s in chunked extension value string", _chr_sanitize(*tcstream->cur)); return -1; } tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING; if (tcstream->cur >= tcstream->end) return 0; break; case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN: if ((ret=http_transfer_chunked_skip_token(tcstream)) <= 0) { if (ret < 0) tcstream->error = "Invalid chunked extension value"; return ret; } tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT; break; case HTTP_CHUNKED_PARSE_STATE_CR: tcstream->state = HTTP_CHUNKED_PARSE_STATE_LF; if (*tcstream->cur == '\r') { tcstream->cur++; if (tcstream->cur >= tcstream->end) return 0; } /* fall through */ case HTTP_CHUNKED_PARSE_STATE_LF: if (*tcstream->cur != '\n') { tcstream->error = t_strdup_printf( "Expected new line after chunk size, but found %s", _chr_sanitize(*tcstream->cur)); return -1; } tcstream->cur++; if (tcstream->chunk_size > 0) tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA; else tcstream->state = HTTP_CHUNKED_PARSE_STATE_TRAILER; return 1; case HTTP_CHUNKED_PARSE_STATE_DATA_READY: /* fall through */ case HTTP_CHUNKED_PARSE_STATE_DATA_CR: tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_LF; if (*tcstream->cur == '\r') { tcstream->cur++; if (tcstream->cur >= tcstream->end) return 0; } /* fall through */ case HTTP_CHUNKED_PARSE_STATE_DATA_LF: if (*tcstream->cur != '\n') { tcstream->error = t_strdup_printf( "Expected new line after chunk data, but found %s", _chr_sanitize(*tcstream->cur)); return -1; } tcstream->cur++; tcstream->state = HTTP_CHUNKED_PARSE_STATE_INIT; break; default: i_unreached(); } } i_unreached(); return -1; }