1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, 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 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON 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
25 SOFTWARE, 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 
38 typedef struct VC_URI_QUERY_T
39 {
40    char *name;
41    char *value;
42 } VC_URI_QUERY_T;
43 
44 struct 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 
57 typedef const uint32_t *RESERVED_CHARS_TABLE_T;
58 
59 /** Reserved character table for scheme component
60  * Controls, space, !"#$%&'()*,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */
61 static 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. */
67 static 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. */
73 static 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. */
79 static 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. */
85 static 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. */
91 static 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. */
97 static 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 
to_hex(int v)113 static char to_hex(int v)
114 {
115    if (v > 9)
116       return 'A' + v - 10;
117    return '0' + v;
118 }
119 
120 /*****************************************************************************/
from_hex(const char * str,uint32_t str_len)121 static 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 /*****************************************************************************/
escaped_length(const char * str,RESERVED_CHARS_TABLE_T reserved)143 static 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 /*****************************************************************************/
escape_string(const char * str,char * escaped,RESERVED_CHARS_TABLE_T reserved)165 static 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 /*****************************************************************************/
unescaped_length(const char * str,uint32_t str_len)192 static 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 /*****************************************************************************/
unescape_string(const char * str,uint32_t str_len,char * unescaped)211 static 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 /*****************************************************************************/
create_unescaped_string(const char * escstr,uint32_t esclen)232 static 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 /*****************************************************************************/
duplicate_string(const char * src,char ** p_dst)244 static 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 /*****************************************************************************/
release_string(char ** str)265 static void release_string( char **str )
266 {
267    if (*str)
268    {
269       free(*str);
270       *str = NULL;
271    }
272 }
273 
274 /*****************************************************************************/
to_lower_string(char * str)275 static 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 /*****************************************************************************/
vc_uri_find_delimiter(const char * str,const char * delimiters)288 static 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 /*****************************************************************************/
vc_uri_set_path_extension(VC_URI_PARTS_T * p_uri)304 static 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 /*****************************************************************************/
parse_authority(VC_URI_PARTS_T * p_uri,const char * str,uint32_t str_len,const char * userinfo_end)325 static 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 /*****************************************************************************/
store_query(VC_URI_PARTS_T * p_uri,const char * name_start,const char * equals_ptr,const char * query_end)380 static 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 /*****************************************************************************/
parse_query(VC_URI_PARTS_T * p_uri,const char * str,uint32_t str_len)426 static 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 /*****************************************************************************/
calculate_uri_length(const VC_URI_PARTS_T * p_uri)480 static 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 /*****************************************************************************/
build_uri(const VC_URI_PARTS_T * p_uri,char * buffer,size_t buffer_size)528 static 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 /*****************************************************************************/
vc_uri_copy_base_path(const VC_URI_PARTS_T * base_uri,VC_URI_PARTS_T * relative_uri)599 static 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 /*****************************************************************************/
vc_uri_remove_single_dot_segments(char * path_str)627 static 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 /*****************************************************************************/
vc_uri_remove_double_dot_segments(char * path_str)652 static 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 
vc_uri_create(void)706 VC_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 /*****************************************************************************/
vc_uri_clear(VC_URI_PARTS_T * p_uri)720 void 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 /*****************************************************************************/
vc_uri_release(VC_URI_PARTS_T * p_uri)750 void 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 /*****************************************************************************/
vc_uri_parse(VC_URI_PARTS_T * p_uri,const char * uri)761 bool 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 
846 error:
847    vc_uri_clear(p_uri);
848    return false;
849 }
850 
851 /*****************************************************************************/
vc_uri_build(const VC_URI_PARTS_T * p_uri,char * buffer,size_t buffer_size)852 uint32_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 /*****************************************************************************/
vc_uri_scheme(const VC_URI_PARTS_T * p_uri)867 const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri )
868 {
869    return p_uri ? p_uri->scheme : NULL;
870 }
871 
872 /*****************************************************************************/
vc_uri_userinfo(const VC_URI_PARTS_T * p_uri)873 const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri )
874 {
875    return p_uri ? p_uri->userinfo : NULL;
876 }
877 
878 /*****************************************************************************/
vc_uri_host(const VC_URI_PARTS_T * p_uri)879 const char *vc_uri_host( const VC_URI_PARTS_T *p_uri )
880 {
881    return p_uri ? p_uri->host : NULL;
882 }
883 
884 /*****************************************************************************/
vc_uri_port(const VC_URI_PARTS_T * p_uri)885 const char *vc_uri_port( const VC_URI_PARTS_T *p_uri )
886 {
887    return p_uri ? p_uri->port : NULL;
888 }
889 
890 /*****************************************************************************/
vc_uri_path(const VC_URI_PARTS_T * p_uri)891 const char *vc_uri_path( const VC_URI_PARTS_T *p_uri )
892 {
893    return p_uri ? p_uri->path : NULL;
894 }
895 
896 /*****************************************************************************/
vc_uri_path_extension(const VC_URI_PARTS_T * p_uri)897 const 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 /*****************************************************************************/
vc_uri_fragment(const VC_URI_PARTS_T * p_uri)903 const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri )
904 {
905    return p_uri ? p_uri->fragment : NULL;
906 }
907 
908 /*****************************************************************************/
vc_uri_num_queries(const VC_URI_PARTS_T * p_uri)909 uint32_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 /*****************************************************************************/
vc_uri_query(const VC_URI_PARTS_T * p_uri,uint32_t index,const char ** p_name,const char ** p_value)915 void 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 /*****************************************************************************/
vc_uri_find_query(VC_URI_PARTS_T * p_uri,uint32_t * p_index,const char * name,const char ** p_value)936 bool 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 /*****************************************************************************/
vc_uri_set_scheme(VC_URI_PARTS_T * p_uri,const char * scheme)959 bool 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 /*****************************************************************************/
vc_uri_set_userinfo(VC_URI_PARTS_T * p_uri,const char * userinfo)965 bool 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 /*****************************************************************************/
vc_uri_set_host(VC_URI_PARTS_T * p_uri,const char * host)971 bool 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 /*****************************************************************************/
vc_uri_set_port(VC_URI_PARTS_T * p_uri,const char * port)977 bool 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 /*****************************************************************************/
vc_uri_set_path(VC_URI_PARTS_T * p_uri,const char * path)983 bool 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 /*****************************************************************************/
vc_uri_set_fragment(VC_URI_PARTS_T * p_uri,const char * fragment)991 bool 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 /*****************************************************************************/
vc_uri_add_query(VC_URI_PARTS_T * p_uri,const char * name,const char * value)997 bool 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 /*****************************************************************************/
vc_uri_merge(const VC_URI_PARTS_T * base_uri,VC_URI_PARTS_T * relative_uri)1035 bool 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