bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch/* [URI-GEN] RFC3986 Appendix A:
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch absolute-URI = scheme ":" hier-part [ "?" query ]
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch URI-reference = URI / relative-ref
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch relative-ref = relative-part [ "?" query ] [ "#" fragment ]
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch relative-part = "//" authority path-abempty
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-absolute
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-noscheme
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch hier-part = "//" authority path-abempty
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-absolute
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-rootless
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch authority = [ userinfo "@" ] host [ ":" port ]
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch host = IP-literal / IPv4address / reg-name
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch port = *DIGIT
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch IP-literal = "[" ( IPv6address / IPvFuture ) "]"
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch IPv6address = 6( h16 ":" ) ls32
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / "::" 5( h16 ":" ) ls32
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / [ h16 ] "::" 4( h16 ":" ) ls32
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / [ *4( h16 ":" ) h16 ] "::" ls32
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / [ *5( h16 ":" ) h16 ] "::" h16
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / [ *6( h16 ":" ) h16 ] "::"
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch h16 = 1*4HEXDIG
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch ls32 = ( h16 ":" h16 ) / IPv4address
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch dec-octet = DIGIT ; 0-9
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / %x31-39 DIGIT ; 10-99
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / "1" 2DIGIT ; 100-199
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / "2" %x30-34 DIGIT ; 200-249
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / "25" %x30-35 ; 250-255
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch reg-name = *( unreserved / pct-encoded / sub-delims )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch path = path-abempty ; begins with "/" or is empty
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-absolute ; begins with "/" but not "//"
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-noscheme ; begins with a non-colon segment
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-rootless ; begins with a segment
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-empty ; zero characters
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch path-abempty = *( "/" segment )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch path-absolute = "/" [ segment-nz *( "/" segment ) ]
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch path-noscheme = segment-nz-nc *( "/" segment )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch path-rootless = segment-nz *( "/" segment )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch path-empty = 0<pchar>
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch segment = *pchar
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch segment-nz = 1*pchar
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch ; non-zero-length segment without any colon ":"
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch query = *( pchar / "/" / "?" )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch fragment = *( pchar / "/" / "?" )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch pct-encoded = "%" HEXDIG HEXDIG
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch reserved = gen-delims / sub-delims
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / "*" / "+" / "," / ";" / "="
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch/* Character lookup table
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" [bit0]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * / "*" / "+" / "," / ";" / "=" [bit1]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" [bit2]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * pchar = unreserved / sub-delims / ":" / "@" [bit0|bit1|bit3]
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch * 'pfchar' = unreserved / sub-delims / ":" / "@" / "/"
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch * [bit0|bit1|bit3|bit5]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * 'uchar' = unreserved / sub-delims / ":" [bit0|bit1|bit4]
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch * 'qchar' = pchar / "/" / "?" [bit0|bit1|bit3|bit5|bit6]
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch#define CHAR_MASK_PFCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5))
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch#define CHAR_MASK_QCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<6))
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic unsigned const char _uri_char_lookup[256] = {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch 0, 2, 0, 4, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, 36, // 20
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 2, 0, 2, 0, 68, // 30
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4, 0, 1, // 50
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic inline int _decode_hex_digit(const unsigned char digit)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch case '0': case '1': case '2': case '3': case '4':
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch case '5': case '6': case '7': case '8': case '9':
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Boschuri_parse_pct_encoded_data(struct uri_parser *parser,
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch const unsigned char **p, const unsigned char *pend,
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch if (**p != '%' || (pend != NULL && *p >= pend))
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (**p == 0 || *(*p+1) == 0 || (pend != NULL && *p+1 >= pend)) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->error = "Unexpected URI boundary after '%'";
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Expecting hex digit after '%%', but found '%c'", **p);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Expecting hex digit after '%%%c', but found '%c'", *((*p)-1), **p);
89e040049336e69c43fec09dcbdfd0f2ae5efd51Martti Rannanjärvi if (!parser->allow_pct_nul && *ch_r == '\0') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Percent encoding is not allowed to encode NUL character";
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Boschint uri_parse_pct_encoded(struct uri_parser *parser,
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch unsigned char *ch_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschuri_parse_unreserved_char(struct uri_parser *parser, unsigned char *ch_r)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if ((_uri_char_lookup[*parser->cur] & CHAR_MASK_UNRESERVED) != 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschint uri_parse_unreserved(struct uri_parser *parser, string_t *part)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_unreserved_char(parser, &ch)) < 0)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Boschint uri_parse_unreserved_pct(struct uri_parser *parser, string_t *part)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch else if (ret == 0 &&
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch (ret=uri_parse_unreserved_char(parser, &ch)) < 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschbool uri_data_decode(struct uri_parser *parser, const char *data,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *p = (const unsigned char *)data;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *pend = (const unsigned char *)until;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* NULL means unlimited; solely rely on '\0' */
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen *decoded_r = p_strdup(parser->pool, str_c(decoded));
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Boschint uri_parse_scheme(struct uri_parser *parser, const char **scheme_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch if (parser->cur >= parser->end || !i_isalpha(*parser->cur))
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch if (parser->cur >= parser->end || *parser->cur != ':') {
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch *scheme_r = t_strndup(first, parser->cur - first);
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Boschint uri_cut_scheme(const char **uri_p, const char **scheme_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschuri_parse_dec_octet(struct uri_parser *parser, string_t *literal,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * dec-octet = DIGIT ; 0-9
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * / %x31-39 DIGIT ; 10-99
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * / "1" 2DIGIT ; 100-199
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * / "2" %x30-34 DIGIT ; 200-249
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * / "25" %x30-35 ; 250-255
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch while (parser->cur < parser->end && i_isdigit(*parser->cur)) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschuri_parse_ipv4address(struct uri_parser *parser, string_t *literal,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (i = 0; i < 3 && parser->cur < parser->end; i++) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschuri_do_parse_reg_name(struct uri_parser *parser,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * reg-name = *( unreserved / pct-encoded / sub-delims )
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch unsigned char c;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* unreserved / pct-encoded */
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch if ((ret=uri_parse_pct_encoded(parser, &c)) < 0)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch else if (ret == 0 &&
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch (ret=uri_parse_unreserved_char(parser, &c)) < 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* sub-delims */
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if ((c & 0x80) == 0 && (_uri_char_lookup[c] & CHAR_MASK_SUB_DELIMS) != 0) {
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_reg_name(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch reg_name = uri_parser_get_tmpbuf(parser, 256);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if ((ret=uri_do_parse_reg_name(parser, reg_name)) <= 0)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschstatic int uri_do_parse_host_name(struct uri_parser *parser,
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* RFC 3986, Section 3.2.2:
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch A registered name intended for lookup in the DNS uses the syntax
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch defined in Section 3.5 of [RFC1034] and Section 2.1 of [RFC1123].
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch Such a name consists of a sequence of domain labels separated by ".",
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch each domain label starting and ending with an alphanumeric character
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch and possibly also containing "-" characters. The rightmost domain
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch label of a fully qualified domain name in DNS may be followed by a
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch single "." and should be if it is necessary to distinguish between
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch the complete domain name and some local domain.
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch RFC 2396, Section 3.2.2 (old URI specification):
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch hostname = *( domainlabel "." ) toplabel [ "." ]
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch toplabel = alpha | alpha *( alphanum | "-" ) alphanum
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch The description in RFC 3986 is more liberal, so:
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch hostname = *( domainlabel "." ) domainlabel [ "." ]
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch We also support percent encoding in spirit of the generic reg-name,
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch even though this should explicitly not be used according to the RFC.
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch It is, however, not strictly forbidden (unlike older RFC), so we
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* alphanum */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch } else if (ret > 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* *( alphanum | "-" ) alphanum */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch } else if (ret > 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch parser->error = "Invalid domain label in hostname";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch str_append_n(host_name, part, parser->cur - part);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* remove trailing '.' */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch str_truncate(host_name, str_len(host_name)-1);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_host_name(struct uri_parser *parser,
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host_name = uri_parser_get_tmpbuf(parser, 256);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if ((ret=uri_do_parse_host_name(parser, host_name)) <= 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschuri_parse_ip_literal(struct uri_parser *parser, string_t *literal,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *p;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* IP-literal = "[" ( IPv6address / IPvFuture ) "]"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * IPv6address = ; Syntax not relevant: parsed using inet_pton()
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* "[" already verified */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* Scan for end of address */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (p = parser->cur+1; p < parser->end; p++) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (*p == ']')
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->error = "Expecting ']' at end of IP-literal";
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_n(literal, parser->cur, p-parser->cur+1);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Future IP host address '%s' not supported", address);
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if ((ret = inet_pton(AF_INET6, address, &ip6)) <= 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * host = IP-literal / IPv4address / reg-name
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* IP-literal / */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (parser->cur < parser->end && *parser->cur == '[') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret=uri_parse_ip_literal(parser, literal, &ip6)) <= 0)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->name = p_strdup(parser->pool, str_c(literal));;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* IPv4address /
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * If it fails to parse, we try to parse it as a reg-name
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_ipv4address(parser, literal, &ip4)) > 0) {
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->name = p_strdup(parser->pool, str_c(literal));
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* reg-name */
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if (uri_do_parse_host_name(parser, literal) < 0)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch } else if (uri_do_parse_reg_name(parser, literal) < 0)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->name = p_strdup(parser->pool, str_c(literal));
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * port = *DIGIT
009217abb57a24a4076092e8e4e165545747839eStephan Bosch while (parser->cur < parser->end && i_isdigit(*parser->cur))
009217abb57a24a4076092e8e4e165545747839eStephan Bosch if (net_str2port(t_strdup_until(first, parser->cur), &port) < 0) {
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschuri_do_parse_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_authority *auth, bool host_name) ATTR_NULL(2)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *p;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * authority = [ userinfo "@" ] host [ ":" port ]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* Scan ahead to check whether there is a [userinfo "@"] uri component */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* refuse 8bit characters */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((*p & 0x80) != 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* break at first delimiter */
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* Extract userinfo */
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen auth->enc_userinfo = p_strdup_until(parser->pool, parser->cur, p);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch (auth == NULL ? NULL : &auth->host), host_name) < 0)
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch /* [":" port] */
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschuri_do_parse_slashslash_authority(struct uri_parser *parser,
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch /* "//" authority */
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch if ((parser->end - parser->cur) <= 2 || parser->cur[0] != '/' ||
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_authority(parser, auth, host_name);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_authority(parser, auth, FALSE);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_slashslash_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_slashslash_authority(parser, auth, FALSE);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_host_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_authority(parser, auth, TRUE);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_slashslash_host_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_slashslash_authority(parser, auth, TRUE);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschint uri_parse_path_segment(struct uri_parser *parser, const char **segment_r)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch (_uri_char_lookup[*parser->cur] & CHAR_MASK_PCHAR) == 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch *parser->cur != '/' && *parser->cur != '?' && *parser->cur != '#' ) {
dbbdcc1224f81a40e746a09e6d44af7c4f24ff71Stephan Bosch "Path component contains invalid character";
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch *segment_r = p_strdup_until(parser->pool, first, parser->cur);
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* check for a leading '/' and indicate absolute path
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch when it is present
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (parser->cur < parser->end && *parser->cur == '/') {
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* parse first segment */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* strip dot segments */
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* '..' -> skip and... */
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* ... pop last segment (if any) */
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch } else if ( relative > 0 ) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* '.' -> skip */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (parser->cur >= parser->end || *parser->cur != '/')
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* parse next path segment */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
62aa68310d6f42467ca26880f678173bf1d26a83Stephan Bosch /* path part of URI is empty */
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch /* special treatment for a trailing '..' or '.' */
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->error = "Path component contains invalid character";
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschint uri_parse_query(struct uri_parser *parser, const char **query_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * URI = { ... } [ "?" query ] { ... }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * query = *( pchar / "/" / "?" )
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (parser->cur >= parser->end || *parser->cur != '?')
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch (_uri_char_lookup[*parser->cur] & CHAR_MASK_QCHAR) == 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (parser->cur < parser->end && *parser->cur != '#') {
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->error = "Query component contains invalid character";
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch *query_r = p_strdup_until(parser->pool, first+1, parser->cur);
639bb36b12b9f9bb54c8bb1be50eac623622f8a0Timo Sirainenint uri_parse_fragment(struct uri_parser *parser, const char **fragment_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * URI = { ... } [ "#" fragment ]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * fragment = *( pchar / "/" / "?" )
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (parser->cur >= parser->end || *parser->cur != '#')
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch (_uri_char_lookup[*parser->cur] & CHAR_MASK_QCHAR) == 0)
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->error = "Fragment component contains invalid character";
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch *fragment_r = p_strdup_until(parser->pool, first+1, parser->cur);
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Boschvoid uri_parser_init_data(struct uri_parser *parser,
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch pool_t pool, const unsigned char *data, size_t size)
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Boschvoid uri_parser_init(struct uri_parser *parser,
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch (parser, pool, (const unsigned char *)uri, strlen(uri));
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstring_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Boschint uri_parse_absolute_generic(struct uri_parser *parser,
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch hier-part = "//" authority path-abempty
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch / path-absolute
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch / path-rootless
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch path-abempty = *( "/" segment )
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch path-absolute = "/" [ segment-nz *( "/" segment ) ]
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch path-rootless = segment-nz *( "/" segment )
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch path-empty = 0<pchar>
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch segment = *pchar
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch segment-nz = 1*pchar
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* scheme ":" */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if ((flags & URI_PARSE_SCHEME_EXTERNAL) == 0 &&
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* "//" authority */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* path-absolute / path-rootless / path-empty */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch ret = uri_parse_path(parser, &relative, NULL);
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* path-abempty */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch } else if (parser->cur < parser->end && *parser->cur == '/') {
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch ret = uri_parse_path(parser, &relative, NULL);
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* [ "?" query ] */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* [ "#" fragment ] */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if ((ret=uri_parse_fragment(parser, NULL)) < 0)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if (ret > 0 && (flags & URI_PARSE_ALLOW_FRAGMENT_PART) == 0) {
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch * Generic URI manipulation
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Boschvoid uri_host_copy(pool_t pool, struct uri_host *dest,
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch /* create host name literal if caller is lazy */
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (host_name == NULL && src->ip.family != 0) {
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch * Check generic URI
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Boschint uri_check_data(const unsigned char *data, size_t size,
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch enum uri_parse_flags flags, const char **error_r)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch ret = uri_parse_absolute_generic(&parser, flags);
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Boschint uri_check(const char *uri, enum uri_parse_flags flags,
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch const char **error_r)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch ((unsigned char *)uri, strlen(uri), flags, error_r);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch * Generic URI construction
e4e9ba5f43f9bf7e072d7d9fcc3259a42ecb15c8Stephan Bosch unsigned char esc_mask, const char *esc_extra,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch while (*p != '\0') {
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if ((*p & 0x80) != 0 || (esc_table[*p] & esc_mask) == 0 ||
4e52bade0f83746405ecc1d0396e9b2ac7b98cd8Stephan Bosch (esc_extra != NULL && strchr(esc_extra, (char)*p) != NULL)) {
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch if ((p - pbegin) > 0)
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch if ((p - pbegin) > 0)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_scheme(string_t *out, const char *scheme)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_user_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_userinfo(string_t *out, const char *userinfo)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_host_name(string_t *out, const char *name)
4e52bade0f83746405ecc1d0396e9b2ac7b98cd8Stephan Bosch CHAR_MASK_UNRESERVED | CHAR_MASK_SUB_DELIMS, NULL, name);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_host_ip(string_t *out, const struct ip_addr *host_ip)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Boschvoid uri_append_host(string_t *out, const struct uri_host *host)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch /* assume IPv6 literal if starts with '['; avoid encoding */
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_port(string_t *out, in_port_t port)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_path_segment_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_path_segment(string_t *out, const char *segment)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_path_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PFCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_path(string_t *out, const char *path)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_query_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_query(string_t *out, const char *query)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_fragment_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_fragment(string_t *out, const char *fragment)