1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include <ctype.h>
29#include <string.h>
30#include <stdlib.h>
31
32#include "containers/core/containers_uri.h"
33
34/*****************************************************************************/
35/* Internal types and definitions */
36/*****************************************************************************/
37
38typedef struct VC_URI_QUERY_T
39{
40 char *name;
41 char *value;
42} VC_URI_QUERY_T;
43
44struct VC_URI_PARTS_T
45{
46 char *scheme; /**< Unescaped scheme */
47 char *userinfo; /**< Unescaped userinfo */
48 char *host; /**< Unescaped host name/IP address */
49 char *port; /**< Unescaped port */
50 char *path; /**< Unescaped path */
51 char *path_extension; /**< Unescaped path extension */
52 char *fragment; /**< Unescaped fragment */
53 VC_URI_QUERY_T *queries; /**< Array of queries */
54 uint32_t num_queries; /**< Number of queries in array */
55};
56
57typedef const uint32_t *RESERVED_CHARS_TABLE_T;
58
59/** Reserved character table for scheme component
60 * Controls, space, !"#$%&'()*,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */
61static uint32_t scheme_reserved_chars[8] = {
62 0xFFFFFFFF, 0xFC0097FF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
63};
64
65/** Reserved character table for userinfo component
66 * Controls, space, "#%/<>?@[\]^`{|} and 0x7F and above reserved. */
67static uint32_t userinfo_reserved_chars[8] = {
68 0xFFFFFFFF, 0xD000802D, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
69};
70
71/** Reserved character table for host component
72 * Controls, space, "#%/<>?@\^`{|} and 0x7F and above reserved. */
73static uint32_t host_reserved_chars[8] = {
74 0xFFFFFFFF, 0xD000802D, 0x50000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
75};
76
77/** Reserved character table for port component
78 * Controls, space, !"#$%&'()*+,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */
79static uint32_t port_reserved_chars[8] = {
80 0xFFFFFFFF, 0xFC009FFF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
81};
82
83/** Reserved character table for path component
84 * Controls, space, "#%<>?[\]^`{|} and 0x7F and above reserved. */
85static uint32_t path_reserved_chars[8] = {
86 0xFFFFFFFF, 0xD000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
87};
88
89/** Reserved character table for query component
90 * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */
91static uint32_t query_reserved_chars[8] = {
92 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
93};
94
95/** Reserved character table for fragment component
96 * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */
97static uint32_t fragment_reserved_chars[8] = {
98 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
99};
100
101#define URI_RESERVED(C, TABLE) (!!((TABLE)[(unsigned char)(C) >> 5] & (1 << ((C) & 0x1F))))
102
103#define SCHEME_DELIMITERS ":/?#"
104#define NETWORK_DELIMITERS "@/?#"
105#define HOST_PORT_DELIMITERS "/?#"
106#define PATH_DELIMITERS "?#"
107#define QUERY_DELIMITERS "#"
108
109/*****************************************************************************/
110/* Internal functions */
111/*****************************************************************************/
112
113static char to_hex(int v)
114{
115 if (v > 9)
116 return 'A' + v - 10;
117 return '0' + v;
118}
119
120/*****************************************************************************/
121static uint32_t from_hex(const char *str, uint32_t str_len)
122{
123 uint32_t val = 0;
124
125 while (str_len--)
126 {
127 char c = *str++;
128 if (c >= '0' && c <= '9')
129 c -= '0';
130 else if (c >= 'A' && c <= 'F')
131 c -= 'A' - 10;
132 else if (c >= 'a' && c <= 'f')
133 c -= 'a' - 10;
134 else
135 c = 0; /* Illegal character (not hex) */
136 val = (val << 4) + c;
137 }
138
139 return val;
140}
141
142/*****************************************************************************/
143static uint32_t escaped_length( const char *str, RESERVED_CHARS_TABLE_T reserved )
144{
145 uint32_t ii;
146 uint32_t esclen = 0;
147 char c;
148
149 for (ii = strlen(str); ii > 0; ii--)
150 {
151 c = *str++;
152 if (URI_RESERVED(c, reserved))
153 {
154 /* Reserved character needs escaping as %xx */
155 esclen += 3;
156 } else {
157 esclen++;
158 }
159 }
160
161 return esclen;
162}
163
164/*****************************************************************************/
165static uint32_t escape_string( const char *str, char *escaped,
166 RESERVED_CHARS_TABLE_T reserved )
167{
168 uint32_t ii;
169 uint32_t esclen = 0;
170
171 if (!str)
172 return 0;
173
174 for (ii = strlen(str); ii > 0; ii--)
175 {
176 char c = *str++;
177
178 if (URI_RESERVED(c, reserved))
179 {
180 escaped[esclen++] = '%';
181 escaped[esclen++] = to_hex((c >> 4) & 0xF);
182 escaped[esclen++] = to_hex(c & 0xF);
183 } else {
184 escaped[esclen++] = c;
185 }
186 }
187
188 return esclen;
189}
190
191/*****************************************************************************/
192static uint32_t unescaped_length( const char *str, uint32_t str_len )
193{
194 uint32_t ii;
195 uint32_t unesclen = 0;
196
197 for (ii = 0; ii < str_len; ii++)
198 {
199 if (*str++ == '%' && (ii + 2) < str_len)
200 {
201 str += 2; /* Should be two hex values next */
202 ii += 2;
203 }
204 unesclen++;
205 }
206
207 return unesclen;
208}
209
210/*****************************************************************************/
211static void unescape_string( const char *str, uint32_t str_len, char *unescaped )
212{
213 uint32_t ii;
214
215 for (ii = 0; ii < str_len; ii++)
216 {
217 char c = *str++;
218
219 if (c == '%' && (ii + 2) < str_len )
220 {
221 c = (char)(from_hex(str, 2) & 0xFF);
222 str += 2;
223 ii += 2;
224 }
225 *unescaped++ = c;
226 }
227
228 *unescaped = '\0';
229}
230
231/*****************************************************************************/
232static char *create_unescaped_string( const char *escstr, uint32_t esclen )
233{
234 char *unescstr;
235
236 unescstr = (char *)malloc(unescaped_length(escstr, esclen) + 1); /* Allow for NUL */
237 if (unescstr)
238 unescape_string(escstr, esclen, unescstr);
239
240 return unescstr;
241}
242
243/*****************************************************************************/
244static bool duplicate_string( const char *src, char **p_dst )
245{
246 if (*p_dst)
247 free(*p_dst);
248
249 if (src)
250 {
251 size_t str_size = strlen(src) + 1;
252
253 *p_dst = (char *)malloc(str_size);
254 if (!*p_dst)
255 return false;
256
257 memcpy(*p_dst, src, str_size);
258 } else
259 *p_dst = NULL;
260
261 return true;
262}
263
264/*****************************************************************************/
265static void release_string( char **str )
266{
267 if (*str)
268 {
269 free(*str);
270 *str = NULL;
271 }
272}
273
274/*****************************************************************************/
275static void to_lower_string( char *str )
276{
277 char c;
278
279 while ((c = *str) != '\0')
280 {
281 if (c >= 'A' && c <= 'Z')
282 *str = c - 'A' + 'a';
283 str++;
284 }
285}
286
287/*****************************************************************************/
288static const char *vc_uri_find_delimiter(const char *str, const char *delimiters)
289{
290 const char *ptr = str;
291 char c;
292
293 while ((c = *ptr) != 0)
294 {
295 if (strchr(delimiters, c) != 0)
296 break;
297 ptr++;
298 }
299
300 return ptr;
301}
302
303/*****************************************************************************/
304static void vc_uri_set_path_extension(VC_URI_PARTS_T *p_uri)
305{
306 char *end;
307
308 if (!p_uri)
309 return;
310
311 p_uri->path_extension = NULL;
312
313 if (!p_uri->path)
314 return;
315
316 /* Look for the magic dot */
317 for (end = p_uri->path + strlen(p_uri->path); *end != '.'; end--)
318 if (end == p_uri->path || *end == '/' || *end == '\\')
319 return;
320
321 p_uri->path_extension = end + 1;
322}
323
324/*****************************************************************************/
325static bool parse_authority( VC_URI_PARTS_T *p_uri, const char *str,
326 uint32_t str_len, const char *userinfo_end )
327{
328 const char *marker = userinfo_end;
329 const char *str_end = str + str_len;
330 char c;
331
332 if (marker)
333 {
334 p_uri->userinfo = create_unescaped_string(str, marker - str);
335 if (!p_uri->userinfo)
336 return false;
337 str = marker + 1; /* Past '@' character */
338 }
339
340 if (*str == '[') /* IPvFuture / IPv6 address */
341 {
342 /* Find end of address marker */
343 for (marker = str; marker < str_end; marker++)
344 {
345 c = *marker;
346 if (c == ']')
347 break;
348 }
349
350 if (marker < str_end)
351 marker++; /* Found marker, move to next character */
352 } else {
353 /* Find port value marker*/
354 for (marker = str; marker < str_end; marker++)
355 {
356 c = *marker;
357 if (c == ':')
358 break;
359 }
360 }
361
362 /* Always store the host, even if empty, to trigger the "://" form of URI */
363 p_uri->host = create_unescaped_string(str, marker - str);
364 if (!p_uri->host)
365 return false;
366 to_lower_string(p_uri->host); /* Host names are case-insensitive */
367
368 if (*marker == ':')
369 {
370 str = marker + 1;
371 p_uri->port = create_unescaped_string(str, str_end - str);
372 if (!p_uri->port)
373 return false;
374 }
375
376 return true;
377}
378
379/*****************************************************************************/
380static bool store_query( VC_URI_PARTS_T *p_uri, const char *name_start,
381 const char *equals_ptr, const char *query_end)
382{
383 uint32_t name_len, value_len;
384
385 if (equals_ptr)
386 {
387 name_len = equals_ptr - name_start;
388 value_len = query_end - equals_ptr - 1; /* Don't include '=' itself */
389 } else {
390 name_len = query_end - name_start;
391 value_len = 0;
392 }
393
394 /* Only store something if there is a name */
395 if (name_len)
396 {
397 char *name, *value = NULL;
398 VC_URI_QUERY_T *p_query;
399
400 if (equals_ptr)
401 {
402 value = create_unescaped_string(equals_ptr + 1, value_len);
403 if (!value)
404 return false;
405 equals_ptr = query_end;
406 }
407
408 name = create_unescaped_string(name_start, name_len);
409 if (!name)
410 {
411 if (value)
412 free(value);
413 return false;
414 }
415
416 /* Store query data in URI structure */
417 p_query = &p_uri->queries[ p_uri->num_queries++ ];
418 p_query->name = name;
419 p_query->value = value;
420 }
421
422 return true;
423}
424
425/*****************************************************************************/
426static bool parse_query( VC_URI_PARTS_T *p_uri, const char *str, uint32_t str_len )
427{
428 uint32_t ii;
429 uint32_t query_count;
430 VC_URI_QUERY_T *queries;
431 const char *name_start = str;
432 const char *equals_ptr = NULL;
433 char c;
434
435 if (!str_len)
436 return true;
437
438 /* Scan for the number of query items, so array can be allocated the right size */
439 query_count = 1; /* At least */
440 for (ii = 0; ii < str_len; ii++)
441 {
442 c = str[ii];
443
444 if (c == '&' || c ==';')
445 query_count++;
446 }
447
448 queries = (VC_URI_QUERY_T *)malloc(query_count * sizeof(VC_URI_QUERY_T));
449 if (!queries)
450 return false;
451
452 p_uri->queries = queries;
453
454 /* Go back and parse the string for each query item and store in array */
455 for (ii = 0; ii < str_len; ii++)
456 {
457 c = *str;
458
459 /* Take first '=' as break between name and value */
460 if (c == '=' && !equals_ptr)
461 equals_ptr = str;
462
463 /* If at the end of the name or name/value pair */
464 if (c == '&' || c ==';')
465 {
466 if (!store_query(p_uri, name_start, equals_ptr, str))
467 return false;
468
469 equals_ptr = NULL;
470 name_start = str + 1;
471 }
472
473 str++;
474 }
475
476 return store_query(p_uri, name_start, equals_ptr, str);
477}
478
479/*****************************************************************************/
480static uint32_t calculate_uri_length(const VC_URI_PARTS_T *p_uri)
481{
482 uint32_t length = 0;
483 uint32_t count;
484
485 /* With no scheme, assume this is a plain path (without escaping) */
486 if (!p_uri->scheme)
487 return p_uri->path ? strlen(p_uri->path) : 0;
488
489 length += escaped_length(p_uri->scheme, scheme_reserved_chars);
490 length++; /* for the colon */
491
492 if (p_uri->host)
493 {
494 length += escaped_length(p_uri->host, host_reserved_chars) + 2; /* for the double slash */
495 if (p_uri->userinfo)
496 length += escaped_length(p_uri->userinfo, userinfo_reserved_chars) + 1; /* for the '@' */
497 if (p_uri->port)
498 length += escaped_length(p_uri->port, port_reserved_chars) + 1; /* for the ':' */
499 }
500
501 if (p_uri->path)
502 length += escaped_length(p_uri->path, path_reserved_chars);
503
504 count = p_uri->num_queries;
505 if (count)
506 {
507 VC_URI_QUERY_T * queries = p_uri->queries;
508
509 while (count--)
510 {
511 /* The name is preceded by either the '?' or the '&' */
512 length += escaped_length(queries->name, query_reserved_chars) + 1;
513
514 /* The value is optional, but if present will require an '=' */
515 if (queries->value)
516 length += escaped_length(queries->value, query_reserved_chars) + 1;
517 queries++;
518 }
519 }
520
521 if (p_uri->fragment)
522 length += escaped_length(p_uri->fragment, fragment_reserved_chars) + 1; /* for the '#' */
523
524 return length;
525}
526
527/*****************************************************************************/
528static void build_uri(const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size)
529{
530 uint32_t count;
531
532 /* With no scheme, assume this is a plain path (without escaping) */
533 if (!p_uri->scheme)
534 {
535 if (p_uri->path)
536 strncpy(buffer, p_uri->path, buffer_size);
537 else
538 buffer[0] = '\0';
539 return;
540 }
541
542 buffer += escape_string(p_uri->scheme, buffer, scheme_reserved_chars);
543 *buffer++ = ':';
544
545 if (p_uri->host)
546 {
547 *buffer++ = '/';
548 *buffer++ = '/';
549 if (p_uri->userinfo)
550 {
551 buffer += escape_string(p_uri->userinfo, buffer, userinfo_reserved_chars);
552 *buffer++ = '@';
553 }
554 buffer += escape_string(p_uri->host, buffer, host_reserved_chars);
555 if (p_uri->port)
556 {
557 *buffer++ = ':';
558 buffer += escape_string(p_uri->port, buffer, port_reserved_chars);
559 }
560 }
561
562 if (p_uri->path)
563 buffer += escape_string(p_uri->path, buffer, path_reserved_chars);
564
565 count = p_uri->num_queries;
566 if (count)
567 {
568 VC_URI_QUERY_T * queries = p_uri->queries;
569
570 *buffer++ = '?';
571 while (count--)
572 {
573 buffer += escape_string(queries->name, buffer, query_reserved_chars);
574
575 if (queries->value)
576 {
577 *buffer++ = '=';
578 buffer += escape_string(queries->value, buffer, query_reserved_chars);
579 }
580
581 /* Add separator if there is another item to add */
582 if (count)
583 *buffer++ = '&';
584
585 queries++;
586 }
587 }
588
589 if (p_uri->fragment)
590 {
591 *buffer++ = '#';
592 buffer += escape_string(p_uri->fragment, buffer, fragment_reserved_chars);
593 }
594
595 *buffer = '\0';
596}
597
598/*****************************************************************************/
599static bool vc_uri_copy_base_path( const VC_URI_PARTS_T *base_uri,
600 VC_URI_PARTS_T *relative_uri )
601{
602 const char *base_path = vc_uri_path(base_uri);
603
604 /* No path set (or empty), copy from base */
605 if (!vc_uri_set_path(relative_uri, base_path))
606 return false;
607
608 /* If relative path has no queries, copy base queries across */
609 if (!vc_uri_num_queries(relative_uri))
610 {
611 uint32_t base_queries = vc_uri_num_queries(base_uri);
612 const char *name, *value;
613 uint32_t ii;
614
615 for (ii = 0; ii < base_queries; ii++)
616 {
617 vc_uri_query(base_uri, ii, &name, &value);
618 if (!vc_uri_add_query(relative_uri, name, value))
619 return false;
620 }
621 }
622
623 return true;
624}
625
626/*****************************************************************************/
627static void vc_uri_remove_single_dot_segments( char *path_str )
628{
629 char *slash = path_str - 1;
630
631 while (slash++)
632 {
633 if (*slash == '.')
634 {
635 switch (slash[1])
636 {
637 case '/': /* Single dot segment, remove it */
638 memmove(slash, slash + 2, strlen(slash + 2) + 1);
639 break;
640 case '\0': /* Trailing single dot, remove it */
641 *slash = '\0';
642 break;
643 default: /* Something else (e.g. ".." or ".foo") */
644 ; /* Do nothing */
645 }
646 }
647 slash = strchr(slash, '/');
648 }
649}
650
651/*****************************************************************************/
652static void vc_uri_remove_double_dot_segments( char *path_str )
653{
654 char *previous_segment = path_str;
655 char *slash;
656
657 if (previous_segment[0] == '/')
658 previous_segment++;
659
660 /* Remove strings of the form "<segment>/../" (or "<segment>/.." at the end of the path)
661 * as long as <segment> is not itself ".." */
662 slash = strchr(previous_segment, '/');
663 while (slash)
664 {
665 if (previous_segment[0] != '.' || previous_segment[1] != '.' || previous_segment[2] != '/')
666 {
667 if (slash[1] == '.' && slash[2] == '.')
668 {
669 bool previous_segment_removed = true;
670
671 switch (slash[3])
672 {
673 case '/': /* "/../" inside path, snip it and last segment out */
674 memmove(previous_segment, slash + 4, strlen(slash + 4) + 1);
675 break;
676 case '\0': /* Trailing "/.." on path, just terminate path at last segment */
677 *previous_segment = '\0';
678 break;
679 default: /* Not a simple ".." segment, so skip over it */
680 previous_segment_removed = false;
681 }
682
683 if (previous_segment_removed)
684 {
685 /* The segment just removed was the first one in the path (optionally
686 * prefixed by a slash), so no more can be removed: stop. */
687 if (previous_segment < path_str + 2)
688 break;
689
690 /* Move back to slash before previous segment, or the start of the path */
691 slash = previous_segment - 1;
692 while (--slash >= path_str && *slash != '/')
693 ; /* Everything done in the while */
694 }
695 }
696 }
697 previous_segment = slash + 1;
698 slash = strchr(previous_segment, '/');
699 }
700}
701
702/*****************************************************************************/
703/* API functions */
704/*****************************************************************************/
705
706VC_URI_PARTS_T *vc_uri_create( void )
707{
708 VC_URI_PARTS_T *p_uri;
709
710 p_uri = (VC_URI_PARTS_T *)malloc(sizeof(VC_URI_PARTS_T));
711 if (p_uri)
712 {
713 memset(p_uri, 0, sizeof(VC_URI_PARTS_T));
714 }
715
716 return p_uri;
717}
718
719/*****************************************************************************/
720void vc_uri_clear( VC_URI_PARTS_T *p_uri )
721{
722 if (!p_uri)
723 return;
724
725 release_string(&p_uri->scheme);
726 release_string(&p_uri->userinfo);
727 release_string(&p_uri->host);
728 release_string(&p_uri->port);
729 release_string(&p_uri->path);
730 release_string(&p_uri->fragment);
731
732 if (p_uri->queries)
733 {
734 VC_URI_QUERY_T *queries = p_uri->queries;
735 uint32_t count = p_uri->num_queries;
736
737 while (count--)
738 {
739 release_string(&queries[count].name);
740 release_string(&queries[count].value);
741 }
742
743 free(queries);
744 p_uri->queries = NULL;
745 p_uri->num_queries = 0;
746 }
747}
748
749/*****************************************************************************/
750void vc_uri_release( VC_URI_PARTS_T *p_uri )
751{
752 if (!p_uri)
753 return;
754
755 vc_uri_clear(p_uri);
756
757 free(p_uri);
758}
759
760/*****************************************************************************/
761bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri )
762{
763 const char *marker;
764 uint32_t len;
765
766 if (!p_uri || !uri)
767 return false;
768
769 vc_uri_clear(p_uri);
770
771 /* URI = scheme ":" hier_part [ "?" query ] [ "#" fragment ] */
772
773 /* Find end of scheme, or another separator */
774 marker = vc_uri_find_delimiter(uri, SCHEME_DELIMITERS);
775
776 if (*marker == ':')
777 {
778 len = (marker - uri);
779 if (isalpha((int)*uri) && len == 1 && marker[1] == '\\')
780 {
781 /* Looks like a bare, absolute DOS/Windows filename with a drive letter */
782 /* coverity[double_free] Pointer freed and set to NULL */
783 bool ret = duplicate_string(uri, &p_uri->path);
784 vc_uri_set_path_extension(p_uri);
785 return ret;
786 }
787
788 p_uri->scheme = create_unescaped_string(uri, len);
789 if (!p_uri->scheme)
790 goto error;
791
792 to_lower_string(p_uri->scheme); /* Schemes should be handled case-insensitively */
793 uri = marker + 1;
794 }
795
796 if (uri[0] == '/' && uri[1] == '/') /* hier-part includes authority */
797 {
798 const char *userinfo_end = NULL;
799
800 /* authority = [ userinfo "@" ] host [ ":" port ] */
801 uri += 2;
802
803 marker = vc_uri_find_delimiter(uri, NETWORK_DELIMITERS);
804 if (*marker == '@')
805 {
806 userinfo_end = marker;
807 marker = vc_uri_find_delimiter(marker + 1, HOST_PORT_DELIMITERS);
808 }
809
810 if (!parse_authority(p_uri, uri, marker - uri, userinfo_end))
811 goto error;
812 uri = marker;
813 }
814
815 /* path */
816 marker = vc_uri_find_delimiter(uri, PATH_DELIMITERS);
817 len = marker - uri;
818 if (len)
819 {
820 p_uri->path = create_unescaped_string(uri, len);
821 vc_uri_set_path_extension(p_uri);
822 if (!p_uri->path)
823 goto error;
824 }
825
826 /* query */
827 if (*marker == '?')
828 {
829 uri = marker + 1;
830 marker = vc_uri_find_delimiter(uri, QUERY_DELIMITERS);
831 if (!parse_query(p_uri, uri, marker - uri))
832 goto error;
833 }
834
835 /* fragment */
836 if (*marker == '#')
837 {
838 uri = marker + 1;
839 p_uri->fragment = create_unescaped_string(uri, strlen(uri));
840 if (!p_uri->fragment)
841 goto error;
842 }
843
844 return true;
845
846error:
847 vc_uri_clear(p_uri);
848 return false;
849}
850
851/*****************************************************************************/
852uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size )
853{
854 uint32_t required_length;
855
856 if (!p_uri)
857 return 0;
858
859 required_length = calculate_uri_length(p_uri);
860 if (buffer && required_length < buffer_size) /* Allow for NUL */
861 build_uri(p_uri, buffer, buffer_size);
862
863 return required_length;
864}
865
866/*****************************************************************************/
867const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri )
868{
869 return p_uri ? p_uri->scheme : NULL;
870}
871
872/*****************************************************************************/
873const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri )
874{
875 return p_uri ? p_uri->userinfo : NULL;
876}
877
878/*****************************************************************************/
879const char *vc_uri_host( const VC_URI_PARTS_T *p_uri )
880{
881 return p_uri ? p_uri->host : NULL;
882}
883
884/*****************************************************************************/
885const char *vc_uri_port( const VC_URI_PARTS_T *p_uri )
886{
887 return p_uri ? p_uri->port : NULL;
888}
889
890/*****************************************************************************/
891const char *vc_uri_path( const VC_URI_PARTS_T *p_uri )
892{
893 return p_uri ? p_uri->path : NULL;
894}
895
896/*****************************************************************************/
897const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri )
898{
899 return p_uri ? p_uri->path_extension : NULL;
900}
901
902/*****************************************************************************/
903const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri )
904{
905 return p_uri ? p_uri->fragment : NULL;
906}
907
908/*****************************************************************************/
909uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri )
910{
911 return p_uri ? p_uri->num_queries : 0;
912}
913
914/*****************************************************************************/
915void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value )
916{
917 const char *name = NULL;
918 const char *value = NULL;
919
920 if (p_uri)
921 {
922 if (index < p_uri->num_queries)
923 {
924 name = p_uri->queries[index].name;
925 value = p_uri->queries[index].value;
926 }
927 }
928
929 if (p_name)
930 *p_name = name;
931 if (p_value)
932 *p_value = value;
933}
934
935/*****************************************************************************/
936bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value )
937{
938 unsigned int i = p_index ? *p_index : 0;
939
940 if (!p_uri)
941 return false;
942
943 for (; name && i < p_uri->num_queries; i++)
944 {
945 if (!strcmp(name, p_uri->queries[i].name))
946 {
947 if (p_value)
948 *p_value = p_uri->queries[i].value;
949 if (p_index)
950 *p_index = i;
951 return true;
952 }
953 }
954
955 return false;
956}
957
958/*****************************************************************************/
959bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme )
960{
961 return p_uri ? duplicate_string(scheme, &p_uri->scheme) : false;
962}
963
964/*****************************************************************************/
965bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo )
966{
967 return p_uri ? duplicate_string(userinfo, &p_uri->userinfo) : false;
968}
969
970/*****************************************************************************/
971bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host )
972{
973 return p_uri ? duplicate_string(host, &p_uri->host) : false;
974}
975
976/*****************************************************************************/
977bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port )
978{
979 return p_uri ? duplicate_string(port, &p_uri->port) : false;
980}
981
982/*****************************************************************************/
983bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path )
984{
985 bool ret = p_uri ? duplicate_string(path, &p_uri->path) : false;
986 vc_uri_set_path_extension(p_uri);
987 return ret;
988}
989
990/*****************************************************************************/
991bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment )
992{
993 return p_uri ? duplicate_string(fragment, &p_uri->fragment) : false;
994}
995
996/*****************************************************************************/
997bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value )
998{
999 VC_URI_QUERY_T *queries;
1000 uint32_t count;
1001
1002 if (!p_uri || !name)
1003 return false;
1004
1005 count = p_uri->num_queries;
1006 if (p_uri->queries)
1007 queries = (VC_URI_QUERY_T *)realloc(p_uri->queries, (count + 1) * sizeof(VC_URI_QUERY_T));
1008 else
1009 queries = (VC_URI_QUERY_T *)malloc(sizeof(VC_URI_QUERY_T));
1010
1011 if (!queries)
1012 return false;
1013
1014 /* Always store the pointer, in case it has changed, and even if we fail to copy name/value */
1015 p_uri->queries = queries;
1016 queries[count].name = NULL;
1017 queries[count].value = NULL;
1018
1019 if (duplicate_string(name, &queries[count].name))
1020 {
1021 if (duplicate_string(value, &queries[count].value))
1022 {
1023 /* Successful exit path */
1024 p_uri->num_queries++;
1025 return true;
1026 }
1027
1028 release_string(&queries[count].name);
1029 }
1030
1031 return false;
1032}
1033
1034/*****************************************************************************/
1035bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri )
1036{
1037 bool success = true;
1038 const char *relative_path;
1039
1040 /* If scheme is already set, the URI is already absolute */
1041 if (relative_uri->scheme)
1042 return true;
1043
1044 /* Otherwise, copy the base scheme */
1045 if (!duplicate_string(base_uri->scheme, &relative_uri->scheme))
1046 return false;
1047
1048 /* If any of the network info is set, use the rest of the relative URI as-is */
1049 if (relative_uri->host || relative_uri->port || relative_uri->userinfo)
1050 return true;
1051
1052 /* Otherwise, copy the base network info */
1053 if (!duplicate_string(base_uri->host, &relative_uri->host) ||
1054 !duplicate_string(base_uri->port, &relative_uri->port) ||
1055 !duplicate_string(base_uri->userinfo, &relative_uri->userinfo))
1056 return false;
1057
1058 relative_path = relative_uri->path;
1059
1060 if (!relative_path || !*relative_path)
1061 {
1062 /* No relative path (could be queries and/or fragment), so take base path */
1063 success = vc_uri_copy_base_path(base_uri, relative_uri);
1064 }
1065 else if (*relative_path != '/')
1066 {
1067 const char *base_path = base_uri->path;
1068 char *merged_path;
1069 char *slash;
1070 size_t len;
1071
1072 /* Path is relative, merge in with base path */
1073 if (!base_path || !*base_path)
1074 {
1075 if (relative_uri->host || relative_uri->port || relative_uri->userinfo)
1076 base_path = "/"; /* Need a separator to split network info from path */
1077 else
1078 base_path = "";
1079 }
1080
1081 len = strlen(base_path) + strlen(relative_path) + 1;
1082
1083 /* Allocate space for largest possible combined path */
1084 merged_path = (char *)malloc(len);
1085 if (!merged_path)
1086 return false;
1087
1088 strncpy(merged_path, base_path, len);
1089
1090 slash = strrchr(merged_path, '/'); /* Note: reverse search */
1091 if (*relative_path == ';')
1092 {
1093 char *semi;
1094
1095 /* Relative path is just parameters, so remove any base parameters in final segment */
1096 if (!slash)
1097 slash = merged_path;
1098 semi = strchr(slash, ';');
1099 if (semi)
1100 semi[0] = '\0';
1101 } else {
1102 /* Remove final segment */
1103 if (slash)
1104 slash[1] = '\0';
1105 else
1106 merged_path[0] = '\0';
1107 }
1108 strncat(merged_path, relative_path, len - strlen(merged_path) - 1);
1109
1110 vc_uri_remove_single_dot_segments(merged_path);
1111 vc_uri_remove_double_dot_segments(merged_path);
1112
1113 success = duplicate_string(merged_path, &relative_uri->path);
1114
1115 free(merged_path);
1116 }
1117 /* Otherwise path is absolute, which can be left as-is */
1118
1119 return success;
1120}
1121