ali@475: /* ali@475: * Copyright (C) 2016 J. Ali Harlow ali@475: * ali@475: * This program is free software; you can redistribute it and/or modify ali@475: * it under the terms of the GNU General Public License as published by ali@475: * the Free Software Foundation; either version 2 of the License, or ali@475: * (at your option) any later version. ali@475: * ali@475: * This program is distributed in the hope that it will be useful, ali@475: * but WITHOUT ANY WARRANTY; without even the implied warranty of ali@475: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ali@475: * GNU General Public License for more details. ali@475: * ali@475: * You should have received a copy of the GNU General Public License along ali@475: * with this program; if not, write to the Free Software Foundation, Inc., ali@475: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ali@475: */ ali@475: ali@475: #include "config.h" ali@475: ali@475: #undef DEBUG ali@475: ali@475: #include ali@475: #include ali@475: #include "razor.h" ali@475: #include "types/types.h" ali@475: #include "razor-internal.h" ali@475: #include "uri.h" ali@475: ali@475: /* ali@475: * Following RFC 3986 § 3. ali@475: * Note that we don't validate queries or fragments. ali@475: */ ali@475: ali@475: #define strdup0(s) ((s) ? strdup(s) : NULL) ali@475: ali@475: #define string_str(str) ((char *)(str)->data) ali@475: ali@475: #define string_init(str) do { \ ali@475: char *_p; \ ali@475: array_init(str); \ ali@475: _p = array_add(str, 1); \ ali@475: *_p = '\0'; \ ali@475: } while(0) ali@475: ali@475: #define string_append_len(str, s, len) do { \ ali@475: char *_p; \ ali@475: _p = array_add(str, len); \ ali@475: _p--; \ ali@475: strncpy(_p, s, len); \ ali@475: _p[(len)] = '\0'; \ ali@475: } while(0) ali@475: ali@475: #define string_append(str, s) string_append_len(str, s, strlen(s)) ali@475: ali@475: #define string_truncate_at(str, s) do { \ ali@475: int _len; \ ali@475: _len = (s) - \ ali@475: (char *)(str)->data; \ ali@475: *(s) = '\0'; \ ali@475: (str)->size = _len + 1; \ ali@475: } while(0) ali@475: ali@475: ali@475: static const char *skip_uri_scheme(const char *uri) ali@475: { ali@475: /* ali@475: * RFC 3986 defines scheme as: ali@475: * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ali@475: */ ali@475: if (*uri >= 'a' && *uri <= 'z' || *uri >= 'A' && *uri <= 'Z') { ali@475: do { ali@475: uri++; ali@475: } while (is_alnum(*uri) || *uri == '+' || *uri == '-' || ali@475: *uri == '.'); ali@475: if (*uri == ':') ali@475: return uri; ali@475: } ali@475: return NULL; ali@475: } ali@475: ali@475: static char *razor_strndup(const char *s, size_t n) ali@475: { ali@475: char *result; ali@475: ali@475: if (memchr(s, '\0', n)) ali@475: result = strdup(s); ali@475: else { ali@475: result = malloc(n + 1); ali@475: memcpy(result, s, n); ali@475: result[n] = '\0'; ali@475: } ali@475: ali@475: return result; ali@475: } ali@475: ali@475: #if 0 ali@475: /* ali@475: * Return the (possibly decoded) pchar or 0 on end-of-string or -1 on error ali@475: */ ali@475: static int pchar_get_char_validated(const char *p) ali@475: { ali@475: int c; ali@475: ali@475: if (p[0]=='\0') ali@475: c = 0; ali@475: else if (p[0]=='%') { ali@475: if (xdigit_value(p[1]) < 0) ali@475: return -1; ali@475: c = xdigit_value(p[1]) * 16; ali@475: if (xdigit_value(p[2]) < 0) ali@475: return -1; ali@475: c += xdigit_value(p[2]); ali@475: } else if (p[0] >= 'a' && p[0] <= 'z' || p[0] >= 'A' && p[0] <= 'Z' || ali@475: p[0] >= '0' && p[0] <= '9' || ali@475: strchr("-._~!$&'()*+,;=:@", p[0])) ali@475: c = p[0]; ali@475: else ali@475: c = -1; ali@475: ali@475: return c; ali@475: } ali@475: #endif ali@475: ali@475: /* ali@475: * Verify the percent encoding. All '%' characters must be followed by ali@475: * exactly two hexadecimal digits. ali@475: */ ali@475: static int pct_encoding_validate(const char *s) ali@475: { ali@475: while (*s) { ali@475: if (*s == '%') { ali@475: if (xdigit_value(s[1]) < 0 || xdigit_value(s[2]) < 0) ali@475: return -1; ali@475: s += 2; ali@475: } ali@475: ali@475: s++; ali@475: } ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: static char *pct_encoding_normalize(char *s) ali@475: { ali@475: char *retval, *p; ali@475: int c; ali@475: ali@475: if (!s) ali@475: return NULL; ali@475: ali@475: p = retval = malloc(strlen(s) + 1); ali@475: ali@475: while (*s) { ali@475: if (*s == '%') { ali@475: c = pchar_get_char(s); ali@475: if (is_unreserved(c)) ali@475: *p++ = c; ali@475: else { ali@475: *p++ = '%'; ali@475: *p++ = "0123456789ABCDEF"[c/16]; ali@475: *p++ = "0123456789ABCDEF"[c%16]; ali@475: } ali@475: pchar_next_char(s); ali@475: } else ali@475: *p++ = *s++; ali@475: } ali@475: ali@475: *p++ = '\0'; ali@475: ali@475: return realloc(retval, p - retval); ali@475: } ali@475: ali@475: static int validate_userinfo(const char *userinfo, struct razor_error **error) ali@475: { ali@475: const char *s; ali@475: ali@475: for (s = userinfo; *s; s++) { ali@475: if (!is_unreserved(*s) && *s != '%' && !is_sub_delim(*s) ali@475: && *s != ':') { ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, userinfo, ali@475: "Invalid URI userinfo"); ali@475: return -1; ali@475: } ali@475: } ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: static int validate_reg_name(const char *reg_name) ali@475: { ali@475: const char *s; ali@475: ali@475: for (s = reg_name; *s; s++) { ali@475: if (!is_unreserved(*s) && *s != '%' && !is_sub_delim(*s)) ali@475: return -1; ali@475: } ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: static int validate_ipv4address(const char *s, int length) ali@475: { ali@475: int count = 0, digits, octet; ali@475: ali@475: for (;;) { ali@475: if (!length) ali@475: return -1; ali@475: ali@475: if (*s == '0') { ali@475: digits = 1; ali@475: octet = 0; ali@475: } else { ali@475: if (*s < '1' || *s > '9') ali@475: return -1; ali@475: ali@475: octet = *s - '0'; ali@475: ali@475: for (digits = 1; digits < length; digits++) { ali@475: if (s[digits] >= '0' && s[digits] <= '9') { ali@475: octet *= 10; ali@475: octet += s[digits] - '0'; ali@475: if (octet > 255) ali@475: return -1; ali@475: } else ali@475: break; ali@475: } ali@475: } ali@475: ali@475: s += digits; ali@475: length -= digits; ali@475: ali@475: if (++count == 4) ali@475: break; ali@475: ali@475: if (length < 1 || *s != '.') ali@475: return -1; ali@475: ali@475: s++; ali@475: length--; ali@475: } ali@475: ali@475: return length ? -1 : 0; ali@475: } ali@475: ali@475: static int count_ipv6_pieces(const char **s, int *length) ali@475: { ali@475: int count, digits; ali@475: ali@475: for (digits = 0; digits < 4 && digits < *length; digits++) { ali@475: if (!is_xdigit((*s)[digits])) ali@475: break; ali@475: } ali@475: ali@475: if (!digits) ali@475: return 0; ali@475: ali@475: (*s) += digits; ali@475: (*length) -= digits; ali@475: count = 1; ali@475: ali@475: if (*length && **s == ':') { ali@475: (*s)++; ali@475: (*length)--; ali@475: count += count_ipv6_pieces(s, length); ali@475: if (count == 1) { ali@475: (*s)--; ali@475: (*length)++; ali@475: } ali@475: } ali@475: ali@475: return count; ali@475: } ali@475: ali@475: static int validate_ip_literal(const char *ip_literal, int length) ali@475: { ali@475: const char *s, *dot; ali@475: int len, no_pieces, elide; ali@475: ali@475: if (length >= 4 && ip_literal[0] == 'v') { ali@475: /* IPvFuture */ ali@475: dot = strchr(ip_literal + 2, '.'); ali@475: if (!dot || dot >= ip_literal + length) ali@475: return -1; ali@475: for (s = ip_literal + 1; s < dot; s++) { ali@475: if (!is_xdigit(*s)) ali@475: return -1; ali@475: } ali@475: for (s = dot + 1; s < ip_literal + length; s++) { ali@475: if (!is_unreserved(*s) && !is_sub_delim(*s) && *s != ':') ali@475: return -1; ali@475: } ali@475: } else { ali@475: /* IPv6address */ ali@475: s = ip_literal; ali@475: len = length; ali@475: no_pieces = count_ipv6_pieces(&s, &len); ali@475: ali@475: if (len > 1 && s[0] == ':' && s[1] == ':') { ali@475: s += 2; ali@475: len -= 2; ali@475: elide = 1; ali@475: no_pieces += count_ipv6_pieces(&s, &len); ali@475: } else ali@475: elide = 0; ali@475: ali@475: if (!validate_ipv4address(s, len)) ali@475: no_pieces += 2; ali@475: else if (len) ali@475: return -1; ali@475: ali@475: if (no_pieces > 8 || no_pieces == 8 && elide || no_pieces < 1) ali@475: return -1; ali@475: } ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: static int validate_host(const char *host, struct razor_error **error) ali@475: { ali@475: int retval; ali@475: ali@475: if (host[0] == '[' && host[strlen(host) - 1] == ']') ali@475: retval = validate_ip_literal(host + 1, strlen(host) - 2); ali@475: else { ali@475: retval = validate_ipv4address(host, strlen(host)); ali@475: if (retval < 0) ali@475: retval = validate_reg_name(host); ali@475: } ali@475: ali@475: if (retval) ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, host, ali@475: "Invalid URI host"); ali@475: ali@475: return retval; ali@475: } ali@475: ali@475: static char *strdown(char *s) ali@475: { ali@475: while (*s) { ali@475: if (*s >= 'A' && *s <= 'Z') { ali@475: *s -= 'A'; ali@475: *s += 'a'; ali@475: } ali@475: s++; ali@475: } ali@475: ali@475: return s; ali@475: } ali@475: ali@475: static int razor_uri_parse_authority(struct razor_uri *ru, ali@475: const char *authority, int length, ali@475: struct razor_error **error) ali@475: { ali@475: const char *s, *auth = authority; ali@475: char *userinfo, *port, *host; ali@475: ali@475: s = strchr(auth, '@'); ali@475: if (s && s < auth + length) { ali@475: userinfo = razor_strndup(auth, s - auth); ali@475: s++; ali@475: length -= s - auth; ali@475: auth = s; ali@475: ali@475: if (validate_userinfo(userinfo, error)) { ali@475: free(userinfo); ali@475: return -1; ali@475: } ali@475: } else ali@475: userinfo = NULL; ali@475: ali@475: s = strchr(auth, ':'); ali@475: if (s && s < auth + length) { ali@475: s++; ali@475: port = razor_strndup(s, length - (s - auth)); ali@475: s--; ali@475: length = s - auth; ali@475: ali@475: if (strspn(port, "0123456789") != strlen(port)) { ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, port, ali@475: "Invalid URI port"); ali@475: free(userinfo); ali@475: free(port); ali@475: return -1; ali@475: } ali@475: } else ali@475: port = NULL; ali@475: ali@475: host = razor_strndup(auth, length); ali@475: ali@475: if (validate_host(host, error)) { ali@475: free(userinfo); ali@475: free(port); ali@475: free(host); ali@475: return -1; ali@475: } ali@475: ali@475: ru->userinfo = userinfo; ali@475: ru->port = port; ali@475: ru->host = host; ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: /* ali@475: * Parse either a hier-part or a relative-part ali@475: */ ali@475: static int razor_uri_parse_part(struct razor_uri *ru, const char *part, ali@475: int relative_part, struct razor_error **error) ali@475: { ali@475: const char *s, *hp = part; ali@475: char *path, *p; ali@475: int noscheme = 0; ali@475: ali@475: if (hp[0] == '/' && hp[1] == '/') { ali@475: hp += 2; ali@475: s = strpbrk(hp, "/?#"); ali@475: if (!s) ali@475: s = hp + strlen(hp); ali@475: if (razor_uri_parse_authority(ru, hp, s - hp, error) < 0) ali@475: return -1; ali@475: hp = s; ali@475: } else { ali@475: ru->userinfo = NULL; ali@475: ru->host = NULL; ali@475: ru->port = NULL; ali@475: } ali@475: ali@475: if (!*hp) { ali@475: /* path-empty */ ali@475: ru->path = strdup(""); ali@475: return 0; ali@475: } else if (*hp == '/') { ali@475: /* path-absolute */ ali@475: p = path = malloc(strlen(hp) + 1); ali@475: *p++ = '/'; ali@475: hp++; ali@475: if (!*hp) { ali@475: *p++ = '\0'; ali@475: ru->path = realloc(path, p - path); ali@475: return 0; ali@475: } ali@475: } else if (!ru->host) { ali@475: /* path-rootless or path-noscheme */ ali@475: noscheme = relative_part; ali@475: p = path = malloc(strlen(hp) + 1); ali@475: } else { ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, part, ali@475: relative_part ? "Invalid URI relative part" : ali@475: "Invalid URI hierarchical part"); ali@475: return -1; ali@475: } ali@475: ali@475: if (!is_pchar(*hp) || noscheme && *hp == ':') { ali@475: free(path); ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, part, ali@475: "Invalid character in URI path"); ali@475: return -1; ali@475: } ali@475: *p++ = *hp++; ali@475: ali@475: while (*hp) { ali@475: if (*hp == '/') ali@475: noscheme = 0; ali@475: else if (!is_pchar(*hp) || noscheme && *hp == ':') { ali@475: free(path); ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, part, ali@475: "Invalid character in URI path"); ali@475: return -1; ali@475: } ali@475: *p++ = *hp++; ali@475: } ali@475: ali@475: *p++ = '\0'; ali@475: ali@475: ru->path = realloc(path, p - path); ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: void razor_uri_destroy(struct razor_uri *ru) ali@475: { ali@475: free(ru->scheme); ali@475: free(ru->userinfo); ali@475: free(ru->host); ali@475: free(ru->port); ali@475: free(ru->path); ali@475: free(ru->query); ali@475: free(ru->fragment); ali@475: } ali@475: ali@475: int razor_uri_parse_uri(struct razor_uri *ru, const char *uri, int absolute, ali@475: struct razor_error **error) ali@475: { ali@475: int r; ali@475: const char *s; ali@475: char *hier_part; ali@475: ali@475: if (pct_encoding_validate(uri) < 0) { ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, uri, ali@475: "Invalid percent encoding"); ali@475: return -1; ali@475: } ali@475: ali@475: memset(ru, 0, sizeof(*ru)); ali@475: ali@475: s = skip_uri_scheme(uri); ali@475: if (!s) { ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, uri, ali@475: "Invalid URI scheme"); ali@475: return -1; ali@475: } ali@475: ru->scheme = razor_strndup(uri, s - uri); ali@475: uri = s + 1; ali@475: ali@475: s = strchr(uri, '?'); ali@475: if (!s) ali@475: s = strchr(uri, '#'); ali@475: if (!s) ali@475: s = uri + strlen(uri); ali@475: hier_part = razor_strndup(uri, s - uri); ali@475: uri = s; ali@475: ali@475: r = razor_uri_parse_part(ru, hier_part, 0, error); ali@475: free(hier_part); ali@475: if (r) { ali@475: razor_uri_destroy(ru); ali@475: return -1; ali@475: } ali@475: ali@475: if (*uri != '?') ali@475: ru->query = NULL; ali@475: else { ali@475: uri++; ali@475: s = strchr(uri, '#'); ali@475: if (!s) ali@475: s = uri + strlen(uri); ali@475: ru->query = razor_strndup(uri, s - uri); ali@475: uri = s; ali@475: } ali@475: ali@475: if (*uri != '#') ali@475: ru->fragment = NULL; ali@475: else if (absolute) { ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, uri, ali@475: "Fragments are not allowed in absolute URIs"); ali@475: razor_uri_destroy(ru); ali@475: return -1; ali@475: } else { ali@475: uri++; ali@475: ru->fragment = strdup(uri); ali@475: } ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: int razor_uri_parse_relative_ref(struct razor_uri *ru, const char *uri, ali@475: struct razor_error **error) ali@475: { ali@475: int r; ali@475: const char *s; ali@475: char *relative_part; ali@475: ali@475: if (pct_encoding_validate(uri) < 0) { ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_BAD_URI, uri, ali@475: "Invalid percent encoding"); ali@475: return -1; ali@475: } ali@475: ali@475: memset(ru, 0, sizeof(*ru)); ali@475: ali@475: s = strchr(uri, '?'); ali@475: if (!s) ali@475: s = strchr(uri, '#'); ali@475: if (!s) ali@475: s = uri + strlen(uri); ali@475: relative_part = razor_strndup(uri, s - uri); ali@475: uri = s; ali@475: ali@475: r = razor_uri_parse_part(ru, relative_part, 1, error); ali@475: free(relative_part); ali@475: if (r) ali@475: return -1; ali@475: ali@475: if (*uri == '?') { ali@475: uri++; ali@475: s = strchr(uri, '#'); ali@475: if (!s) ali@475: s = uri + strlen(uri); ali@475: ru->query = razor_strndup(uri, s - uri); ali@475: uri = s; ali@475: } else ali@475: ru->query = NULL; ali@475: ali@475: if (*uri == '#') { ali@475: uri++; ali@475: ru->fragment = strdup(uri); ali@475: } else ali@475: ru->fragment = NULL; ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: int razor_uri_parse(struct razor_uri *ru, const char *uri, ali@475: struct razor_error **error) ali@475: { ali@475: struct razor_error *tmp_error = NULL; ali@475: int r; ali@475: ali@475: r = razor_uri_parse_uri(ru, uri, 0, &tmp_error); ali@475: if (r < 0) { ali@475: r = razor_uri_parse_relative_ref(ru, uri, NULL); ali@475: if (r < 0) ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: else ali@475: razor_error_free(tmp_error); ali@475: } ali@475: ali@475: return r; ali@475: } ali@475: ali@475: /* ali@475: * Following RFC 3986 § 5.2.4 ali@475: */ ali@475: static char *remove_dot_segments(const char *path) ali@475: { ali@475: struct array output; ali@475: char *input, *in, *s, *t; ali@475: const char *step; ali@475: ali@475: #ifdef DEBUG ali@475: fprintf(stderr, "STEP OUTPUT BUFFER INPUT BUFFER\n"); ali@475: #endif ali@475: ali@475: input = strdup(path); ali@475: in = input; ali@475: string_init(&output); ali@475: ali@475: #ifdef DEBUG ali@475: fprintf(stderr, " 1 : %-21s %s\n", string_str(&output), in); ali@475: #endif ali@475: ali@475: while (*in) { ali@475: if (str_has_prefix(in, "../")) { ali@475: step = "2A"; ali@475: in += 3; ali@475: } else if (str_has_prefix(in, "./")) { ali@475: step = "2A"; ali@475: in += 2; ali@475: } else if (str_has_prefix(in, "/./")) { ali@475: step = "2B"; ali@475: in += 2; ali@475: } else if (!strcmp(in, "/.")) { ali@475: step = "2B"; ali@475: in++; ali@475: *in = '/'; ali@475: } else if (str_has_prefix(in, "/../")) { ali@475: step = "2C"; ali@475: in += 3; ali@475: s = strrchr(string_str(&output), '/'); ali@475: if (!s) ali@475: s = string_str(&output); ali@475: string_truncate_at(&output, s); ali@475: } else if (!strcmp(in, "/..")) { ali@475: step = "2C"; ali@475: in += 2; ali@475: *in = '/'; ali@475: s = strrchr(string_str(&output), '/'); ali@475: if (!s) ali@475: s = string_str(&output); ali@475: string_truncate_at(&output, s); ali@475: } else if (!strcmp(in, ".") || !strcmp(in, "..")) { ali@475: step = "2D"; ali@475: in += strlen(in); ali@475: } else { ali@475: step = "2E"; ali@475: t = strchr(in + 1, '/'); ali@475: if (!t) ali@475: t = in + strlen(in); ali@475: string_append_len(&output, in, t - in); ali@475: in = t; ali@475: } ali@475: #ifdef DEBUG ali@475: fprintf(stderr, " %s: %-21s %s\n", step, string_str(&output), ali@475: in); ali@475: #endif ali@475: } ali@475: ali@475: free(input); ali@475: return string_str(&output); ali@475: } ali@475: ali@475: ali@475: /* ali@475: * Following RFC 3986 § 6.2.2 ali@475: */ ali@475: void razor_uri_normalize(struct razor_uri *ru) ali@475: { ali@475: char *s; ali@475: ali@475: strdown(ru->scheme); ali@475: if (ru->host) ali@475: strdown(ru->host); ali@475: ali@475: s = pct_encoding_normalize(ru->userinfo); ali@475: free(ru->userinfo); ali@475: ru->userinfo = s; ali@475: ali@475: s = pct_encoding_normalize(ru->host); ali@475: free(ru->host); ali@475: ru->host = s; ali@475: ali@475: s = pct_encoding_normalize(ru->path); ali@475: free(ru->path); ali@475: ru->path = s; ali@475: ali@475: s = pct_encoding_normalize(ru->query); ali@475: free(ru->query); ali@475: ru->query = s; ali@475: ali@475: s = pct_encoding_normalize(ru->fragment); ali@475: free(ru->fragment); ali@475: ru->fragment = s; ali@475: ali@475: s = remove_dot_segments(ru->path); ali@475: free(ru->path); ali@475: ru->path = s; ali@475: } ali@475: ali@475: char *razor_uri_get_authority(const struct razor_uri *ru) ali@475: { ali@475: char *result, *r; ali@475: int len = 1; ali@475: ali@475: if (ru->host) { ali@475: if (ru->userinfo) ali@475: len += strlen(ru->userinfo) + 1; ali@475: len += strlen(ru->host); ali@475: if (ru->port) ali@475: len += strlen(ru->port) + 1; ali@475: } else ali@475: return NULL; ali@475: ali@475: r = result = malloc(len); ali@475: ali@475: if (ru->userinfo) { ali@475: strcpy(r, ru->userinfo); ali@475: r += strlen(r); ali@475: *r++ = '@'; ali@475: } ali@475: ali@475: strcpy(r, ru->host); ali@475: r += strlen(r); ali@475: ali@475: if (ru->port) { ali@475: *r++ = ':'; ali@475: strcpy(r, ru->port); ali@475: } ali@475: ali@475: return result; ali@475: } ali@475: ali@475: /* ali@475: * Following RFC 3986 § 5.3 ali@475: */ ali@475: char *razor_uri_recompose(const struct razor_uri *ru) ali@475: { ali@475: char *authority, *result, *r; ali@475: int len = 1; ali@475: ali@475: authority = razor_uri_get_authority(ru); ali@475: ali@475: if (ru->scheme) ali@475: len += strlen(ru->scheme) + 1; ali@475: if (authority) ali@475: len += strlen(authority) + 2; ali@475: len += strlen(ru->path); ali@475: if (ru->query) ali@475: len += strlen(ru->query) + 1; ali@475: if (ru->fragment) ali@475: len += strlen(ru->fragment) + 1; ali@475: ali@475: r = result = malloc(len); ali@475: ali@475: if (ru->scheme) { ali@475: strcpy(r, ru->scheme); ali@475: r += strlen(r); ali@475: *r++ = ':'; ali@475: } ali@475: ali@475: if (authority) { ali@475: *r++ = '/'; ali@475: *r++ = '/'; ali@475: strcpy(r, authority); ali@475: free(authority); ali@475: r += strlen(r); ali@475: } ali@475: ali@475: strcpy(r, ru->path); ali@475: r += strlen(r); ali@475: ali@475: if (ru->query) { ali@475: *r++ = '?'; ali@475: strcpy(r, ru->query); ali@475: r += strlen(r); ali@475: } ali@475: ali@475: if (ru->fragment) { ali@475: *r++ = '#'; ali@475: strcpy(r, ru->fragment); ali@475: } ali@475: ali@475: return result; ali@475: } ali@475: ali@475: /* ali@475: * Following RFC 3986 § 5.2.3 ali@475: */ ali@475: static char *merge_paths(const struct razor_uri *base,const struct razor_uri *R) ali@475: { ali@475: char *s, *t, *path; ali@475: ali@475: if (base->host && !*base->path) ali@475: path = razor_concat("/", R->path, NULL); ali@475: else { ali@475: s = strrchr(base->path, '/'); ali@475: if (s) { ali@475: t = razor_strndup(base->path, s + 1 - base->path); ali@475: path = razor_concat(t, R->path, NULL); ali@475: free(t); ali@475: } else ali@475: path = strdup(R->path); ali@475: } ali@475: ali@475: return path; ali@475: } ali@475: ali@475: /* ali@475: * Following RFC 3986 § 5.2 ali@475: */ ali@475: void razor_uri_resolve(struct razor_uri *T, const struct razor_uri *base, ali@475: const struct razor_uri *R) ali@475: { ali@475: char *s; ali@475: ali@475: if (R->scheme) { ali@475: T->scheme = strdup(R->scheme); ali@475: T->userinfo = strdup0(R->userinfo); ali@475: T->host = strdup0(R->host); ali@475: T->port = strdup0(R->port); ali@475: T->path = remove_dot_segments(R->path); ali@475: T->query = strdup0(R->query); ali@475: } else { ali@475: if (R->host) { ali@475: T->userinfo = strdup0(R->userinfo); ali@475: T->host = strdup0(R->host); ali@475: T->port = strdup0(R->port); ali@475: T->path = remove_dot_segments(R->path); ali@475: T->query = strdup0(R->query); ali@475: } else { ali@475: if (!*R->path) { ali@475: T->path = strdup(base->path); ali@475: if (R->query) ali@475: T->query = strdup(R->query); ali@475: else ali@475: T->query = strdup0(base->query); ali@475: } else { ali@475: if (*R->path == '/') ali@475: T->path = remove_dot_segments(R->path); ali@475: else { ali@475: s = merge_paths(base, R); ali@475: T->path = remove_dot_segments(s); ali@475: free(s); ali@475: } ali@475: T->query = strdup0(R->query); ali@475: } ali@475: T->userinfo = strdup0(base->userinfo); ali@475: T->host = strdup0(base->host); ali@475: T->port = strdup0(base->port); ali@475: } ali@475: T->scheme = strdup(base->scheme); ali@475: } ali@475: T->fragment = strdup0(R->fragment); ali@475: } ali@475: ali@475: /* ali@475: * This differs from razor_uri_resolve() both in the types of its arguments ali@475: * and in the fact that it takes a root URI rather than a base URI. The base ali@475: * URI is determined by appending a slash to the root URI (if it doesn't ali@475: * already end in a slash). Finally, uri can be explicitly marked as either ali@475: * relative (ie., a relative-ref) or not (ie., a URI). This is important as ali@475: * otherwise "c:/xxx" could be interpreted as a URI in the "c" scheme. ali@475: */ ali@475: char *razor_resolve_uri_root(const char *root_uri, const char *uri, ali@475: int is_relative, struct razor_error **error) ali@475: { ali@475: int r; ali@475: char *base_uri, *s, *result; ali@475: struct razor_uri ru, base, file; ali@475: ali@475: if (!root_uri || !*root_uri) ali@475: root_uri = "file:/"; ali@475: ali@475: if (root_uri[strlen(root_uri) - 1] == '/') ali@475: base_uri = strdup(root_uri); ali@475: else ali@475: base_uri = razor_concat(root_uri, "/", NULL); ali@475: ali@475: r = razor_uri_parse_uri(&base, base_uri, 1, error); ali@475: free(base_uri); ali@475: if (r) ali@475: return NULL; ali@475: ali@475: if (is_relative > 0) { ali@475: /* ali@475: * We can't use razor_uri_parse_relative_ref() to parse ali@475: * uri in case it starts with a segment that includes a ali@475: * colon. Thus we use this kludge. ali@475: */ ali@475: s = razor_concat("scheme:", uri, NULL); ali@475: r = razor_uri_parse_uri(&file, s, 0, error); ali@475: free(s); ali@475: if (!r) { ali@475: free(file.scheme); ali@475: file.scheme = NULL; ali@475: } ali@475: } ali@475: else if (!is_relative) ali@475: r = razor_uri_parse_uri(&file, uri, 0, error); ali@475: else ali@475: r = razor_uri_parse(&file, uri, error); ali@475: if (r) { ali@475: razor_uri_destroy(&base); ali@475: return NULL; ali@475: } ali@475: ali@475: razor_uri_resolve(&ru, &base, &file); ali@475: ali@475: razor_uri_destroy(&base); ali@475: razor_uri_destroy(&file); ali@475: ali@475: result = razor_uri_recompose(&ru); ali@475: ali@475: razor_uri_destroy(&ru); ali@475: ali@475: return result; ali@475: }