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 */
72 bfa33dbe 2021-04-22 op static inline int
73 bfa33dbe 2021-04-22 op gen_delims(int c)
75 bfa33dbe 2021-04-22 op return c == ':'
85 bfa33dbe 2021-04-22 op static inline int
86 bfa33dbe 2021-04-22 op sub_delims(int c)
88 bfa33dbe 2021-04-22 op return c == '!'
102 bfa33dbe 2021-04-22 op static inline int
103 bfa33dbe 2021-04-22 op reserved(int c)
105 bfa33dbe 2021-04-22 op return gen_delims(c) || sub_delims(c);
109 bfa33dbe 2021-04-22 op static inline int
110 bfa33dbe 2021-04-22 op unreserved(int c)
112 bfa33dbe 2021-04-22 op return isalpha(c)
113 bfa33dbe 2021-04-22 op || isdigit(c)
124 bfa33dbe 2021-04-22 op * IP-literal = "[" ( IPv6address / IPvFuture ) "]"
126 bfa33dbe 2021-04-22 op * in reality, we parse [.*]
128 bfa33dbe 2021-04-22 op static const char *
129 bfa33dbe 2021-04-22 op sub_ip_literal(const char *s)
131 bfa33dbe 2021-04-22 op if (*s != '[')
134 bfa33dbe 2021-04-22 op while (*s != '\0' && *s != ']')
137 bfa33dbe 2021-04-22 op if (*s == '\0')
143 bfa33dbe 2021-04-22 op * parse everything until : or / (or \0).
144 bfa33dbe 2021-04-22 op * NB: empty hosts are technically valid!
146 bfa33dbe 2021-04-22 op static const char *
147 bfa33dbe 2021-04-22 op sub_host_dummy(const char *s)
149 bfa33dbe 2021-04-22 op while (*s != '\0' && *s != ':' && *s != '/')
155 bfa33dbe 2021-04-22 op * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
157 bfa33dbe 2021-04-22 op static const char *
158 bfa33dbe 2021-04-22 op sub_pchar(const char *s)
160 bfa33dbe 2021-04-22 op if (*s == '\0')
163 bfa33dbe 2021-04-22 op if (unreserved(*s))
166 bfa33dbe 2021-04-22 op if (*s == '%') {
167 bfa33dbe 2021-04-22 op if (isxdigit(s[1]) && isxdigit(s[2]))
168 bfa33dbe 2021-04-22 op return s + 3;
171 bfa33dbe 2021-04-22 op if (sub_delims(*s))
174 bfa33dbe 2021-04-22 op if (*s == ':' || *s == '@')
181 bfa33dbe 2021-04-22 op * segment = *pchar
183 bfa33dbe 2021-04-22 op static const char *
184 bfa33dbe 2021-04-22 op sub_segment(const char *s)
186 bfa33dbe 2021-04-22 op const char *t;
188 bfa33dbe 2021-04-22 op while ((t = sub_pchar(s)) != NULL)
193 bfa33dbe 2021-04-22 op /* segment-nz = 1*pchar */
194 bfa33dbe 2021-04-22 op static const char *
195 bfa33dbe 2021-04-22 op sub_segment_nz(const char *s)
197 bfa33dbe 2021-04-22 op if ((s = sub_pchar(s)) == NULL)
199 bfa33dbe 2021-04-22 op return sub_segment(s);
203 bfa33dbe 2021-04-22 op * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
205 bfa33dbe 2021-04-22 op * so, 1*pchar excluding ":"
207 bfa33dbe 2021-04-22 op static const char *
208 bfa33dbe 2021-04-22 op sub_segment_nz_nc(const char *s)
210 bfa33dbe 2021-04-22 op const char *t;
212 bfa33dbe 2021-04-22 op if (*s == ':')
215 bfa33dbe 2021-04-22 op while (*s != ':' && (t = sub_pchar(s)) != NULL)
220 bfa33dbe 2021-04-22 op /* *( "/" segment ) */
221 bfa33dbe 2021-04-22 op static const char *
222 bfa33dbe 2021-04-22 op sub_path_common(const char *s)
225 bfa33dbe 2021-04-22 op if (*s != '/')
228 bfa33dbe 2021-04-22 op s = sub_segment(s);
233 bfa33dbe 2021-04-22 op /* parse fns */
236 bfa33dbe 2021-04-22 op * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
238 bfa33dbe 2021-04-22 op static const char *
239 bfa33dbe 2021-04-22 op parse_scheme(const char *s, struct phos_uri *parsed)
241 bfa33dbe 2021-04-22 op const char *start = s;
244 bfa33dbe 2021-04-22 op if (!isalpha(*s))
247 bfa33dbe 2021-04-22 op while (*s != '\0') {
248 bfa33dbe 2021-04-22 op if (isalpha(*s) ||
249 bfa33dbe 2021-04-22 op isdigit(*s) ||
258 bfa33dbe 2021-04-22 op if (*s == '\0')
261 bfa33dbe 2021-04-22 op len = s - start;
262 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->scheme))
265 bfa33dbe 2021-04-22 op memcpy(parsed->scheme, start, len);
270 bfa33dbe 2021-04-22 op * host = IP-literal / IPv4address / reg-name
272 bfa33dbe 2021-04-22 op * rules IPv4address and reg-name are relaxed into parse_host_dummy.
274 bfa33dbe 2021-04-22 op static const char *
275 bfa33dbe 2021-04-22 op parse_host(const char *s, struct phos_uri *parsed)
277 bfa33dbe 2021-04-22 op const char *t;
280 bfa33dbe 2021-04-22 op if ((t = sub_ip_literal(s)) != NULL ||
281 bfa33dbe 2021-04-22 op (t = sub_host_dummy(s)) != NULL) {
283 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->scheme))
285 bfa33dbe 2021-04-22 op memcpy(parsed->host, s, len);
293 bfa33dbe 2021-04-22 op * port = *digit
295 bfa33dbe 2021-04-22 op static const char *
296 bfa33dbe 2021-04-22 op parse_port(const char *s, struct phos_uri *parsed)
298 bfa33dbe 2021-04-22 op const char *errstr, *start = s;
301 bfa33dbe 2021-04-22 op while (isdigit(*s))
304 bfa33dbe 2021-04-22 op if (s == start)
307 bfa33dbe 2021-04-22 op len = s - start;
308 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->port))
311 bfa33dbe 2021-04-22 op memcpy(parsed->port, start, len);
313 bfa33dbe 2021-04-22 op parsed->dec_port = strtonum(parsed->port, 0, 65535, &errstr);
314 bfa33dbe 2021-04-22 op if (errstr != NULL)
321 bfa33dbe 2021-04-22 op * authority = host [ ":" port ]
322 bfa33dbe 2021-04-22 op * (yep, blatantly ignore the userinfo stuff -- not relevant for Gemini)
324 bfa33dbe 2021-04-22 op static const char *
325 bfa33dbe 2021-04-22 op parse_authority(const char *s, struct phos_uri *parsed)
327 bfa33dbe 2021-04-22 op if ((s = parse_host(s, parsed)) == NULL)
330 bfa33dbe 2021-04-22 op if (*s == ':') {
332 bfa33dbe 2021-04-22 op return parse_port(s, parsed);
338 bfa33dbe 2021-04-22 op static inline const char *
339 bfa33dbe 2021-04-22 op set_path(const char *start, const char *end, struct phos_uri *parsed)
343 bfa33dbe 2021-04-22 op if (end == NULL)
346 bfa33dbe 2021-04-22 op len = end - start;
347 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->path))
349 bfa33dbe 2021-04-22 op memcpy(parsed->path, start, len);
354 bfa33dbe 2021-04-22 op * path-abempty = *( "/" segment )
356 bfa33dbe 2021-04-22 op static const char *
357 bfa33dbe 2021-04-22 op parse_path_abempty(const char *s, struct phos_uri *parsed)
359 bfa33dbe 2021-04-22 op const char *t;
361 bfa33dbe 2021-04-22 op t = sub_path_common(s);
362 bfa33dbe 2021-04-22 op return set_path(s, t, parsed);
366 bfa33dbe 2021-04-22 op * path-absolute = "/" [ segment-nz *( "/" segment ) ]
368 bfa33dbe 2021-04-22 op static const char *
369 bfa33dbe 2021-04-22 op parse_path_absolute(const char *s, struct phos_uri *parsed)
371 bfa33dbe 2021-04-22 op const char *t, *start = s;
373 bfa33dbe 2021-04-22 op if (*s != '/')
377 bfa33dbe 2021-04-22 op if ((t = sub_segment_nz(s)) == NULL)
378 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
380 bfa33dbe 2021-04-22 op s = sub_path_common(t);
381 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
385 bfa33dbe 2021-04-22 op * path-noscheme = segment-nz-nc *( "/" segment )
387 bfa33dbe 2021-04-22 op static const char *
388 bfa33dbe 2021-04-22 op parse_path_noscheme(const char *s, struct phos_uri *parsed)
390 bfa33dbe 2021-04-22 op const char *start = s;
392 bfa33dbe 2021-04-22 op if ((s = sub_segment_nz_nc(s)) == NULL)
394 bfa33dbe 2021-04-22 op s = sub_path_common(s);
395 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
399 bfa33dbe 2021-04-22 op * path-rootless = segment-nz *( "/" segment )
401 bfa33dbe 2021-04-22 op static const char *
402 bfa33dbe 2021-04-22 op parse_path_rootless(const char *s, struct phos_uri *parsed)
404 bfa33dbe 2021-04-22 op const char *start = s;
406 bfa33dbe 2021-04-22 op if ((s = sub_segment_nz(s)) == NULL)
408 bfa33dbe 2021-04-22 op s = sub_path_common(s);
409 bfa33dbe 2021-04-22 op return set_path(start, s, parsed);
413 bfa33dbe 2021-04-22 op * path-empty = 0<pchar>
415 bfa33dbe 2021-04-22 op static const char *
416 bfa33dbe 2021-04-22 op parse_path_empty(const char *s, struct phos_uri *parsed)
422 bfa33dbe 2021-04-22 op * hier-part = "//" authority path-abempty
423 bfa33dbe 2021-04-22 op * / path-absolute
424 bfa33dbe 2021-04-22 op * / path-rootless
425 bfa33dbe 2021-04-22 op * / path-empty
427 bfa33dbe 2021-04-22 op static const char *
428 bfa33dbe 2021-04-22 op parse_hier_part(const char *s, struct phos_uri *parsed)
430 bfa33dbe 2021-04-22 op const char *t;
432 bfa33dbe 2021-04-22 op if (s[0] == '/' && s[1] == '/') {
434 bfa33dbe 2021-04-22 op if ((s = parse_authority(s, parsed)) == NULL)
436 bfa33dbe 2021-04-22 op return parse_path_abempty(s, parsed);
439 bfa33dbe 2021-04-22 op if ((t = parse_path_absolute(s, parsed)) != NULL)
442 bfa33dbe 2021-04-22 op if ((t = parse_path_rootless(s, parsed)) != NULL)
445 bfa33dbe 2021-04-22 op return parse_path_empty(s, parsed);
449 bfa33dbe 2021-04-22 op * query = *( pchar / "/" / "?" )
451 bfa33dbe 2021-04-22 op static const char *
452 bfa33dbe 2021-04-22 op parse_query(const char *s, struct phos_uri *parsed)
454 bfa33dbe 2021-04-22 op const char *t, *start = s;
457 bfa33dbe 2021-04-22 op while (*s != '\0') {
458 bfa33dbe 2021-04-22 op if (*s == '/' || *s == '?') {
463 bfa33dbe 2021-04-22 op if ((t = sub_pchar(s)) == NULL)
468 bfa33dbe 2021-04-22 op len = s - start;
469 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->query))
472 bfa33dbe 2021-04-22 op memcpy(parsed->query, start, len);
477 bfa33dbe 2021-04-22 op * fragment = *( pchar / "/" / "?" )
479 bfa33dbe 2021-04-22 op static const char *
480 bfa33dbe 2021-04-22 op parse_fragment(const char *s, struct phos_uri *parsed)
482 bfa33dbe 2021-04-22 op const char *start = s;
486 bfa33dbe 2021-04-22 op if (*s == '\0')
489 bfa33dbe 2021-04-22 op if (*s == '/' || *s == '?') {
494 bfa33dbe 2021-04-22 op if ((s = sub_pchar(s)) == NULL)
498 bfa33dbe 2021-04-22 op len = s - start;
499 bfa33dbe 2021-04-22 op if (len >= sizeof(parsed->fragment))
502 bfa33dbe 2021-04-22 op memcpy(parsed->fragment, start, len);
507 bfa33dbe 2021-04-22 op * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
509 bfa33dbe 2021-04-22 op static const char *
510 bfa33dbe 2021-04-22 op parse_uri(const char *s, struct phos_uri *parsed)
512 bfa33dbe 2021-04-22 op if ((s = parse_scheme(s, parsed)) == NULL)
515 bfa33dbe 2021-04-22 op if (*s != ':')
519 bfa33dbe 2021-04-22 op if ((s = parse_hier_part(s, parsed)) == NULL)
522 bfa33dbe 2021-04-22 op if (*s == '?') {
524 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
528 bfa33dbe 2021-04-22 op if (*s == '#') {
530 bfa33dbe 2021-04-22 op if ((s = parse_fragment(s, parsed)) == NULL)
538 bfa33dbe 2021-04-22 op * relative-part = "//" authority path-abempty
539 bfa33dbe 2021-04-22 op * / path-absolute
540 bfa33dbe 2021-04-22 op * / path-noscheme
541 bfa33dbe 2021-04-22 op * / path-empty
543 bfa33dbe 2021-04-22 op static const char *
544 bfa33dbe 2021-04-22 op parse_relative_part(const char *s, struct phos_uri *parsed)
546 bfa33dbe 2021-04-22 op const char *t;
548 bfa33dbe 2021-04-22 op if (s[0] == '/' && s[1] == '/') {
550 bfa33dbe 2021-04-22 op if ((s = parse_authority(s, parsed)) == NULL)
552 bfa33dbe 2021-04-22 op return parse_path_abempty(s, parsed);
555 bfa33dbe 2021-04-22 op if ((t = parse_path_absolute(s, parsed)) != NULL)
558 bfa33dbe 2021-04-22 op if ((t = parse_path_noscheme(s, parsed)) != NULL)
561 bfa33dbe 2021-04-22 op return parse_path_empty(s, parsed);
565 bfa33dbe 2021-04-22 op * relative-ref = relative-part [ "?" query ] [ "#" fragment ]
567 bfa33dbe 2021-04-22 op static const char *
568 bfa33dbe 2021-04-22 op parse_relative_ref(const char *s, struct phos_uri *parsed)
570 bfa33dbe 2021-04-22 op if ((s = parse_relative_part(s, parsed)) == NULL)
573 bfa33dbe 2021-04-22 op if (*s == '?') {
575 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
579 bfa33dbe 2021-04-22 op if (*s == '#') {
581 bfa33dbe 2021-04-22 op if ((s = parse_fragment(s, parsed)) == NULL)
589 bfa33dbe 2021-04-22 op * URI-reference = URI / relative-ref
591 bfa33dbe 2021-04-22 op static const char *
592 bfa33dbe 2021-04-22 op parse_uri_reference(const char *s, struct phos_uri *parsed)
594 bfa33dbe 2021-04-22 op const char *t;
596 bfa33dbe 2021-04-22 op if ((t = parse_uri(s, parsed)) != NULL)
598 bfa33dbe 2021-04-22 op memset(parsed, 0, sizeof(*parsed));
599 bfa33dbe 2021-04-22 op return parse_relative_ref(s, parsed);
604 bfa33dbe 2021-04-22 op * absolute-URI = scheme ":" hier-part [ "?" query ]
606 bfa33dbe 2021-04-22 op static const char *
607 bfa33dbe 2021-04-22 op parse_absolute_uri(const char *s, struct phos_uri *parsed)
609 bfa33dbe 2021-04-22 op if ((s = parse_scheme(s, parsed)) == NULL)
612 bfa33dbe 2021-04-22 op if (*s != ':')
616 bfa33dbe 2021-04-22 op if ((s = parse_hier_part(s, parsed)) == NULL)
619 bfa33dbe 2021-04-22 op if (*s == '?') {
621 bfa33dbe 2021-04-22 op if ((s = parse_query(s, parsed)) == NULL)
629 bfa33dbe 2021-04-22 op /* normalizing fns */
632 bfa33dbe 2021-04-22 op hasprefix(const char *str, const char *prfx)
634 bfa33dbe 2021-04-22 op for (; *str == *prfx && *prfx != '\0'; str++, prfx++)
637 bfa33dbe 2021-04-22 op return *prfx == '\0';
640 bfa33dbe 2021-04-22 op static char *
641 bfa33dbe 2021-04-22 op dotdot(char *point, char *start)
645 bfa33dbe 2021-04-22 op for (t = point-1; t > start; --t) {
646 bfa33dbe 2021-04-22 op if (*t == '/')
649 bfa33dbe 2021-04-22 op if (t < start)
652 bfa33dbe 2021-04-22 op memmove(t, point, strlen(point)+1);
657 bfa33dbe 2021-04-22 op * This is the "Remove Dot Segments" straight outta RFC3986, section
661 bfa33dbe 2021-04-22 op path_clean(struct phos_uri *uri)
663 bfa33dbe 2021-04-22 op char *in = uri->path;
665 bfa33dbe 2021-04-22 op while (in != NULL && *in != '\0') {
666 bfa33dbe 2021-04-22 op assert(in >= uri->path);
668 bfa33dbe 2021-04-22 op /* A) drop leading ../ or ./ */
669 bfa33dbe 2021-04-22 op if (hasprefix(in, "../"))
670 bfa33dbe 2021-04-22 op memmove(in, &in[3], strlen(&in[3])+1);
671 bfa33dbe 2021-04-22 op else if (hasprefix(in, "./"))
672 bfa33dbe 2021-04-22 op memmove(in, &in[2], strlen(&in[2])+1);
674 bfa33dbe 2021-04-22 op /* B) replace /./ or /. with / */
675 bfa33dbe 2021-04-22 op else if (hasprefix(in, "/./"))
676 bfa33dbe 2021-04-22 op memmove(&in[1], &in[3], strlen(&in[3])+1);
677 bfa33dbe 2021-04-22 op else if (!strcmp(in, "/."))
678 bfa33dbe 2021-04-22 op in[1] = '\0';
680 bfa33dbe 2021-04-22 op /* C) resolve dot-dot */
681 bfa33dbe 2021-04-22 op else if (hasprefix(in, "/../")) {
682 bfa33dbe 2021-04-22 op in = dotdot(in, uri->path);
683 bfa33dbe 2021-04-22 op memmove(&in[1], &in[4], strlen(&in[4])+1);
684 bfa33dbe 2021-04-22 op } else if (!strcmp(in, "/..")) {
685 bfa33dbe 2021-04-22 op in = dotdot(in, uri->path);
686 bfa33dbe 2021-04-22 op in[1] = '\0';
691 bfa33dbe 2021-04-22 op else if (!strcmp(in, "."))
693 bfa33dbe 2021-04-22 op else if (!strcmp(in, ".."))
698 bfa33dbe 2021-04-22 op in = strchr(in+1, '/');
703 bfa33dbe 2021-04-22 op * see RFC3986 5.3.3 "Merge Paths".
706 bfa33dbe 2021-04-22 op merge_path(struct phos_uri *ret, const struct phos_uri *base,
707 bfa33dbe 2021-04-22 op const struct phos_uri *ref)
709 bfa33dbe 2021-04-22 op const char *s;
712 bfa33dbe 2021-04-22 op len = sizeof(ret->path);
714 bfa33dbe 2021-04-22 op s = strrchr(base->path, '/');
715 bfa33dbe 2021-04-22 op if ((*base->host != '\0' && *base->path == '\0') || s == NULL) {
716 bfa33dbe 2021-04-22 op strlcpy(ret->path, "/", len);
718 bfa33dbe 2021-04-22 op /* copy the / too */
719 bfa33dbe 2021-04-22 op memcpy(ret->path, base->path, s - base->path + 1);
722 bfa33dbe 2021-04-22 op return strlcat(ret->path, ref->path, len) < len;
726 bfa33dbe 2021-04-22 op /* public interface */
729 bfa33dbe 2021-04-22 op phos_parse_absolute_uri(const char *s, struct phos_uri *uri)
731 bfa33dbe 2021-04-22 op memset(uri, 0, sizeof(*uri));
733 bfa33dbe 2021-04-22 op if ((s = parse_absolute_uri(s, uri)) == NULL)
735 bfa33dbe 2021-04-22 op if (*s != '\0')
737 bfa33dbe 2021-04-22 op path_clean(uri);
742 bfa33dbe 2021-04-22 op phos_parse_uri_reference(const char *s, struct phos_uri *uri)
744 bfa33dbe 2021-04-22 op memset(uri, 0, sizeof(*uri));
746 bfa33dbe 2021-04-22 op if ((s = parse_uri_reference(s, uri)) == NULL)
748 bfa33dbe 2021-04-22 op if (*s != '\0')
750 bfa33dbe 2021-04-22 op path_clean(uri);
755 bfa33dbe 2021-04-22 op * Implementation of the "transform references" algorithm from
756 bfa33dbe 2021-04-22 op * RFC3986, see 5.2.2.
758 bfa33dbe 2021-04-22 op * We expect base and ref to be URIs constructed by this library
759 bfa33dbe 2021-04-22 op * (because we emit only normalized URIs).
761 bfa33dbe 2021-04-22 op * ATM this is marked as private because:
762 bfa33dbe 2021-04-22 op * - let's say the URI is "."
763 bfa33dbe 2021-04-22 op * - one calls phos_parse_uri_references
764 bfa33dbe 2021-04-22 op * - it exists with success, but the path becomes ""
765 bfa33dbe 2021-04-22 op * - this routine does the right thing, but the outcome is not what expected.
767 bfa33dbe 2021-04-22 op * so users for now have to user resolve_uri_from_str, which parses
768 bfa33dbe 2021-04-22 op * the URI but not normalize it, and then call into us.
771 bfa33dbe 2021-04-22 op phos_resolve_uri_from(const struct phos_uri *base, const struct phos_uri *ref,
772 bfa33dbe 2021-04-22 op struct phos_uri *ret)
774 bfa33dbe 2021-04-22 op memset(ret, 0, sizeof(*ret));
776 bfa33dbe 2021-04-22 op if (*ref->scheme != '\0') {
777 bfa33dbe 2021-04-22 op strlcpy(ret->scheme, ref->scheme, sizeof(ret->scheme));
778 bfa33dbe 2021-04-22 op strlcpy(ret->host, ref->host, sizeof(ret->host));
779 bfa33dbe 2021-04-22 op strlcpy(ret->port, ref->port, sizeof(ret->port));
780 bfa33dbe 2021-04-22 op ret->dec_port = ret->dec_port;
781 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
782 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
784 bfa33dbe 2021-04-22 op if (*ref->host != '\0') {
785 bfa33dbe 2021-04-22 op strlcpy(ret->host, ref->host, sizeof(ret->host));
786 bfa33dbe 2021-04-22 op strlcpy(ret->port, ref->port, sizeof(ret->port));
787 bfa33dbe 2021-04-22 op ret->dec_port = ret->dec_port;
788 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
789 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
791 bfa33dbe 2021-04-22 op if (*ref->path == '\0') {
792 bfa33dbe 2021-04-22 op strlcpy(ret->path, base->path, sizeof(ret->path));
793 bfa33dbe 2021-04-22 op if (*ref->query != '\0')
794 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
796 bfa33dbe 2021-04-22 op strlcpy(ret->query, base->query, sizeof(ret->query));
798 bfa33dbe 2021-04-22 op if (*ref->path == '/')
799 bfa33dbe 2021-04-22 op strlcpy(ret->path, ref->path, sizeof(ret->path));
801 bfa33dbe 2021-04-22 op if (!merge_path(ret, base, ref))
804 bfa33dbe 2021-04-22 op path_clean(ret);
806 bfa33dbe 2021-04-22 op strlcpy(ret->query, ref->query, sizeof(ret->query));
809 bfa33dbe 2021-04-22 op strlcpy(ret->host, base->host, sizeof(ret->host));
810 bfa33dbe 2021-04-22 op strlcpy(ret->port, base->port, sizeof(ret->port));
811 bfa33dbe 2021-04-22 op ret->dec_port = base->dec_port;
814 bfa33dbe 2021-04-22 op strlcpy(ret->scheme, base->scheme, sizeof(ret->scheme));
817 bfa33dbe 2021-04-22 op strlcpy(ret->fragment, ref->fragment, sizeof(ret->fragment));
823 bfa33dbe 2021-04-22 op phos_resolve_uri_from_str(const struct phos_uri *base, const char *refstr,
824 bfa33dbe 2021-04-22 op struct phos_uri *ret)
826 bfa33dbe 2021-04-22 op struct phos_uri ref;
828 bfa33dbe 2021-04-22 op memset(&ref, 0, sizeof(ref));
830 bfa33dbe 2021-04-22 op if ((refstr = parse_uri_reference(refstr, &ref)) == NULL)
833 bfa33dbe 2021-04-22 op if (*refstr != '\0')
836 bfa33dbe 2021-04-22 op return phos_resolve_uri_from(base, &ref, ret);
840 bfa33dbe 2021-04-22 op phos_uri_drop_empty_segments(struct phos_uri *uri)
844 bfa33dbe 2021-04-22 op for (i = uri->path; *i; ++i) {
845 bfa33dbe 2021-04-22 op if (*i == '/' && *(i+1) == '/') {
846 bfa33dbe 2021-04-22 op memmove(i, i+1, strlen(i)); /* move also the \0 */
853 4190a5c2 2021-04-24 op phos_uri_set_query(struct phos_uri *uri, const char *query)
859 4190a5c2 2021-04-24 op len = sizeof(uri->query);
860 4190a5c2 2021-04-24 op out = uri->query;
861 4190a5c2 2021-04-24 op memset(uri->query, 0, len);
863 4190a5c2 2021-04-24 op for (; *query != '\0' && len > 0; ++query) {
864 4190a5c2 2021-04-24 op if (*query == '/' ||
865 4190a5c2 2021-04-24 op *query == '?' ||
866 4190a5c2 2021-04-24 op *query == ':' ||
867 4190a5c2 2021-04-24 op *query == '@' ||
868 4190a5c2 2021-04-24 op unreserved(*query) ||
869 4190a5c2 2021-04-24 op sub_delims(*query)) {
870 4190a5c2 2021-04-24 op *out++ = *query;
873 4190a5c2 2021-04-24 op if (len <= 4)
876 4190a5c2 2021-04-24 op *out++ = '%';
878 4190a5c2 2021-04-24 op sprintf(out, "%02X", t);
883 4190a5c2 2021-04-24 op return *query == '\0';
887 bfa33dbe 2021-04-22 op phos_serialize_uri(const struct phos_uri *uri, char *buf, size_t len)
889 bfa33dbe 2021-04-22 op #define CAT(s) \
890 bfa33dbe 2021-04-22 op if (strlcat(buf, s, len) >= len) \
893 bfa33dbe 2021-04-22 op strlcpy(buf, "", len);
895 bfa33dbe 2021-04-22 op if (*uri->scheme != '\0') {
896 bfa33dbe 2021-04-22 op CAT(uri->scheme);
900 29c139d5 2021-08-13 op if (*uri->host != '\0' || strcmp(uri->scheme, "file") == 0) {
902 29c139d5 2021-08-13 op * The file URI scheme has a quirk that even if a
903 29c139d5 2021-08-13 op * hostname is not present, we still have to append
904 29c139d5 2021-08-13 op * the two slashes. This is why we have
905 29c139d5 2021-08-13 op * file:///etc/hosts and not file:/etc/hosts
908 bfa33dbe 2021-04-22 op CAT(uri->host);
911 81d1e61a 2021-04-25 op if (*uri->port != '\0' && strcmp(uri->port, "1965")) {
913 81d1e61a 2021-04-25 op CAT(uri->port);
916 bfa33dbe 2021-04-22 op CAT(uri->path);
918 bfa33dbe 2021-04-22 op if (*uri->query != '\0') {
920 bfa33dbe 2021-04-22 op CAT(uri->query);
923 bfa33dbe 2021-04-22 op if (*uri->fragment) {
925 bfa33dbe 2021-04-22 op CAT(uri->fragment);