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 4190a5c2 2021-04-24 op #include <stdio.h>
32 bfa33dbe 2021-04-22 op #include <stdlib.h>
33 bfa33dbe 2021-04-22 op #include <string.h>
35 bfa33dbe 2021-04-22 op static const char *sub_ip_literal(const char*);
36 bfa33dbe 2021-04-22 op static const char *sub_host_dummy(const char*);
37 bfa33dbe 2021-04-22 op static const char *sub_pchar(const char*);
39 bfa33dbe 2021-04-22 op static const char *sub_segment(const char*);
40 bfa33dbe 2021-04-22 op static const char *sub_segment_nz(const char*);
41 bfa33dbe 2021-04-22 op static const char *sub_segment_nz_nc(const char*);
42 bfa33dbe 2021-04-22 op static const char *sub_path_common(const char*);
44 bfa33dbe 2021-04-22 op static const char *parse_scheme(const char*, struct phos_uri*);
45 bfa33dbe 2021-04-22 op static const char *parse_host(const char*, struct phos_uri*);
46 bfa33dbe 2021-04-22 op static const char *parse_port(const char*, struct phos_uri*);
47 bfa33dbe 2021-04-22 op static const char *parse_authority(const char*, struct phos_uri*);
48 bfa33dbe 2021-04-22 op static const char *parse_path_abempty(const char*, struct phos_uri*);
49 bfa33dbe 2021-04-22 op static const char *parse_path_absolute(const char*, struct phos_uri*);
50 bfa33dbe 2021-04-22 op static const char *parse_path_noscheme(const char*, struct phos_uri*);
51 bfa33dbe 2021-04-22 op static const char *parse_path_rootless(const char*, struct phos_uri*);
52 bfa33dbe 2021-04-22 op static const char *parse_path_empty(const char*, struct phos_uri*);
53 bfa33dbe 2021-04-22 op static const char *parse_hier_part(const char*, struct phos_uri*);
54 bfa33dbe 2021-04-22 op static const char *parse_query(const char*, struct phos_uri*);
55 bfa33dbe 2021-04-22 op static const char *parse_fragment(const char*, struct phos_uri*);
56 bfa33dbe 2021-04-22 op static const char *parse_uri(const char*, struct phos_uri*);
57 bfa33dbe 2021-04-22 op static const char *parse_relative_part(const char*, struct phos_uri*);
58 bfa33dbe 2021-04-22 op static const char *parse_relative_ref(const char*, struct phos_uri*);
59 bfa33dbe 2021-04-22 op static const char *parse_uri_reference(const char*, struct phos_uri*);
61 bfa33dbe 2021-04-22 op static int hasprefix(const char*, const char*);
62 bfa33dbe 2021-04-22 op static char *dotdot(char*, char*);
63 bfa33dbe 2021-04-22 op static void path_clean(struct phos_uri*);
64 bfa33dbe 2021-04-22 op static int merge_path(struct phos_uri*, const struct phos_uri*, const struct phos_uri*);
66 bfa33dbe 2021-04-22 op static int phos_resolve_uri_from(const struct phos_uri*, const struct phos_uri*, struct phos_uri*);
69 bfa33dbe 2021-04-22 op /* common defs */
71 bfa33dbe 2021-04-22 op static inline int
72 bfa33dbe 2021-04-22 op gen_delims(int c)
74 bfa33dbe 2021-04-22 op return c == ':'
83 bfa33dbe 2021-04-22 op static inline int
84 bfa33dbe 2021-04-22 op sub_delims(int c)
86 bfa33dbe 2021-04-22 op return c == '!'
99 bfa33dbe 2021-04-22 op static inline int
100 bfa33dbe 2021-04-22 op reserved(int c)
102 bfa33dbe 2021-04-22 op return gen_delims(c) || sub_delims(c);
105 bfa33dbe 2021-04-22 op static inline int
106 bfa33dbe 2021-04-22 op unreserved(int c)
108 bfa33dbe 2021-04-22 op return isalpha(c)
109 bfa33dbe 2021-04-22 op || isdigit(c)
120 bfa33dbe 2021-04-22 op * IP-literal = "[" ( IPv6address / IPvFuture ) "]"
122 bfa33dbe 2021-04-22 op * in reality, we parse [.*]
124 bfa33dbe 2021-04-22 op static const char *
125 bfa33dbe 2021-04-22 op sub_ip_literal(const char *s)
127 bfa33dbe 2021-04-22 op if (*s != '[')
130 bfa33dbe 2021-04-22 op while (*s != '\0' && *s != ']')
133 bfa33dbe 2021-04-22 op if (*s == '\0')
139 bfa33dbe 2021-04-22 op * parse everything until : or / (or \0).
140 bfa33dbe 2021-04-22 op * NB: empty hosts are technically valid!
142 bfa33dbe 2021-04-22 op static const char *
143 bfa33dbe 2021-04-22 op sub_host_dummy(const char *s)
145 bfa33dbe 2021-04-22 op while (*s != '\0' && *s != ':' && *s != '/')
151 bfa33dbe 2021-04-22 op * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
153 bfa33dbe 2021-04-22 op static const char *
154 bfa33dbe 2021-04-22 op sub_pchar(const char *s)
156 bfa33dbe 2021-04-22 op if (*s == '\0')
159 bfa33dbe 2021-04-22 op if (unreserved(*s))
162 bfa33dbe 2021-04-22 op if (*s == '%') {
163 bfa33dbe 2021-04-22 op if (isxdigit(s[1]) && isxdigit(s[2]))
164 bfa33dbe 2021-04-22 op return s + 3;
167 bfa33dbe 2021-04-22 op if (sub_delims(*s))
170 bfa33dbe 2021-04-22 op if (*s == ':' || *s == '@')
177 bfa33dbe 2021-04-22 op * segment = *pchar
179 bfa33dbe 2021-04-22 op static const char *
180 bfa33dbe 2021-04-22 op sub_segment(const char *s)
182 bfa33dbe 2021-04-22 op const char *t;
184 bfa33dbe 2021-04-22 op while ((t = sub_pchar(s)) != NULL)
189 bfa33dbe 2021-04-22 op /* segment-nz = 1*pchar */
190 bfa33dbe 2021-04-22 op static const char *
191 bfa33dbe 2021-04-22 op sub_segment_nz(const char *s)
193 bfa33dbe 2021-04-22 op if ((s = sub_pchar(s)) == NULL)
195 bfa33dbe 2021-04-22 op return sub_segment(s);
199 bfa33dbe 2021-04-22 op * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
201 bfa33dbe 2021-04-22 op * so, 1*pchar excluding ":"
203 bfa33dbe 2021-04-22 op static const char *
204 bfa33dbe 2021-04-22 op sub_segment_nz_nc(const char *s)
206 bfa33dbe 2021-04-22 op const char *t;
208 bfa33dbe 2021-04-22 op if (*s == ':')
211 bfa33dbe 2021-04-22 op while (*s != ':' && (t = sub_pchar(s)) != NULL)
216 bfa33dbe 2021-04-22 op /* *( "/" segment ) */
217 bfa33dbe 2021-04-22 op static const char *
218 bfa33dbe 2021-04-22 op sub_path_common(const char *s)
221 bfa33dbe 2021-04-22 op if (*s != '/')
224 bfa33dbe 2021-04-22 op s = sub_segment(s);
229 bfa33dbe 2021-04-22 op /* parse fns */
232 bfa33dbe 2021-04-22 op * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
234 bfa33dbe 2021-04-22 op static const char *
235 bfa33dbe 2021-04-22 op parse_scheme(const char *s, struct phos_uri *parsed)
237 bfa33dbe 2021-04-22 op const char *start = s;
240 bfa33dbe 2021-04-22 op if (!isalpha(*s))
243 bfa33dbe 2021-04-22 op while (*s != '\0') {
244 bfa33dbe 2021-04-22 op if (isalpha(*s) ||
245 bfa33dbe 2021-04-22 op isdigit(*s) ||
254 bfa33dbe 2021-04-22 op if (*s == '\0')
257 bfa33dbe 2021-04-22 op len = s - start;
258 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->scheme))
261 bfa33dbe 2021-04-22 op memcpy(parsed->scheme, start, len);
266 bfa33dbe 2021-04-22 op * host = IP-literal / IPv4address / reg-name
268 bfa33dbe 2021-04-22 op * rules IPv4address and reg-name are relaxed into parse_host_dummy.
270 bfa33dbe 2021-04-22 op static const char *
271 bfa33dbe 2021-04-22 op parse_host(const char *s, struct phos_uri *parsed)
273 bfa33dbe 2021-04-22 op const char *t;
276 bfa33dbe 2021-04-22 op if ((t = sub_ip_literal(s)) != NULL ||
277 bfa33dbe 2021-04-22 op (t = sub_host_dummy(s)) != NULL) {
279 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->scheme))
281 bfa33dbe 2021-04-22 op memcpy(parsed->host, s, len);
289 bfa33dbe 2021-04-22 op * port = *digit
291 bfa33dbe 2021-04-22 op static const char *
292 bfa33dbe 2021-04-22 op parse_port(const char *s, struct phos_uri *parsed)
294 bfa33dbe 2021-04-22 op const char *errstr, *start = s;
297 bfa33dbe 2021-04-22 op while (isdigit(*s))
300 bfa33dbe 2021-04-22 op if (s == start)
303 bfa33dbe 2021-04-22 op len = s - start;
304 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->port))
307 bfa33dbe 2021-04-22 op memcpy(parsed->port, start, len);
309 bfa33dbe 2021-04-22 op parsed->dec_port = strtonum(parsed->port, 0, 65535, &errstr);
310 bfa33dbe 2021-04-22 op if (errstr != NULL)
317 bfa33dbe 2021-04-22 op * authority = host [ ":" port ]
318 bfa33dbe 2021-04-22 op * (yep, blatantly ignore the userinfo stuff -- not relevant for Gemini)
320 bfa33dbe 2021-04-22 op static const char *
321 bfa33dbe 2021-04-22 op parse_authority(const char *s, struct phos_uri *parsed)
323 bfa33dbe 2021-04-22 op if ((s = parse_host(s, parsed)) == NULL)
326 bfa33dbe 2021-04-22 op if (*s == ':') {
328 bfa33dbe 2021-04-22 op return parse_port(s, parsed);
334 bfa33dbe 2021-04-22 op static inline const char *
335 bfa33dbe 2021-04-22 op set_path(const char *start, const char *end, struct phos_uri *parsed)
339 bfa33dbe 2021-04-22 op if (end == NULL)
342 bfa33dbe 2021-04-22 op len = end - start;
343 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->path))
345 bfa33dbe 2021-04-22 op memcpy(parsed->path, start, len);
350 bfa33dbe 2021-04-22 op * path-abempty = *( "/" segment )
352 bfa33dbe 2021-04-22 op static const char *
353 bfa33dbe 2021-04-22 op parse_path_abempty(const char *s, struct phos_uri *parsed)
355 bfa33dbe 2021-04-22 op const char *t;
357 bfa33dbe 2021-04-22 op t = sub_path_common(s);
358 bfa33dbe 2021-04-22 op return set_path(s, t, parsed);
362 bfa33dbe 2021-04-22 op * path-absolute = "/" [ segment-nz *( "/" segment ) ]
364 bfa33dbe 2021-04-22 op static const char *
365 bfa33dbe 2021-04-22 op parse_path_absolute(const char *s, struct phos_uri *parsed)
367 bfa33dbe 2021-04-22 op const char *t, *start = s;
369 bfa33dbe 2021-04-22 op if (*s != '/')
373 bfa33dbe 2021-04-22 op if ((t = sub_segment_nz(s)) == NULL)
374 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
376 bfa33dbe 2021-04-22 op s = sub_path_common(t);
377 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
381 bfa33dbe 2021-04-22 op * path-noscheme = segment-nz-nc *( "/" segment )
383 bfa33dbe 2021-04-22 op static const char *
384 bfa33dbe 2021-04-22 op parse_path_noscheme(const char *s, struct phos_uri *parsed)
386 bfa33dbe 2021-04-22 op const char *start = s;
388 bfa33dbe 2021-04-22 op if ((s = sub_segment_nz_nc(s)) == NULL)
390 bfa33dbe 2021-04-22 op s = sub_path_common(s);
391 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
395 bfa33dbe 2021-04-22 op * path-rootless = segment-nz *( "/" segment )
397 bfa33dbe 2021-04-22 op static const char *
398 bfa33dbe 2021-04-22 op parse_path_rootless(const char *s, struct phos_uri *parsed)
400 bfa33dbe 2021-04-22 op const char *start = s;
402 bfa33dbe 2021-04-22 op if ((s = sub_segment_nz(s)) == NULL)
404 bfa33dbe 2021-04-22 op s = sub_path_common(s);
405 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
409 bfa33dbe 2021-04-22 op * path-empty = 0<pchar>
411 bfa33dbe 2021-04-22 op static const char *
412 bfa33dbe 2021-04-22 op parse_path_empty(const char *s, struct phos_uri *parsed)
418 bfa33dbe 2021-04-22 op * hier-part = "//" authority path-abempty
419 bfa33dbe 2021-04-22 op * / path-absolute
420 bfa33dbe 2021-04-22 op * / path-rootless
421 bfa33dbe 2021-04-22 op * / path-empty
423 bfa33dbe 2021-04-22 op static const char *
424 bfa33dbe 2021-04-22 op parse_hier_part(const char *s, struct phos_uri *parsed)
426 bfa33dbe 2021-04-22 op const char *t;
428 bfa33dbe 2021-04-22 op if (s[0] == '/' && s[1] == '/') {
430 bfa33dbe 2021-04-22 op if ((s = parse_authority(s, parsed)) == NULL)
432 bfa33dbe 2021-04-22 op return parse_path_abempty(s, parsed);
435 bfa33dbe 2021-04-22 op if ((t = parse_path_absolute(s, parsed)) != NULL)
438 bfa33dbe 2021-04-22 op if ((t = parse_path_rootless(s, parsed)) != NULL)
441 bfa33dbe 2021-04-22 op return parse_path_empty(s, parsed);
445 bfa33dbe 2021-04-22 op * query = *( pchar / "/" / "?" )
447 bfa33dbe 2021-04-22 op static const char *
448 bfa33dbe 2021-04-22 op parse_query(const char *s, struct phos_uri *parsed)
450 bfa33dbe 2021-04-22 op const char *t, *start = s;
453 bfa33dbe 2021-04-22 op while (*s != '\0') {
454 bfa33dbe 2021-04-22 op if (*s == '/' || *s == '?') {
459 bfa33dbe 2021-04-22 op if ((t = sub_pchar(s)) == NULL)
464 bfa33dbe 2021-04-22 op len = s - start;
465 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->query))
468 bfa33dbe 2021-04-22 op memcpy(parsed->query, start, len);
473 bfa33dbe 2021-04-22 op * fragment = *( pchar / "/" / "?" )
475 bfa33dbe 2021-04-22 op static const char *
476 bfa33dbe 2021-04-22 op parse_fragment(const char *s, struct phos_uri *parsed)
478 bfa33dbe 2021-04-22 op const char *start = s;
482 bfa33dbe 2021-04-22 op if (*s == '\0')
485 bfa33dbe 2021-04-22 op if (*s == '/' || *s == '?') {
490 bfa33dbe 2021-04-22 op if ((s = sub_pchar(s)) == NULL)
494 bfa33dbe 2021-04-22 op len = s - start;
495 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->fragment))
498 bfa33dbe 2021-04-22 op memcpy(parsed->fragment, start, len);
503 bfa33dbe 2021-04-22 op * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
505 bfa33dbe 2021-04-22 op static const char *
506 bfa33dbe 2021-04-22 op parse_uri(const char *s, struct phos_uri *parsed)
508 bfa33dbe 2021-04-22 op if ((s = parse_scheme(s, parsed)) == NULL)
511 bfa33dbe 2021-04-22 op if (*s != ':')
515 bfa33dbe 2021-04-22 op if ((s = parse_hier_part(s, parsed)) == NULL)
518 bfa33dbe 2021-04-22 op if (*s == '?') {
520 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
524 bfa33dbe 2021-04-22 op if (*s == '#') {
526 bfa33dbe 2021-04-22 op if ((s = parse_fragment(s, parsed)) == NULL)
534 bfa33dbe 2021-04-22 op * relative-part = "//" authority path-abempty
535 bfa33dbe 2021-04-22 op * / path-absolute
536 bfa33dbe 2021-04-22 op * / path-noscheme
537 bfa33dbe 2021-04-22 op * / path-empty
539 bfa33dbe 2021-04-22 op static const char *
540 bfa33dbe 2021-04-22 op parse_relative_part(const char *s, struct phos_uri *parsed)
542 bfa33dbe 2021-04-22 op const char *t;
544 bfa33dbe 2021-04-22 op if (s[0] == '/' && s[1] == '/') {
546 bfa33dbe 2021-04-22 op if ((s = parse_authority(s, parsed)) == NULL)
548 bfa33dbe 2021-04-22 op return parse_path_abempty(s, parsed);
551 bfa33dbe 2021-04-22 op if ((t = parse_path_absolute(s, parsed)) != NULL)
554 bfa33dbe 2021-04-22 op if ((t = parse_path_noscheme(s, parsed)) != NULL)
557 bfa33dbe 2021-04-22 op return parse_path_empty(s, parsed);
561 bfa33dbe 2021-04-22 op * relative-ref = relative-part [ "?" query ] [ "#" fragment ]
563 bfa33dbe 2021-04-22 op static const char *
564 bfa33dbe 2021-04-22 op parse_relative_ref(const char *s, struct phos_uri *parsed)
566 bfa33dbe 2021-04-22 op if ((s = parse_relative_part(s, parsed)) == NULL)
569 bfa33dbe 2021-04-22 op if (*s == '?') {
571 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
575 bfa33dbe 2021-04-22 op if (*s == '#') {
577 bfa33dbe 2021-04-22 op if ((s = parse_fragment(s, parsed)) == NULL)
585 bfa33dbe 2021-04-22 op * URI-reference = URI / relative-ref
587 bfa33dbe 2021-04-22 op static const char *
588 bfa33dbe 2021-04-22 op parse_uri_reference(const char *s, struct phos_uri *parsed)
590 bfa33dbe 2021-04-22 op const char *t;
592 bfa33dbe 2021-04-22 op if ((t = parse_uri(s, parsed)) != NULL)
594 bfa33dbe 2021-04-22 op memset(parsed, 0, sizeof(*parsed));
595 bfa33dbe 2021-04-22 op return parse_relative_ref(s, parsed);
600 bfa33dbe 2021-04-22 op * absolute-URI = scheme ":" hier-part [ "?" query ]
602 bfa33dbe 2021-04-22 op static const char *
603 bfa33dbe 2021-04-22 op parse_absolute_uri(const char *s, struct phos_uri *parsed)
605 bfa33dbe 2021-04-22 op if ((s = parse_scheme(s, parsed)) == NULL)
608 bfa33dbe 2021-04-22 op if (*s != ':')
612 bfa33dbe 2021-04-22 op if ((s = parse_hier_part(s, parsed)) == NULL)
615 bfa33dbe 2021-04-22 op if (*s == '?') {
617 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
625 bfa33dbe 2021-04-22 op /* normalizing fns */
628 bfa33dbe 2021-04-22 op hasprefix(const char *str, const char *prfx)
630 bfa33dbe 2021-04-22 op for (; *str == *prfx && *prfx != '\0'; str++, prfx++)
633 bfa33dbe 2021-04-22 op return *prfx == '\0';
636 bfa33dbe 2021-04-22 op static char *
637 bfa33dbe 2021-04-22 op dotdot(char *point, char *start)
641 bfa33dbe 2021-04-22 op for (t = point-1; t > start; --t) {
642 bfa33dbe 2021-04-22 op if (*t == '/')
645 bfa33dbe 2021-04-22 op if (t < start)
648 bfa33dbe 2021-04-22 op memmove(t, point, strlen(point)+1);
653 bfa33dbe 2021-04-22 op * This is the "Remove Dot Segments" straight outta RFC3986, section
657 bfa33dbe 2021-04-22 op path_clean(struct phos_uri *uri)
659 bfa33dbe 2021-04-22 op char *in = uri->path;
661 bfa33dbe 2021-04-22 op while (in != NULL && *in != '\0') {
662 bfa33dbe 2021-04-22 op assert(in >= uri->path);
664 bfa33dbe 2021-04-22 op /* A) drop leading ../ or ./ */
665 bfa33dbe 2021-04-22 op if (hasprefix(in, "../"))
666 bfa33dbe 2021-04-22 op memmove(in, &in[3], strlen(&in[3])+1);
667 bfa33dbe 2021-04-22 op else if (hasprefix(in, "./"))
668 bfa33dbe 2021-04-22 op memmove(in, &in[2], strlen(&in[2])+1);
670 bfa33dbe 2021-04-22 op /* B) replace /./ or /. with / */
671 bfa33dbe 2021-04-22 op else if (hasprefix(in, "/./"))
672 bfa33dbe 2021-04-22 op memmove(&in[1], &in[3], strlen(&in[3])+1);
673 bfa33dbe 2021-04-22 op else if (!strcmp(in, "/."))
674 bfa33dbe 2021-04-22 op in[1] = '\0';
676 bfa33dbe 2021-04-22 op /* C) resolve dot-dot */
677 bfa33dbe 2021-04-22 op else if (hasprefix(in, "/../")) {
678 bfa33dbe 2021-04-22 op in = dotdot(in, uri->path);
679 bfa33dbe 2021-04-22 op memmove(&in[1], &in[4], strlen(&in[4])+1);
680 bfa33dbe 2021-04-22 op } else if (!strcmp(in, "/..")) {
681 bfa33dbe 2021-04-22 op in = dotdot(in, uri->path);
682 bfa33dbe 2021-04-22 op in[1] = '\0';
687 bfa33dbe 2021-04-22 op else if (!strcmp(in, "."))
689 bfa33dbe 2021-04-22 op else if (!strcmp(in, ".."))
694 bfa33dbe 2021-04-22 op in = strchr(in+1, '/');
699 bfa33dbe 2021-04-22 op * see RFC3986 5.3.3 "Merge Paths".
702 bfa33dbe 2021-04-22 op merge_path(struct phos_uri *ret, const struct phos_uri *base,
703 bfa33dbe 2021-04-22 op const struct phos_uri *ref)
705 bfa33dbe 2021-04-22 op const char *s;
708 bfa33dbe 2021-04-22 op len = sizeof(ret->path);
710 bfa33dbe 2021-04-22 op s = strrchr(base->path, '/');
711 bfa33dbe 2021-04-22 op if ((*base->host != '\0' && *base->path == '\0') || s == NULL) {
712 bfa33dbe 2021-04-22 op strlcpy(ret->path, "/", len);
714 bfa33dbe 2021-04-22 op /* copy the / too */
715 bfa33dbe 2021-04-22 op memcpy(ret->path, base->path, s - base->path + 1);
718 bfa33dbe 2021-04-22 op return strlcat(ret->path, ref->path, len) < len;
722 bfa33dbe 2021-04-22 op /* public interface */
725 bfa33dbe 2021-04-22 op phos_parse_absolute_uri(const char *s, struct phos_uri *uri)
727 bfa33dbe 2021-04-22 op memset(uri, 0, sizeof(*uri));
729 bfa33dbe 2021-04-22 op if ((s = parse_absolute_uri(s, uri)) == NULL)
731 bfa33dbe 2021-04-22 op if (*s != '\0')
733 bfa33dbe 2021-04-22 op path_clean(uri);
738 bfa33dbe 2021-04-22 op phos_parse_uri_reference(const char *s, struct phos_uri *uri)
740 bfa33dbe 2021-04-22 op memset(uri, 0, sizeof(*uri));
742 bfa33dbe 2021-04-22 op if ((s = parse_uri_reference(s, uri)) == NULL)
744 bfa33dbe 2021-04-22 op if (*s != '\0')
746 bfa33dbe 2021-04-22 op path_clean(uri);
751 bfa33dbe 2021-04-22 op * Implementation of the "transform references" algorithm from
752 bfa33dbe 2021-04-22 op * RFC3986, see 5.2.2.
754 bfa33dbe 2021-04-22 op * We expect base and ref to be URIs constructed by this library
755 bfa33dbe 2021-04-22 op * (because we emit only normalized URIs).
757 bfa33dbe 2021-04-22 op * ATM this is marked as private because:
758 bfa33dbe 2021-04-22 op * - let's say the URI is "."
759 bfa33dbe 2021-04-22 op * - one calls phos_parse_uri_references
760 bfa33dbe 2021-04-22 op * - it exists with success, but the path becomes ""
761 bfa33dbe 2021-04-22 op * - this routine does the right thing, but the outcome is not what expected.
763 bfa33dbe 2021-04-22 op * so users for now have to user resolve_uri_from_str, which parses
764 bfa33dbe 2021-04-22 op * the URI but not normalize it, and then call into us.
767 bfa33dbe 2021-04-22 op phos_resolve_uri_from(const struct phos_uri *base, const struct phos_uri *ref,
768 bfa33dbe 2021-04-22 op struct phos_uri *ret)
770 bfa33dbe 2021-04-22 op memset(ret, 0, sizeof(*ret));
772 bfa33dbe 2021-04-22 op if (*ref->scheme != '\0') {
773 bfa33dbe 2021-04-22 op strlcpy(ret->scheme, ref->scheme, sizeof(ret->scheme));
774 bfa33dbe 2021-04-22 op strlcpy(ret->host, ref->host, sizeof(ret->host));
775 bfa33dbe 2021-04-22 op strlcpy(ret->port, ref->port, sizeof(ret->port));
776 bfa33dbe 2021-04-22 op ret->dec_port = ret->dec_port;
777 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
778 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
780 bfa33dbe 2021-04-22 op if (*ref->host != '\0') {
781 bfa33dbe 2021-04-22 op strlcpy(ret->host, ref->host, sizeof(ret->host));
782 bfa33dbe 2021-04-22 op strlcpy(ret->port, ref->port, sizeof(ret->port));
783 bfa33dbe 2021-04-22 op ret->dec_port = ret->dec_port;
784 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
785 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
787 bfa33dbe 2021-04-22 op if (*ref->path == '\0') {
788 bfa33dbe 2021-04-22 op strlcpy(ret->path, base->path, sizeof(ret->path));
789 bfa33dbe 2021-04-22 op if (*ref->query != '\0')
790 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
792 bfa33dbe 2021-04-22 op strlcpy(ret->query, base->query, sizeof(ret->query));
794 bfa33dbe 2021-04-22 op if (*ref->path == '/')
795 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
797 bfa33dbe 2021-04-22 op if (!merge_path(ret, base, ref))
800 bfa33dbe 2021-04-22 op path_clean(ret);
802 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
805 bfa33dbe 2021-04-22 op strlcpy(ret->host, base->host, sizeof(ret->host));
806 bfa33dbe 2021-04-22 op strlcpy(ret->port, base->port, sizeof(ret->port));
807 bfa33dbe 2021-04-22 op ret->dec_port = base->dec_port;
810 bfa33dbe 2021-04-22 op strlcpy(ret->scheme, base->scheme, sizeof(ret->scheme));
813 bfa33dbe 2021-04-22 op strlcpy(ret->fragment, ref->fragment, sizeof(ret->fragment));
819 bfa33dbe 2021-04-22 op phos_resolve_uri_from_str(const struct phos_uri *base, const char *refstr,
820 bfa33dbe 2021-04-22 op struct phos_uri *ret)
822 bfa33dbe 2021-04-22 op struct phos_uri ref;
824 bfa33dbe 2021-04-22 op memset(&ref, 0, sizeof(ref));
826 bfa33dbe 2021-04-22 op if ((refstr = parse_uri_reference(refstr, &ref)) == NULL)
829 bfa33dbe 2021-04-22 op if (*refstr != '\0')
832 bfa33dbe 2021-04-22 op return phos_resolve_uri_from(base, &ref, ret);
836 bfa33dbe 2021-04-22 op phos_uri_drop_empty_segments(struct phos_uri *uri)
840 bfa33dbe 2021-04-22 op for (i = uri->path; *i; ++i) {
841 bfa33dbe 2021-04-22 op if (*i == '/' && *(i+1) == '/') {
842 bfa33dbe 2021-04-22 op memmove(i, i+1, strlen(i)); /* move also the \0 */
849 4190a5c2 2021-04-24 op phos_uri_set_query(struct phos_uri *uri, const char *query)
855 4190a5c2 2021-04-24 op len = sizeof(uri->query);
856 4190a5c2 2021-04-24 op out = uri->query;
857 4190a5c2 2021-04-24 op memset(uri->query, 0, len);
859 4190a5c2 2021-04-24 op for (; *query != '\0' && len > 0; ++query) {
860 4190a5c2 2021-04-24 op if (*query == '/' ||
861 4190a5c2 2021-04-24 op *query == '?' ||
862 4190a5c2 2021-04-24 op *query == ':' ||
863 4190a5c2 2021-04-24 op *query == '@' ||
864 4190a5c2 2021-04-24 op unreserved(*query) ||
865 4190a5c2 2021-04-24 op sub_delims(*query)) {
866 4190a5c2 2021-04-24 op *out++ = *query;
869 4190a5c2 2021-04-24 op if (len <= 4)
872 4190a5c2 2021-04-24 op *out++ = '%';
874 4190a5c2 2021-04-24 op sprintf(out, "%02X", t);
879 4190a5c2 2021-04-24 op return *query == '\0';
883 bfa33dbe 2021-04-22 op phos_serialize_uri(const struct phos_uri *uri, char *buf, size_t len)
885 bfa33dbe 2021-04-22 op #define CAT(s) \
886 bfa33dbe 2021-04-22 op if (strlcat(buf, s, len) >= len) \
889 bfa33dbe 2021-04-22 op strlcpy(buf, "", len);
891 bfa33dbe 2021-04-22 op if (*uri->scheme != '\0') {
892 bfa33dbe 2021-04-22 op CAT(uri->scheme);
896 bfa33dbe 2021-04-22 op if (*uri->host != '\0') {
898 bfa33dbe 2021-04-22 op CAT(uri->host);
901 bfa33dbe 2021-04-22 op CAT(uri->path);
903 bfa33dbe 2021-04-22 op if (*uri->query != '\0') {
905 bfa33dbe 2021-04-22 op CAT(uri->query);
908 bfa33dbe 2021-04-22 op if (*uri->fragment) {
910 bfa33dbe 2021-04-22 op CAT(uri->fragment);