2 bfa33dbe 2021-04-22 op * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
4 bfa33dbe 2021-04-22 op * Permission to use, copy, modify, and distribute this software for any
5 bfa33dbe 2021-04-22 op * purpose with or without fee is hereby granted, provided that the above
6 bfa33dbe 2021-04-22 op * copyright notice and this permission notice appear in all copies.
8 bfa33dbe 2021-04-22 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 bfa33dbe 2021-04-22 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 bfa33dbe 2021-04-22 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 bfa33dbe 2021-04-22 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 bfa33dbe 2021-04-22 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 bfa33dbe 2021-04-22 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 bfa33dbe 2021-04-22 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 bfa33dbe 2021-04-22 op * - distinguish between an empty component and a undefined one
23 bfa33dbe 2021-04-22 op #include <assert.h>
25 bfa33dbe 2021-04-22 op #include "compat.h"
27 bfa33dbe 2021-04-22 op #include "phos.h"
29 bfa33dbe 2021-04-22 op #include <ctype.h>
30 bfa33dbe 2021-04-22 op #include <stdint.h>
31 bfa33dbe 2021-04-22 op #include <stdlib.h>
32 bfa33dbe 2021-04-22 op #include <string.h>
34 bfa33dbe 2021-04-22 op static const char *sub_ip_literal(const char*);
35 bfa33dbe 2021-04-22 op static const char *sub_host_dummy(const char*);
36 bfa33dbe 2021-04-22 op static const char *sub_pchar(const char*);
37 bfa33dbe 2021-04-22 op static const char *sub_segment(const char*);
38 bfa33dbe 2021-04-22 op static const char *sub_segment_nz(const char*);
39 bfa33dbe 2021-04-22 op static const char *sub_segment_nz_nc(const char*);
40 bfa33dbe 2021-04-22 op static const char *sub_path_common(const char*);
42 bfa33dbe 2021-04-22 op static const char *parse_scheme(const char*, struct phos_uri*);
43 bfa33dbe 2021-04-22 op static const char *parse_host(const char*, struct phos_uri*);
44 bfa33dbe 2021-04-22 op static const char *parse_port(const char*, struct phos_uri*);
45 bfa33dbe 2021-04-22 op static const char *parse_authority(const char*, struct phos_uri*);
46 bfa33dbe 2021-04-22 op static const char *parse_path_abempty(const char*, struct phos_uri*);
47 bfa33dbe 2021-04-22 op static const char *parse_path_absolute(const char*, struct phos_uri*);
48 bfa33dbe 2021-04-22 op static const char *parse_path_noscheme(const char*, struct phos_uri*);
49 bfa33dbe 2021-04-22 op static const char *parse_path_rootless(const char*, struct phos_uri*);
50 bfa33dbe 2021-04-22 op static const char *parse_path_empty(const char*, struct phos_uri*);
51 bfa33dbe 2021-04-22 op static const char *parse_hier_part(const char*, struct phos_uri*);
52 bfa33dbe 2021-04-22 op static const char *parse_query(const char*, struct phos_uri*);
53 bfa33dbe 2021-04-22 op static const char *parse_fragment(const char*, struct phos_uri*);
54 bfa33dbe 2021-04-22 op static const char *parse_uri(const char*, struct phos_uri*);
55 bfa33dbe 2021-04-22 op static const char *parse_relative_part(const char*, struct phos_uri*);
56 bfa33dbe 2021-04-22 op static const char *parse_relative_ref(const char*, struct phos_uri*);
57 bfa33dbe 2021-04-22 op static const char *parse_uri_reference(const char*, struct phos_uri*);
59 bfa33dbe 2021-04-22 op static int hasprefix(const char*, const char*);
60 bfa33dbe 2021-04-22 op static char *dotdot(char*, char*);
61 bfa33dbe 2021-04-22 op static void path_clean(struct phos_uri*);
62 bfa33dbe 2021-04-22 op static int merge_path(struct phos_uri*, const struct phos_uri*, const struct phos_uri*);
64 bfa33dbe 2021-04-22 op static int phos_resolve_uri_from(const struct phos_uri*, const struct phos_uri*, struct phos_uri*);
67 bfa33dbe 2021-04-22 op /* common defs */
69 bfa33dbe 2021-04-22 op static inline int
70 bfa33dbe 2021-04-22 op gen_delims(int c)
72 bfa33dbe 2021-04-22 op return c == ':'
81 bfa33dbe 2021-04-22 op static inline int
82 bfa33dbe 2021-04-22 op sub_delims(int c)
84 bfa33dbe 2021-04-22 op return c == '!'
97 bfa33dbe 2021-04-22 op static inline int
98 bfa33dbe 2021-04-22 op reserved(int c)
100 bfa33dbe 2021-04-22 op return gen_delims(c) || sub_delims(c);
103 bfa33dbe 2021-04-22 op static inline int
104 bfa33dbe 2021-04-22 op unreserved(int c)
106 bfa33dbe 2021-04-22 op return isalpha(c)
107 bfa33dbe 2021-04-22 op || isdigit(c)
118 bfa33dbe 2021-04-22 op * IP-literal = "[" ( IPv6address / IPvFuture ) "]"
120 bfa33dbe 2021-04-22 op * in reality, we parse [.*]
122 bfa33dbe 2021-04-22 op static const char *
123 bfa33dbe 2021-04-22 op sub_ip_literal(const char *s)
125 bfa33dbe 2021-04-22 op if (*s != '[')
128 bfa33dbe 2021-04-22 op while (*s != '\0' && *s != ']')
131 bfa33dbe 2021-04-22 op if (*s == '\0')
137 bfa33dbe 2021-04-22 op * parse everything until : or / (or \0).
138 bfa33dbe 2021-04-22 op * NB: empty hosts are technically valid!
140 bfa33dbe 2021-04-22 op static const char *
141 bfa33dbe 2021-04-22 op sub_host_dummy(const char *s)
143 bfa33dbe 2021-04-22 op while (*s != '\0' && *s != ':' && *s != '/')
149 bfa33dbe 2021-04-22 op * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
151 bfa33dbe 2021-04-22 op static const char *
152 bfa33dbe 2021-04-22 op sub_pchar(const char *s)
154 bfa33dbe 2021-04-22 op if (*s == '\0')
157 bfa33dbe 2021-04-22 op if (unreserved(*s))
160 bfa33dbe 2021-04-22 op if (*s == '%') {
161 bfa33dbe 2021-04-22 op if (isxdigit(s[1]) && isxdigit(s[2]))
162 bfa33dbe 2021-04-22 op return s + 3;
165 bfa33dbe 2021-04-22 op if (sub_delims(*s))
168 bfa33dbe 2021-04-22 op if (*s == ':' || *s == '@')
175 bfa33dbe 2021-04-22 op * segment = *pchar
177 bfa33dbe 2021-04-22 op static const char *
178 bfa33dbe 2021-04-22 op sub_segment(const char *s)
180 bfa33dbe 2021-04-22 op const char *t;
182 bfa33dbe 2021-04-22 op while ((t = sub_pchar(s)) != NULL)
187 bfa33dbe 2021-04-22 op /* segment-nz = 1*pchar */
188 bfa33dbe 2021-04-22 op static const char *
189 bfa33dbe 2021-04-22 op sub_segment_nz(const char *s)
191 bfa33dbe 2021-04-22 op if ((s = sub_pchar(s)) == NULL)
193 bfa33dbe 2021-04-22 op return sub_segment(s);
197 bfa33dbe 2021-04-22 op * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
199 bfa33dbe 2021-04-22 op * so, 1*pchar excluding ":"
201 bfa33dbe 2021-04-22 op static const char *
202 bfa33dbe 2021-04-22 op sub_segment_nz_nc(const char *s)
204 bfa33dbe 2021-04-22 op const char *t;
206 bfa33dbe 2021-04-22 op if (*s == ':')
209 bfa33dbe 2021-04-22 op while (*s != ':' && (t = sub_pchar(s)) != NULL)
214 bfa33dbe 2021-04-22 op /* *( "/" segment ) */
215 bfa33dbe 2021-04-22 op static const char *
216 bfa33dbe 2021-04-22 op sub_path_common(const char *s)
219 bfa33dbe 2021-04-22 op if (*s != '/')
222 bfa33dbe 2021-04-22 op s = sub_segment(s);
227 bfa33dbe 2021-04-22 op /* parse fns */
230 bfa33dbe 2021-04-22 op * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
232 bfa33dbe 2021-04-22 op static const char *
233 bfa33dbe 2021-04-22 op parse_scheme(const char *s, struct phos_uri *parsed)
235 bfa33dbe 2021-04-22 op const char *start = s;
238 bfa33dbe 2021-04-22 op if (!isalpha(*s))
241 bfa33dbe 2021-04-22 op while (*s != '\0') {
242 bfa33dbe 2021-04-22 op if (isalpha(*s) ||
243 bfa33dbe 2021-04-22 op isdigit(*s) ||
252 bfa33dbe 2021-04-22 op if (*s == '\0')
255 bfa33dbe 2021-04-22 op len = s - start;
256 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->scheme))
259 bfa33dbe 2021-04-22 op memcpy(parsed->scheme, start, len);
264 bfa33dbe 2021-04-22 op * host = IP-literal / IPv4address / reg-name
266 bfa33dbe 2021-04-22 op * rules IPv4address and reg-name are relaxed into parse_host_dummy.
268 bfa33dbe 2021-04-22 op static const char *
269 bfa33dbe 2021-04-22 op parse_host(const char *s, struct phos_uri *parsed)
271 bfa33dbe 2021-04-22 op const char *t;
274 bfa33dbe 2021-04-22 op if ((t = sub_ip_literal(s)) != NULL ||
275 bfa33dbe 2021-04-22 op (t = sub_host_dummy(s)) != NULL) {
277 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->scheme))
279 bfa33dbe 2021-04-22 op memcpy(parsed->host, s, len);
287 bfa33dbe 2021-04-22 op * port = *digit
289 bfa33dbe 2021-04-22 op static const char *
290 bfa33dbe 2021-04-22 op parse_port(const char *s, struct phos_uri *parsed)
292 bfa33dbe 2021-04-22 op const char *errstr, *start = s;
295 bfa33dbe 2021-04-22 op while (isdigit(*s))
298 bfa33dbe 2021-04-22 op if (s == start)
301 bfa33dbe 2021-04-22 op len = s - start;
302 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->port))
305 bfa33dbe 2021-04-22 op memcpy(parsed->port, start, len);
307 bfa33dbe 2021-04-22 op parsed->dec_port = strtonum(parsed->port, 0, 65535, &errstr);
308 bfa33dbe 2021-04-22 op if (errstr != NULL)
315 bfa33dbe 2021-04-22 op * authority = host [ ":" port ]
316 bfa33dbe 2021-04-22 op * (yep, blatantly ignore the userinfo stuff -- not relevant for Gemini)
318 bfa33dbe 2021-04-22 op static const char *
319 bfa33dbe 2021-04-22 op parse_authority(const char *s, struct phos_uri *parsed)
321 bfa33dbe 2021-04-22 op if ((s = parse_host(s, parsed)) == NULL)
324 bfa33dbe 2021-04-22 op if (*s == ':') {
326 bfa33dbe 2021-04-22 op return parse_port(s, parsed);
332 bfa33dbe 2021-04-22 op static inline const char *
333 bfa33dbe 2021-04-22 op set_path(const char *start, const char *end, struct phos_uri *parsed)
337 bfa33dbe 2021-04-22 op if (end == NULL)
340 bfa33dbe 2021-04-22 op len = end - start;
341 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->path))
343 bfa33dbe 2021-04-22 op memcpy(parsed->path, start, len);
348 bfa33dbe 2021-04-22 op * path-abempty = *( "/" segment )
350 bfa33dbe 2021-04-22 op static const char *
351 bfa33dbe 2021-04-22 op parse_path_abempty(const char *s, struct phos_uri *parsed)
353 bfa33dbe 2021-04-22 op const char *t;
355 bfa33dbe 2021-04-22 op t = sub_path_common(s);
356 bfa33dbe 2021-04-22 op return set_path(s, t, parsed);
360 bfa33dbe 2021-04-22 op * path-absolute = "/" [ segment-nz *( "/" segment ) ]
362 bfa33dbe 2021-04-22 op static const char *
363 bfa33dbe 2021-04-22 op parse_path_absolute(const char *s, struct phos_uri *parsed)
365 bfa33dbe 2021-04-22 op const char *t, *start = s;
367 bfa33dbe 2021-04-22 op if (*s != '/')
371 bfa33dbe 2021-04-22 op if ((t = sub_segment_nz(s)) == NULL)
372 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
374 bfa33dbe 2021-04-22 op s = sub_path_common(t);
375 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
379 bfa33dbe 2021-04-22 op * path-noscheme = segment-nz-nc *( "/" segment )
381 bfa33dbe 2021-04-22 op static const char *
382 bfa33dbe 2021-04-22 op parse_path_noscheme(const char *s, struct phos_uri *parsed)
384 bfa33dbe 2021-04-22 op const char *start = s;
386 bfa33dbe 2021-04-22 op if ((s = sub_segment_nz_nc(s)) == NULL)
388 bfa33dbe 2021-04-22 op s = sub_path_common(s);
389 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
393 bfa33dbe 2021-04-22 op * path-rootless = segment-nz *( "/" segment )
395 bfa33dbe 2021-04-22 op static const char *
396 bfa33dbe 2021-04-22 op parse_path_rootless(const char *s, struct phos_uri *parsed)
398 bfa33dbe 2021-04-22 op const char *start = s;
400 bfa33dbe 2021-04-22 op if ((s = sub_segment_nz(s)) == NULL)
402 bfa33dbe 2021-04-22 op s = sub_path_common(s);
403 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
407 bfa33dbe 2021-04-22 op * path-empty = 0<pchar>
409 bfa33dbe 2021-04-22 op static const char *
410 bfa33dbe 2021-04-22 op parse_path_empty(const char *s, struct phos_uri *parsed)
416 bfa33dbe 2021-04-22 op * hier-part = "//" authority path-abempty
417 bfa33dbe 2021-04-22 op * / path-absolute
418 bfa33dbe 2021-04-22 op * / path-rootless
419 bfa33dbe 2021-04-22 op * / path-empty
421 bfa33dbe 2021-04-22 op static const char *
422 bfa33dbe 2021-04-22 op parse_hier_part(const char *s, struct phos_uri *parsed)
424 bfa33dbe 2021-04-22 op const char *t;
426 bfa33dbe 2021-04-22 op if (s[0] == '/' && s[1] == '/') {
428 bfa33dbe 2021-04-22 op if ((s = parse_authority(s, parsed)) == NULL)
430 bfa33dbe 2021-04-22 op return parse_path_abempty(s, parsed);
433 bfa33dbe 2021-04-22 op if ((t = parse_path_absolute(s, parsed)) != NULL)
436 bfa33dbe 2021-04-22 op if ((t = parse_path_rootless(s, parsed)) != NULL)
439 bfa33dbe 2021-04-22 op return parse_path_empty(s, parsed);
443 bfa33dbe 2021-04-22 op * query = *( pchar / "/" / "?" )
445 bfa33dbe 2021-04-22 op static const char *
446 bfa33dbe 2021-04-22 op parse_query(const char *s, struct phos_uri *parsed)
448 bfa33dbe 2021-04-22 op const char *t, *start = s;
451 bfa33dbe 2021-04-22 op while (*s != '\0') {
452 bfa33dbe 2021-04-22 op if (*s == '/' || *s == '?') {
457 bfa33dbe 2021-04-22 op if ((t = sub_pchar(s)) == NULL)
462 bfa33dbe 2021-04-22 op len = s - start;
463 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->query))
466 bfa33dbe 2021-04-22 op memcpy(parsed->query, start, len);
471 bfa33dbe 2021-04-22 op * fragment = *( pchar / "/" / "?" )
473 bfa33dbe 2021-04-22 op static const char *
474 bfa33dbe 2021-04-22 op parse_fragment(const char *s, struct phos_uri *parsed)
476 bfa33dbe 2021-04-22 op const char *start = s;
480 bfa33dbe 2021-04-22 op if (*s == '\0')
483 bfa33dbe 2021-04-22 op if (*s == '/' || *s == '?') {
488 bfa33dbe 2021-04-22 op if ((s = sub_pchar(s)) == NULL)
492 bfa33dbe 2021-04-22 op len = s - start;
493 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->fragment))
496 bfa33dbe 2021-04-22 op memcpy(parsed->fragment, start, len);
501 bfa33dbe 2021-04-22 op * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
503 bfa33dbe 2021-04-22 op static const char *
504 bfa33dbe 2021-04-22 op parse_uri(const char *s, struct phos_uri *parsed)
506 bfa33dbe 2021-04-22 op if ((s = parse_scheme(s, parsed)) == NULL)
509 bfa33dbe 2021-04-22 op if (*s != ':')
513 bfa33dbe 2021-04-22 op if ((s = parse_hier_part(s, parsed)) == NULL)
516 bfa33dbe 2021-04-22 op if (*s == '?') {
518 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
522 bfa33dbe 2021-04-22 op if (*s == '#') {
524 bfa33dbe 2021-04-22 op if ((s = parse_fragment(s, parsed)) == NULL)
532 bfa33dbe 2021-04-22 op * relative-part = "//" authority path-abempty
533 bfa33dbe 2021-04-22 op * / path-absolute
534 bfa33dbe 2021-04-22 op * / path-noscheme
535 bfa33dbe 2021-04-22 op * / path-empty
537 bfa33dbe 2021-04-22 op static const char *
538 bfa33dbe 2021-04-22 op parse_relative_part(const char *s, struct phos_uri *parsed)
540 bfa33dbe 2021-04-22 op const char *t;
542 bfa33dbe 2021-04-22 op if (s[0] == '/' && s[1] == '/') {
544 bfa33dbe 2021-04-22 op if ((s = parse_authority(s, parsed)) == NULL)
546 bfa33dbe 2021-04-22 op return parse_path_abempty(s, parsed);
549 bfa33dbe 2021-04-22 op if ((t = parse_path_absolute(s, parsed)) != NULL)
552 bfa33dbe 2021-04-22 op if ((t = parse_path_noscheme(s, parsed)) != NULL)
555 bfa33dbe 2021-04-22 op return parse_path_empty(s, parsed);
559 bfa33dbe 2021-04-22 op * relative-ref = relative-part [ "?" query ] [ "#" fragment ]
561 bfa33dbe 2021-04-22 op static const char *
562 bfa33dbe 2021-04-22 op parse_relative_ref(const char *s, struct phos_uri *parsed)
564 bfa33dbe 2021-04-22 op if ((s = parse_relative_part(s, parsed)) == NULL)
567 bfa33dbe 2021-04-22 op if (*s == '?') {
569 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
573 bfa33dbe 2021-04-22 op if (*s == '#') {
575 bfa33dbe 2021-04-22 op if ((s = parse_fragment(s, parsed)) == NULL)
583 bfa33dbe 2021-04-22 op * URI-reference = URI / relative-ref
585 bfa33dbe 2021-04-22 op static const char *
586 bfa33dbe 2021-04-22 op parse_uri_reference(const char *s, struct phos_uri *parsed)
588 bfa33dbe 2021-04-22 op const char *t;
590 bfa33dbe 2021-04-22 op if ((t = parse_uri(s, parsed)) != NULL)
592 bfa33dbe 2021-04-22 op memset(parsed, 0, sizeof(*parsed));
593 bfa33dbe 2021-04-22 op return parse_relative_ref(s, parsed);
598 bfa33dbe 2021-04-22 op * absolute-URI = scheme ":" hier-part [ "?" query ]
600 bfa33dbe 2021-04-22 op static const char *
601 bfa33dbe 2021-04-22 op parse_absolute_uri(const char *s, struct phos_uri *parsed)
603 bfa33dbe 2021-04-22 op if ((s = parse_scheme(s, parsed)) == NULL)
606 bfa33dbe 2021-04-22 op if (*s != ':')
610 bfa33dbe 2021-04-22 op if ((s = parse_hier_part(s, parsed)) == NULL)
613 bfa33dbe 2021-04-22 op if (*s == '?') {
615 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
623 bfa33dbe 2021-04-22 op /* normalizing fns */
626 bfa33dbe 2021-04-22 op hasprefix(const char *str, const char *prfx)
628 bfa33dbe 2021-04-22 op for (; *str == *prfx && *prfx != '\0'; str++, prfx++)
631 bfa33dbe 2021-04-22 op return *prfx == '\0';
634 bfa33dbe 2021-04-22 op static char *
635 bfa33dbe 2021-04-22 op dotdot(char *point, char *start)
639 bfa33dbe 2021-04-22 op for (t = point-1; t > start; --t) {
640 bfa33dbe 2021-04-22 op if (*t == '/')
643 bfa33dbe 2021-04-22 op if (t < start)
646 bfa33dbe 2021-04-22 op memmove(t, point, strlen(point)+1);
651 bfa33dbe 2021-04-22 op * This is the "Remove Dot Segments" straight outta RFC3986, section
655 bfa33dbe 2021-04-22 op path_clean(struct phos_uri *uri)
657 bfa33dbe 2021-04-22 op char *in = uri->path;
659 bfa33dbe 2021-04-22 op while (in != NULL && *in != '\0') {
660 bfa33dbe 2021-04-22 op assert(in >= uri->path);
662 bfa33dbe 2021-04-22 op /* A) drop leading ../ or ./ */
663 bfa33dbe 2021-04-22 op if (hasprefix(in, "../"))
664 bfa33dbe 2021-04-22 op memmove(in, &in[3], strlen(&in[3])+1);
665 bfa33dbe 2021-04-22 op else if (hasprefix(in, "./"))
666 bfa33dbe 2021-04-22 op memmove(in, &in[2], strlen(&in[2])+1);
668 bfa33dbe 2021-04-22 op /* B) replace /./ or /. with / */
669 bfa33dbe 2021-04-22 op else if (hasprefix(in, "/./"))
670 bfa33dbe 2021-04-22 op memmove(&in[1], &in[3], strlen(&in[3])+1);
671 bfa33dbe 2021-04-22 op else if (!strcmp(in, "/."))
672 bfa33dbe 2021-04-22 op in[1] = '\0';
674 bfa33dbe 2021-04-22 op /* C) resolve dot-dot */
675 bfa33dbe 2021-04-22 op else if (hasprefix(in, "/../")) {
676 bfa33dbe 2021-04-22 op in = dotdot(in, uri->path);
677 bfa33dbe 2021-04-22 op memmove(&in[1], &in[4], strlen(&in[4])+1);
678 bfa33dbe 2021-04-22 op } else if (!strcmp(in, "/..")) {
679 bfa33dbe 2021-04-22 op in = dotdot(in, uri->path);
680 bfa33dbe 2021-04-22 op in[1] = '\0';
685 bfa33dbe 2021-04-22 op else if (!strcmp(in, "."))
687 bfa33dbe 2021-04-22 op else if (!strcmp(in, ".."))
692 bfa33dbe 2021-04-22 op in = strchr(in+1, '/');
697 bfa33dbe 2021-04-22 op * see RFC3986 5.3.3 "Merge Paths".
700 bfa33dbe 2021-04-22 op merge_path(struct phos_uri *ret, const struct phos_uri *base,
701 bfa33dbe 2021-04-22 op const struct phos_uri *ref)
703 bfa33dbe 2021-04-22 op const char *s;
706 bfa33dbe 2021-04-22 op len = sizeof(ret->path);
708 bfa33dbe 2021-04-22 op s = strrchr(base->path, '/');
709 bfa33dbe 2021-04-22 op if ((*base->host != '\0' && *base->path == '\0') || s == NULL) {
710 bfa33dbe 2021-04-22 op strlcpy(ret->path, "/", len);
712 bfa33dbe 2021-04-22 op /* copy the / too */
713 bfa33dbe 2021-04-22 op memcpy(ret->path, base->path, s - base->path + 1);
716 bfa33dbe 2021-04-22 op return strlcat(ret->path, ref->path, len) < len;
720 bfa33dbe 2021-04-22 op /* public interface */
723 bfa33dbe 2021-04-22 op phos_parse_absolute_uri(const char *s, struct phos_uri *uri)
725 bfa33dbe 2021-04-22 op memset(uri, 0, sizeof(*uri));
727 bfa33dbe 2021-04-22 op if ((s = parse_absolute_uri(s, uri)) == NULL)
729 bfa33dbe 2021-04-22 op if (*s != '\0')
731 bfa33dbe 2021-04-22 op path_clean(uri);
736 bfa33dbe 2021-04-22 op phos_parse_uri_reference(const char *s, struct phos_uri *uri)
738 bfa33dbe 2021-04-22 op memset(uri, 0, sizeof(*uri));
740 bfa33dbe 2021-04-22 op if ((s = parse_uri_reference(s, uri)) == NULL)
742 bfa33dbe 2021-04-22 op if (*s != '\0')
744 bfa33dbe 2021-04-22 op path_clean(uri);
749 bfa33dbe 2021-04-22 op * Implementation of the "transform references" algorithm from
750 bfa33dbe 2021-04-22 op * RFC3986, see 5.2.2.
752 bfa33dbe 2021-04-22 op * We expect base and ref to be URIs constructed by this library
753 bfa33dbe 2021-04-22 op * (because we emit only normalized URIs).
755 bfa33dbe 2021-04-22 op * ATM this is marked as private because:
756 bfa33dbe 2021-04-22 op * - let's say the URI is "."
757 bfa33dbe 2021-04-22 op * - one calls phos_parse_uri_references
758 bfa33dbe 2021-04-22 op * - it exists with success, but the path becomes ""
759 bfa33dbe 2021-04-22 op * - this routine does the right thing, but the outcome is not what expected.
761 bfa33dbe 2021-04-22 op * so users for now have to user resolve_uri_from_str, which parses
762 bfa33dbe 2021-04-22 op * the URI but not normalize it, and then call into us.
765 bfa33dbe 2021-04-22 op phos_resolve_uri_from(const struct phos_uri *base, const struct phos_uri *ref,
766 bfa33dbe 2021-04-22 op struct phos_uri *ret)
768 bfa33dbe 2021-04-22 op memset(ret, 0, sizeof(*ret));
770 bfa33dbe 2021-04-22 op if (*ref->scheme != '\0') {
771 bfa33dbe 2021-04-22 op strlcpy(ret->scheme, ref->scheme, sizeof(ret->scheme));
772 bfa33dbe 2021-04-22 op strlcpy(ret->host, ref->host, sizeof(ret->host));
773 bfa33dbe 2021-04-22 op strlcpy(ret->port, ref->port, sizeof(ret->port));
774 bfa33dbe 2021-04-22 op ret->dec_port = ret->dec_port;
775 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
776 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
778 bfa33dbe 2021-04-22 op if (*ref->host != '\0') {
779 bfa33dbe 2021-04-22 op strlcpy(ret->host, ref->host, sizeof(ret->host));
780 bfa33dbe 2021-04-22 op strlcpy(ret->port, ref->port, sizeof(ret->port));
781 bfa33dbe 2021-04-22 op ret->dec_port = ret->dec_port;
782 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
783 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
785 bfa33dbe 2021-04-22 op if (*ref->path == '\0') {
786 bfa33dbe 2021-04-22 op strlcpy(ret->path, base->path, sizeof(ret->path));
787 bfa33dbe 2021-04-22 op if (*ref->query != '\0')
788 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
790 bfa33dbe 2021-04-22 op strlcpy(ret->query, base->query, sizeof(ret->query));
792 bfa33dbe 2021-04-22 op if (*ref->path == '/')
793 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
795 bfa33dbe 2021-04-22 op if (!merge_path(ret, base, ref))
798 bfa33dbe 2021-04-22 op path_clean(ret);
800 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
803 bfa33dbe 2021-04-22 op strlcpy(ret->host, base->host, sizeof(ret->host));
804 bfa33dbe 2021-04-22 op strlcpy(ret->port, base->port, sizeof(ret->port));
805 bfa33dbe 2021-04-22 op ret->dec_port = base->dec_port;
808 bfa33dbe 2021-04-22 op strlcpy(ret->scheme, base->scheme, sizeof(ret->scheme));
811 bfa33dbe 2021-04-22 op strlcpy(ret->fragment, ref->fragment, sizeof(ret->fragment));
817 bfa33dbe 2021-04-22 op phos_resolve_uri_from_str(const struct phos_uri *base, const char *refstr,
818 bfa33dbe 2021-04-22 op struct phos_uri *ret)
820 bfa33dbe 2021-04-22 op struct phos_uri ref;
822 bfa33dbe 2021-04-22 op memset(&ref, 0, sizeof(ref));
824 bfa33dbe 2021-04-22 op if ((refstr = parse_uri_reference(refstr, &ref)) == NULL)
827 bfa33dbe 2021-04-22 op if (*refstr != '\0')
830 bfa33dbe 2021-04-22 op return phos_resolve_uri_from(base, &ref, ret);
834 bfa33dbe 2021-04-22 op phos_uri_drop_empty_segments(struct phos_uri *uri)
838 bfa33dbe 2021-04-22 op for (i = uri->path; *i; ++i) {
839 bfa33dbe 2021-04-22 op if (*i == '/' && *(i+1) == '/') {
840 bfa33dbe 2021-04-22 op memmove(i, i+1, strlen(i)); /* move also the \0 */
847 bfa33dbe 2021-04-22 op phos_serialize_uri(const struct phos_uri *uri, char *buf, size_t len)
849 bfa33dbe 2021-04-22 op #define CAT(s) \
850 bfa33dbe 2021-04-22 op if (strlcat(buf, s, len) >= len) \
853 bfa33dbe 2021-04-22 op strlcpy(buf, "", len);
855 bfa33dbe 2021-04-22 op if (*uri->scheme != '\0') {
856 bfa33dbe 2021-04-22 op CAT(uri->scheme);
860 bfa33dbe 2021-04-22 op if (*uri->host != '\0') {
862 bfa33dbe 2021-04-22 op CAT(uri->host);
865 bfa33dbe 2021-04-22 op CAT(uri->path);
867 bfa33dbe 2021-04-22 op if (*uri->query != '\0') {
869 bfa33dbe 2021-04-22 op CAT(uri->query);
872 bfa33dbe 2021-04-22 op if (*uri->fragment) {
874 bfa33dbe 2021-04-22 op CAT(uri->fragment);