librazor/uri.c
author J. Ali Harlow <ali@juiblex.co.uk>
Tue Apr 24 19:27:29 2018 +0100 (2018-04-24)
changeset 496 203fa998c6df
parent 475 008c75a5e08d
permissions -rw-r--r--
Support expat v2.2
     1 /*
     2  * Copyright (C) 2016  J. Ali Harlow <ali@juiblex.co.uk>
     3  *
     4  * This program is free software; you can redistribute it and/or modify
     5  * it under the terms of the GNU General Public License as published by
     6  * the Free Software Foundation; either version 2 of the License, or
     7  * (at your option) any later version.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License along
    15  * with this program; if not, write to the Free Software Foundation, Inc.,
    16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    17  */
    18 
    19 #include "config.h"
    20 
    21 #undef DEBUG
    22 
    23 #include <stdlib.h>
    24 #include <string.h>
    25 #include "razor.h"
    26 #include "types/types.h"
    27 #include "razor-internal.h"
    28 #include "uri.h"
    29 
    30 /*
    31  * Following RFC 3986 § 3.
    32  * Note that we don't validate queries or fragments.
    33  */
    34 
    35 #define strdup0(s)			((s) ? strdup(s) : NULL)
    36 
    37 #define string_str(str)			((char *)(str)->data)
    38 
    39 #define string_init(str)		do { \
    40 						char *_p; \
    41 						array_init(str); \
    42 						_p = array_add(str, 1); \
    43 						*_p = '\0'; \
    44 					} while(0)
    45 
    46 #define string_append_len(str, s, len)	do { \
    47 						char *_p; \
    48 						_p = array_add(str, len); \
    49 						_p--; \
    50 						strncpy(_p, s, len); \
    51 						_p[(len)] = '\0'; \
    52 					} while(0)
    53 
    54 #define string_append(str, s)		string_append_len(str, s, strlen(s))
    55 
    56 #define string_truncate_at(str, s)	do { \
    57 						int _len; \
    58 						_len = (s) - \
    59 						       (char *)(str)->data; \
    60 						*(s) = '\0'; \
    61 						(str)->size = _len + 1; \
    62 					} while(0)
    63 
    64 
    65 static const char *skip_uri_scheme(const char *uri)
    66 {
    67 	/*
    68 	 * RFC 3986 defines scheme as:
    69 	 *	scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
    70 	 */
    71 	if (*uri >= 'a' && *uri <= 'z' || *uri >= 'A' && *uri <= 'Z') {
    72 		do {
    73 		    uri++;
    74 		} while (is_alnum(*uri) || *uri == '+' || *uri == '-' ||
    75 			 *uri == '.');
    76 		if (*uri == ':')
    77 			return uri;
    78 	}
    79 	return NULL;
    80 }
    81 
    82 static char *razor_strndup(const char *s, size_t n)
    83 {
    84 	char *result;
    85 
    86 	if (memchr(s, '\0', n))
    87 		result = strdup(s);
    88 	else {
    89 		result = malloc(n + 1);
    90 		memcpy(result, s, n);
    91 		result[n] = '\0';
    92 	}
    93 
    94 	return result;
    95 }
    96 
    97 #if 0
    98 /*
    99  * Return the (possibly decoded) pchar or 0 on end-of-string or -1 on error
   100  */
   101 static int pchar_get_char_validated(const char *p)
   102 {
   103 	int c;
   104 
   105 	if (p[0]=='\0')
   106 		c = 0;
   107 	else if (p[0]=='%') {
   108 		if (xdigit_value(p[1]) < 0)
   109 			return -1;
   110 		c = xdigit_value(p[1]) * 16;
   111 		if (xdigit_value(p[2]) < 0)
   112 			return -1;
   113 		c += xdigit_value(p[2]);
   114 	} else if (p[0] >= 'a' && p[0] <= 'z' || p[0] >= 'A' && p[0] <= 'Z' || 
   115 		   p[0] >= '0' && p[0] <= '9' ||
   116 		   strchr("-._~!$&'()*+,;=:@", p[0]))
   117 		c = p[0];
   118 	else
   119 		c = -1;
   120 
   121 	return c;
   122 }
   123 #endif
   124 
   125 /*
   126  * Verify the percent encoding. All '%' characters must be followed by
   127  * exactly two hexadecimal digits.
   128  */
   129 static int pct_encoding_validate(const char *s)
   130 {
   131 	while (*s) {
   132 		if (*s == '%') {
   133 			if (xdigit_value(s[1]) < 0 || xdigit_value(s[2]) < 0)
   134 				return -1;
   135 			s += 2;
   136 		}
   137 
   138 		s++;
   139 	}
   140 
   141 	return 0;
   142 }
   143 
   144 static char *pct_encoding_normalize(char *s)
   145 {
   146 	char *retval, *p;
   147 	int c;
   148 
   149 	if (!s)
   150 		return NULL;
   151 
   152 	p = retval = malloc(strlen(s) + 1);
   153 
   154 	while (*s) {
   155 		if (*s == '%') {
   156 			c = pchar_get_char(s);
   157 			if (is_unreserved(c))
   158 				*p++ = c;
   159 			else {
   160 				*p++ = '%';
   161 				*p++ = "0123456789ABCDEF"[c/16];
   162 				*p++ = "0123456789ABCDEF"[c%16];
   163 			}
   164 			pchar_next_char(s);
   165 		} else
   166 			*p++ = *s++;
   167 	}
   168 
   169 	*p++ = '\0';
   170 
   171 	return realloc(retval, p - retval);
   172 }
   173 
   174 static int validate_userinfo(const char *userinfo, struct razor_error **error)
   175 {
   176 	const char *s;
   177 
   178 	for (s = userinfo; *s; s++) {
   179 		if (!is_unreserved(*s) && *s != '%' && !is_sub_delim(*s)
   180 		    && *s != ':') {
   181 			razor_set_error(error, RAZOR_GENERAL_ERROR,
   182 					RAZOR_GENERAL_ERROR_BAD_URI, userinfo,
   183 					"Invalid URI userinfo");
   184 			return -1;
   185 		}
   186 	}
   187 
   188 	return 0;
   189 }
   190 
   191 static int validate_reg_name(const char *reg_name)
   192 {
   193 	const char *s;
   194 
   195 	for (s = reg_name; *s; s++) {
   196 		if (!is_unreserved(*s) && *s != '%' && !is_sub_delim(*s))
   197 			return -1;
   198 	}
   199 
   200 	return 0;
   201 }
   202 
   203 static int validate_ipv4address(const char *s, int length)
   204 {
   205 	int count = 0, digits, octet;
   206 
   207 	for (;;) {
   208 		if (!length)
   209 			return -1;
   210 
   211 		if (*s == '0') {
   212 			digits = 1;
   213 			octet = 0;
   214 		} else {
   215 			if (*s < '1' || *s > '9')
   216 				return -1;
   217 
   218 			octet = *s - '0';
   219 
   220 			for (digits = 1; digits < length; digits++) {
   221 				if (s[digits] >= '0' && s[digits] <= '9') {
   222 					octet *= 10;
   223 					octet += s[digits] - '0';
   224 					if (octet > 255)
   225 						return -1;
   226 				} else
   227 					break;
   228 			}
   229 		}
   230 
   231 		s += digits;
   232 		length -= digits;
   233 
   234 		if (++count == 4)
   235 			break;
   236 
   237 		if (length < 1 || *s != '.')
   238 			return -1;
   239 
   240 		s++;
   241 		length--;
   242 	}
   243 
   244 	return length ? -1 : 0;
   245 }
   246 
   247 static int count_ipv6_pieces(const char **s, int *length)
   248 {
   249 	int count, digits;
   250 
   251 	for (digits = 0; digits < 4 && digits < *length; digits++) {
   252 		if (!is_xdigit((*s)[digits]))
   253 			break;
   254 	}
   255 
   256 	if (!digits)
   257 		return 0;
   258 
   259 	(*s) += digits;
   260 	(*length) -= digits;
   261 	count = 1;
   262 
   263 	if (*length && **s == ':') {
   264 		(*s)++;
   265 		(*length)--;
   266 		count += count_ipv6_pieces(s, length);
   267 		if (count == 1) {
   268 			(*s)--;
   269 			(*length)++;
   270 		}
   271 	}
   272 
   273 	return count;
   274 }
   275 
   276 static int validate_ip_literal(const char *ip_literal, int length)
   277 {
   278 	const char *s, *dot;
   279 	int len, no_pieces, elide;
   280 
   281 	if (length >= 4 && ip_literal[0] == 'v') {
   282 		/* IPvFuture */
   283 		dot = strchr(ip_literal + 2, '.');
   284 		if (!dot || dot >= ip_literal + length)
   285 			return -1;
   286 		for (s = ip_literal + 1; s < dot; s++) {
   287 			if (!is_xdigit(*s))
   288 				return -1;
   289 		}
   290 		for (s = dot + 1; s < ip_literal + length; s++) {
   291 			if (!is_unreserved(*s) && !is_sub_delim(*s) && *s != ':')
   292 				return -1;
   293 		}
   294 	} else {
   295 		/* IPv6address */
   296 		s = ip_literal;
   297 		len = length;
   298 		no_pieces = count_ipv6_pieces(&s, &len);
   299 
   300 		if (len > 1 && s[0] == ':' && s[1] == ':') {
   301 			s += 2;
   302 			len -= 2;
   303 			elide = 1;
   304 			no_pieces += count_ipv6_pieces(&s, &len);
   305 		} else
   306 			elide = 0;
   307 
   308 		if (!validate_ipv4address(s, len))
   309 			no_pieces += 2;
   310 		else if (len)
   311 			return -1;
   312 
   313 		if (no_pieces > 8 || no_pieces == 8 && elide || no_pieces < 1)
   314 			return -1;
   315 	}
   316 
   317 	return 0;
   318 }
   319 
   320 static int validate_host(const char *host, struct razor_error **error)
   321 {
   322 	int retval;
   323 
   324 	if (host[0] == '[' && host[strlen(host) - 1] == ']')
   325 		retval = validate_ip_literal(host + 1, strlen(host) - 2);
   326 	else {
   327 		retval = validate_ipv4address(host, strlen(host));
   328 		if (retval < 0)
   329 			retval = validate_reg_name(host);
   330 	}
   331 
   332 	if (retval)
   333 		razor_set_error(error, RAZOR_GENERAL_ERROR,
   334 				RAZOR_GENERAL_ERROR_BAD_URI, host,
   335 				"Invalid URI host");
   336 
   337 	return retval;
   338 }
   339 
   340 static char *strdown(char *s)
   341 {
   342 	while (*s) {
   343 		if (*s >= 'A' && *s <= 'Z') {
   344 			*s -= 'A';
   345 			*s += 'a';
   346 		}
   347 		s++;
   348 	}
   349 
   350 	return s;
   351 }
   352 
   353 static int razor_uri_parse_authority(struct razor_uri *ru,
   354 				     const char *authority, int length,
   355 				     struct razor_error **error)
   356 {
   357 	const char *s, *auth = authority;
   358 	char *userinfo, *port, *host;
   359 
   360 	s = strchr(auth, '@');
   361 	if (s && s < auth + length) {
   362 		userinfo = razor_strndup(auth, s - auth);
   363 		s++;
   364 		length -= s - auth;
   365 		auth = s;
   366 
   367 		if (validate_userinfo(userinfo, error)) {
   368 			free(userinfo);
   369 			return -1;
   370 		}
   371 	} else
   372 		userinfo = NULL;
   373 
   374 	s = strchr(auth, ':');
   375 	if (s && s < auth + length) {
   376 		s++;
   377 		port = razor_strndup(s, length - (s - auth));
   378 		s--;
   379 		length = s - auth;
   380 
   381 		if (strspn(port, "0123456789") != strlen(port)) {
   382 			razor_set_error(error, RAZOR_GENERAL_ERROR,
   383 					RAZOR_GENERAL_ERROR_BAD_URI, port,
   384 					"Invalid URI port");
   385 			free(userinfo);
   386 			free(port);
   387 			return -1;
   388 		}
   389 	} else
   390 		port = NULL;
   391 
   392 	host = razor_strndup(auth, length);
   393 
   394 	if (validate_host(host, error)) {
   395 		free(userinfo);
   396 		free(port);
   397 		free(host);
   398 		return -1;
   399 	}
   400 
   401 	ru->userinfo = userinfo;
   402 	ru->port = port;
   403 	ru->host = host;
   404 
   405 	return 0;
   406 }
   407 
   408 /*
   409  * Parse either a hier-part or a relative-part
   410  */
   411 static int razor_uri_parse_part(struct razor_uri *ru, const char *part,
   412 				int relative_part, struct razor_error **error)
   413 {
   414 	const char *s, *hp = part;
   415 	char *path, *p;
   416 	int noscheme = 0;
   417 
   418 	if (hp[0] == '/' && hp[1] == '/') {
   419 		hp += 2;
   420 		s = strpbrk(hp, "/?#");
   421 		if (!s)
   422 			s = hp + strlen(hp);
   423 		if (razor_uri_parse_authority(ru, hp, s - hp, error) < 0)
   424 			return -1;
   425 		hp = s;
   426 	} else {
   427 		ru->userinfo = NULL;
   428 		ru->host = NULL;
   429 		ru->port = NULL;
   430 	}
   431 
   432 	if (!*hp) {
   433 		/* path-empty */
   434 		ru->path = strdup("");
   435 		return 0;
   436 	} else if (*hp == '/') {
   437 		/* path-absolute */
   438 		p = path = malloc(strlen(hp) + 1);
   439 		*p++ = '/';
   440 		hp++;
   441 		if (!*hp) {
   442 			*p++ = '\0';
   443 			ru->path = realloc(path, p - path);
   444 			return 0;
   445 		}
   446 	} else if (!ru->host) {
   447 		/* path-rootless or path-noscheme */
   448 		noscheme = relative_part;
   449 		p = path = malloc(strlen(hp) + 1);
   450 	} else {
   451 		razor_set_error(error, RAZOR_GENERAL_ERROR,
   452 				RAZOR_GENERAL_ERROR_BAD_URI, part,
   453 				relative_part ? "Invalid URI relative part" :
   454 				"Invalid URI hierarchical part");
   455 		return -1;
   456 	}
   457 
   458 	if (!is_pchar(*hp) || noscheme && *hp == ':') {
   459 		free(path);
   460 		razor_set_error(error, RAZOR_GENERAL_ERROR,
   461 				RAZOR_GENERAL_ERROR_BAD_URI, part,
   462 				"Invalid character in URI path");
   463 		return -1;
   464 	}
   465 	*p++ = *hp++;
   466 
   467 	while (*hp) {
   468 		if (*hp == '/')
   469 			noscheme = 0;
   470 		else if (!is_pchar(*hp) || noscheme && *hp == ':') {
   471 			free(path);
   472 			razor_set_error(error, RAZOR_GENERAL_ERROR,
   473 					RAZOR_GENERAL_ERROR_BAD_URI, part,
   474 					"Invalid character in URI path");
   475 			return -1;
   476 		}
   477 		*p++ = *hp++;
   478 	}
   479 
   480 	*p++ = '\0';
   481 
   482 	ru->path = realloc(path, p - path);
   483 
   484 	return 0;
   485 }
   486 
   487 void razor_uri_destroy(struct razor_uri *ru)
   488 {
   489 	free(ru->scheme);
   490 	free(ru->userinfo);
   491 	free(ru->host);
   492 	free(ru->port);
   493 	free(ru->path);
   494 	free(ru->query);
   495 	free(ru->fragment);
   496 }
   497 
   498 int razor_uri_parse_uri(struct razor_uri *ru, const char *uri, int absolute,
   499 			struct razor_error **error)
   500 {
   501 	int r;
   502 	const char *s;
   503 	char *hier_part;
   504 
   505 	if (pct_encoding_validate(uri) < 0) {
   506 		razor_set_error(error, RAZOR_GENERAL_ERROR,
   507 				RAZOR_GENERAL_ERROR_BAD_URI, uri,
   508 				"Invalid percent encoding");
   509 		return -1;
   510 	}
   511 
   512 	memset(ru, 0, sizeof(*ru));
   513 
   514 	s = skip_uri_scheme(uri);
   515 	if (!s) {
   516 		razor_set_error(error, RAZOR_GENERAL_ERROR,
   517 				RAZOR_GENERAL_ERROR_BAD_URI, uri,
   518 				"Invalid URI scheme");
   519 		return -1;
   520 	}
   521 	ru->scheme = razor_strndup(uri, s - uri);
   522 	uri = s + 1;
   523 
   524 	s = strchr(uri, '?');
   525 	if (!s)
   526 		s = strchr(uri, '#');
   527 	if (!s)
   528 		s = uri + strlen(uri);
   529 	hier_part = razor_strndup(uri, s - uri);
   530 	uri = s;
   531 
   532 	r = razor_uri_parse_part(ru, hier_part, 0, error);
   533 	free(hier_part);
   534 	if (r) {
   535 		razor_uri_destroy(ru);
   536 		return -1;
   537 	}
   538 
   539 	if (*uri != '?')
   540 		ru->query = NULL;
   541 	else {
   542 		uri++;
   543 		s = strchr(uri, '#');
   544 		if (!s)
   545 			s = uri + strlen(uri);
   546 		ru->query = razor_strndup(uri, s - uri);
   547 		uri = s;
   548 	}
   549 
   550 	if (*uri != '#')
   551 		ru->fragment = NULL;
   552 	else if (absolute) {
   553 		razor_set_error(error, RAZOR_GENERAL_ERROR,
   554 				RAZOR_GENERAL_ERROR_BAD_URI, uri,
   555 				"Fragments are not allowed in absolute URIs");
   556 		razor_uri_destroy(ru);
   557 		return -1;
   558 	} else {
   559 		uri++;
   560 		ru->fragment = strdup(uri);
   561 	}
   562 
   563 	return 0;
   564 }
   565 
   566 int razor_uri_parse_relative_ref(struct razor_uri *ru, const char *uri,
   567 				 struct razor_error **error)
   568 {
   569 	int r;
   570 	const char *s;
   571 	char *relative_part;
   572 
   573 	if (pct_encoding_validate(uri) < 0) {
   574 		razor_set_error(error, RAZOR_GENERAL_ERROR,
   575 				RAZOR_GENERAL_ERROR_BAD_URI, uri,
   576 				"Invalid percent encoding");
   577 		return -1;
   578 	}
   579 
   580 	memset(ru, 0, sizeof(*ru));
   581 
   582 	s = strchr(uri, '?');
   583 	if (!s)
   584 		s = strchr(uri, '#');
   585 	if (!s)
   586 		s = uri + strlen(uri);
   587 	relative_part = razor_strndup(uri, s - uri);
   588 	uri = s;
   589 
   590 	r = razor_uri_parse_part(ru, relative_part, 1, error);
   591 	free(relative_part);
   592 	if (r)
   593 		return -1;
   594 
   595 	if (*uri == '?') {
   596 		uri++;
   597 		s = strchr(uri, '#');
   598 		if (!s)
   599 			s = uri + strlen(uri);
   600 		ru->query = razor_strndup(uri, s - uri);
   601 		uri = s;
   602 	} else
   603 		ru->query = NULL;
   604 
   605 	if (*uri == '#') {
   606 		uri++;
   607 		ru->fragment = strdup(uri);
   608 	} else
   609 		ru->fragment = NULL;
   610 
   611 	return 0;
   612 }
   613 
   614 int razor_uri_parse(struct razor_uri *ru, const char *uri,
   615 		    struct razor_error **error)
   616 {
   617 	struct razor_error *tmp_error = NULL;
   618 	int r;
   619 
   620 	r = razor_uri_parse_uri(ru, uri, 0, &tmp_error);
   621 	if (r < 0) {
   622 		r = razor_uri_parse_relative_ref(ru, uri, NULL);
   623 		if (r < 0)
   624 			razor_propagate_error(error, tmp_error, NULL);
   625 		else
   626 			razor_error_free(tmp_error);
   627 	}
   628 
   629 	return r;
   630 }
   631 
   632 /*
   633  * Following RFC 3986 § 5.2.4
   634  */
   635 static char *remove_dot_segments(const char *path)
   636 {
   637 	struct array output;
   638 	char *input, *in, *s, *t;
   639 	const char *step;
   640 
   641 #ifdef DEBUG
   642 	fprintf(stderr, "STEP   OUTPUT BUFFER         INPUT BUFFER\n");
   643 #endif
   644 
   645 	input = strdup(path);
   646 	in = input;
   647 	string_init(&output);
   648 
   649 #ifdef DEBUG
   650 	fprintf(stderr, " 1 :   %-21s %s\n", string_str(&output), in);
   651 #endif
   652 
   653 	while (*in) {
   654 		if (str_has_prefix(in, "../")) {
   655 			step = "2A";
   656 			in += 3;
   657 		} else if (str_has_prefix(in, "./")) {
   658 			step = "2A";
   659 			in += 2;
   660 		} else if (str_has_prefix(in, "/./")) {
   661 			step = "2B";
   662 			in += 2;
   663 		} else if (!strcmp(in, "/.")) {
   664 			step = "2B";
   665 			in++;
   666 			*in = '/';
   667 		} else if (str_has_prefix(in, "/../")) {
   668 			step = "2C";
   669 			in += 3;
   670 			s = strrchr(string_str(&output), '/');
   671 			if (!s)
   672 				s = string_str(&output);
   673 			string_truncate_at(&output, s);
   674 		} else if (!strcmp(in, "/..")) {
   675 			step = "2C";
   676 			in += 2;
   677 			*in = '/';
   678 			s = strrchr(string_str(&output), '/');
   679 			if (!s)
   680 				s = string_str(&output);
   681 			string_truncate_at(&output, s);
   682 		} else if (!strcmp(in, ".") || !strcmp(in, "..")) {
   683 			step = "2D";
   684 			in += strlen(in);
   685 		} else {
   686 			step = "2E";
   687 			t = strchr(in + 1, '/');
   688 			if (!t)
   689 				t = in + strlen(in);
   690 			string_append_len(&output, in, t - in);
   691 			in = t;
   692 		}
   693 #ifdef DEBUG
   694 		fprintf(stderr, " %s:   %-21s %s\n", step, string_str(&output),
   695 			in);
   696 #endif
   697 	}
   698 
   699 	free(input);
   700 	return string_str(&output);
   701 }
   702 
   703 /*
   704  * Following RFC 3986 § 6.2.2
   705  */
   706 void razor_uri_normalize(struct razor_uri *ru)
   707 {
   708 	char *s;
   709 
   710 	strdown(ru->scheme);
   711 	if (ru->host)
   712 		strdown(ru->host);
   713 
   714 	s = pct_encoding_normalize(ru->userinfo);
   715 	free(ru->userinfo);
   716 	ru->userinfo = s;
   717 
   718 	s = pct_encoding_normalize(ru->host);
   719 	free(ru->host);
   720 	ru->host = s;
   721 
   722 	s = pct_encoding_normalize(ru->path);
   723 	free(ru->path);
   724 	ru->path = s;
   725 
   726 	s = pct_encoding_normalize(ru->query);
   727 	free(ru->query);
   728 	ru->query = s;
   729 
   730 	s = pct_encoding_normalize(ru->fragment);
   731 	free(ru->fragment);
   732 	ru->fragment = s;
   733 
   734 	s = remove_dot_segments(ru->path);
   735 	free(ru->path);
   736 	ru->path = s;
   737 }
   738 
   739 char *razor_uri_get_authority(const struct razor_uri *ru)
   740 {
   741 	char *result, *r;
   742 	int len = 1;
   743 
   744 	if (ru->host) {
   745 		if (ru->userinfo)
   746 			len += strlen(ru->userinfo) + 1;
   747 		len += strlen(ru->host);
   748 		if (ru->port)
   749 			len += strlen(ru->port) + 1;
   750 	} else
   751 		return NULL;
   752 
   753 	r = result = malloc(len);
   754 
   755 	if (ru->userinfo) {
   756 		strcpy(r, ru->userinfo);
   757 		r += strlen(r);
   758 		*r++ = '@';
   759 	}
   760 
   761 	strcpy(r, ru->host);
   762 	r += strlen(r);
   763 
   764 	if (ru->port) {
   765 		*r++ = ':';
   766 		strcpy(r, ru->port);
   767 	}
   768 
   769 	return result;
   770 }
   771 
   772 /*
   773  * Following RFC 3986 § 5.3
   774  */
   775 char *razor_uri_recompose(const struct razor_uri *ru)
   776 {
   777 	char *authority, *result, *r;
   778 	int len = 1;
   779 
   780 	authority = razor_uri_get_authority(ru);
   781 
   782 	if (ru->scheme)
   783 		len += strlen(ru->scheme) + 1;
   784 	if (authority)
   785 		len += strlen(authority) + 2;
   786 	len += strlen(ru->path);
   787 	if (ru->query)
   788 		len += strlen(ru->query) + 1;
   789 	if (ru->fragment)
   790 		len += strlen(ru->fragment) + 1;
   791 
   792 	r = result = malloc(len);
   793 
   794 	if (ru->scheme) {
   795 		strcpy(r, ru->scheme);
   796 		r += strlen(r);
   797 		*r++ = ':';
   798 	}
   799 
   800 	if (authority) {
   801 		*r++ = '/';
   802 		*r++ = '/';
   803 		strcpy(r, authority);
   804 		free(authority);
   805 		r += strlen(r);
   806 	}
   807 
   808 	strcpy(r, ru->path);
   809 	r += strlen(r);
   810 
   811 	if (ru->query) {
   812 		*r++ = '?';
   813 		strcpy(r, ru->query);
   814 		r += strlen(r);
   815 	}
   816 
   817 	if (ru->fragment) {
   818 		*r++ = '#';
   819 		strcpy(r, ru->fragment);
   820 	}
   821 
   822 	return result;
   823 }
   824 
   825 /*
   826  * Following RFC 3986 § 5.2.3
   827  */
   828 static char *merge_paths(const struct razor_uri *base,const struct razor_uri *R)
   829 {
   830 	char *s, *t, *path;
   831 
   832 	if (base->host && !*base->path)
   833 		path = razor_concat("/", R->path, NULL);
   834 	else {
   835 		s = strrchr(base->path, '/');
   836 		if (s) {
   837 			t = razor_strndup(base->path, s + 1 - base->path);
   838 			path = razor_concat(t, R->path, NULL);
   839 			free(t);
   840 		} else
   841 			path = strdup(R->path);
   842 	}
   843 
   844 	return path;
   845 }
   846 
   847 /*
   848  * Following RFC 3986 § 5.2
   849  */
   850 void razor_uri_resolve(struct razor_uri *T, const struct razor_uri *base,
   851 		       const struct razor_uri *R)
   852 {
   853 	char *s;
   854 
   855 	if (R->scheme) {
   856 		T->scheme = strdup(R->scheme);
   857 		T->userinfo = strdup0(R->userinfo);
   858 		T->host = strdup0(R->host);
   859 		T->port = strdup0(R->port);
   860 		T->path = remove_dot_segments(R->path);
   861 		T->query = strdup0(R->query);
   862 	} else {
   863 		if (R->host) {
   864 			T->userinfo = strdup0(R->userinfo);
   865 			T->host = strdup0(R->host);
   866 			T->port = strdup0(R->port);
   867 			T->path = remove_dot_segments(R->path);
   868 			T->query = strdup0(R->query);
   869 		} else {
   870 			if (!*R->path) {
   871 				T->path = strdup(base->path);
   872 				if (R->query)
   873 					T->query = strdup(R->query);
   874 				else
   875 					T->query = strdup0(base->query);
   876 			} else {
   877 				if (*R->path == '/')
   878 					T->path = remove_dot_segments(R->path);
   879 				else {
   880 					s = merge_paths(base, R);
   881 					T->path = remove_dot_segments(s);
   882 					free(s);
   883 				}
   884 				T->query = strdup0(R->query);
   885 			}
   886 			T->userinfo = strdup0(base->userinfo);
   887 			T->host = strdup0(base->host);
   888 			T->port = strdup0(base->port);
   889 		}
   890 		T->scheme = strdup(base->scheme);
   891 	}
   892 	T->fragment = strdup0(R->fragment);
   893 }
   894 
   895 /*
   896  * This differs from razor_uri_resolve() both in the types of its arguments
   897  * and in the fact that it takes a root URI rather than a base URI. The base
   898  * URI is determined by appending a slash to the root URI (if it doesn't
   899  * already end in a slash). Finally, uri can be explicitly marked as either
   900  * relative (ie., a relative-ref) or not (ie., a URI). This is important as
   901  * otherwise "c:/xxx" could be interpreted as a URI in the "c" scheme.
   902  */
   903 char *razor_resolve_uri_root(const char *root_uri, const char *uri,
   904 			     int is_relative, struct razor_error **error)
   905 {
   906 	int r;
   907         char *base_uri, *s, *result;
   908 	struct razor_uri ru, base, file;
   909 
   910 	if (!root_uri || !*root_uri)
   911 		root_uri = "file:/";
   912 
   913 	if (root_uri[strlen(root_uri) - 1] == '/')
   914 		base_uri = strdup(root_uri);
   915 	else
   916 		base_uri = razor_concat(root_uri, "/", NULL);
   917 
   918 	r = razor_uri_parse_uri(&base, base_uri, 1, error);
   919 	free(base_uri);
   920 	if (r)
   921 		return NULL;
   922 
   923 	if (is_relative > 0) {
   924 		/*
   925 		 * We can't use razor_uri_parse_relative_ref() to parse
   926 		 * uri in case it starts with a segment that includes a
   927 		 * colon. Thus we use this kludge.
   928 		 */
   929 		s = razor_concat("scheme:", uri, NULL);
   930 		r = razor_uri_parse_uri(&file, s, 0, error);
   931 		free(s);
   932 		if (!r) {
   933 			free(file.scheme);
   934 			file.scheme = NULL;
   935 		}
   936 	}
   937 	else if (!is_relative)
   938 		r = razor_uri_parse_uri(&file, uri, 0, error);
   939 	else
   940 		r = razor_uri_parse(&file, uri, error);
   941 	if (r) {
   942 		razor_uri_destroy(&base);
   943 		return NULL;
   944 	}
   945 
   946 	razor_uri_resolve(&ru, &base, &file);
   947 
   948 	razor_uri_destroy(&base);
   949 	razor_uri_destroy(&file);
   950 
   951 	result = razor_uri_recompose(&ru);
   952 
   953 	razor_uri_destroy(&ru);
   954 
   955 	return result;
   956 }