uri-util.c revision 4e52bade0f83746405ecc1d0396e9b2ac7b98cd8
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* Copyright (c) 2010-2016 Dovecot authors, see the included COPYING file */
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include "lib.h"
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include "array.h"
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock#include "str.h"
ea8dc4b6d2251b437950c0056bc626b311c73c27eschrock#include "net.h"
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include "uri-util.h"
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <ctype.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* [URI-GEN] RFC3986 Appendix A:
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
fa9e4066f08beec538e775443c5be79dd423fcabahrens absolute-URI = scheme ":" hier-part [ "?" query ]
fa9e4066f08beec538e775443c5be79dd423fcabahrens scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens URI-reference = URI / relative-ref
fa9e4066f08beec538e775443c5be79dd423fcabahrens relative-ref = relative-part [ "?" query ] [ "#" fragment ]
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens relative-part = "//" authority path-abempty
fa9e4066f08beec538e775443c5be79dd423fcabahrens / path-absolute
98d1cbfec254295273b6a761bc1861c0374bdf02George Wilson / path-noscheme
97e81309571898df9fdd94aab1216dfcf23e060bPrakash Surya / path-empty
fa9e4066f08beec538e775443c5be79dd423fcabahrens hier-part = "//" authority path-abempty
fa9e4066f08beec538e775443c5be79dd423fcabahrens / path-absolute
fa9e4066f08beec538e775443c5be79dd423fcabahrens / path-rootless
fa9e4066f08beec538e775443c5be79dd423fcabahrens / path-empty
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
fa9e4066f08beec538e775443c5be79dd423fcabahrens authority = [ userinfo "@" ] host [ ":" port ]
fa9e4066f08beec538e775443c5be79dd423fcabahrens userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
fa9e4066f08beec538e775443c5be79dd423fcabahrens host = IP-literal / IPv4address / reg-name
fa9e4066f08beec538e775443c5be79dd423fcabahrens port = *DIGIT
51ece83525fa18f5e72627610f480dffc7e492fdeschrock
fa9e4066f08beec538e775443c5be79dd423fcabahrens IP-literal = "[" ( IPv6address / IPvFuture ) "]"
fa9e4066f08beec538e775443c5be79dd423fcabahrens IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
fa9e4066f08beec538e775443c5be79dd423fcabahrens IPv6address = 6( h16 ":" ) ls32
fa9e4066f08beec538e775443c5be79dd423fcabahrens / "::" 5( h16 ":" ) ls32
fa9e4066f08beec538e775443c5be79dd423fcabahrens / [ h16 ] "::" 4( h16 ":" ) ls32
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson / [ *4( h16 ":" ) h16 ] "::" ls32
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson / [ *5( h16 ":" ) h16 ] "::" h16
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson / [ *6( h16 ":" ) h16 ] "::"
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson h16 = 1*4HEXDIG
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson ls32 = ( h16 ":" h16 ) / IPv4address
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson dec-octet = DIGIT ; 0-9
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson / %x31-39 DIGIT ; 10-99
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson / "1" 2DIGIT ; 100-199
fa9e4066f08beec538e775443c5be79dd423fcabahrens / "2" %x30-34 DIGIT ; 200-249
4263d13f00c9691fa14620eff82abef795be0693George Wilson / "25" %x30-35 ; 250-255
4263d13f00c9691fa14620eff82abef795be0693George Wilson reg-name = *( unreserved / pct-encoded / sub-delims )
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens path = path-abempty ; begins with "/" or is empty
fa9e4066f08beec538e775443c5be79dd423fcabahrens / path-absolute ; begins with "/" but not "//"
e14bb3258d05c1b1077e2db7cf77088924e56919Jeff Bonwick / path-noscheme ; begins with a non-colon segment
fa9e4066f08beec538e775443c5be79dd423fcabahrens / path-rootless ; begins with a segment
fa9e4066f08beec538e775443c5be79dd423fcabahrens / path-empty ; zero characters
fa9e4066f08beec538e775443c5be79dd423fcabahrens path-abempty = *( "/" segment )
fa9e4066f08beec538e775443c5be79dd423fcabahrens path-absolute = "/" [ segment-nz *( "/" segment ) ]
fa9e4066f08beec538e775443c5be79dd423fcabahrens path-noscheme = segment-nz-nc *( "/" segment )
fa9e4066f08beec538e775443c5be79dd423fcabahrens path-rootless = segment-nz *( "/" segment )
fa9e4066f08beec538e775443c5be79dd423fcabahrens path-empty = 0<pchar>
be6fd75a69ae679453d9cda5bff3326111e6d1caMatthew Ahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens segment = *pchar
fa9e4066f08beec538e775443c5be79dd423fcabahrens segment-nz = 1*pchar
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson ; non-zero-length segment without any colon ":"
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson query = *( pchar / "/" / "?" )
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson fragment = *( pchar / "/" / "?" )
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson pct-encoded = "%" HEXDIG HEXDIG
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson reserved = gen-delims / sub-delims
fa9e4066f08beec538e775443c5be79dd423fcabahrens gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
fa9e4066f08beec538e775443c5be79dd423fcabahrens sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
fa9e4066f08beec538e775443c5be79dd423fcabahrens / "*" / "+" / "," / ";" / "="
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define URI_MAX_SCHEME_NAME_LEN 64
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* Character lookup table
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
0a4e9518a44f226be6d39383330b5b1792d2f184gw * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" [bit0]
8ad4d6dd86f5bc65fb3afa566c8133f3bac21648Jeff Bonwick * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
fa9e4066f08beec538e775443c5be79dd423fcabahrens * / "*" / "+" / "," / ";" / "=" [bit1]
fa9e4066f08beec538e775443c5be79dd423fcabahrens * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" [bit2]
fa9e4066f08beec538e775443c5be79dd423fcabahrens * pchar = unreserved / sub-delims / ":" / "@" [bit0|bit1|bit3]
fa9e4066f08beec538e775443c5be79dd423fcabahrens * 'pfchar' = unreserved / sub-delims / ":" / "@" / "/"
fa9e4066f08beec538e775443c5be79dd423fcabahrens * [bit0|bit1|bit3|bit5]
fa9e4066f08beec538e775443c5be79dd423fcabahrens * 'uchar' = unreserved / sub-delims / ":" [bit0|bit1|bit4]
fa9e4066f08beec538e775443c5be79dd423fcabahrens * 'qchar' = pchar / "/" / "?" [bit0|bit1|bit3|bit5|bit6]
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define CHAR_MASK_UNRESERVED (1<<0)
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define CHAR_MASK_SUB_DELIMS (1<<1)
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define CHAR_MASK_PCHAR ((1<<0)|(1<<1)|(1<<3))
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define CHAR_MASK_PFCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5))
be6fd75a69ae679453d9cda5bff3326111e6d1caMatthew Ahrens#define CHAR_MASK_UCHAR ((1<<0)|(1<<1)|(1<<4))
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define CHAR_MASK_QCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<6))
fa9e4066f08beec538e775443c5be79dd423fcabahrens
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilsonstatic unsigned const char _uri_char_lookup[256] = {
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
fa9e4066f08beec538e775443c5be79dd423fcabahrens 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
fa9e4066f08beec538e775443c5be79dd423fcabahrens 0, 2, 0, 4, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, 36, // 20
fa9e4066f08beec538e775443c5be79dd423fcabahrens 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 2, 0, 2, 0, 68, // 30
fa9e4066f08beec538e775443c5be79dd423fcabahrens 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4, 0, 1, // 50
fa9e4066f08beec538e775443c5be79dd423fcabahrens 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
fa9e4066f08beec538e775443c5be79dd423fcabahrens 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70
fa9e4066f08beec538e775443c5be79dd423fcabahrens};
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic inline int _decode_hex_digit(const unsigned char digit)
4263d13f00c9691fa14620eff82abef795be0693George Wilson{
fa9e4066f08beec538e775443c5be79dd423fcabahrens switch (digit) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens case '0': case '1': case '2': case '3': case '4':
fa9e4066f08beec538e775443c5be79dd423fcabahrens case '5': case '6': case '7': case '8': case '9':
fa9e4066f08beec538e775443c5be79dd423fcabahrens return digit - '0';
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
fa9e4066f08beec538e775443c5be79dd423fcabahrens return digit - 'a' + 0x0a;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
fa9e4066f08beec538e775443c5be79dd423fcabahrens return digit - 'A' + 0x0A;
095bcd6622e3b3520eb3b71039a3be5cfab25b74George Wilson }
fa9e4066f08beec538e775443c5be79dd423fcabahrens return -1;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic int
8ad4d6dd86f5bc65fb3afa566c8133f3bac21648Jeff Bonwickuri_parse_pct_encoded_data(struct uri_parser *parser,
8ad4d6dd86f5bc65fb3afa566c8133f3bac21648Jeff Bonwick const unsigned char **p, const unsigned char *pend,
fa9e4066f08beec538e775443c5be79dd423fcabahrens unsigned char *ch_r) ATTR_NULL(3)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens int value;
98d1cbfec254295273b6a761bc1861c0374bdf02George Wilson
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (**p != '%' || (pend != NULL && *p >= pend))
fa9e4066f08beec538e775443c5be79dd423fcabahrens return 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens *p += 1;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson if (**p == 0 || *(*p+1) == 0 || (pend != NULL && *p+1 >= pend)) {
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson parser->error = "Unexpected URI boundary after '%'";
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson return -1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson }
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson if ((value = _decode_hex_digit(**p)) < 0) {
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson parser->error = p_strdup_printf(parser->pool,
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson "Expecting hex digit after '%%', but found '%c'", **p);
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson return -1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson }
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson *ch_r = (value & 0x0f) << 4;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson *p += 1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
be6fd75a69ae679453d9cda5bff3326111e6d1caMatthew Ahrens if ((value = _decode_hex_digit(**p)) < 0) {
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson parser->error = p_strdup_printf(parser->pool,
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson "Expecting hex digit after '%%%c', but found '%c'", *((*p)-1), **p);
97e81309571898df9fdd94aab1216dfcf23e060bPrakash Surya return -1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson }
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson *ch_r |= (value & 0x0f);
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson *p += 1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson if (*ch_r == '\0') {
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson parser->error =
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson "Percent encoding is not allowed to encode NUL character";
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson return -1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson }
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson return 1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson}
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilsonint uri_parse_pct_encoded(struct uri_parser *parser,
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson unsigned char *ch_r)
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson{
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson return uri_parse_pct_encoded_data
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson (parser, &parser->cur, parser->end, ch_r);
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson}
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilsonstatic int
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilsonuri_parse_unreserved_char(struct uri_parser *parser, unsigned char *ch_r)
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson{
738f37bc3dcd61e8a893af0f2d466d76690b70ecGeorge Wilson if ((*parser->cur & 0x80) != 0)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens if ((_uri_char_lookup[*parser->cur] & CHAR_MASK_UNRESERVED) != 0) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens *ch_r = *parser->cur;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson parser->cur++;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson return 1;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens return 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
0a4e9518a44f226be6d39383330b5b1792d2f184gw
be6fd75a69ae679453d9cda5bff3326111e6d1caMatthew Ahrensint uri_parse_unreserved(struct uri_parser *parser, string_t *part)
738f37bc3dcd61e8a893af0f2d466d76690b70ecGeorge Wilson{
738f37bc3dcd61e8a893af0f2d466d76690b70ecGeorge Wilson int len = 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (parser->cur < parser->end) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens int ret;
fa9e4066f08beec538e775443c5be79dd423fcabahrens unsigned char ch = 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if ((ret = uri_parse_unreserved_char(parser, &ch)) < 0)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return -1;
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (ret == 0)
be6fd75a69ae679453d9cda5bff3326111e6d1caMatthew Ahrens break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (part != NULL)
738f37bc3dcd61e8a893af0f2d466d76690b70ecGeorge Wilson str_append_c(part, ch);
738f37bc3dcd61e8a893af0f2d466d76690b70ecGeorge Wilson len++;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
f693d300fbefaa2bd9a229a874b5994532a30d87Steven Hartland return len > 0 ? 1 : 0;
97e81309571898df9fdd94aab1216dfcf23e060bPrakash Surya}
f693d300fbefaa2bd9a229a874b5994532a30d87Steven Hartland
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilsonint uri_parse_unreserved_pct(struct uri_parser *parser, string_t *part)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson int len = 0;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (parser->cur < parser->end) {
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson int ret;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson unsigned char ch = 0;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson return -1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson else if (ret == 0 &&
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson (ret=uri_parse_unreserved_char(parser, &ch)) < 0)
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson return -1;
31d7e8fa33fae995f558673adb22641b5aa8b6e1George Wilson if (ret == 0)
2c1e2b44148432fb7a509dd216a99299b6740250George Wilson break;
2c1e2b44148432fb7a509dd216a99299b6740250George Wilson
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (part != NULL)
fa9e4066f08beec538e775443c5be79dd423fcabahrens str_append_c(part, ch);
e14bb3258d05c1b1077e2db7cf77088924e56919Jeff Bonwick len++;
e14bb3258d05c1b1077e2db7cf77088924e56919Jeff Bonwick }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return len > 0 ? 1 : 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensbool uri_data_decode(struct uri_parser *parser, const char *data,
fa9e4066f08beec538e775443c5be79dd423fcabahrens const char *until, const char **decoded_r)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens const unsigned char *p = (const unsigned char *)data;
fa9e4066f08beec538e775443c5be79dd423fcabahrens const unsigned char *pend = (const unsigned char *)until;
fa9e4066f08beec538e775443c5be79dd423fcabahrens string_t *decoded;
fa9e4066f08beec538e775443c5be79dd423fcabahrens int ret;
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson if (pend == NULL) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* NULL means unlimited; solely rely on '\0' */
fa9e4066f08beec538e775443c5be79dd423fcabahrens pend = (const unsigned char *)(size_t)-1;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (p >= pend || *p == '\0') {
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (decoded_r != NULL)
fa9e4066f08beec538e775443c5be79dd423fcabahrens *decoded_r = "";
fa9e4066f08beec538e775443c5be79dd423fcabahrens return TRUE;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens decoded = uri_parser_get_tmpbuf(parser, 256);
fa9e4066f08beec538e775443c5be79dd423fcabahrens while (p < pend && *p != '\0') {
fa9e4066f08beec538e775443c5be79dd423fcabahrens unsigned char ch;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens if ((ret=uri_parse_pct_encoded_data
fa9e4066f08beec538e775443c5be79dd423fcabahrens (parser, &p, NULL, &ch)) != 0) {
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson if (ret < 0)
dcba9f3fbefe06ad19972b4de0351924601e5767George Wilson return FALSE;
fa9e4066f08beec538e775443c5be79dd423fcabahrens str_append_c(decoded, ch);
fa9e4066f08beec538e775443c5be79dd423fcabahrens } else {
fa9e4066f08beec538e775443c5be79dd423fcabahrens str_append_c(decoded, *p);
fa9e4066f08beec538e775443c5be79dd423fcabahrens p++;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
}
if (decoded_r != NULL)
*decoded_r = p_strdup(parser->pool, str_c(decoded));
return TRUE;
}
int uri_parse_scheme(struct uri_parser *parser, const char **scheme_r)
{
const unsigned char *first = parser->cur;
size_t len = 1;
/* RFC 3968:
* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
*/
if (parser->cur >= parser->end || !i_isalpha(*parser->cur))
return 0;
parser->cur++;
while (len < URI_MAX_SCHEME_NAME_LEN &&
parser->cur < parser->end) {
if (!i_isalnum(*parser->cur) &&
*parser->cur != '+' && *parser->cur != '-' &&
*parser->cur != '.')
break;
parser->cur++;
len++;
}
if (parser->cur >= parser->end || *parser->cur != ':') {
parser->error = "Invalid URI scheme";
return -1;
}
if (scheme_r != NULL)
*scheme_r = t_strndup(first, parser->cur - first);
parser->cur++;
return 1;
}
int uri_cut_scheme(const char **uri_p, const char **scheme_r)
{
struct uri_parser parser;
uri_parser_init(&parser, NULL, *uri_p);
if (uri_parse_scheme(&parser, scheme_r) <= 0)
return -1;
*uri_p = (const char *)parser.cur;
return 0;
}
static int
uri_parse_dec_octet(struct uri_parser *parser, string_t *literal,
uint8_t *octet_r) ATTR_NULL(2)
{
unsigned int octet = 0;
int count = 0;
/* RFC 3986:
*
* dec-octet = DIGIT ; 0-9
* / %x31-39 DIGIT ; 10-99
* / "1" 2DIGIT ; 100-199
* / "2" %x30-34 DIGIT ; 200-249
* / "25" %x30-35 ; 250-255
*/
while (parser->cur < parser->end && i_isdigit(*parser->cur)) {
octet = octet * 10 + (parser->cur[0] - '0');
if (octet > 255)
return -1;
if (literal != NULL)
str_append_c(literal, *parser->cur);
parser->cur++;
count++;
}
if (count > 0) {
*octet_r = octet;
return 1;
}
return 0;
}
static int
uri_parse_ipv4address(struct uri_parser *parser, string_t *literal,
struct in_addr *ip4_r) ATTR_NULL(2,3)
{
uint8_t octet;
uint32_t ip = 0;
int ret;
int i;
/* RFC 3986:
*
* IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
*/
if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
return ret;
ip = octet;
for (i = 0; i < 3 && parser->cur < parser->end; i++) {
if (*parser->cur != '.')
return -1;
if (literal != NULL)
str_append_c(literal, '.');
parser->cur++;
if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
return -1;
ip = (ip << 8) + octet;
}
if (ip4_r != NULL)
ip4_r->s_addr = htonl(ip);
return 1;
}
static int
uri_parse_reg_name(struct uri_parser *parser,
string_t *reg_name) ATTR_NULL(2)
{
/* RFC 3986:
*
* reg-name = *( unreserved / pct-encoded / sub-delims )
*/
while (parser->cur < parser->end) {
int ret;
unsigned char c;
/* unreserved / pct-encoded */
if ((ret=uri_parse_pct_encoded(parser, &c)) < 0)
return -1;
else if (ret == 0 &&
(ret=uri_parse_unreserved_char(parser, &c)) < 0)
return -1;
if (ret > 0) {
if (reg_name != NULL)
str_append_c(reg_name, c);
continue;
}
/* sub-delims */
c = *parser->cur;
if ((c & 0x80) == 0 && (_uri_char_lookup[c] & CHAR_MASK_SUB_DELIMS) != 0) {
if (reg_name != NULL)
str_append_c(reg_name, *parser->cur);
parser->cur++;
continue;
}
break;
}
return 0;
}
static int uri_do_parse_host_name_dns(struct uri_parser *parser,
string_t *host_name) ATTR_NULL(2, 3)
{
const unsigned char *first, *part;
int ret;
/* RFC 3986, Section 3.2.2:
A registered name intended for lookup in the DNS uses the syntax
defined in Section 3.5 of [RFC1034] and Section 2.1 of [RFC1123].
Such a name consists of a sequence of domain labels separated by ".",
each domain label starting and ending with an alphanumeric character
and possibly also containing "-" characters. The rightmost domain
label of a fully qualified domain name in DNS may be followed by a
single "." and should be if it is necessary to distinguish between
the complete domain name and some local domain.
RFC 2396, Section 3.2.2 (old URI specification):
hostname = *( domainlabel "." ) toplabel [ "." ]
domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
toplabel = alpha | alpha *( alphanum | "-" ) alphanum
The description in RFC 3986 is more liberal, so:
hostname = *( domainlabel "." ) domainlabel [ "." ]
domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
We also support percent encoding in spirit of the generic reg-name,
even though this should explicitly not be used according to the RFC.
It is, however, not strictly forbidden (unlike older RFC), so we
support it.
*/
first = part = parser->cur;
for (;;) {
const unsigned char *offset;
unsigned char ch, pch;
/* alphanum */
offset = parser->cur;
ch = pch = *parser->cur;
if (parser->cur >= parser->end)
break;
if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) {
return -1;
} else if (ret > 0) {
if (!i_isalnum(ch))
return -1;
if (host_name != NULL)
str_append_c(host_name, ch);
part = parser->cur;
} else {
if (!i_isalnum(*parser->cur))
break;
parser->cur++;
}
if (parser->cur < parser->end) {
/* *( alphanum | "-" ) alphanum */
do {
offset = parser->cur;
if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) {
return -1;
} else if (ret > 0) {
if (!i_isalnum(ch) && ch != '-')
break;
if (host_name != NULL) {
if (offset > part)
str_append_n(host_name, part, offset - part);
str_append_c(host_name, ch);
}
part = parser->cur;
} else {
ch = *parser->cur;
if (!i_isalnum(ch) && ch != '-')
break;
parser->cur++;
}
pch = ch;
} while (parser->cur < parser->end);
if (!i_isalnum(pch)) {
parser->error = "Invalid domain label in hostname";
return -1;
}
}
if (host_name != NULL && parser->cur > part)
str_append_n(host_name, part, parser->cur - part);
/* "." */
if (parser->cur >= parser->end || ch != '.')
break;
if (host_name != NULL)
str_append_c(host_name, '.');
if (parser->cur == offset)
parser->cur++;
part = parser->cur;
}
if (parser->cur == first)
return 0;
/* remove trailing '.' */
if (host_name != NULL) {
const char *name = str_c(host_name);
i_assert(str_len(host_name) > 0);
if (name[str_len(host_name)-1] == '.')
str_truncate(host_name, str_len(host_name)-1);
}
return 1;
}
int uri_parse_host_name_dns(struct uri_parser *parser,
const char **host_name_r)
{
string_t *host_name = NULL;
int ret;
if (host_name_r != NULL)
host_name = uri_parser_get_tmpbuf(parser, 256);
if ((ret=uri_do_parse_host_name_dns(parser, host_name)) <= 0)
return ret;
*host_name_r = str_c(host_name);
return 1;
}
static int
uri_parse_ip_literal(struct uri_parser *parser, string_t *literal,
struct in6_addr *ip6_r) ATTR_NULL(2,3)
{
const unsigned char *p;
const char *address;
struct in6_addr ip6;
int ret;
/* IP-literal = "[" ( IPv6address / IPvFuture ) "]"
* IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
* IPv6address = ; Syntax not relevant: parsed using inet_pton()
*/
/* "[" already verified */
/* Scan for end of address */
for (p = parser->cur+1; p < parser->end; p++) {
if (*p == ']')
break;
}
if (p >= parser->end || *p != ']') {
parser->error = "Expecting ']' at end of IP-literal";
return -1;
}
if (literal != NULL)
str_append_n(literal, parser->cur, p-parser->cur+1);
address = t_strdup_until(parser->cur+1, p);
parser->cur = p + 1;
if (*address == '\0') {
parser->error = "Empty IPv6 host address";
return -1;
}
if (*address == 'v') {
parser->error = p_strdup_printf(parser->pool,
"Future IP host address '%s' not supported", address);
return -1;
}
if ((ret = inet_pton(AF_INET6, address, &ip6)) <= 0) {
parser->error = p_strdup_printf(parser->pool,
"Invalid IPv6 host address '%s'", address);
return -1;
}
if (ip6_r != NULL)
*ip6_r = ip6;
return 1;
}
int uri_parse_host(struct uri_parser *parser,
struct uri_host *host, bool dns_name) ATTR_NULL(2)
{
const unsigned char *preserve;
struct in_addr ip4;
struct in6_addr ip6;
string_t *literal = NULL;
int ret;
/* RFC 3986:
*
* host = IP-literal / IPv4address / reg-name
*/
if (host != NULL)
memset(host, 0, sizeof(*host));
literal = uri_parser_get_tmpbuf(parser, 256);
/* IP-literal / */
if (parser->cur < parser->end && *parser->cur == '[') {
if ((ret=uri_parse_ip_literal(parser, literal, &ip6)) <= 0)
return -1;
if (host != NULL) {
host->name = p_strdup(parser->pool, str_c(literal));;
host->ip.family = AF_INET6;
host->ip.u.ip6 = ip6;
}
return 1;
}
/* IPv4address /
*
* If it fails to parse, we try to parse it as a reg-name
*/
preserve = parser->cur;
if ((ret = uri_parse_ipv4address(parser, literal, &ip4)) > 0) {
if (host != NULL) {
host->name = p_strdup(parser->pool, str_c(literal));
host->ip.family = AF_INET;
host->ip.u.ip4 = ip4;
}
return ret;
}
parser->cur = preserve;
str_truncate(literal, 0);
/* reg-name */
if (dns_name) {
if (uri_do_parse_host_name_dns(parser, literal) < 0)
return -1;
} else if (uri_parse_reg_name(parser, literal) < 0)
return -1;
if (host != NULL)
host->name = p_strdup(parser->pool, str_c(literal));
return 0;
}
static int
uri_parse_port(struct uri_parser *parser,
struct uri_authority *auth) ATTR_NULL(2)
{
const unsigned char *first;
in_port_t port;
/* RFC 3986:
*
* port = *DIGIT
*/
first = parser->cur;
while (parser->cur < parser->end && i_isdigit(*parser->cur))
parser->cur++;
if (parser->cur == first)
return 0;
if (net_str2port(t_strdup_until(first, parser->cur), &port) < 0) {
parser->error = "Invalid port number";
return -1;
}
if (auth != NULL)
auth->port = port;
return 1;
}
int uri_parse_authority(struct uri_parser *parser,
struct uri_authority *auth, bool dns_name)
{
const unsigned char *p;
int ret;
/*
* authority = [ userinfo "@" ] host [ ":" port ]
*/
if (auth != NULL)
memset(auth, 0, sizeof(*auth));
/* Scan ahead to check whether there is a [userinfo "@"] uri component */
for (p = parser->cur; p < parser->end; p++){
/* refuse 8bit characters */
if ((*p & 0x80) != 0)
break;
/* break at first delimiter */
if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0)
break;
}
/* Extract userinfo */
if (p < parser->end && *p == '@') {
if (auth != NULL)
auth->enc_userinfo = p_strdup_until(parser->pool, parser->cur, p);
parser->cur = p+1;
}
/* host */
if (uri_parse_host(parser,
(auth == NULL ? NULL : &auth->host), dns_name) < 0)
return -1;
if (parser->cur == parser->end)
return 1;
switch (*parser->cur) {
case ':': case '/': case '?': case '#':
break;
default:
parser->error = "Invalid host identifier";
return -1;
}
/* [":" port] */
if (*parser->cur == ':') {
parser->cur++;
if ((ret = uri_parse_port(parser, auth)) < 0)
return ret;
if (parser->cur == parser->end)
return 1;
switch (*parser->cur) {
case '/': case '?': case '#':
break;
default:
parser->error = "Invalid host port";
return -1;
}
}
return 1;
}
int uri_parse_slashslash_authority(struct uri_parser *parser,
struct uri_authority *auth, bool dns_name)
{
/* "//" authority */
if ((parser->end - parser->cur) <= 2 || parser->cur[0] != '/' ||
parser->cur[1] != '/')
return 0;
parser->cur += 2;
return uri_parse_authority(parser, auth, dns_name);
}
int uri_parse_path_segment(struct uri_parser *parser, const char **segment_r)
{
const unsigned char *p = parser->cur;
while (p < parser->end) {
if (*p == '%') {
p++;
continue;
}
if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_PCHAR) == 0)
break;
p++;
}
if (p < parser->end &&
*p != '/' && *p != '?' && *p != '#' ) {
parser->error =
"Path component contains invalid character";
return -1;
}
if (p == parser->cur)
return 0;
if (segment_r != NULL)
*segment_r = p_strdup_until(parser->pool, parser->cur, p);
parser->cur = p;
return 1;
}
int uri_parse_path(struct uri_parser *parser,
int *relative_r, const char *const **path_r)
{
const unsigned char *pbegin = parser->cur;
ARRAY_TYPE(const_string) segments;
const char *segment = NULL;
unsigned int count;
int relative = 1;
int ret;
count = 0;
if (path_r != NULL)
p_array_init(&segments, parser->pool, 16);
else
memset(&segments, 0, sizeof(segments));
/* check for a leading '/' and indicate absolute path
when it is present
*/
if (parser->cur < parser->end && *parser->cur == '/') {
parser->cur++;
relative = 0;
}
/* parse first segment */
if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
return -1;
for (;;) {
if (ret > 0) {
/* strip dot segments */
if (segment[0] == '.') {
if (segment[1] == '.') {
if (segment[2] == '\0') {
/* '..' -> skip and... */
segment = NULL;
/* ... pop last segment (if any) */
if (count > 0) {
if (path_r != NULL) {
i_assert(count == array_count(&segments));
array_delete(&segments, count-1, 1);
}
count--;
} else if ( relative > 0 ) {
relative++;
}
}
} else if (segment[1] == '\0') {
/* '.' -> skip */
segment = NULL;
}
}
} else {
segment = "";
}
if (segment != NULL) {
if (path_r != NULL)
array_append(&segments, &segment, 1);
count++;
}
if (parser->cur >= parser->end || *parser->cur != '/')
break;
parser->cur++;
/* parse next path segment */
if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
return -1;
}
if (relative_r != NULL)
*relative_r = relative;
if (path_r != NULL)
*path_r = NULL;
if (parser->cur == pbegin) {
/* path part of URI is empty */
return 0;
}
if (path_r != NULL) {
/* special treatment for a trailing '..' or '.' */
if (segment == NULL) {
segment = "";
array_append(&segments, &segment, 1);
}
array_append_zero(&segments);
*path_r = array_get(&segments, &count);
}
if (parser->cur < parser->end &&
*parser->cur != '?' && *parser->cur != '#') {
parser->error = "Path component contains invalid character";
return -1;
}
return 1;
}
int uri_parse_query(struct uri_parser *parser, const char **query_r)
{
const unsigned char *p = parser->cur;
/* RFC 3986:
*
* URI = { ... } [ "?" query ] { ... }
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
*/
if (p >= parser->end || *p != '?')
return 0;
p++;
while (p < parser->end) {
if (*p == '%') {
p++;
continue;
}
if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_QCHAR) == 0)
break;
p++;
}
if (p < parser->end && *p != '#') {
parser->error = "Query component contains invalid character";
return -1;
}
if (query_r != NULL)
*query_r = p_strdup_until(parser->pool, parser->cur+1, p);
parser->cur = p;
return 1;
}
int uri_parse_fragment(struct uri_parser *parser, const char **fragment_r)
{
const unsigned char *p = parser->cur;
/* RFC 3986:
*
* URI = { ... } [ "#" fragment ]
* fragment = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
*/
if (p >= parser->end || *p != '#')
return 0;
p++;
while (p < parser->end) {
if (*p == '%') {
p++;
continue;
}
if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_QCHAR) == 0)
break;
p++;
}
if (p < parser->end) {
parser->error = "Fragment component contains invalid character";
return -1;
}
if (fragment_r != NULL)
*fragment_r = p_strdup_until(parser->pool, parser->cur+1, p);
parser->cur = p;
return 1;
}
void uri_parser_init_data(struct uri_parser *parser,
pool_t pool, const unsigned char *data, size_t size)
{
parser->pool = pool;
parser->begin = parser->cur = data;
parser->end = data + size;
parser->error = NULL;
parser->tmpbuf = NULL;
}
void uri_parser_init(struct uri_parser *parser,
pool_t pool, const char *uri)
{
uri_parser_init_data
(parser, pool, (const unsigned char *)uri, strlen(uri));
}
string_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size)
{
if (parser->tmpbuf == NULL)
parser->tmpbuf = str_new(parser->pool, size);
else
str_truncate(parser->tmpbuf, 0);
return parser->tmpbuf;
}
/*
* Generic URI manipulation
*/
void uri_host_copy(pool_t pool, struct uri_host *dest,
const struct uri_host *src)
{
const char *host_name = src->name;
/* create host name literal if caller is lazy */
if (host_name == NULL && src->ip.family != 0) {
host_name = net_ip2addr(&src->ip);
i_assert(*host_name != '\0');
}
*dest = *src;
dest->name = p_strdup(pool, host_name);
}
/*
* Generic URI construction
*/
void uri_data_encode(string_t *out,
const unsigned char esc_table[256],
unsigned char esc_mask, const char *esc_extra,
const char *data)
{
const unsigned char *pbegin, *p;
pbegin = p = (const unsigned char *)data;
while (*p != '\0') {
if ((*p & 0x80) != 0 || (esc_table[*p] & esc_mask) == 0 ||
(esc_extra != NULL && strchr(esc_extra, (char)*p) != NULL)) {
if ((p - pbegin) > 0)
str_append_n(out, pbegin, p - pbegin);
str_printfa(out, "%%%02x", *p);
p++;
pbegin = p;
} else {
p++;
}
}
if ((p - pbegin) > 0)
str_append_n(out, pbegin, p - pbegin);
}
void uri_append_scheme(string_t *out, const char *scheme)
{
str_append(out, scheme);
str_append_c(out, ':');
}
void uri_append_user_data(string_t *out, const char *esc,
const char *data)
{
uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UCHAR, esc, data);
}
void uri_append_userinfo(string_t *out, const char *userinfo)
{
uri_append_user_data(out, NULL, userinfo);
str_append_c(out, '@');
}
void uri_append_host_name(string_t *out, const char *name)
{
uri_data_encode(out, _uri_char_lookup,
CHAR_MASK_UNRESERVED | CHAR_MASK_SUB_DELIMS, NULL, name);
}
void uri_append_host_ip(string_t *out, const struct ip_addr *host_ip)
{
const char *addr = net_ip2addr(host_ip);
i_assert(host_ip->family != 0);
if (host_ip->family == AF_INET) {
str_append(out, addr);
return;
}
i_assert(host_ip->family == AF_INET6);
str_append_c(out, '[');
str_append(out, addr);
str_append_c(out, ']');
}
void uri_append_host(string_t *out, const struct uri_host *host)
{
if (host->name != NULL) {
/* assume IPv6 literal if starts with '['; avoid encoding */
if (*host->name == '[')
str_append(out, host->name);
else
uri_append_host_name(out, host->name);
} else
uri_append_host_ip(out, &host->ip);
}
void uri_append_port(string_t *out, in_port_t port)
{
if (port != 0)
str_printfa(out, ":%u", port);
}
void uri_append_path_segment_data(string_t *out, const char *esc,
const char *data)
{
uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PCHAR, esc, data);
}
void uri_append_path_segment(string_t *out, const char *segment)
{
str_append_c(out, '/');
if (*segment != '\0')
uri_append_path_data(out, NULL, segment);
}
void uri_append_path_data(string_t *out, const char *esc,
const char *data)
{
uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PFCHAR, esc, data);
}
void uri_append_path(string_t *out, const char *path)
{
str_append_c(out, '/');
if (*path != '\0')
uri_append_path_data(out, NULL, path);
}
void uri_append_query_data(string_t *out, const char *esc,
const char *data)
{
uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data);
}
void uri_append_query(string_t *out, const char *query)
{
str_append_c(out, '?');
if (*query != '\0')
uri_append_query_data(out, NULL, query);
}
void uri_append_fragment_data(string_t *out, const char *esc,
const char *data)
{
uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data);
}
void uri_append_fragment(string_t *out, const char *fragment)
{
str_append_c(out, '#');
if (*fragment != '\0')
uri_append_fragment_data(out, NULL, fragment);
}