bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#include "lib.h"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#include "array.h"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#include "str.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "net.h"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#include "uri-util.h"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#include <ctype.h>
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch/* [URI-GEN] RFC3986 Appendix A:
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch absolute-URI = scheme ":" hier-part [ "?" query ]
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch URI-reference = URI / relative-ref
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch relative-ref = relative-part [ "?" query ] [ "#" fragment ]
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch relative-part = "//" authority path-abempty
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-absolute
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-noscheme
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-empty
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch hier-part = "//" authority path-abempty
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-absolute
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-rootless
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch / path-empty
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch
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
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
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
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
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch query = *( pchar / "/" / "?" )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch fragment = *( pchar / "/" / "?" )
62ed3307fb8a4a038a32a5181c503f1b645bf946Stephan Bosch
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 */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#define URI_MAX_SCHEME_NAME_LEN 64
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch/* Character lookup table
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
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]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch#define CHAR_MASK_UNRESERVED (1<<0)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch#define CHAR_MASK_SUB_DELIMS (1<<1)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch#define CHAR_MASK_PCHAR ((1<<0)|(1<<1)|(1<<3))
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch#define CHAR_MASK_PFCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5))
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch#define CHAR_MASK_UCHAR ((1<<0)|(1<<1)|(1<<4))
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch#define CHAR_MASK_QCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<6))
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
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 Bosch};
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic inline int _decode_hex_digit(const unsigned char digit)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch switch (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 return digit - '0';
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return digit - 'a' + 0x0a;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return digit - 'A' + 0x0A;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Boschstatic int
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Boschuri_parse_pct_encoded_data(struct uri_parser *parser,
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch const unsigned char **p, const unsigned char *pend,
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch unsigned char *ch_r) ATTR_NULL(3)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int value;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch if (**p != '%' || (pend != NULL && *p >= pend))
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch return 0;
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch *p += 1;
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (**p == 0 || *(*p+1) == 0 || (pend != NULL && *p+1 >= pend)) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->error = "Unexpected URI boundary after '%'";
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((value = _decode_hex_digit(**p)) < 0) {
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen parser->error = p_strdup_printf(parser->pool,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Expecting hex digit after '%%', but found '%c'", **p);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *ch_r = (value & 0x0f) << 4;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *p += 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((value = _decode_hex_digit(**p)) < 0) {
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen parser->error = p_strdup_printf(parser->pool,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Expecting hex digit after '%%%c', but found '%c'", *((*p)-1), **p);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *ch_r |= (value & 0x0f);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *p += 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
89e040049336e69c43fec09dcbdfd0f2ae5efd51Martti Rannanjärvi if (!parser->allow_pct_nul && *ch_r == '\0') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->error =
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Percent encoding is not allowed to encode NUL character";
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Boschint uri_parse_pct_encoded(struct uri_parser *parser,
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch unsigned char *ch_r)
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch{
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch return uri_parse_pct_encoded_data
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch (parser, &parser->cur, parser->end, ch_r);
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch}
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic int
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschuri_parse_unreserved_char(struct uri_parser *parser, unsigned char *ch_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((*parser->cur & 0x80) != 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if ((_uri_char_lookup[*parser->cur] & CHAR_MASK_UNRESERVED) != 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *ch_r = *parser->cur;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschint uri_parse_unreserved(struct uri_parser *parser, string_t *part)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int len = 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch while (parser->cur < parser->end) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch unsigned char ch = 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_unreserved_char(parser, &ch)) < 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (ret == 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (part != NULL)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_append_c(part, ch);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch len++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return len > 0 ? 1 : 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Boschint uri_parse_unreserved_pct(struct uri_parser *parser, string_t *part)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch{
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch int len = 0;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch while (parser->cur < parser->end) {
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch int ret;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch unsigned char ch = 0;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch return -1;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch else if (ret == 0 &&
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch (ret=uri_parse_unreserved_char(parser, &ch)) < 0)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch return -1;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch if (ret == 0)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch break;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch if (part != NULL)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch str_append_c(part, ch);
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch len++;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch }
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch return len > 0 ? 1 : 0;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch}
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschbool uri_data_decode(struct uri_parser *parser, const char *data,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const char *until, const char **decoded_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *p = (const unsigned char *)data;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *pend = (const unsigned char *)until;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch string_t *decoded;
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (pend == NULL) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* NULL means unlimited; solely rely on '\0' */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch pend = (const unsigned char *)(size_t)-1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (p >= pend || *p == '\0') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (decoded_r != NULL)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *decoded_r = "";
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return TRUE;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch decoded = uri_parser_get_tmpbuf(parser, 256);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch while (p < pend && *p != '\0') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch unsigned char ch;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch if ((ret=uri_parse_pct_encoded_data
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch (parser, &p, NULL, &ch)) != 0) {
32f28ff765ef6983af0df78ebc5289b478abf3feStephan Bosch if (ret < 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return FALSE;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_append_c(decoded, ch);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch } else {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_append_c(decoded, *p);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch p++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (decoded_r != NULL)
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen *decoded_r = p_strdup(parser->pool, str_c(decoded));
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return TRUE;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Boschint uri_parse_scheme(struct uri_parser *parser, const char **scheme_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch const unsigned char *first = parser->cur;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch size_t len = 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* RFC 3968:
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch if (parser->cur >= parser->end || !i_isalpha(*parser->cur))
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch return 0;
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch while (len < URI_MAX_SCHEME_NAME_LEN &&
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch parser->cur < parser->end) {
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch if (!i_isalnum(*parser->cur) &&
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch *parser->cur != '+' && *parser->cur != '-' &&
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch *parser->cur != '.')
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch len++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch if (parser->cur >= parser->end || *parser->cur != ':') {
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch parser->error = "Invalid URI scheme";
639bb36b12b9f9bb54c8bb1be50eac623622f8a0Timo Sirainen return -1;
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch }
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (scheme_r != NULL)
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch *scheme_r = t_strndup(first, parser->cur - first);
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch parser->cur++;
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Boschint uri_cut_scheme(const char **uri_p, const char **scheme_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch struct uri_parser parser;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch uri_parser_init(&parser, NULL, *uri_p);
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch if (uri_parse_scheme(&parser, scheme_r) <= 0)
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch return -1;
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch *uri_p = (const char *)parser.cur;
b4167b174b4eeebceaf80e240635e601f0a9860cStephan Bosch return 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic int
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschuri_parse_dec_octet(struct uri_parser *parser, string_t *literal,
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch uint8_t *octet_r) ATTR_NULL(2)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
0e702716169a158b87d08e21d1a128eb99e23fa1Phil Carmody unsigned int octet = 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int count = 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* RFC 3986:
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
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 */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch while (parser->cur < parser->end && i_isdigit(*parser->cur)) {
639bb36b12b9f9bb54c8bb1be50eac623622f8a0Timo Sirainen octet = octet * 10 + (parser->cur[0] - '0');
0e702716169a158b87d08e21d1a128eb99e23fa1Phil Carmody if (octet > 255)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (literal != NULL)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_append_c(literal, *parser->cur);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch count++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (count > 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *octet_r = octet;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic int
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschuri_parse_ipv4address(struct uri_parser *parser, string_t *literal,
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch struct in_addr *ip4_r) ATTR_NULL(2,3)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch uint8_t octet;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch uint32_t ip = 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int i;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* RFC 3986:
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch ip = octet;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (i = 0; i < 3 && parser->cur < parser->end; i++) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (*parser->cur != '.')
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (literal != NULL)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_append_c(literal, '.');
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch ip = (ip << 8) + octet;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (ip4_r != NULL)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch ip4_r->s_addr = htonl(ip);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Boschstatic int
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschuri_do_parse_reg_name(struct uri_parser *parser,
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch string_t *reg_name) ATTR_NULL(2)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* RFC 3986:
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * reg-name = *( unreserved / pct-encoded / sub-delims )
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch while (parser->cur < parser->end) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch unsigned char c;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* unreserved / pct-encoded */
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch if ((ret=uri_parse_pct_encoded(parser, &c)) < 0)
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch return -1;
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch else if (ret == 0 &&
b7e953d7eecd18f1d0de701cc181e8830d8167b1Stephan Bosch (ret=uri_parse_unreserved_char(parser, &c)) < 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (ret > 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (reg_name != NULL)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_append_c(reg_name, c);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch continue;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* sub-delims */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch c = *parser->cur;
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if ((c & 0x80) == 0 && (_uri_char_lookup[c] & CHAR_MASK_SUB_DELIMS) != 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (reg_name != NULL)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_append_c(reg_name, *parser->cur);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch continue;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
f3cf2f02155c4bac23fd50f0de96c0cae9c46478Stephan Bosch return 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_reg_name(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch const char **reg_name_r)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch{
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch string_t *reg_name = NULL;
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch int ret;
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if (reg_name_r != NULL)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch reg_name = uri_parser_get_tmpbuf(parser, 256);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if ((ret=uri_do_parse_reg_name(parser, reg_name)) <= 0)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return ret;
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if (reg_name_r != NULL)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch *reg_name_r = str_c(reg_name);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return 1;
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch}
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschstatic int uri_do_parse_host_name(struct uri_parser *parser,
14383bf2be0296954609df5afd3c63c6555815f9Stephan Bosch string_t *host_name) ATTR_NULL(2)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch{
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch const unsigned char *first, *part;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch int ret;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* RFC 3986, Section 3.2.2:
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
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
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch RFC 2396, Section 3.2.2 (old URI specification):
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch hostname = *( domainlabel "." ) toplabel [ "." ]
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch toplabel = alpha | alpha *( alphanum | "-" ) alphanum
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch The description in RFC 3986 is more liberal, so:
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch hostname = *( domainlabel "." ) domainlabel [ "." ]
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
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 support it.
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch first = part = parser->cur;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch for (;;) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch const unsigned char *offset;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch unsigned char ch, pch;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* alphanum */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch offset = parser->cur;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch ch = pch = *parser->cur;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (parser->cur >= parser->end)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch break;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return -1;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch } else if (ret > 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (!i_isalnum(ch))
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return -1;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (host_name != NULL)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch str_append_c(host_name, ch);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch part = parser->cur;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch } else {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (!i_isalnum(*parser->cur))
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch break;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch parser->cur++;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch }
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (parser->cur < parser->end) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* *( alphanum | "-" ) alphanum */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch do {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch offset = parser->cur;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return -1;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch } else if (ret > 0) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (!i_isalnum(ch) && ch != '-')
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch break;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (host_name != NULL) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (offset > part)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch str_append_n(host_name, part, offset - part);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch str_append_c(host_name, ch);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch }
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch part = parser->cur;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch } else {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch ch = *parser->cur;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (!i_isalnum(ch) && ch != '-')
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch break;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch parser->cur++;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch }
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch pch = ch;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch } while (parser->cur < parser->end);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (!i_isalnum(pch)) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch parser->error = "Invalid domain label in hostname";
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return -1;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch }
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch }
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (host_name != NULL && parser->cur > part)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch str_append_n(host_name, part, parser->cur - part);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* "." */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (parser->cur >= parser->end || ch != '.')
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch break;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (host_name != NULL)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch str_append_c(host_name, '.');
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (parser->cur == offset)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch parser->cur++;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch part = parser->cur;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch }
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (parser->cur == first)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return 0;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch /* remove trailing '.' */
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (host_name != NULL) {
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch const char *name = str_c(host_name);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch i_assert(str_len(host_name) > 0);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch if (name[str_len(host_name)-1] == '.')
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch str_truncate(host_name, str_len(host_name)-1);
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch }
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return 1;
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch}
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_host_name(struct uri_parser *parser,
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch const char **host_name_r)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch{
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch string_t *host_name = NULL;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch int ret;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (host_name_r != NULL)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host_name = uri_parser_get_tmpbuf(parser, 256);
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if ((ret=uri_do_parse_host_name(parser, host_name)) <= 0)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch return ret;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
f95afa9df14bee49d82baa3679fc94ef3a101d92Stephan Bosch if (host_name_r != NULL)
f95afa9df14bee49d82baa3679fc94ef3a101d92Stephan Bosch *host_name_r = str_c(host_name);
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch return 1;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch}
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic int
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschuri_parse_ip_literal(struct uri_parser *parser, string_t *literal,
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch struct in6_addr *ip6_r) ATTR_NULL(2,3)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *p;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const char *address;
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch struct in6_addr ip6;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
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 */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* "[" already verified */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* Scan for end of address */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (p = parser->cur+1; p < parser->end; p++) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (*p == ']')
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (p >= parser->end || *p != ']') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->error = "Expecting ']' at end of IP-literal";
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (literal != NULL)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_n(literal, parser->cur, p-parser->cur+1);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch address = t_strdup_until(parser->cur+1, p);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur = p + 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (*address == '\0') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->error = "Empty IPv6 host address";
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (*address == 'v') {
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen parser->error = p_strdup_printf(parser->pool,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Future IP host address '%s' not supported", address);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if ((ret = inet_pton(AF_INET6, address, &ip6)) <= 0) {
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen parser->error = p_strdup_printf(parser->pool,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "Invalid IPv6 host address '%s'", address);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (ip6_r != NULL)
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch *ip6_r = ip6;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschstatic int
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschuri_do_parse_host(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_host *host, bool host_name)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch ATTR_NULL(2)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *preserve;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct in_addr ip4;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct in6_addr ip6;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch string_t *literal = NULL;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* RFC 3986:
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * host = IP-literal / IPv4address / reg-name
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (host != NULL)
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(host);
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch literal = uri_parser_get_tmpbuf(parser, 256);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
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)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (host != NULL) {
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->name = p_strdup(parser->pool, str_c(literal));;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->ip.family = AF_INET6;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->ip.u.ip6 = ip6;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* IPv4address /
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * If it fails to parse, we try to parse it as a reg-name
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch preserve = parser->cur;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_ipv4address(parser, literal, &ip4)) > 0) {
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (host != NULL) {
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->name = p_strdup(parser->pool, str_c(literal));
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->ip.family = AF_INET;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->ip.u.ip4 = ip4;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur = preserve;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_truncate(literal, 0);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* reg-name */
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if (host_name) {
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if (uri_do_parse_host_name(parser, literal) < 0)
b0dfc8fc0dd5d08fe6a746e346dca6c737749d89Stephan Bosch return -1;
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch } else if (uri_do_parse_reg_name(parser, literal) < 0)
f3cf2f02155c4bac23fd50f0de96c0cae9c46478Stephan Bosch return -1;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (host != NULL)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host->name = p_strdup(parser->pool, str_c(literal));
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_host(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_host *host)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch{
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_host(parser, host, TRUE);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch}
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Boschstatic int
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Boschuri_parse_port(struct uri_parser *parser,
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch struct uri_authority *auth) ATTR_NULL(2)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
009217abb57a24a4076092e8e4e165545747839eStephan Bosch const unsigned char *first;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch in_port_t port;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* RFC 3986:
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * port = *DIGIT
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
009217abb57a24a4076092e8e4e165545747839eStephan Bosch first = parser->cur;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch while (parser->cur < parser->end && i_isdigit(*parser->cur))
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur++;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch
009217abb57a24a4076092e8e4e165545747839eStephan Bosch if (parser->cur == first)
009217abb57a24a4076092e8e4e165545747839eStephan Bosch return 0;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch if (net_str2port(t_strdup_until(first, parser->cur), &port) < 0) {
009217abb57a24a4076092e8e4e165545747839eStephan Bosch parser->error = "Invalid port number";
009217abb57a24a4076092e8e4e165545747839eStephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch if (auth != NULL)
009217abb57a24a4076092e8e4e165545747839eStephan Bosch auth->port = port;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschstatic int
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschuri_do_parse_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_authority *auth, bool host_name) ATTR_NULL(2)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *p;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch /*
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * authority = [ userinfo "@" ] host [ ":" port ]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (auth != NULL)
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(auth);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* Scan ahead to check whether there is a [userinfo "@"] uri component */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (p = parser->cur; p < parser->end; p++){
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* refuse 8bit characters */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((*p & 0x80) != 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* break at first delimiter */
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* Extract userinfo */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (p < parser->end && *p == '@') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (auth != NULL)
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen auth->enc_userinfo = p_strdup_until(parser->pool, parser->cur, p);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur = p+1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* host */
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch if (uri_do_parse_host(parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch (auth == NULL ? NULL : &auth->host), host_name) < 0)
f3cf2f02155c4bac23fd50f0de96c0cae9c46478Stephan Bosch return -1;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch if (parser->cur == parser->end)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch switch (*parser->cur) {
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch case ':': case '/': case '?': case '#':
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch break;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch default:
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->error = "Invalid host identifier";
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch return -1;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch }
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch /* [":" port] */
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch if (*parser->cur == ':') {
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch if ((ret = uri_parse_port(parser, auth)) < 0)
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch return ret;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch if (parser->cur == parser->end)
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch return 1;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch switch (*parser->cur) {
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch case '/': case '?': case '#':
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch break;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch default:
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->error = "Invalid host port";
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch return -1;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch }
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch }
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschstatic int
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschuri_do_parse_slashslash_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_authority *auth, bool host_name)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch ATTR_NULL(2)
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch{
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch /* "//" authority */
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch if ((parser->end - parser->cur) <= 2 || parser->cur[0] != '/' ||
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch parser->cur[1] != '/')
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch return 0;
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch
3ec5378aadaa699c38b2e02be30aae1add36eb7cTimo Sirainen parser->cur += 2;
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_authority(parser, auth, host_name);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch}
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_authority *auth)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch{
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_authority(parser, auth, FALSE);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch}
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_slashslash_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_authority *auth)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch{
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_slashslash_authority(parser, auth, FALSE);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch}
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_host_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_authority *auth)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch{
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_authority(parser, auth, TRUE);
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch}
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Boschint uri_parse_slashslash_host_authority(struct uri_parser *parser,
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch struct uri_authority *auth)
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch{
d3b0b5d2389acc43c75b63d2960daf82cf1f8aa7Stephan Bosch return uri_do_parse_slashslash_authority(parser, auth, TRUE);
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch}
df70bf8c997cd91452cdb3a5c2e20605d30446d2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschint uri_parse_path_segment(struct uri_parser *parser, const char **segment_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch const unsigned char *first = parser->cur;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch while (parser->cur < parser->end) {
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (*parser->cur == '%') {
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch unsigned char ch = 0;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch return -1;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (ret > 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch continue;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((*parser->cur & 0x80) != 0 ||
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch (_uri_char_lookup[*parser->cur] & CHAR_MASK_PCHAR) == 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (parser->cur < parser->end &&
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch *parser->cur != '/' && *parser->cur != '?' && *parser->cur != '#' ) {
dbbdcc1224f81a40e746a09e6d44af7c4f24ff71Stephan Bosch parser->error =
dbbdcc1224f81a40e746a09e6d44af7c4f24ff71Stephan Bosch "Path component contains invalid character";
dbbdcc1224f81a40e746a09e6d44af7c4f24ff71Stephan Bosch return -1;
dbbdcc1224f81a40e746a09e6d44af7c4f24ff71Stephan Bosch }
dbbdcc1224f81a40e746a09e6d44af7c4f24ff71Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (first == parser->cur)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (segment_r != NULL)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch *segment_r = p_strdup_until(parser->pool, first, parser->cur);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschint uri_parse_path(struct uri_parser *parser,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int *relative_r, const char *const **path_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch const unsigned char *pbegin = parser->cur;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch ARRAY_TYPE(const_string) segments;
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch const char *segment = NULL;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch unsigned int count;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int relative = 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch count = 0;
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (path_r != NULL)
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen p_array_init(&segments, parser->pool, 16);
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch else
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&segments);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* check for a leading '/' and indicate absolute path
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch when it is present
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (parser->cur < parser->end && *parser->cur == '/') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch relative = 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* parse first segment */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (;;) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (ret > 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* strip dot segments */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (segment[0] == '.') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (segment[1] == '.') {
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch if (segment[2] == '\0') {
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* '..' -> skip and... */
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch segment = NULL;
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* ... pop last segment (if any) */
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch if (count > 0) {
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (path_r != NULL) {
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch i_assert(count == array_count(&segments));
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch array_delete(&segments, count-1, 1);
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch }
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch count--;
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch } else if ( relative > 0 ) {
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch relative++;
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch } else if (segment[1] == '\0') {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* '.' -> skip */
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch segment = NULL;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch } else {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch segment = "";
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (segment != NULL) {
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (path_r != NULL)
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch array_append(&segments, &segment, 1);
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch count++;
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch }
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (parser->cur >= parser->end || *parser->cur != '/')
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch /* parse next path segment */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return -1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (relative_r != NULL)
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch *relative_r = relative;
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (path_r != NULL)
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch *path_r = NULL;
62aa68310d6f42467ca26880f678173bf1d26a83Stephan Bosch
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch if (parser->cur == pbegin) {
62aa68310d6f42467ca26880f678173bf1d26a83Stephan Bosch /* path part of URI is empty */
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch return 0;
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch }
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (path_r != NULL) {
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch /* special treatment for a trailing '..' or '.' */
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch if (segment == NULL) {
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch segment = "";
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch array_append(&segments, &segment, 1);
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch }
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch array_append_zero(&segments);
7a545edbd1fca7a330bcb4a807002373ee18762aStephan Bosch *path_r = array_get(&segments, &count);
05262e3132642bbdc4a8087c17b0903cf2ff22d2Stephan Bosch }
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch if (parser->cur < parser->end &&
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch *parser->cur != '?' && *parser->cur != '#') {
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->error = "Path component contains invalid character";
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch return -1;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschint uri_parse_query(struct uri_parser *parser, const char **query_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch const unsigned char *first = parser->cur;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* RFC 3986:
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * URI = { ... } [ "?" query ] { ... }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * query = *( pchar / "/" / "?" )
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (parser->cur >= parser->end || *parser->cur != '?')
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 0;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch while (parser->cur < parser->end) {
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (*parser->cur == '%') {
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch unsigned char ch = 0;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch return -1;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (ret > 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch continue;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((*parser->cur & 0x80) != 0 ||
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch (_uri_char_lookup[*parser->cur] & CHAR_MASK_QCHAR) == 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (parser->cur < parser->end && *parser->cur != '#') {
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->error = "Query component contains invalid character";
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch return -1;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch }
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (query_r != NULL)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch *query_r = p_strdup_until(parser->pool, first+1, parser->cur);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
639bb36b12b9f9bb54c8bb1be50eac623622f8a0Timo Sirainenint uri_parse_fragment(struct uri_parser *parser, const char **fragment_r)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch const unsigned char *first = parser->cur;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch int ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* RFC 3986:
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * URI = { ... } [ "#" fragment ]
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * fragment = *( pchar / "/" / "?" )
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (parser->cur >= parser->end || *parser->cur != '#')
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 0;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch while (parser->cur < parser->end) {
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (*parser->cur == '%') {
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch unsigned char ch = 0;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch return -1;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (ret > 0)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch continue;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if ((*parser->cur & 0x80) != 0 ||
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch (_uri_char_lookup[*parser->cur] & CHAR_MASK_QCHAR) == 0)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch parser->cur++;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch if (parser->cur < parser->end) {
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch parser->error = "Fragment component contains invalid character";
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch return -1;
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch }
bcd6c13936df11167350ac86598a781dce9038c3Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (fragment_r != NULL)
61f962e8f32a4870d08fb6f2189fdf6a10fd1abbStephan Bosch *fragment_r = p_strdup_until(parser->pool, first+1, parser->cur);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Boschvoid uri_parser_init_data(struct uri_parser *parser,
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch pool_t pool, const unsigned char *data, size_t size)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
9bea325875d114ff90529cc8f1e8ff89fc4fe700Timo Sirainen i_zero(parser);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch parser->pool = pool;
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch parser->begin = parser->cur = data;
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch parser->end = data + size;
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch}
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Boschvoid uri_parser_init(struct uri_parser *parser,
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch pool_t pool, const char *uri)
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch{
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch uri_parser_init_data
a643e1e3e5d9d8a60eeff83601f86ee00c26332aStephan Bosch (parser, pool, (const unsigned char *)uri, strlen(uri));
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstring_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (parser->tmpbuf == NULL)
72fc989c43a0dc94ec2f114b5e221beeab45519bTimo Sirainen parser->tmpbuf = str_new(parser->pool, size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch else
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch str_truncate(parser->tmpbuf, 0);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return parser->tmpbuf;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Boschint uri_parse_absolute_generic(struct uri_parser *parser,
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch enum uri_parse_flags flags)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch{
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch int relative, aret, ret = 0;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /*
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch hier-part = "//" authority path-abempty
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch / path-absolute
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch / path-rootless
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch / path-empty
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
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch segment = *pchar
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch segment-nz = 1*pchar
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* scheme ":" */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if ((flags & URI_PARSE_SCHEME_EXTERNAL) == 0 &&
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch (ret=uri_parse_scheme(parser, NULL)) <= 0) {
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if (ret == 0)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch parser->error = "Missing scheme";
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return -1;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch }
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* "//" authority */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if ((aret=uri_parse_slashslash_authority
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch (parser, NULL)) < 0)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return -1;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* path-absolute / path-rootless / path-empty */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if (aret == 0) {
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 i_assert(ret <= 0 || relative == 0);
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch }
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if (ret < 0)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return -1;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* [ "?" query ] */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if (uri_parse_query(parser, NULL) < 0)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return -1;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch /* [ "#" fragment ] */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if ((ret=uri_parse_fragment(parser, NULL)) < 0)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return ret;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch if (ret > 0 && (flags & URI_PARSE_ALLOW_FRAGMENT_PART) == 0) {
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch parser->error = "Fragment part not allowed";
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return -1;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch }
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch i_assert(parser->cur == parser->end);
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return 0;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch}
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch/*
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch * Generic URI manipulation
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch */
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Boschvoid uri_host_copy(pool_t pool, struct uri_host *dest,
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch const struct uri_host *src)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch{
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch const char *host_name = src->name;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch /* create host name literal if caller is lazy */
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (host_name == NULL && src->ip.family != 0) {
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch host_name = net_ip2addr(&src->ip);
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch i_assert(*host_name != '\0');
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch }
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch *dest = *src;
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch dest->name = p_strdup(pool, host_name);
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch}
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch/*
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch * Check generic URI
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch */
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
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{
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch struct uri_parser parser;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch int ret;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&parser);
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch parser.pool = pool_datastack_create();
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch parser.begin = parser.cur = data;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch parser.end = data + size;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch ret = uri_parse_absolute_generic(&parser, flags);
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch *error_r = parser.error;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return ret;
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch}
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Boschint uri_check(const char *uri, enum uri_parse_flags flags,
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch const char **error_r)
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch{
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch return uri_check_data
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch ((unsigned char *)uri, strlen(uri), flags, error_r);
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch}
458d877d84f62005e0ffc338f8591a8e2a36adadStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch/*
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch * Generic URI construction
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch */
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
e4e9ba5f43f9bf7e072d7d9fcc3259a42ecb15c8Stephan Boschvoid uri_data_encode(string_t *out,
e4e9ba5f43f9bf7e072d7d9fcc3259a42ecb15c8Stephan Bosch const unsigned char esc_table[256],
e4e9ba5f43f9bf7e072d7d9fcc3259a42ecb15c8Stephan Bosch unsigned char esc_mask, const char *esc_extra,
e4e9ba5f43f9bf7e072d7d9fcc3259a42ecb15c8Stephan Bosch const char *data)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch const unsigned char *pbegin, *p;
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch pbegin = p = (const unsigned char *)data;
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 str_append_n(out, pbegin, p - pbegin);
787d28c5d61cf24582b96d7275aee001e03520ddTimo Sirainen str_printfa(out, "%%%02x", *p);
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch p++;
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch pbegin = p;
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch } else {
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch p++;
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch }
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch }
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch if ((p - pbegin) > 0)
af53c056cf1e3d133a78c201e72a678b5431d0fbStephan Bosch str_append_n(out, pbegin, p - pbegin);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_scheme(string_t *out, const char *scheme)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append(out, scheme);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_c(out, ':');
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_user_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch const char *data)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_userinfo(string_t *out, const char *userinfo)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4e52bade0f83746405ecc1d0396e9b2ac7b98cd8Stephan Bosch uri_append_user_data(out, NULL, userinfo);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_c(out, '@');
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_host_name(string_t *out, const char *name)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup,
4e52bade0f83746405ecc1d0396e9b2ac7b98cd8Stephan Bosch CHAR_MASK_UNRESERVED | CHAR_MASK_SUB_DELIMS, NULL, name);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_host_ip(string_t *out, const struct ip_addr *host_ip)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch const char *addr = net_ip2addr(host_ip);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch i_assert(host_ip->family != 0);
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if (host_ip->family == AF_INET) {
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append(out, addr);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch return;
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch }
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch i_assert(host_ip->family == AF_INET6);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_c(out, '[');
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append(out, addr);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_c(out, ']');
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Boschvoid uri_append_host(string_t *out, const struct uri_host *host)
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch{
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (host->name != NULL) {
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch /* assume IPv6 literal if starts with '['; avoid encoding */
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch if (*host->name == '[')
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch str_append(out, host->name);
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch else
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch uri_append_host_name(out, host->name);
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch } else
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch uri_append_host_ip(out, &host->ip);
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch}
8d2d2780c9e71581ff9c3e8bce527b492c295ec1Stephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_port(string_t *out, in_port_t port)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch if (port != 0)
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch str_printfa(out, ":%u", port);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_path_segment_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch const char *data)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_path_segment(string_t *out, const char *segment)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_c(out, '/');
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if (*segment != '\0')
4e52bade0f83746405ecc1d0396e9b2ac7b98cd8Stephan Bosch uri_append_path_data(out, NULL, segment);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_path_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch const char *data)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PFCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_path(string_t *out, const char *path)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_c(out, '/');
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if (*path != '\0')
4e52bade0f83746405ecc1d0396e9b2ac7b98cd8Stephan Bosch uri_append_path_data(out, NULL, path);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_query_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch const char *data)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_query(string_t *out, const char *query)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_c(out, '?');
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if (*query != '\0')
4e52bade0f83746405ecc1d0396e9b2ac7b98cd8Stephan Bosch uri_append_query_data(out, NULL, query);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_fragment_data(string_t *out, const char *esc,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch const char *data)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Boschvoid uri_append_fragment(string_t *out, const char *fragment)
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch{
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch str_append_c(out, '#');
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch if (*fragment != '\0')
4e52bade0f83746405ecc1d0396e9b2ac7b98cd8Stephan Bosch uri_append_fragment_data(out, NULL, fragment);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch}