1/* GLIB - Library of useful routines for C programming
2 * Copyright © 2020 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19#include "config.h"
20
21#include <stdlib.h>
22#include <string.h>
23
24#include "glib.h"
25#include "glibintl.h"
26#include "guriprivate.h"
27
28/**
29 * SECTION:guri
30 * @short_description: URI-handling utilities
31 * @include: glib.h
32 *
33 * The #GUri type and related functions can be used to parse URIs into
34 * their components, and build valid URIs from individual components.
35 *
36 * Note that #GUri scope is to help manipulate URIs in various applications,
37 * following [RFC 3986](https://tools.ietf.org/html/rfc3986). In particular,
38 * it doesn't intend to cover web browser needs, and doesn't implement the
39 * [WHATWG URL](https://url.spec.whatwg.org/) standard. No APIs are provided to
40 * help prevent
41 * [homograph attacks](https://en.wikipedia.org/wiki/IDN_homograph_attack), so
42 * #GUri is not suitable for formatting URIs for display to the user for making
43 * security-sensitive decisions.
44 *
45 * ## Relative and absolute URIs # {#relative-absolute-uris}
46 *
47 * As defined in [RFC 3986](https://tools.ietf.org/html/rfc3986#section-4), the
48 * hierarchical nature of URIs means that they can either be ‘relative
49 * references’ (sometimes referred to as ‘relative URIs’) or ‘URIs’ (for
50 * clarity, ‘URIs’ are referred to in this documentation as
51 * ‘absolute URIs’ — although
52 * [in constrast to RFC 3986](https://tools.ietf.org/html/rfc3986#section-4.3),
53 * fragment identifiers are always allowed).
54 *
55 * Relative references have one or more components of the URI missing. In
56 * particular, they have no scheme. Any other component, such as hostname,
57 * query, etc. may be missing, apart from a path, which has to be specified (but
58 * may be empty). The path may be relative, starting with `./` rather than `/`.
59 *
60 * For example, a valid relative reference is `./path?query`,
61 * `/?query#fragment` or `//example.com`.
62 *
63 * Absolute URIs have a scheme specified. Any other components of the URI which
64 * are missing are specified as explicitly unset in the URI, rather than being
65 * resolved relative to a base URI using g_uri_parse_relative().
66 *
67 * For example, a valid absolute URI is `file:///home/bob` or
68 * `https://search.com?query=string`.
69 *
70 * A #GUri instance is always an absolute URI. A string may be an absolute URI
71 * or a relative reference; see the documentation for individual functions as to
72 * what forms they accept.
73 *
74 * ## Parsing URIs
75 *
76 * The most minimalist APIs for parsing URIs are g_uri_split() and
77 * g_uri_split_with_user(). These split a URI into its component
78 * parts, and return the parts; the difference between the two is that
79 * g_uri_split() treats the ‘userinfo’ component of the URI as a
80 * single element, while g_uri_split_with_user() can (depending on the
81 * #GUriFlags you pass) treat it as containing a username, password,
82 * and authentication parameters. Alternatively, g_uri_split_network()
83 * can be used when you are only interested in the components that are
84 * needed to initiate a network connection to the service (scheme,
85 * host, and port).
86 *
87 * g_uri_parse() is similar to g_uri_split(), but instead of returning
88 * individual strings, it returns a #GUri structure (and it requires
89 * that the URI be an absolute URI).
90 *
91 * g_uri_resolve_relative() and g_uri_parse_relative() allow you to
92 * resolve a relative URI relative to a base URI.
93 * g_uri_resolve_relative() takes two strings and returns a string,
94 * and g_uri_parse_relative() takes a #GUri and a string and returns a
95 * #GUri.
96 *
97 * All of the parsing functions take a #GUriFlags argument describing
98 * exactly how to parse the URI; see the documentation for that type
99 * for more details on the specific flags that you can pass. If you
100 * need to choose different flags based on the type of URI, you can
101 * use g_uri_peek_scheme() on the URI string to check the scheme
102 * first, and use that to decide what flags to parse it with.
103 *
104 * For example, you might want to use %G_URI_PARAMS_WWW_FORM when parsing the
105 * params for a web URI, so compare the result of g_uri_peek_scheme() against
106 * `http` and `https`.
107 *
108 * ## Building URIs
109 *
110 * g_uri_join() and g_uri_join_with_user() can be used to construct
111 * valid URI strings from a set of component strings. They are the
112 * inverse of g_uri_split() and g_uri_split_with_user().
113 *
114 * Similarly, g_uri_build() and g_uri_build_with_user() can be used to
115 * construct a #GUri from a set of component strings.
116 *
117 * As with the parsing functions, the building functions take a
118 * #GUriFlags argument. In particular, it is important to keep in mind
119 * whether the URI components you are using are already `%`-encoded. If so,
120 * you must pass the %G_URI_FLAGS_ENCODED flag.
121 *
122 * ## `file://` URIs
123 *
124 * Note that Windows and Unix both define special rules for parsing
125 * `file://` URIs (involving non-UTF-8 character sets on Unix, and the
126 * interpretation of path separators on Windows). #GUri does not
127 * implement these rules. Use g_filename_from_uri() and
128 * g_filename_to_uri() if you want to properly convert between
129 * `file://` URIs and local filenames.
130 *
131 * ## URI Equality
132 *
133 * Note that there is no `g_uri_equal ()` function, because comparing
134 * URIs usefully requires scheme-specific knowledge that #GUri does
135 * not have. #GUri can help with normalization if you use the various
136 * encoded #GUriFlags as well as %G_URI_FLAGS_SCHEME_NORMALIZE however
137 * it is not comprehensive.
138 * For example, `data:,foo` and `data:;base64,Zm9v` resolve to the same
139 * thing according to the `data:` URI specification which GLib does not
140 * handle.
141 *
142 * Since: 2.66
143 */
144
145/**
146 * GUri:
147 *
148 * A parsed absolute URI.
149 *
150 * Since #GUri only represents absolute URIs, all #GUris will have a
151 * URI scheme, so g_uri_get_scheme() will always return a non-%NULL
152 * answer. Likewise, by definition, all URIs have a path component, so
153 * g_uri_get_path() will always return a non-%NULL string (which may be empty).
154 *
155 * If the URI string has an
156 * [‘authority’ component](https://tools.ietf.org/html/rfc3986#section-3) (that
157 * is, if the scheme is followed by `://` rather than just `:`), then the
158 * #GUri will contain a hostname, and possibly a port and ‘userinfo’.
159 * Additionally, depending on how the #GUri was constructed/parsed (for example,
160 * using the %G_URI_FLAGS_HAS_PASSWORD and %G_URI_FLAGS_HAS_AUTH_PARAMS flags),
161 * the userinfo may be split out into a username, password, and
162 * additional authorization-related parameters.
163 *
164 * Normally, the components of a #GUri will have all `%`-encoded
165 * characters decoded. However, if you construct/parse a #GUri with
166 * %G_URI_FLAGS_ENCODED, then the `%`-encoding will be preserved instead in
167 * the userinfo, path, and query fields (and in the host field if also
168 * created with %G_URI_FLAGS_NON_DNS). In particular, this is necessary if
169 * the URI may contain binary data or non-UTF-8 text, or if decoding
170 * the components might change the interpretation of the URI.
171 *
172 * For example, with the encoded flag:
173 *
174 * |[<!-- language="C" -->
175 * g_autoptr(GUri) uri = g_uri_parse ("http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fparam%3Dvalue", G_URI_FLAGS_ENCODED, &err);
176 * g_assert_cmpstr (g_uri_get_query (uri), ==, "query=http%3A%2F%2Fhost%2Fpath%3Fparam%3Dvalue");
177 * ]|
178 *
179 * While the default `%`-decoding behaviour would give:
180 *
181 * |[<!-- language="C" -->
182 * g_autoptr(GUri) uri = g_uri_parse ("http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fparam%3Dvalue", G_URI_FLAGS_NONE, &err);
183 * g_assert_cmpstr (g_uri_get_query (uri), ==, "query=http://host/path?param=value");
184 * ]|
185 *
186 * During decoding, if an invalid UTF-8 string is encountered, parsing will fail
187 * with an error indicating the bad string location:
188 *
189 * |[<!-- language="C" -->
190 * g_autoptr(GUri) uri = g_uri_parse ("http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fbad%3D%00alue", G_URI_FLAGS_NONE, &err);
191 * g_assert_error (err, G_URI_ERROR, G_URI_ERROR_BAD_QUERY);
192 * ]|
193 *
194 * You should pass %G_URI_FLAGS_ENCODED or %G_URI_FLAGS_ENCODED_QUERY if you
195 * need to handle that case manually. In particular, if the query string
196 * contains `=` characters that are `%`-encoded, you should let
197 * g_uri_parse_params() do the decoding once of the query.
198 *
199 * #GUri is immutable once constructed, and can safely be accessed from
200 * multiple threads. Its reference counting is atomic.
201 *
202 * Since: 2.66
203 */
204struct _GUri {
205 gchar *scheme;
206 gchar *userinfo;
207 gchar *host;
208 gint port;
209 gchar *path;
210 gchar *query;
211 gchar *fragment;
212
213 gchar *user;
214 gchar *password;
215 gchar *auth_params;
216
217 GUriFlags flags;
218};
219
220/**
221 * g_uri_ref: (skip)
222 * @uri: a #GUri
223 *
224 * Increments the reference count of @uri by one.
225 *
226 * Returns: @uri
227 *
228 * Since: 2.66
229 */
230GUri *
231g_uri_ref (GUri *uri)
232{
233 g_return_val_if_fail (uri != NULL, NULL);
234
235 return g_atomic_rc_box_acquire (uri);
236}
237
238static void
239g_uri_clear (GUri *uri)
240{
241 g_free (mem: uri->scheme);
242 g_free (mem: uri->userinfo);
243 g_free (mem: uri->host);
244 g_free (mem: uri->path);
245 g_free (mem: uri->query);
246 g_free (mem: uri->fragment);
247 g_free (mem: uri->user);
248 g_free (mem: uri->password);
249 g_free (mem: uri->auth_params);
250}
251
252/**
253 * g_uri_unref: (skip)
254 * @uri: a #GUri
255 *
256 * Atomically decrements the reference count of @uri by one.
257 *
258 * When the reference count reaches zero, the resources allocated by
259 * @uri are freed
260 *
261 * Since: 2.66
262 */
263void
264g_uri_unref (GUri *uri)
265{
266 g_return_if_fail (uri != NULL);
267
268 g_atomic_rc_box_release_full (mem_block: uri, clear_func: (GDestroyNotify)g_uri_clear);
269}
270
271static gboolean
272g_uri_char_is_unreserved (gchar ch)
273{
274 if (g_ascii_isalnum (ch))
275 return TRUE;
276 return ch == '-' || ch == '.' || ch == '_' || ch == '~';
277}
278
279#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
280#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
281
282static gssize
283uri_decoder (gchar **out,
284 const gchar *illegal_chars,
285 const gchar *start,
286 gsize length,
287 gboolean just_normalize,
288 gboolean www_form,
289 GUriFlags flags,
290 GUriError parse_error,
291 GError **error)
292{
293 gchar c;
294 GString *decoded;
295 const gchar *invalid, *s, *end;
296 gssize len;
297
298 if (!(flags & G_URI_FLAGS_ENCODED))
299 just_normalize = FALSE;
300
301 decoded = g_string_sized_new (dfl_size: length + 1);
302 for (s = start, end = s + length; s < end; s++)
303 {
304 if (*s == '%')
305 {
306 if (s + 2 >= end ||
307 !g_ascii_isxdigit (s[1]) ||
308 !g_ascii_isxdigit (s[2]))
309 {
310 /* % followed by non-hex or the end of the string; this is an error */
311 if (!(flags & G_URI_FLAGS_PARSE_RELAXED))
312 {
313 g_set_error_literal (err: error, G_URI_ERROR, code: parse_error,
314 /* xgettext: no-c-format */
315 _("Invalid %-encoding in URI"));
316 g_string_free (string: decoded, TRUE);
317 return -1;
318 }
319
320 /* In non-strict mode, just let it through; we *don't*
321 * fix it to "%25", since that might change the way that
322 * the URI's owner would interpret it.
323 */
324 g_string_append_c (decoded, *s);
325 continue;
326 }
327
328 c = HEXCHAR (s);
329 if (illegal_chars && strchr (s: illegal_chars, c: c))
330 {
331 g_set_error_literal (err: error, G_URI_ERROR, code: parse_error,
332 _("Illegal character in URI"));
333 g_string_free (string: decoded, TRUE);
334 return -1;
335 }
336 if (just_normalize && !g_uri_char_is_unreserved (ch: c))
337 {
338 /* Leave the % sequence there but normalize it. */
339 g_string_append_c (decoded, *s);
340 g_string_append_c (decoded, g_ascii_toupper (s[1]));
341 g_string_append_c (decoded, g_ascii_toupper (s[2]));
342 s += 2;
343 }
344 else
345 {
346 g_string_append_c (decoded, c);
347 s += 2;
348 }
349 }
350 else if (www_form && *s == '+')
351 g_string_append_c (decoded, ' ');
352 /* Normalize any illegal characters. */
353 else if (just_normalize && (!g_ascii_isgraph (*s)))
354 g_string_append_printf (string: decoded, format: "%%%02X", (guchar)*s);
355 else
356 g_string_append_c (decoded, *s);
357 }
358
359 len = decoded->len;
360 g_assert (len >= 0);
361
362 if (!(flags & G_URI_FLAGS_ENCODED) &&
363 !g_utf8_validate (str: decoded->str, max_len: len, end: &invalid))
364 {
365 g_set_error_literal (err: error, G_URI_ERROR, code: parse_error,
366 _("Non-UTF-8 characters in URI"));
367 g_string_free (string: decoded, TRUE);
368 return -1;
369 }
370
371 if (out)
372 *out = g_string_free (string: decoded, FALSE);
373 else
374 g_string_free (string: decoded, TRUE);
375
376 return len;
377}
378
379static gboolean
380uri_decode (gchar **out,
381 const gchar *illegal_chars,
382 const gchar *start,
383 gsize length,
384 gboolean www_form,
385 GUriFlags flags,
386 GUriError parse_error,
387 GError **error)
388{
389 return uri_decoder (out, illegal_chars, start, length, FALSE, www_form, flags,
390 parse_error, error) != -1;
391}
392
393static gboolean
394uri_normalize (gchar **out,
395 const gchar *start,
396 gsize length,
397 GUriFlags flags,
398 GUriError parse_error,
399 GError **error)
400{
401 return uri_decoder (out, NULL, start, length, TRUE, FALSE, flags,
402 parse_error, error) != -1;
403}
404
405static gboolean
406is_valid (guchar c,
407 const gchar *reserved_chars_allowed)
408{
409 if (g_uri_char_is_unreserved (ch: c))
410 return TRUE;
411
412 if (reserved_chars_allowed && strchr (s: reserved_chars_allowed, c: c))
413 return TRUE;
414
415 return FALSE;
416}
417
418void
419_uri_encoder (GString *out,
420 const guchar *start,
421 gsize length,
422 const gchar *reserved_chars_allowed,
423 gboolean allow_utf8)
424{
425 static const gchar hex[16] = "0123456789ABCDEF";
426 const guchar *p = start;
427 const guchar *end = p + length;
428
429 while (p < end)
430 {
431 gunichar multibyte_utf8_char = 0;
432
433 if (allow_utf8 && *p >= 0x80)
434 multibyte_utf8_char = g_utf8_get_char_validated (p: (gchar *)p, max_len: end - p);
435
436 if (multibyte_utf8_char > 0 &&
437 multibyte_utf8_char != (gunichar) -1 && multibyte_utf8_char != (gunichar) -2)
438 {
439 gint len = g_utf8_skip [*p];
440 g_string_append_len (string: out, val: (gchar *)p, len);
441 p += len;
442 }
443 else if (is_valid (c: *p, reserved_chars_allowed))
444 {
445 g_string_append_c (out, *p);
446 p++;
447 }
448 else
449 {
450 g_string_append_c (out, '%');
451 g_string_append_c (out, hex[*p >> 4]);
452 g_string_append_c (out, hex[*p & 0xf]);
453 p++;
454 }
455 }
456}
457
458/* Parse the IP-literal construction from RFC 6874 (which extends RFC 3986 to
459 * support IPv6 zone identifiers.
460 *
461 * Currently, IP versions beyond 6 (i.e. the IPvFuture rule) are unsupported.
462 * There’s no point supporting them until (a) they exist and (b) the rest of the
463 * stack (notably, sockets) supports them.
464 *
465 * Rules:
466 *
467 * IP-literal = "[" ( IPv6address / IPv6addrz / IPvFuture ) "]"
468 *
469 * ZoneID = 1*( unreserved / pct-encoded )
470 *
471 * IPv6addrz = IPv6address "%25" ZoneID
472 *
473 * If %G_URI_FLAGS_PARSE_RELAXED is specified, this function also accepts:
474 *
475 * IPv6addrz = IPv6address "%" ZoneID
476 */
477static gboolean
478parse_ip_literal (const gchar *start,
479 gsize length,
480 GUriFlags flags,
481 gchar **out,
482 GError **error)
483{
484 gchar *pct, *zone_id = NULL;
485 gchar *addr = NULL;
486 gsize addr_length = 0;
487 gsize zone_id_length = 0;
488 gchar *decoded_zone_id = NULL;
489
490 if (start[length - 1] != ']')
491 goto bad_ipv6_literal;
492
493 /* Drop the square brackets */
494 addr = g_strndup (str: start + 1, n: length - 2);
495 addr_length = length - 2;
496
497 /* If there's an IPv6 scope ID, split out the zone. */
498 pct = strchr (s: addr, c: '%');
499 if (pct != NULL)
500 {
501 *pct = '\0';
502
503 if (addr_length - (pct - addr) >= 4 &&
504 *(pct + 1) == '2' && *(pct + 2) == '5')
505 {
506 zone_id = pct + 3;
507 zone_id_length = addr_length - (zone_id - addr);
508 }
509 else if (flags & G_URI_FLAGS_PARSE_RELAXED &&
510 addr_length - (pct - addr) >= 2)
511 {
512 zone_id = pct + 1;
513 zone_id_length = addr_length - (zone_id - addr);
514 }
515 else
516 goto bad_ipv6_literal;
517
518 g_assert (zone_id_length >= 1);
519 }
520
521 /* addr must be an IPv6 address */
522 if (!g_hostname_is_ip_address (hostname: addr) || !strchr (s: addr, c: ':'))
523 goto bad_ipv6_literal;
524
525 /* Zone ID must be valid. It can contain %-encoded characters. */
526 if (zone_id != NULL &&
527 !uri_decode (out: &decoded_zone_id, NULL, start: zone_id, length: zone_id_length, FALSE,
528 flags, parse_error: G_URI_ERROR_BAD_HOST, NULL))
529 goto bad_ipv6_literal;
530
531 /* Success */
532 if (out != NULL && decoded_zone_id != NULL)
533 *out = g_strconcat (string1: addr, "%", decoded_zone_id, NULL);
534 else if (out != NULL)
535 *out = g_steal_pointer (&addr);
536
537 g_free (mem: addr);
538 g_free (mem: decoded_zone_id);
539
540 return TRUE;
541
542bad_ipv6_literal:
543 g_free (mem: addr);
544 g_free (mem: decoded_zone_id);
545 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_HOST,
546 _("Invalid IPv6 address ‘%.*s’ in URI"),
547 (gint)length, start);
548
549 return FALSE;
550}
551
552static gboolean
553parse_host (const gchar *start,
554 gsize length,
555 GUriFlags flags,
556 gchar **out,
557 GError **error)
558{
559 gchar *decoded = NULL, *host;
560 gchar *addr = NULL;
561
562 if (*start == '[')
563 {
564 if (!parse_ip_literal (start, length, flags, out: &host, error))
565 return FALSE;
566 goto ok;
567 }
568
569 if (g_ascii_isdigit (*start))
570 {
571 addr = g_strndup (str: start, n: length);
572 if (g_hostname_is_ip_address (hostname: addr))
573 {
574 host = addr;
575 goto ok;
576 }
577 g_free (mem: addr);
578 }
579
580 if (flags & G_URI_FLAGS_NON_DNS)
581 {
582 if (!uri_normalize (out: &decoded, start, length, flags,
583 parse_error: G_URI_ERROR_BAD_HOST, error))
584 return FALSE;
585 host = g_steal_pointer (&decoded);
586 goto ok;
587 }
588
589 flags &= ~G_URI_FLAGS_ENCODED;
590 if (!uri_decode (out: &decoded, NULL, start, length, FALSE, flags,
591 parse_error: G_URI_ERROR_BAD_HOST, error))
592 return FALSE;
593
594 /* You're not allowed to %-encode an IP address, so if it wasn't
595 * one before, it better not be one now.
596 */
597 if (g_hostname_is_ip_address (hostname: decoded))
598 {
599 g_free (mem: decoded);
600 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_HOST,
601 _("Illegal encoded IP address ‘%.*s’ in URI"),
602 (gint)length, start);
603 return FALSE;
604 }
605
606 if (g_hostname_is_non_ascii (hostname: decoded))
607 {
608 host = g_hostname_to_ascii (hostname: decoded);
609 if (host == NULL)
610 {
611 g_free (mem: decoded);
612 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_HOST,
613 _("Illegal internationalized hostname ‘%.*s’ in URI"),
614 (gint) length, start);
615 return FALSE;
616 }
617 }
618 else
619 {
620 host = g_steal_pointer (&decoded);
621 }
622
623 ok:
624 if (out)
625 *out = g_steal_pointer (&host);
626 g_free (mem: host);
627 g_free (mem: decoded);
628
629 return TRUE;
630}
631
632static gboolean
633parse_port (const gchar *start,
634 gsize length,
635 gint *out,
636 GError **error)
637{
638 gchar *end;
639 gulong parsed_port;
640
641 /* strtoul() allows leading + or -, so we have to check this first. */
642 if (!g_ascii_isdigit (*start))
643 {
644 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_PORT,
645 _("Could not parse port ‘%.*s’ in URI"),
646 (gint)length, start);
647 return FALSE;
648 }
649
650 /* We know that *(start + length) is either '\0' or a non-numeric
651 * character, so strtoul() won't scan beyond it.
652 */
653 parsed_port = strtoul (nptr: start, endptr: &end, base: 10);
654 if (end != start + length)
655 {
656 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_PORT,
657 _("Could not parse port ‘%.*s’ in URI"),
658 (gint)length, start);
659 return FALSE;
660 }
661 else if (parsed_port > 65535)
662 {
663 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_PORT,
664 _("Port ‘%.*s’ in URI is out of range"),
665 (gint)length, start);
666 return FALSE;
667 }
668
669 if (out)
670 *out = parsed_port;
671 return TRUE;
672}
673
674static gboolean
675parse_userinfo (const gchar *start,
676 gsize length,
677 GUriFlags flags,
678 gchar **user,
679 gchar **password,
680 gchar **auth_params,
681 GError **error)
682{
683 const gchar *user_end = NULL, *password_end = NULL, *auth_params_end;
684
685 auth_params_end = start + length;
686 if (flags & G_URI_FLAGS_HAS_AUTH_PARAMS)
687 password_end = memchr (s: start, c: ';', n: auth_params_end - start);
688 if (!password_end)
689 password_end = auth_params_end;
690 if (flags & G_URI_FLAGS_HAS_PASSWORD)
691 user_end = memchr (s: start, c: ':', n: password_end - start);
692 if (!user_end)
693 user_end = password_end;
694
695 if (!uri_normalize (out: user, start, length: user_end - start, flags,
696 parse_error: G_URI_ERROR_BAD_USER, error))
697 return FALSE;
698
699 if (*user_end == ':')
700 {
701 start = user_end + 1;
702 if (!uri_normalize (out: password, start, length: password_end - start, flags,
703 parse_error: G_URI_ERROR_BAD_PASSWORD, error))
704 {
705 if (user)
706 g_clear_pointer (user, g_free);
707 return FALSE;
708 }
709 }
710 else if (password)
711 *password = NULL;
712
713 if (*password_end == ';')
714 {
715 start = password_end + 1;
716 if (!uri_normalize (out: auth_params, start, length: auth_params_end - start, flags,
717 parse_error: G_URI_ERROR_BAD_AUTH_PARAMS, error))
718 {
719 if (user)
720 g_clear_pointer (user, g_free);
721 if (password)
722 g_clear_pointer (password, g_free);
723 return FALSE;
724 }
725 }
726 else if (auth_params)
727 *auth_params = NULL;
728
729 return TRUE;
730}
731
732static gchar *
733uri_cleanup (const gchar *uri_string)
734{
735 GString *copy;
736 const gchar *end;
737
738 /* Skip leading whitespace */
739 while (g_ascii_isspace (*uri_string))
740 uri_string++;
741
742 /* Ignore trailing whitespace */
743 end = uri_string + strlen (s: uri_string);
744 while (end > uri_string && g_ascii_isspace (*(end - 1)))
745 end--;
746
747 /* Copy the rest, encoding unencoded spaces and stripping other whitespace */
748 copy = g_string_sized_new (dfl_size: end - uri_string);
749 while (uri_string < end)
750 {
751 if (*uri_string == ' ')
752 g_string_append (string: copy, val: "%20");
753 else if (g_ascii_isspace (*uri_string))
754 ;
755 else
756 g_string_append_c (copy, *uri_string);
757 uri_string++;
758 }
759
760 return g_string_free (string: copy, FALSE);
761}
762
763static gboolean
764should_normalize_empty_path (const char *scheme)
765{
766 const char * const schemes[] = { "https", "http", "wss", "ws" };
767 gsize i;
768 for (i = 0; i < G_N_ELEMENTS (schemes); ++i)
769 {
770 if (!strcmp (s1: schemes[i], s2: scheme))
771 return TRUE;
772 }
773 return FALSE;
774}
775
776static int
777normalize_port (const char *scheme,
778 int port)
779{
780 const char *default_schemes[3] = { NULL };
781 int i;
782
783 switch (port)
784 {
785 case 21:
786 default_schemes[0] = "ftp";
787 break;
788 case 80:
789 default_schemes[0] = "http";
790 default_schemes[1] = "ws";
791 break;
792 case 443:
793 default_schemes[0] = "https";
794 default_schemes[1] = "wss";
795 break;
796 default:
797 break;
798 }
799
800 for (i = 0; default_schemes[i]; ++i)
801 {
802 if (!strcmp (s1: scheme, s2: default_schemes[i]))
803 return -1;
804 }
805
806 return port;
807}
808
809static int
810default_scheme_port (const char *scheme)
811{
812 if (strcmp (s1: scheme, s2: "http") == 0 || strcmp (s1: scheme, s2: "ws") == 0)
813 return 80;
814
815 if (strcmp (s1: scheme, s2: "https") == 0 || strcmp (s1: scheme, s2: "wss") == 0)
816 return 443;
817
818 if (strcmp (s1: scheme, s2: "ftp") == 0)
819 return 21;
820
821 return -1;
822}
823
824static gboolean
825g_uri_split_internal (const gchar *uri_string,
826 GUriFlags flags,
827 gchar **scheme,
828 gchar **userinfo,
829 gchar **user,
830 gchar **password,
831 gchar **auth_params,
832 gchar **host,
833 gint *port,
834 gchar **path,
835 gchar **query,
836 gchar **fragment,
837 GError **error)
838{
839 const gchar *end, *colon, *at, *path_start, *semi, *question;
840 const gchar *p, *bracket, *hostend;
841 gchar *cleaned_uri_string = NULL;
842 gchar *normalized_scheme = NULL;
843
844 if (scheme)
845 *scheme = NULL;
846 if (userinfo)
847 *userinfo = NULL;
848 if (user)
849 *user = NULL;
850 if (password)
851 *password = NULL;
852 if (auth_params)
853 *auth_params = NULL;
854 if (host)
855 *host = NULL;
856 if (port)
857 *port = -1;
858 if (path)
859 *path = NULL;
860 if (query)
861 *query = NULL;
862 if (fragment)
863 *fragment = NULL;
864
865 if ((flags & G_URI_FLAGS_PARSE_RELAXED) && strpbrk (s: uri_string, accept: " \t\n\r"))
866 {
867 cleaned_uri_string = uri_cleanup (uri_string);
868 uri_string = cleaned_uri_string;
869 }
870
871 /* Find scheme */
872 p = uri_string;
873 while (*p && (g_ascii_isalpha (*p) ||
874 (p > uri_string && (g_ascii_isdigit (*p) ||
875 *p == '.' || *p == '+' || *p == '-'))))
876 p++;
877
878 if (p > uri_string && *p == ':')
879 {
880 normalized_scheme = g_ascii_strdown (str: uri_string, len: p - uri_string);
881 if (scheme)
882 *scheme = g_steal_pointer (&normalized_scheme);
883 p++;
884 }
885 else
886 {
887 if (scheme)
888 *scheme = NULL;
889 p = uri_string;
890 }
891
892 /* Check for authority */
893 if (strncmp (s1: p, s2: "//", n: 2) == 0)
894 {
895 p += 2;
896
897 path_start = p + strcspn (s: p, reject: "/?#");
898 at = memchr (s: p, c: '@', n: path_start - p);
899 if (at)
900 {
901 if (flags & G_URI_FLAGS_PARSE_RELAXED)
902 {
903 gchar *next_at;
904
905 /* Any "@"s in the userinfo must be %-encoded, but
906 * people get this wrong sometimes. Since "@"s in the
907 * hostname are unlikely (and also wrong anyway), assume
908 * that if there are extra "@"s, they belong in the
909 * userinfo.
910 */
911 do
912 {
913 next_at = memchr (s: at + 1, c: '@', n: path_start - (at + 1));
914 if (next_at)
915 at = next_at;
916 }
917 while (next_at);
918 }
919
920 if (user || password || auth_params ||
921 (flags & (G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS)))
922 {
923 if (!parse_userinfo (start: p, length: at - p, flags,
924 user, password, auth_params,
925 error))
926 goto fail;
927 }
928
929 if (!uri_normalize (out: userinfo, start: p, length: at - p, flags,
930 parse_error: G_URI_ERROR_BAD_USER, error))
931 goto fail;
932
933 p = at + 1;
934 }
935
936 if (flags & G_URI_FLAGS_PARSE_RELAXED)
937 {
938 semi = strchr (s: p, c: ';');
939 if (semi && semi < path_start)
940 {
941 /* Technically, semicolons are allowed in the "host"
942 * production, but no one ever does this, and some
943 * schemes mistakenly use semicolon as a delimiter
944 * marking the start of the path. We have to check this
945 * after checking for userinfo though, because a
946 * semicolon before the "@" must be part of the
947 * userinfo.
948 */
949 path_start = semi;
950 }
951 }
952
953 /* Find host and port. The host may be a bracket-delimited IPv6
954 * address, in which case the colon delimiting the port must come
955 * (immediately) after the close bracket.
956 */
957 if (*p == '[')
958 {
959 bracket = memchr (s: p, c: ']', n: path_start - p);
960 if (bracket && *(bracket + 1) == ':')
961 colon = bracket + 1;
962 else
963 colon = NULL;
964 }
965 else
966 colon = memchr (s: p, c: ':', n: path_start - p);
967
968 hostend = colon ? colon : path_start;
969 if (!parse_host (start: p, length: hostend - p, flags, out: host, error))
970 goto fail;
971
972 if (colon && colon != path_start - 1)
973 {
974 p = colon + 1;
975 if (!parse_port (start: p, length: path_start - p, out: port, error))
976 goto fail;
977 }
978
979 p = path_start;
980 }
981
982 /* Find fragment. */
983 end = p + strcspn (s: p, reject: "#");
984 if (*end == '#')
985 {
986 if (!uri_normalize (out: fragment, start: end + 1, length: strlen (s: end + 1),
987 flags: flags | (flags & G_URI_FLAGS_ENCODED_FRAGMENT ? G_URI_FLAGS_ENCODED : 0),
988 parse_error: G_URI_ERROR_BAD_FRAGMENT, error))
989 goto fail;
990 }
991
992 /* Find query */
993 question = memchr (s: p, c: '?', n: end - p);
994 if (question)
995 {
996 if (!uri_normalize (out: query, start: question + 1, length: end - (question + 1),
997 flags: flags | (flags & G_URI_FLAGS_ENCODED_QUERY ? G_URI_FLAGS_ENCODED : 0),
998 parse_error: G_URI_ERROR_BAD_QUERY, error))
999 goto fail;
1000 end = question;
1001 }
1002
1003 if (!uri_normalize (out: path, start: p, length: end - p,
1004 flags: flags | (flags & G_URI_FLAGS_ENCODED_PATH ? G_URI_FLAGS_ENCODED : 0),
1005 parse_error: G_URI_ERROR_BAD_PATH, error))
1006 goto fail;
1007
1008 /* Scheme-based normalization */
1009 if (flags & G_URI_FLAGS_SCHEME_NORMALIZE && ((scheme && *scheme) || normalized_scheme))
1010 {
1011 const char *scheme_str = scheme && *scheme ? *scheme : normalized_scheme;
1012
1013 if (should_normalize_empty_path (scheme: scheme_str) && path && !**path)
1014 {
1015 g_free (mem: *path);
1016 *path = g_strdup (str: "/");
1017 }
1018
1019 if (port && *port == -1)
1020 *port = default_scheme_port (scheme: scheme_str);
1021 }
1022
1023 g_free (mem: normalized_scheme);
1024 g_free (mem: cleaned_uri_string);
1025 return TRUE;
1026
1027 fail:
1028 if (scheme)
1029 g_clear_pointer (scheme, g_free);
1030 if (userinfo)
1031 g_clear_pointer (userinfo, g_free);
1032 if (host)
1033 g_clear_pointer (host, g_free);
1034 if (port)
1035 *port = -1;
1036 if (path)
1037 g_clear_pointer (path, g_free);
1038 if (query)
1039 g_clear_pointer (query, g_free);
1040 if (fragment)
1041 g_clear_pointer (fragment, g_free);
1042
1043 g_free (mem: normalized_scheme);
1044 g_free (mem: cleaned_uri_string);
1045 return FALSE;
1046}
1047
1048/**
1049 * g_uri_split:
1050 * @uri_ref: a string containing a relative or absolute URI
1051 * @flags: flags for parsing @uri_ref
1052 * @scheme: (out) (nullable) (optional) (transfer full): on return, contains
1053 * the scheme (converted to lowercase), or %NULL
1054 * @userinfo: (out) (nullable) (optional) (transfer full): on return, contains
1055 * the userinfo, or %NULL
1056 * @host: (out) (nullable) (optional) (transfer full): on return, contains the
1057 * host, or %NULL
1058 * @port: (out) (optional) (transfer full): on return, contains the
1059 * port, or `-1`
1060 * @path: (out) (not nullable) (optional) (transfer full): on return, contains the
1061 * path
1062 * @query: (out) (nullable) (optional) (transfer full): on return, contains the
1063 * query, or %NULL
1064 * @fragment: (out) (nullable) (optional) (transfer full): on return, contains
1065 * the fragment, or %NULL
1066 * @error: #GError for error reporting, or %NULL to ignore.
1067 *
1068 * Parses @uri_ref (which can be an
1069 * [absolute or relative URI][relative-absolute-uris]) according to @flags, and
1070 * returns the pieces. Any component that doesn't appear in @uri_ref will be
1071 * returned as %NULL (but note that all URIs always have a path component,
1072 * though it may be the empty string).
1073 *
1074 * If @flags contains %G_URI_FLAGS_ENCODED, then `%`-encoded characters in
1075 * @uri_ref will remain encoded in the output strings. (If not,
1076 * then all such characters will be decoded.) Note that decoding will
1077 * only work if the URI components are ASCII or UTF-8, so you will
1078 * need to use %G_URI_FLAGS_ENCODED if they are not.
1079 *
1080 * Note that the %G_URI_FLAGS_HAS_PASSWORD and
1081 * %G_URI_FLAGS_HAS_AUTH_PARAMS @flags are ignored by g_uri_split(),
1082 * since it always returns only the full userinfo; use
1083 * g_uri_split_with_user() if you want it split up.
1084 *
1085 * Returns: (skip): %TRUE if @uri_ref parsed successfully, %FALSE
1086 * on error.
1087 *
1088 * Since: 2.66
1089 */
1090gboolean
1091g_uri_split (const gchar *uri_ref,
1092 GUriFlags flags,
1093 gchar **scheme,
1094 gchar **userinfo,
1095 gchar **host,
1096 gint *port,
1097 gchar **path,
1098 gchar **query,
1099 gchar **fragment,
1100 GError **error)
1101{
1102 g_return_val_if_fail (uri_ref != NULL, FALSE);
1103 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1104
1105 return g_uri_split_internal (uri_string: uri_ref, flags,
1106 scheme, userinfo, NULL, NULL, NULL,
1107 host, port, path, query, fragment,
1108 error);
1109}
1110
1111/**
1112 * g_uri_split_with_user:
1113 * @uri_ref: a string containing a relative or absolute URI
1114 * @flags: flags for parsing @uri_ref
1115 * @scheme: (out) (nullable) (optional) (transfer full): on return, contains
1116 * the scheme (converted to lowercase), or %NULL
1117 * @user: (out) (nullable) (optional) (transfer full): on return, contains
1118 * the user, or %NULL
1119 * @password: (out) (nullable) (optional) (transfer full): on return, contains
1120 * the password, or %NULL
1121 * @auth_params: (out) (nullable) (optional) (transfer full): on return, contains
1122 * the auth_params, or %NULL
1123 * @host: (out) (nullable) (optional) (transfer full): on return, contains the
1124 * host, or %NULL
1125 * @port: (out) (optional) (transfer full): on return, contains the
1126 * port, or `-1`
1127 * @path: (out) (not nullable) (optional) (transfer full): on return, contains the
1128 * path
1129 * @query: (out) (nullable) (optional) (transfer full): on return, contains the
1130 * query, or %NULL
1131 * @fragment: (out) (nullable) (optional) (transfer full): on return, contains
1132 * the fragment, or %NULL
1133 * @error: #GError for error reporting, or %NULL to ignore.
1134 *
1135 * Parses @uri_ref (which can be an
1136 * [absolute or relative URI][relative-absolute-uris]) according to @flags, and
1137 * returns the pieces. Any component that doesn't appear in @uri_ref will be
1138 * returned as %NULL (but note that all URIs always have a path component,
1139 * though it may be the empty string).
1140 *
1141 * See g_uri_split(), and the definition of #GUriFlags, for more
1142 * information on the effect of @flags. Note that @password will only
1143 * be parsed out if @flags contains %G_URI_FLAGS_HAS_PASSWORD, and
1144 * @auth_params will only be parsed out if @flags contains
1145 * %G_URI_FLAGS_HAS_AUTH_PARAMS.
1146 *
1147 * Returns: (skip): %TRUE if @uri_ref parsed successfully, %FALSE
1148 * on error.
1149 *
1150 * Since: 2.66
1151 */
1152gboolean
1153g_uri_split_with_user (const gchar *uri_ref,
1154 GUriFlags flags,
1155 gchar **scheme,
1156 gchar **user,
1157 gchar **password,
1158 gchar **auth_params,
1159 gchar **host,
1160 gint *port,
1161 gchar **path,
1162 gchar **query,
1163 gchar **fragment,
1164 GError **error)
1165{
1166 g_return_val_if_fail (uri_ref != NULL, FALSE);
1167 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1168
1169 return g_uri_split_internal (uri_string: uri_ref, flags,
1170 scheme, NULL, user, password, auth_params,
1171 host, port, path, query, fragment,
1172 error);
1173}
1174
1175
1176/**
1177 * g_uri_split_network:
1178 * @uri_string: a string containing an absolute URI
1179 * @flags: flags for parsing @uri_string
1180 * @scheme: (out) (nullable) (optional) (transfer full): on return, contains
1181 * the scheme (converted to lowercase), or %NULL
1182 * @host: (out) (nullable) (optional) (transfer full): on return, contains the
1183 * host, or %NULL
1184 * @port: (out) (optional) (transfer full): on return, contains the
1185 * port, or `-1`
1186 * @error: #GError for error reporting, or %NULL to ignore.
1187 *
1188 * Parses @uri_string (which must be an [absolute URI][relative-absolute-uris])
1189 * according to @flags, and returns the pieces relevant to connecting to a host.
1190 * See the documentation for g_uri_split() for more details; this is
1191 * mostly a wrapper around that function with simpler arguments.
1192 * However, it will return an error if @uri_string is a relative URI,
1193 * or does not contain a hostname component.
1194 *
1195 * Returns: (skip): %TRUE if @uri_string parsed successfully,
1196 * %FALSE on error.
1197 *
1198 * Since: 2.66
1199 */
1200gboolean
1201g_uri_split_network (const gchar *uri_string,
1202 GUriFlags flags,
1203 gchar **scheme,
1204 gchar **host,
1205 gint *port,
1206 GError **error)
1207{
1208 gchar *my_scheme = NULL, *my_host = NULL;
1209
1210 g_return_val_if_fail (uri_string != NULL, FALSE);
1211 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1212
1213 if (!g_uri_split_internal (uri_string, flags,
1214 scheme: &my_scheme, NULL, NULL, NULL, NULL,
1215 host: &my_host, port, NULL, NULL, NULL,
1216 error))
1217 return FALSE;
1218
1219 if (!my_scheme || !my_host)
1220 {
1221 if (!my_scheme)
1222 {
1223 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_SCHEME,
1224 _("URI ‘%s’ is not an absolute URI"),
1225 uri_string);
1226 }
1227 else
1228 {
1229 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_HOST,
1230 _("URI ‘%s’ has no host component"),
1231 uri_string);
1232 }
1233 g_free (mem: my_scheme);
1234 g_free (mem: my_host);
1235
1236 return FALSE;
1237 }
1238
1239 if (scheme)
1240 *scheme = g_steal_pointer (&my_scheme);
1241 if (host)
1242 *host = g_steal_pointer (&my_host);
1243
1244 g_free (mem: my_scheme);
1245 g_free (mem: my_host);
1246
1247 return TRUE;
1248}
1249
1250/**
1251 * g_uri_is_valid:
1252 * @uri_string: a string containing an absolute URI
1253 * @flags: flags for parsing @uri_string
1254 * @error: #GError for error reporting, or %NULL to ignore.
1255 *
1256 * Parses @uri_string according to @flags, to determine whether it is a valid
1257 * [absolute URI][relative-absolute-uris], i.e. it does not need to be resolved
1258 * relative to another URI using g_uri_parse_relative().
1259 *
1260 * If it’s not a valid URI, an error is returned explaining how it’s invalid.
1261 *
1262 * See g_uri_split(), and the definition of #GUriFlags, for more
1263 * information on the effect of @flags.
1264 *
1265 * Returns: %TRUE if @uri_string is a valid absolute URI, %FALSE on error.
1266 *
1267 * Since: 2.66
1268 */
1269gboolean
1270g_uri_is_valid (const gchar *uri_string,
1271 GUriFlags flags,
1272 GError **error)
1273{
1274 gchar *my_scheme = NULL;
1275
1276 g_return_val_if_fail (uri_string != NULL, FALSE);
1277 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1278
1279 if (!g_uri_split_internal (uri_string, flags,
1280 scheme: &my_scheme, NULL, NULL, NULL, NULL,
1281 NULL, NULL, NULL, NULL, NULL,
1282 error))
1283 return FALSE;
1284
1285 if (!my_scheme)
1286 {
1287 g_set_error (err: error, G_URI_ERROR, code: G_URI_ERROR_BAD_SCHEME,
1288 _("URI ‘%s’ is not an absolute URI"),
1289 uri_string);
1290 return FALSE;
1291 }
1292
1293 g_free (mem: my_scheme);
1294
1295 return TRUE;
1296}
1297
1298
1299/* This does the "Remove Dot Segments" algorithm from section 5.2.4 of
1300 * RFC 3986, except that @path is modified in place.
1301 *
1302 * See https://tools.ietf.org/html/rfc3986#section-5.2.4
1303 */
1304static void
1305remove_dot_segments (gchar *path)
1306{
1307 gchar *p, *q;
1308
1309 if (!*path)
1310 return;
1311
1312 /* Remove "./" where "." is a complete segment. */
1313 for (p = path + 1; *p; )
1314 {
1315 if (*(p - 1) == '/' &&
1316 *p == '.' && *(p + 1) == '/')
1317 memmove (dest: p, src: p + 2, n: strlen (s: p + 2) + 1);
1318 else
1319 p++;
1320 }
1321 /* Remove "." at end. */
1322 if (p > path + 2 &&
1323 *(p - 1) == '.' && *(p - 2) == '/')
1324 *(p - 1) = '\0';
1325
1326 /* Remove "<segment>/../" where <segment> != ".." */
1327 for (p = path + 1; *p; )
1328 {
1329 if (!strncmp (s1: p, s2: "../", n: 3))
1330 {
1331 p += 3;
1332 continue;
1333 }
1334 q = strchr (s: p + 1, c: '/');
1335 if (!q)
1336 break;
1337 if (strncmp (s1: q, s2: "/../", n: 4) != 0)
1338 {
1339 p = q + 1;
1340 continue;
1341 }
1342 memmove (dest: p, src: q + 4, n: strlen (s: q + 4) + 1);
1343 p = path + 1;
1344 }
1345 /* Remove "<segment>/.." at end where <segment> != ".." */
1346 q = strrchr (s: path, c: '/');
1347 if (q && q != path && !strcmp (s1: q, s2: "/.."))
1348 {
1349 p = q - 1;
1350 while (p > path && *p != '/')
1351 p--;
1352 if (strncmp (s1: p, s2: "/../", n: 4) != 0)
1353 *(p + 1) = 0;
1354 }
1355
1356 /* Remove extraneous initial "/.."s */
1357 while (!strncmp (s1: path, s2: "/../", n: 4))
1358 memmove (dest: path, src: path + 3, n: strlen (s: path) - 2);
1359 if (!strcmp (s1: path, s2: "/.."))
1360 path[1] = '\0';
1361}
1362
1363/**
1364 * g_uri_parse:
1365 * @uri_string: a string representing an absolute URI
1366 * @flags: flags describing how to parse @uri_string
1367 * @error: #GError for error reporting, or %NULL to ignore.
1368 *
1369 * Parses @uri_string according to @flags. If the result is not a
1370 * valid [absolute URI][relative-absolute-uris], it will be discarded, and an
1371 * error returned.
1372 *
1373 * Return value: (transfer full): a new #GUri, or NULL on error.
1374 *
1375 * Since: 2.66
1376 */
1377GUri *
1378g_uri_parse (const gchar *uri_string,
1379 GUriFlags flags,
1380 GError **error)
1381{
1382 g_return_val_if_fail (uri_string != NULL, NULL);
1383 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1384
1385 return g_uri_parse_relative (NULL, uri_ref: uri_string, flags, error);
1386}
1387
1388/**
1389 * g_uri_parse_relative:
1390 * @base_uri: (nullable) (transfer none): a base absolute URI
1391 * @uri_ref: a string representing a relative or absolute URI
1392 * @flags: flags describing how to parse @uri_ref
1393 * @error: #GError for error reporting, or %NULL to ignore.
1394 *
1395 * Parses @uri_ref according to @flags and, if it is a
1396 * [relative URI][relative-absolute-uris], resolves it relative to @base_uri.
1397 * If the result is not a valid absolute URI, it will be discarded, and an error
1398 * returned.
1399 *
1400 * Return value: (transfer full): a new #GUri, or NULL on error.
1401 *
1402 * Since: 2.66
1403 */
1404GUri *
1405g_uri_parse_relative (GUri *base_uri,
1406 const gchar *uri_ref,
1407 GUriFlags flags,
1408 GError **error)
1409{
1410 GUri *uri = NULL;
1411
1412 g_return_val_if_fail (uri_ref != NULL, NULL);
1413 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1414 g_return_val_if_fail (base_uri == NULL || base_uri->scheme != NULL, NULL);
1415
1416 /* Use GUri struct to construct the return value: there is no guarantee it is
1417 * actually correct within the function body. */
1418 uri = g_atomic_rc_box_new0 (GUri);
1419 uri->flags = flags;
1420
1421 if (!g_uri_split_internal (uri_string: uri_ref, flags,
1422 scheme: &uri->scheme, userinfo: &uri->userinfo,
1423 user: &uri->user, password: &uri->password, auth_params: &uri->auth_params,
1424 host: &uri->host, port: &uri->port,
1425 path: &uri->path, query: &uri->query, fragment: &uri->fragment,
1426 error))
1427 {
1428 g_uri_unref (uri);
1429 return NULL;
1430 }
1431
1432 if (!uri->scheme && !base_uri)
1433 {
1434 g_set_error_literal (err: error, G_URI_ERROR, code: G_URI_ERROR_FAILED,
1435 _("URI is not absolute, and no base URI was provided"));
1436 g_uri_unref (uri);
1437 return NULL;
1438 }
1439
1440 if (base_uri)
1441 {
1442 /* This is section 5.2.2 of RFC 3986, except that we're doing
1443 * it in place in @uri rather than copying from R to T.
1444 *
1445 * See https://tools.ietf.org/html/rfc3986#section-5.2.2
1446 */
1447 if (uri->scheme)
1448 remove_dot_segments (path: uri->path);
1449 else
1450 {
1451 uri->scheme = g_strdup (str: base_uri->scheme);
1452 if (uri->host)
1453 remove_dot_segments (path: uri->path);
1454 else
1455 {
1456 if (!*uri->path)
1457 {
1458 g_free (mem: uri->path);
1459 uri->path = g_strdup (str: base_uri->path);
1460 if (!uri->query)
1461 uri->query = g_strdup (str: base_uri->query);
1462 }
1463 else
1464 {
1465 if (*uri->path == '/')
1466 remove_dot_segments (path: uri->path);
1467 else
1468 {
1469 gchar *newpath, *last;
1470
1471 last = strrchr (s: base_uri->path, c: '/');
1472 if (last)
1473 {
1474 newpath = g_strdup_printf (format: "%.*s/%s",
1475 (gint)(last - base_uri->path),
1476 base_uri->path,
1477 uri->path);
1478 }
1479 else
1480 newpath = g_strdup_printf (format: "/%s", uri->path);
1481
1482 g_free (mem: uri->path);
1483 uri->path = g_steal_pointer (&newpath);
1484
1485 remove_dot_segments (path: uri->path);
1486 }
1487 }
1488
1489 uri->userinfo = g_strdup (str: base_uri->userinfo);
1490 uri->user = g_strdup (str: base_uri->user);
1491 uri->password = g_strdup (str: base_uri->password);
1492 uri->auth_params = g_strdup (str: base_uri->auth_params);
1493 uri->host = g_strdup (str: base_uri->host);
1494 uri->port = base_uri->port;
1495 }
1496 }
1497
1498 /* Scheme normalization couldn't have been done earlier
1499 * as the relative URI may not have had a scheme */
1500 if (flags & G_URI_FLAGS_SCHEME_NORMALIZE)
1501 {
1502 if (should_normalize_empty_path (scheme: uri->scheme) && !*uri->path)
1503 {
1504 g_free (mem: uri->path);
1505 uri->path = g_strdup (str: "/");
1506 }
1507
1508 uri->port = normalize_port (scheme: uri->scheme, port: uri->port);
1509 }
1510 }
1511
1512 return g_steal_pointer (&uri);
1513}
1514
1515/**
1516 * g_uri_resolve_relative:
1517 * @base_uri_string: (nullable): a string representing a base URI
1518 * @uri_ref: a string representing a relative or absolute URI
1519 * @flags: flags describing how to parse @uri_ref
1520 * @error: #GError for error reporting, or %NULL to ignore.
1521 *
1522 * Parses @uri_ref according to @flags and, if it is a
1523 * [relative URI][relative-absolute-uris], resolves it relative to
1524 * @base_uri_string. If the result is not a valid absolute URI, it will be
1525 * discarded, and an error returned.
1526 *
1527 * (If @base_uri_string is %NULL, this just returns @uri_ref, or
1528 * %NULL if @uri_ref is invalid or not absolute.)
1529 *
1530 * Return value: (transfer full): the resolved URI string,
1531 * or NULL on error.
1532 *
1533 * Since: 2.66
1534 */
1535gchar *
1536g_uri_resolve_relative (const gchar *base_uri_string,
1537 const gchar *uri_ref,
1538 GUriFlags flags,
1539 GError **error)
1540{
1541 GUri *base_uri, *resolved_uri;
1542 gchar *resolved_uri_string;
1543
1544 g_return_val_if_fail (uri_ref != NULL, NULL);
1545 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1546
1547 flags |= G_URI_FLAGS_ENCODED;
1548
1549 if (base_uri_string)
1550 {
1551 base_uri = g_uri_parse (uri_string: base_uri_string, flags, error);
1552 if (!base_uri)
1553 return NULL;
1554 }
1555 else
1556 base_uri = NULL;
1557
1558 resolved_uri = g_uri_parse_relative (base_uri, uri_ref, flags, error);
1559 if (base_uri)
1560 g_uri_unref (uri: base_uri);
1561 if (!resolved_uri)
1562 return NULL;
1563
1564 resolved_uri_string = g_uri_to_string (uri: resolved_uri);
1565 g_uri_unref (uri: resolved_uri);
1566 return g_steal_pointer (&resolved_uri_string);
1567}
1568
1569/* userinfo as a whole can contain sub-delims + ":", but split-out
1570 * user can't contain ":" or ";", and split-out password can't contain
1571 * ";".
1572 */
1573#define USERINFO_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO
1574#define USER_ALLOWED_CHARS "!$&'()*+,="
1575#define PASSWORD_ALLOWED_CHARS "!$&'()*+,=:"
1576#define AUTH_PARAMS_ALLOWED_CHARS USERINFO_ALLOWED_CHARS
1577#define IP_ADDR_ALLOWED_CHARS ":"
1578#define HOST_ALLOWED_CHARS G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS
1579#define PATH_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH
1580#define QUERY_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH "?"
1581#define FRAGMENT_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH "?"
1582
1583static gchar *
1584g_uri_join_internal (GUriFlags flags,
1585 const gchar *scheme,
1586 gboolean userinfo,
1587 const gchar *user,
1588 const gchar *password,
1589 const gchar *auth_params,
1590 const gchar *host,
1591 gint port,
1592 const gchar *path,
1593 const gchar *query,
1594 const gchar *fragment)
1595{
1596 gboolean encoded = (flags & G_URI_FLAGS_ENCODED);
1597 GString *str;
1598 char *normalized_scheme = NULL;
1599
1600 /* Restrictions on path prefixes. See:
1601 * https://tools.ietf.org/html/rfc3986#section-3
1602 */
1603 g_return_val_if_fail (path != NULL, NULL);
1604 g_return_val_if_fail (host == NULL || (path[0] == '\0' || path[0] == '/'), NULL);
1605 g_return_val_if_fail (host != NULL || (path[0] != '/' || path[1] != '/'), NULL);
1606
1607 str = g_string_new (init: scheme);
1608 if (scheme)
1609 g_string_append_c (str, ':');
1610
1611 if (flags & G_URI_FLAGS_SCHEME_NORMALIZE && scheme && ((host && port != -1) || path[0] == '\0'))
1612 normalized_scheme = g_ascii_strdown (str: scheme, len: -1);
1613
1614 if (host)
1615 {
1616 g_string_append (string: str, val: "//");
1617
1618 if (user)
1619 {
1620 if (encoded)
1621 g_string_append (string: str, val: user);
1622 else
1623 {
1624 if (userinfo)
1625 g_string_append_uri_escaped (string: str, unescaped: user, USERINFO_ALLOWED_CHARS, TRUE);
1626 else
1627 /* Encode ':' and ';' regardless of whether we have a
1628 * password or auth params, since it may be parsed later
1629 * under the assumption that it does.
1630 */
1631 g_string_append_uri_escaped (string: str, unescaped: user, USER_ALLOWED_CHARS, TRUE);
1632 }
1633
1634 if (password)
1635 {
1636 g_string_append_c (str, ':');
1637 if (encoded)
1638 g_string_append (string: str, val: password);
1639 else
1640 g_string_append_uri_escaped (string: str, unescaped: password,
1641 PASSWORD_ALLOWED_CHARS, TRUE);
1642 }
1643
1644 if (auth_params)
1645 {
1646 g_string_append_c (str, ';');
1647 if (encoded)
1648 g_string_append (string: str, val: auth_params);
1649 else
1650 g_string_append_uri_escaped (string: str, unescaped: auth_params,
1651 AUTH_PARAMS_ALLOWED_CHARS, TRUE);
1652 }
1653
1654 g_string_append_c (str, '@');
1655 }
1656
1657 if (strchr (s: host, c: ':') && g_hostname_is_ip_address (hostname: host))
1658 {
1659 g_string_append_c (str, '[');
1660 if (encoded)
1661 g_string_append (string: str, val: host);
1662 else
1663 g_string_append_uri_escaped (string: str, unescaped: host, IP_ADDR_ALLOWED_CHARS, TRUE);
1664 g_string_append_c (str, ']');
1665 }
1666 else
1667 {
1668 if (encoded)
1669 g_string_append (string: str, val: host);
1670 else
1671 g_string_append_uri_escaped (string: str, unescaped: host, HOST_ALLOWED_CHARS, TRUE);
1672 }
1673
1674 if (port != -1 && (!normalized_scheme || normalize_port (scheme: normalized_scheme, port) != -1))
1675 g_string_append_printf (string: str, format: ":%d", port);
1676 }
1677
1678 if (path[0] == '\0' && normalized_scheme && should_normalize_empty_path (scheme: normalized_scheme))
1679 g_string_append (string: str, val: "/");
1680 else if (encoded || flags & G_URI_FLAGS_ENCODED_PATH)
1681 g_string_append (string: str, val: path);
1682 else
1683 g_string_append_uri_escaped (string: str, unescaped: path, PATH_ALLOWED_CHARS, TRUE);
1684
1685 g_free (mem: normalized_scheme);
1686
1687 if (query)
1688 {
1689 g_string_append_c (str, '?');
1690 if (encoded || flags & G_URI_FLAGS_ENCODED_QUERY)
1691 g_string_append (string: str, val: query);
1692 else
1693 g_string_append_uri_escaped (string: str, unescaped: query, QUERY_ALLOWED_CHARS, TRUE);
1694 }
1695 if (fragment)
1696 {
1697 g_string_append_c (str, '#');
1698 if (encoded || flags & G_URI_FLAGS_ENCODED_FRAGMENT)
1699 g_string_append (string: str, val: fragment);
1700 else
1701 g_string_append_uri_escaped (string: str, unescaped: fragment, FRAGMENT_ALLOWED_CHARS, TRUE);
1702 }
1703
1704 return g_string_free (string: str, FALSE);
1705}
1706
1707/**
1708 * g_uri_join:
1709 * @flags: flags describing how to build the URI string
1710 * @scheme: (nullable): the URI scheme, or %NULL
1711 * @userinfo: (nullable): the userinfo component, or %NULL
1712 * @host: (nullable): the host component, or %NULL
1713 * @port: the port, or `-1`
1714 * @path: (not nullable): the path component
1715 * @query: (nullable): the query component, or %NULL
1716 * @fragment: (nullable): the fragment, or %NULL
1717 *
1718 * Joins the given components together according to @flags to create
1719 * an absolute URI string. @path may not be %NULL (though it may be the empty
1720 * string).
1721 *
1722 * When @host is present, @path must either be empty or begin with a slash (`/`)
1723 * character. When @host is not present, @path cannot begin with two slash
1724 characters (`//`). See
1725 * [RFC 3986, section 3](https://tools.ietf.org/html/rfc3986#section-3).
1726 *
1727 * See also g_uri_join_with_user(), which allows specifying the
1728 * components of the ‘userinfo’ separately.
1729 *
1730 * %G_URI_FLAGS_HAS_PASSWORD and %G_URI_FLAGS_HAS_AUTH_PARAMS are ignored if set
1731 * in @flags.
1732 *
1733 * Return value: (not nullable) (transfer full): an absolute URI string
1734 *
1735 * Since: 2.66
1736 */
1737gchar *
1738g_uri_join (GUriFlags flags,
1739 const gchar *scheme,
1740 const gchar *userinfo,
1741 const gchar *host,
1742 gint port,
1743 const gchar *path,
1744 const gchar *query,
1745 const gchar *fragment)
1746{
1747 g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
1748 g_return_val_if_fail (path != NULL, NULL);
1749
1750 return g_uri_join_internal (flags,
1751 scheme,
1752 TRUE, user: userinfo, NULL, NULL,
1753 host,
1754 port,
1755 path,
1756 query,
1757 fragment);
1758}
1759
1760/**
1761 * g_uri_join_with_user:
1762 * @flags: flags describing how to build the URI string
1763 * @scheme: (nullable): the URI scheme, or %NULL
1764 * @user: (nullable): the user component of the userinfo, or %NULL
1765 * @password: (nullable): the password component of the userinfo, or
1766 * %NULL
1767 * @auth_params: (nullable): the auth params of the userinfo, or
1768 * %NULL
1769 * @host: (nullable): the host component, or %NULL
1770 * @port: the port, or `-1`
1771 * @path: (not nullable): the path component
1772 * @query: (nullable): the query component, or %NULL
1773 * @fragment: (nullable): the fragment, or %NULL
1774 *
1775 * Joins the given components together according to @flags to create
1776 * an absolute URI string. @path may not be %NULL (though it may be the empty
1777 * string).
1778 *
1779 * In contrast to g_uri_join(), this allows specifying the components
1780 * of the ‘userinfo’ separately. It otherwise behaves the same.
1781 *
1782 * %G_URI_FLAGS_HAS_PASSWORD and %G_URI_FLAGS_HAS_AUTH_PARAMS are ignored if set
1783 * in @flags.
1784 *
1785 * Return value: (not nullable) (transfer full): an absolute URI string
1786 *
1787 * Since: 2.66
1788 */
1789gchar *
1790g_uri_join_with_user (GUriFlags flags,
1791 const gchar *scheme,
1792 const gchar *user,
1793 const gchar *password,
1794 const gchar *auth_params,
1795 const gchar *host,
1796 gint port,
1797 const gchar *path,
1798 const gchar *query,
1799 const gchar *fragment)
1800{
1801 g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
1802 g_return_val_if_fail (path != NULL, NULL);
1803
1804 return g_uri_join_internal (flags,
1805 scheme,
1806 FALSE, user, password, auth_params,
1807 host,
1808 port,
1809 path,
1810 query,
1811 fragment);
1812}
1813
1814/**
1815 * g_uri_build:
1816 * @flags: flags describing how to build the #GUri
1817 * @scheme: (not nullable): the URI scheme
1818 * @userinfo: (nullable): the userinfo component, or %NULL
1819 * @host: (nullable): the host component, or %NULL
1820 * @port: the port, or `-1`
1821 * @path: (not nullable): the path component
1822 * @query: (nullable): the query component, or %NULL
1823 * @fragment: (nullable): the fragment, or %NULL
1824 *
1825 * Creates a new #GUri from the given components according to @flags.
1826 *
1827 * See also g_uri_build_with_user(), which allows specifying the
1828 * components of the "userinfo" separately.
1829 *
1830 * Return value: (not nullable) (transfer full): a new #GUri
1831 *
1832 * Since: 2.66
1833 */
1834GUri *
1835g_uri_build (GUriFlags flags,
1836 const gchar *scheme,
1837 const gchar *userinfo,
1838 const gchar *host,
1839 gint port,
1840 const gchar *path,
1841 const gchar *query,
1842 const gchar *fragment)
1843{
1844 GUri *uri;
1845
1846 g_return_val_if_fail (scheme != NULL, NULL);
1847 g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
1848 g_return_val_if_fail (path != NULL, NULL);
1849
1850 uri = g_atomic_rc_box_new0 (GUri);
1851 uri->flags = flags;
1852 uri->scheme = g_ascii_strdown (str: scheme, len: -1);
1853 uri->userinfo = g_strdup (str: userinfo);
1854 uri->host = g_strdup (str: host);
1855 uri->port = port;
1856 uri->path = g_strdup (str: path);
1857 uri->query = g_strdup (str: query);
1858 uri->fragment = g_strdup (str: fragment);
1859
1860 return g_steal_pointer (&uri);
1861}
1862
1863/**
1864 * g_uri_build_with_user:
1865 * @flags: flags describing how to build the #GUri
1866 * @scheme: (not nullable): the URI scheme
1867 * @user: (nullable): the user component of the userinfo, or %NULL
1868 * @password: (nullable): the password component of the userinfo, or %NULL
1869 * @auth_params: (nullable): the auth params of the userinfo, or %NULL
1870 * @host: (nullable): the host component, or %NULL
1871 * @port: the port, or `-1`
1872 * @path: (not nullable): the path component
1873 * @query: (nullable): the query component, or %NULL
1874 * @fragment: (nullable): the fragment, or %NULL
1875 *
1876 * Creates a new #GUri from the given components according to @flags
1877 * (%G_URI_FLAGS_HAS_PASSWORD is added unconditionally). The @flags must be
1878 * coherent with the passed values, in particular use `%`-encoded values with
1879 * %G_URI_FLAGS_ENCODED.
1880 *
1881 * In contrast to g_uri_build(), this allows specifying the components
1882 * of the ‘userinfo’ field separately. Note that @user must be non-%NULL
1883 * if either @password or @auth_params is non-%NULL.
1884 *
1885 * Return value: (not nullable) (transfer full): a new #GUri
1886 *
1887 * Since: 2.66
1888 */
1889GUri *
1890g_uri_build_with_user (GUriFlags flags,
1891 const gchar *scheme,
1892 const gchar *user,
1893 const gchar *password,
1894 const gchar *auth_params,
1895 const gchar *host,
1896 gint port,
1897 const gchar *path,
1898 const gchar *query,
1899 const gchar *fragment)
1900{
1901 GUri *uri;
1902 GString *userinfo;
1903
1904 g_return_val_if_fail (scheme != NULL, NULL);
1905 g_return_val_if_fail (password == NULL || user != NULL, NULL);
1906 g_return_val_if_fail (auth_params == NULL || user != NULL, NULL);
1907 g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
1908 g_return_val_if_fail (path != NULL, NULL);
1909
1910 uri = g_atomic_rc_box_new0 (GUri);
1911 uri->flags = flags | G_URI_FLAGS_HAS_PASSWORD;
1912 uri->scheme = g_ascii_strdown (str: scheme, len: -1);
1913 uri->user = g_strdup (str: user);
1914 uri->password = g_strdup (str: password);
1915 uri->auth_params = g_strdup (str: auth_params);
1916 uri->host = g_strdup (str: host);
1917 uri->port = port;
1918 uri->path = g_strdup (str: path);
1919 uri->query = g_strdup (str: query);
1920 uri->fragment = g_strdup (str: fragment);
1921
1922 if (user)
1923 {
1924 userinfo = g_string_new (init: user);
1925 if (password)
1926 {
1927 g_string_append_c (userinfo, ':');
1928 g_string_append (string: userinfo, val: uri->password);
1929 }
1930 if (auth_params)
1931 {
1932 g_string_append_c (userinfo, ';');
1933 g_string_append (string: userinfo, val: uri->auth_params);
1934 }
1935 uri->userinfo = g_string_free (string: userinfo, FALSE);
1936 }
1937
1938 return g_steal_pointer (&uri);
1939}
1940
1941/**
1942 * g_uri_to_string:
1943 * @uri: a #GUri
1944 *
1945 * Returns a string representing @uri.
1946 *
1947 * This is not guaranteed to return a string which is identical to the
1948 * string that @uri was parsed from. However, if the source URI was
1949 * syntactically correct (according to RFC 3986), and it was parsed
1950 * with %G_URI_FLAGS_ENCODED, then g_uri_to_string() is guaranteed to return
1951 * a string which is at least semantically equivalent to the source
1952 * URI (according to RFC 3986).
1953 *
1954 * If @uri might contain sensitive details, such as authentication parameters,
1955 * or private data in its query string, and the returned string is going to be
1956 * logged, then consider using g_uri_to_string_partial() to redact parts.
1957 *
1958 * Return value: (not nullable) (transfer full): a string representing @uri,
1959 * which the caller must free.
1960 *
1961 * Since: 2.66
1962 */
1963gchar *
1964g_uri_to_string (GUri *uri)
1965{
1966 g_return_val_if_fail (uri != NULL, NULL);
1967
1968 return g_uri_to_string_partial (uri, flags: G_URI_HIDE_NONE);
1969}
1970
1971/**
1972 * g_uri_to_string_partial:
1973 * @uri: a #GUri
1974 * @flags: flags describing what parts of @uri to hide
1975 *
1976 * Returns a string representing @uri, subject to the options in
1977 * @flags. See g_uri_to_string() and #GUriHideFlags for more details.
1978 *
1979 * Return value: (not nullable) (transfer full): a string representing
1980 * @uri, which the caller must free.
1981 *
1982 * Since: 2.66
1983 */
1984gchar *
1985g_uri_to_string_partial (GUri *uri,
1986 GUriHideFlags flags)
1987{
1988 gboolean hide_user = (flags & G_URI_HIDE_USERINFO);
1989 gboolean hide_password = (flags & (G_URI_HIDE_USERINFO | G_URI_HIDE_PASSWORD));
1990 gboolean hide_auth_params = (flags & (G_URI_HIDE_USERINFO | G_URI_HIDE_AUTH_PARAMS));
1991 gboolean hide_query = (flags & G_URI_HIDE_QUERY);
1992 gboolean hide_fragment = (flags & G_URI_HIDE_FRAGMENT);
1993
1994 g_return_val_if_fail (uri != NULL, NULL);
1995
1996 if (uri->flags & (G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_HAS_AUTH_PARAMS))
1997 {
1998 return g_uri_join_with_user (flags: uri->flags,
1999 scheme: uri->scheme,
2000 user: hide_user ? NULL : uri->user,
2001 password: hide_password ? NULL : uri->password,
2002 auth_params: hide_auth_params ? NULL : uri->auth_params,
2003 host: uri->host,
2004 port: uri->port,
2005 path: uri->path,
2006 query: hide_query ? NULL : uri->query,
2007 fragment: hide_fragment ? NULL : uri->fragment);
2008 }
2009
2010 return g_uri_join (flags: uri->flags,
2011 scheme: uri->scheme,
2012 userinfo: hide_user ? NULL : uri->userinfo,
2013 host: uri->host,
2014 port: uri->port,
2015 path: uri->path,
2016 query: hide_query ? NULL : uri->query,
2017 fragment: hide_fragment ? NULL : uri->fragment);
2018}
2019
2020/* This is just a copy of g_str_hash() with g_ascii_toupper() added */
2021static guint
2022str_ascii_case_hash (gconstpointer v)
2023{
2024 const signed char *p;
2025 guint32 h = 5381;
2026
2027 for (p = v; *p != '\0'; p++)
2028 h = (h << 5) + h + g_ascii_toupper (c: *p);
2029
2030 return h;
2031}
2032
2033static gboolean
2034str_ascii_case_equal (gconstpointer v1,
2035 gconstpointer v2)
2036{
2037 const gchar *string1 = v1;
2038 const gchar *string2 = v2;
2039
2040 return g_ascii_strcasecmp (s1: string1, s2: string2) == 0;
2041}
2042
2043/**
2044 * GUriParamsIter:
2045 *
2046 * Many URI schemes include one or more attribute/value pairs as part of the URI
2047 * value. For example `scheme://server/path?query=string&is=there` has two
2048 * attributes – `query=string` and `is=there` – in its query part.
2049 *
2050 * A #GUriParamsIter structure represents an iterator that can be used to
2051 * iterate over the attribute/value pairs of a URI query string. #GUriParamsIter
2052 * structures are typically allocated on the stack and then initialized with
2053 * g_uri_params_iter_init(). See the documentation for g_uri_params_iter_init()
2054 * for a usage example.
2055 *
2056 * Since: 2.66
2057 */
2058typedef struct
2059{
2060 GUriParamsFlags flags;
2061 const gchar *attr;
2062 const gchar *end;
2063 guint8 sep_table[256]; /* 1 = index is a separator; 0 otherwise */
2064} RealIter;
2065
2066G_STATIC_ASSERT (sizeof (GUriParamsIter) == sizeof (RealIter));
2067G_STATIC_ASSERT (G_ALIGNOF (GUriParamsIter) >= G_ALIGNOF (RealIter));
2068
2069/**
2070 * g_uri_params_iter_init:
2071 * @iter: an uninitialized #GUriParamsIter
2072 * @params: a `%`-encoded string containing `attribute=value`
2073 * parameters
2074 * @length: the length of @params, or `-1` if it is nul-terminated
2075 * @separators: the separator byte character set between parameters. (usually
2076 * `&`, but sometimes `;` or both `&;`). Note that this function works on
2077 * bytes not characters, so it can't be used to delimit UTF-8 strings for
2078 * anything but ASCII characters. You may pass an empty set, in which case
2079 * no splitting will occur.
2080 * @flags: flags to modify the way the parameters are handled.
2081 *
2082 * Initializes an attribute/value pair iterator.
2083 *
2084 * The iterator keeps pointers to the @params and @separators arguments, those
2085 * variables must thus outlive the iterator and not be modified during the
2086 * iteration.
2087 *
2088 * If %G_URI_PARAMS_WWW_FORM is passed in @flags, `+` characters in the param
2089 * string will be replaced with spaces in the output. For example, `foo=bar+baz`
2090 * will give attribute `foo` with value `bar baz`. This is commonly used on the
2091 * web (the `https` and `http` schemes only), but is deprecated in favour of
2092 * the equivalent of encoding spaces as `%20`.
2093 *
2094 * Unlike with g_uri_parse_params(), %G_URI_PARAMS_CASE_INSENSITIVE has no
2095 * effect if passed to @flags for g_uri_params_iter_init(). The caller is
2096 * responsible for doing their own case-insensitive comparisons.
2097 *
2098 * |[<!-- language="C" -->
2099 * GUriParamsIter iter;
2100 * GError *error = NULL;
2101 * gchar *unowned_attr, *unowned_value;
2102 *
2103 * g_uri_params_iter_init (&iter, "foo=bar&baz=bar&Foo=frob&baz=bar2", -1, "&", G_URI_PARAMS_NONE);
2104 * while (g_uri_params_iter_next (&iter, &unowned_attr, &unowned_value, &error))
2105 * {
2106 * g_autofree gchar *attr = g_steal_pointer (&unowned_attr);
2107 * g_autofree gchar *value = g_steal_pointer (&unowned_value);
2108 * // do something with attr and value; this code will be called 4 times
2109 * // for the params string in this example: once with attr=foo and value=bar,
2110 * // then with baz/bar, then Foo/frob, then baz/bar2.
2111 * }
2112 * if (error)
2113 * // handle parsing error
2114 * ]|
2115 *
2116 * Since: 2.66
2117 */
2118void
2119g_uri_params_iter_init (GUriParamsIter *iter,
2120 const gchar *params,
2121 gssize length,
2122 const gchar *separators,
2123 GUriParamsFlags flags)
2124{
2125 RealIter *ri = (RealIter *)iter;
2126 const gchar *s;
2127
2128 g_return_if_fail (iter != NULL);
2129 g_return_if_fail (length == 0 || params != NULL);
2130 g_return_if_fail (length >= -1);
2131 g_return_if_fail (separators != NULL);
2132
2133 ri->flags = flags;
2134
2135 if (length == -1)
2136 ri->end = params + strlen (s: params);
2137 else
2138 ri->end = params + length;
2139
2140 memset (s: ri->sep_table, FALSE, n: sizeof (ri->sep_table));
2141 for (s = separators; *s != '\0'; ++s)
2142 ri->sep_table[*(guchar *)s] = TRUE;
2143
2144 ri->attr = params;
2145}
2146
2147/**
2148 * g_uri_params_iter_next:
2149 * @iter: an initialized #GUriParamsIter
2150 * @attribute: (out) (nullable) (optional) (transfer full): on return, contains
2151 * the attribute, or %NULL.
2152 * @value: (out) (nullable) (optional) (transfer full): on return, contains
2153 * the value, or %NULL.
2154 * @error: #GError for error reporting, or %NULL to ignore.
2155 *
2156 * Advances @iter and retrieves the next attribute/value. %FALSE is returned if
2157 * an error has occurred (in which case @error is set), or if the end of the
2158 * iteration is reached (in which case @attribute and @value are set to %NULL
2159 * and the iterator becomes invalid). If %TRUE is returned,
2160 * g_uri_params_iter_next() may be called again to receive another
2161 * attribute/value pair.
2162 *
2163 * Note that the same @attribute may be returned multiple times, since URIs
2164 * allow repeated attributes.
2165 *
2166 * Returns: %FALSE if the end of the parameters has been reached or an error was
2167 * encountered. %TRUE otherwise.
2168 *
2169 * Since: 2.66
2170 */
2171gboolean
2172g_uri_params_iter_next (GUriParamsIter *iter,
2173 gchar **attribute,
2174 gchar **value,
2175 GError **error)
2176{
2177 RealIter *ri = (RealIter *)iter;
2178 const gchar *attr_end, *val, *val_end;
2179 gchar *decoded_attr, *decoded_value;
2180 gboolean www_form = ri->flags & G_URI_PARAMS_WWW_FORM;
2181 GUriFlags decode_flags = G_URI_FLAGS_NONE;
2182
2183 g_return_val_if_fail (iter != NULL, FALSE);
2184 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2185
2186 /* Pre-clear these in case of failure or finishing. */
2187 if (attribute)
2188 *attribute = NULL;
2189 if (value)
2190 *value = NULL;
2191
2192 if (ri->attr >= ri->end)
2193 return FALSE;
2194
2195 if (ri->flags & G_URI_PARAMS_PARSE_RELAXED)
2196 decode_flags |= G_URI_FLAGS_PARSE_RELAXED;
2197
2198 /* Check if each character in @attr is a separator, by indexing by the
2199 * character value into the @sep_table, which has value 1 stored at an
2200 * index if that index is a separator. */
2201 for (val_end = ri->attr; val_end < ri->end; val_end++)
2202 if (ri->sep_table[*(guchar *)val_end])
2203 break;
2204
2205 attr_end = memchr (s: ri->attr, c: '=', n: val_end - ri->attr);
2206 if (!attr_end)
2207 {
2208 g_set_error_literal (err: error, G_URI_ERROR, code: G_URI_ERROR_FAILED,
2209 _("Missing ‘=’ and parameter value"));
2210 return FALSE;
2211 }
2212 if (!uri_decode (out: &decoded_attr, NULL, start: ri->attr, length: attr_end - ri->attr,
2213 www_form, flags: decode_flags, parse_error: G_URI_ERROR_FAILED, error))
2214 {
2215 return FALSE;
2216 }
2217
2218 val = attr_end + 1;
2219 if (!uri_decode (out: &decoded_value, NULL, start: val, length: val_end - val,
2220 www_form, flags: decode_flags, parse_error: G_URI_ERROR_FAILED, error))
2221 {
2222 g_free (mem: decoded_attr);
2223 return FALSE;
2224 }
2225
2226 if (attribute)
2227 *attribute = g_steal_pointer (&decoded_attr);
2228 if (value)
2229 *value = g_steal_pointer (&decoded_value);
2230
2231 g_free (mem: decoded_attr);
2232 g_free (mem: decoded_value);
2233
2234 ri->attr = val_end + 1;
2235 return TRUE;
2236}
2237
2238/**
2239 * g_uri_parse_params:
2240 * @params: a `%`-encoded string containing `attribute=value`
2241 * parameters
2242 * @length: the length of @params, or `-1` if it is nul-terminated
2243 * @separators: the separator byte character set between parameters. (usually
2244 * `&`, but sometimes `;` or both `&;`). Note that this function works on
2245 * bytes not characters, so it can't be used to delimit UTF-8 strings for
2246 * anything but ASCII characters. You may pass an empty set, in which case
2247 * no splitting will occur.
2248 * @flags: flags to modify the way the parameters are handled.
2249 * @error: #GError for error reporting, or %NULL to ignore.
2250 *
2251 * Many URI schemes include one or more attribute/value pairs as part of the URI
2252 * value. This method can be used to parse them into a hash table. When an
2253 * attribute has multiple occurrences, the last value is the final returned
2254 * value. If you need to handle repeated attributes differently, use
2255 * #GUriParamsIter.
2256 *
2257 * The @params string is assumed to still be `%`-encoded, but the returned
2258 * values will be fully decoded. (Thus it is possible that the returned values
2259 * may contain `=` or @separators, if the value was encoded in the input.)
2260 * Invalid `%`-encoding is treated as with the %G_URI_FLAGS_PARSE_RELAXED
2261 * rules for g_uri_parse(). (However, if @params is the path or query string
2262 * from a #GUri that was parsed without %G_URI_FLAGS_PARSE_RELAXED and
2263 * %G_URI_FLAGS_ENCODED, then you already know that it does not contain any
2264 * invalid encoding.)
2265 *
2266 * %G_URI_PARAMS_WWW_FORM is handled as documented for g_uri_params_iter_init().
2267 *
2268 * If %G_URI_PARAMS_CASE_INSENSITIVE is passed to @flags, attributes will be
2269 * compared case-insensitively, so a params string `attr=123&Attr=456` will only
2270 * return a single attribute–value pair, `Attr=456`. Case will be preserved in
2271 * the returned attributes.
2272 *
2273 * If @params cannot be parsed (for example, it contains two @separators
2274 * characters in a row), then @error is set and %NULL is returned.
2275 *
2276 * Return value: (transfer full) (element-type utf8 utf8):
2277 * A hash table of attribute/value pairs, with both names and values
2278 * fully-decoded; or %NULL on error.
2279 *
2280 * Since: 2.66
2281 */
2282GHashTable *
2283g_uri_parse_params (const gchar *params,
2284 gssize length,
2285 const gchar *separators,
2286 GUriParamsFlags flags,
2287 GError **error)
2288{
2289 GHashTable *hash;
2290 GUriParamsIter iter;
2291 gchar *attribute, *value;
2292 GError *err = NULL;
2293
2294 g_return_val_if_fail (length == 0 || params != NULL, NULL);
2295 g_return_val_if_fail (length >= -1, NULL);
2296 g_return_val_if_fail (separators != NULL, NULL);
2297 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2298
2299 if (flags & G_URI_PARAMS_CASE_INSENSITIVE)
2300 {
2301 hash = g_hash_table_new_full (hash_func: str_ascii_case_hash,
2302 key_equal_func: str_ascii_case_equal,
2303 key_destroy_func: g_free, value_destroy_func: g_free);
2304 }
2305 else
2306 {
2307 hash = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal,
2308 key_destroy_func: g_free, value_destroy_func: g_free);
2309 }
2310
2311 g_uri_params_iter_init (iter: &iter, params, length, separators, flags);
2312
2313 while (g_uri_params_iter_next (iter: &iter, attribute: &attribute, value: &value, error: &err))
2314 g_hash_table_insert (hash_table: hash, key: attribute, value);
2315
2316 if (err)
2317 {
2318 g_propagate_error (dest: error, g_steal_pointer (&err));
2319 g_hash_table_destroy (hash_table: hash);
2320 return NULL;
2321 }
2322
2323 return g_steal_pointer (&hash);
2324}
2325
2326/**
2327 * g_uri_get_scheme:
2328 * @uri: a #GUri
2329 *
2330 * Gets @uri's scheme. Note that this will always be all-lowercase,
2331 * regardless of the string or strings that @uri was created from.
2332 *
2333 * Return value: (not nullable): @uri's scheme.
2334 *
2335 * Since: 2.66
2336 */
2337const gchar *
2338g_uri_get_scheme (GUri *uri)
2339{
2340 g_return_val_if_fail (uri != NULL, NULL);
2341
2342 return uri->scheme;
2343}
2344
2345/**
2346 * g_uri_get_userinfo:
2347 * @uri: a #GUri
2348 *
2349 * Gets @uri's userinfo, which may contain `%`-encoding, depending on
2350 * the flags with which @uri was created.
2351 *
2352 * Return value: (nullable): @uri's userinfo.
2353 *
2354 * Since: 2.66
2355 */
2356const gchar *
2357g_uri_get_userinfo (GUri *uri)
2358{
2359 g_return_val_if_fail (uri != NULL, NULL);
2360
2361 return uri->userinfo;
2362}
2363
2364/**
2365 * g_uri_get_user:
2366 * @uri: a #GUri
2367 *
2368 * Gets the ‘username’ component of @uri's userinfo, which may contain
2369 * `%`-encoding, depending on the flags with which @uri was created.
2370 * If @uri was not created with %G_URI_FLAGS_HAS_PASSWORD or
2371 * %G_URI_FLAGS_HAS_AUTH_PARAMS, this is the same as g_uri_get_userinfo().
2372 *
2373 * Return value: (nullable): @uri's user.
2374 *
2375 * Since: 2.66
2376 */
2377const gchar *
2378g_uri_get_user (GUri *uri)
2379{
2380 g_return_val_if_fail (uri != NULL, NULL);
2381
2382 return uri->user;
2383}
2384
2385/**
2386 * g_uri_get_password:
2387 * @uri: a #GUri
2388 *
2389 * Gets @uri's password, which may contain `%`-encoding, depending on
2390 * the flags with which @uri was created. (If @uri was not created
2391 * with %G_URI_FLAGS_HAS_PASSWORD then this will be %NULL.)
2392 *
2393 * Return value: (nullable): @uri's password.
2394 *
2395 * Since: 2.66
2396 */
2397const gchar *
2398g_uri_get_password (GUri *uri)
2399{
2400 g_return_val_if_fail (uri != NULL, NULL);
2401
2402 return uri->password;
2403}
2404
2405/**
2406 * g_uri_get_auth_params:
2407 * @uri: a #GUri
2408 *
2409 * Gets @uri's authentication parameters, which may contain
2410 * `%`-encoding, depending on the flags with which @uri was created.
2411 * (If @uri was not created with %G_URI_FLAGS_HAS_AUTH_PARAMS then this will
2412 * be %NULL.)
2413 *
2414 * Depending on the URI scheme, g_uri_parse_params() may be useful for
2415 * further parsing this information.
2416 *
2417 * Return value: (nullable): @uri's authentication parameters.
2418 *
2419 * Since: 2.66
2420 */
2421const gchar *
2422g_uri_get_auth_params (GUri *uri)
2423{
2424 g_return_val_if_fail (uri != NULL, NULL);
2425
2426 return uri->auth_params;
2427}
2428
2429/**
2430 * g_uri_get_host:
2431 * @uri: a #GUri
2432 *
2433 * Gets @uri's host. This will never have `%`-encoded characters,
2434 * unless it is non-UTF-8 (which can only be the case if @uri was
2435 * created with %G_URI_FLAGS_NON_DNS).
2436 *
2437 * If @uri contained an IPv6 address literal, this value will be just
2438 * that address, without the brackets around it that are necessary in
2439 * the string form of the URI. Note that in this case there may also
2440 * be a scope ID attached to the address. Eg, `fe80::1234%``em1` (or
2441 * `fe80::1234%``25em1` if the string is still encoded).
2442 *
2443 * Return value: (nullable): @uri's host.
2444 *
2445 * Since: 2.66
2446 */
2447const gchar *
2448g_uri_get_host (GUri *uri)
2449{
2450 g_return_val_if_fail (uri != NULL, NULL);
2451
2452 return uri->host;
2453}
2454
2455/**
2456 * g_uri_get_port:
2457 * @uri: a #GUri
2458 *
2459 * Gets @uri's port.
2460 *
2461 * Return value: @uri's port, or `-1` if no port was specified.
2462 *
2463 * Since: 2.66
2464 */
2465gint
2466g_uri_get_port (GUri *uri)
2467{
2468 g_return_val_if_fail (uri != NULL, -1);
2469
2470 if (uri->port == -1 && uri->flags & G_URI_FLAGS_SCHEME_NORMALIZE)
2471 return default_scheme_port (scheme: uri->scheme);
2472
2473 return uri->port;
2474}
2475
2476/**
2477 * g_uri_get_path:
2478 * @uri: a #GUri
2479 *
2480 * Gets @uri's path, which may contain `%`-encoding, depending on the
2481 * flags with which @uri was created.
2482 *
2483 * Return value: (not nullable): @uri's path.
2484 *
2485 * Since: 2.66
2486 */
2487const gchar *
2488g_uri_get_path (GUri *uri)
2489{
2490 g_return_val_if_fail (uri != NULL, NULL);
2491
2492 return uri->path;
2493}
2494
2495/**
2496 * g_uri_get_query:
2497 * @uri: a #GUri
2498 *
2499 * Gets @uri's query, which may contain `%`-encoding, depending on the
2500 * flags with which @uri was created.
2501 *
2502 * For queries consisting of a series of `name=value` parameters,
2503 * #GUriParamsIter or g_uri_parse_params() may be useful.
2504 *
2505 * Return value: (nullable): @uri's query.
2506 *
2507 * Since: 2.66
2508 */
2509const gchar *
2510g_uri_get_query (GUri *uri)
2511{
2512 g_return_val_if_fail (uri != NULL, NULL);
2513
2514 return uri->query;
2515}
2516
2517/**
2518 * g_uri_get_fragment:
2519 * @uri: a #GUri
2520 *
2521 * Gets @uri's fragment, which may contain `%`-encoding, depending on
2522 * the flags with which @uri was created.
2523 *
2524 * Return value: (nullable): @uri's fragment.
2525 *
2526 * Since: 2.66
2527 */
2528const gchar *
2529g_uri_get_fragment (GUri *uri)
2530{
2531 g_return_val_if_fail (uri != NULL, NULL);
2532
2533 return uri->fragment;
2534}
2535
2536
2537/**
2538 * g_uri_get_flags:
2539 * @uri: a #GUri
2540 *
2541 * Gets @uri's flags set upon construction.
2542 *
2543 * Return value: @uri's flags.
2544 *
2545 * Since: 2.66
2546 **/
2547GUriFlags
2548g_uri_get_flags (GUri *uri)
2549{
2550 g_return_val_if_fail (uri != NULL, G_URI_FLAGS_NONE);
2551
2552 return uri->flags;
2553}
2554
2555/**
2556 * g_uri_unescape_segment:
2557 * @escaped_string: (nullable): A string, may be %NULL
2558 * @escaped_string_end: (nullable): Pointer to end of @escaped_string,
2559 * may be %NULL
2560 * @illegal_characters: (nullable): An optional string of illegal
2561 * characters not to be allowed, may be %NULL
2562 *
2563 * Unescapes a segment of an escaped string.
2564 *
2565 * If any of the characters in @illegal_characters or the NUL
2566 * character appears as an escaped character in @escaped_string, then
2567 * that is an error and %NULL will be returned. This is useful if you
2568 * want to avoid for instance having a slash being expanded in an
2569 * escaped path element, which might confuse pathname handling.
2570 *
2571 * Note: `NUL` byte is not accepted in the output, in contrast to
2572 * g_uri_unescape_bytes().
2573 *
2574 * Returns: (nullable): an unescaped version of @escaped_string,
2575 * or %NULL on error. The returned string should be freed when no longer
2576 * needed. As a special case if %NULL is given for @escaped_string, this
2577 * function will return %NULL.
2578 *
2579 * Since: 2.16
2580 **/
2581gchar *
2582g_uri_unescape_segment (const gchar *escaped_string,
2583 const gchar *escaped_string_end,
2584 const gchar *illegal_characters)
2585{
2586 gchar *unescaped;
2587 gsize length;
2588 gssize decoded_len;
2589
2590 if (!escaped_string)
2591 return NULL;
2592
2593 if (escaped_string_end)
2594 length = escaped_string_end - escaped_string;
2595 else
2596 length = strlen (s: escaped_string);
2597
2598 decoded_len = uri_decoder (out: &unescaped,
2599 illegal_chars: illegal_characters,
2600 start: escaped_string, length,
2601 FALSE, FALSE,
2602 flags: G_URI_FLAGS_ENCODED,
2603 parse_error: 0, NULL);
2604 if (decoded_len < 0)
2605 return NULL;
2606
2607 if (memchr (s: unescaped, c: '\0', n: decoded_len))
2608 {
2609 g_free (mem: unescaped);
2610 return NULL;
2611 }
2612
2613 return unescaped;
2614}
2615
2616/**
2617 * g_uri_unescape_string:
2618 * @escaped_string: an escaped string to be unescaped.
2619 * @illegal_characters: (nullable): a string of illegal characters
2620 * not to be allowed, or %NULL.
2621 *
2622 * Unescapes a whole escaped string.
2623 *
2624 * If any of the characters in @illegal_characters or the NUL
2625 * character appears as an escaped character in @escaped_string, then
2626 * that is an error and %NULL will be returned. This is useful if you
2627 * want to avoid for instance having a slash being expanded in an
2628 * escaped path element, which might confuse pathname handling.
2629 *
2630 * Returns: (nullable): an unescaped version of @escaped_string.
2631 * The returned string should be freed when no longer needed.
2632 *
2633 * Since: 2.16
2634 **/
2635gchar *
2636g_uri_unescape_string (const gchar *escaped_string,
2637 const gchar *illegal_characters)
2638{
2639 return g_uri_unescape_segment (escaped_string, NULL, illegal_characters);
2640}
2641
2642/**
2643 * g_uri_escape_string:
2644 * @unescaped: the unescaped input string.
2645 * @reserved_chars_allowed: (nullable): a string of reserved
2646 * characters that are allowed to be used, or %NULL.
2647 * @allow_utf8: %TRUE if the result can include UTF-8 characters.
2648 *
2649 * Escapes a string for use in a URI.
2650 *
2651 * Normally all characters that are not "unreserved" (i.e. ASCII
2652 * alphanumerical characters plus dash, dot, underscore and tilde) are
2653 * escaped. But if you specify characters in @reserved_chars_allowed
2654 * they are not escaped. This is useful for the "reserved" characters
2655 * in the URI specification, since those are allowed unescaped in some
2656 * portions of a URI.
2657 *
2658 * Returns: (not nullable): an escaped version of @unescaped. The
2659 * returned string should be freed when no longer needed.
2660 *
2661 * Since: 2.16
2662 **/
2663gchar *
2664g_uri_escape_string (const gchar *unescaped,
2665 const gchar *reserved_chars_allowed,
2666 gboolean allow_utf8)
2667{
2668 GString *s;
2669
2670 g_return_val_if_fail (unescaped != NULL, NULL);
2671
2672 s = g_string_sized_new (dfl_size: strlen (s: unescaped) * 1.25);
2673
2674 g_string_append_uri_escaped (string: s, unescaped, reserved_chars_allowed, allow_utf8);
2675
2676 return g_string_free (string: s, FALSE);
2677}
2678
2679/**
2680 * g_uri_unescape_bytes:
2681 * @escaped_string: A URI-escaped string
2682 * @length: the length (in bytes) of @escaped_string to escape, or `-1` if it
2683 * is nul-terminated.
2684 * @illegal_characters: (nullable): a string of illegal characters
2685 * not to be allowed, or %NULL.
2686 * @error: #GError for error reporting, or %NULL to ignore.
2687 *
2688 * Unescapes a segment of an escaped string as binary data.
2689 *
2690 * Note that in contrast to g_uri_unescape_string(), this does allow
2691 * nul bytes to appear in the output.
2692 *
2693 * If any of the characters in @illegal_characters appears as an escaped
2694 * character in @escaped_string, then that is an error and %NULL will be
2695 * returned. This is useful if you want to avoid for instance having a slash
2696 * being expanded in an escaped path element, which might confuse pathname
2697 * handling.
2698 *
2699 * Returns: (transfer full): an unescaped version of @escaped_string
2700 * or %NULL on error (if decoding failed, using %G_URI_ERROR_FAILED error
2701 * code). The returned #GBytes should be unreffed when no longer needed.
2702 *
2703 * Since: 2.66
2704 **/
2705GBytes *
2706g_uri_unescape_bytes (const gchar *escaped_string,
2707 gssize length,
2708 const char *illegal_characters,
2709 GError **error)
2710{
2711 gchar *buf;
2712 gssize unescaped_length;
2713
2714 g_return_val_if_fail (escaped_string != NULL, NULL);
2715 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2716
2717 if (length == -1)
2718 length = strlen (s: escaped_string);
2719
2720 unescaped_length = uri_decoder (out: &buf,
2721 illegal_chars: illegal_characters,
2722 start: escaped_string, length,
2723 FALSE,
2724 FALSE,
2725 flags: G_URI_FLAGS_ENCODED,
2726 parse_error: G_URI_ERROR_FAILED, error);
2727 if (unescaped_length == -1)
2728 return NULL;
2729
2730 return g_bytes_new_take (data: buf, size: unescaped_length);
2731}
2732
2733/**
2734 * g_uri_escape_bytes:
2735 * @unescaped: (array length=length): the unescaped input data.
2736 * @length: the length of @unescaped
2737 * @reserved_chars_allowed: (nullable): a string of reserved
2738 * characters that are allowed to be used, or %NULL.
2739 *
2740 * Escapes arbitrary data for use in a URI.
2741 *
2742 * Normally all characters that are not ‘unreserved’ (i.e. ASCII
2743 * alphanumerical characters plus dash, dot, underscore and tilde) are
2744 * escaped. But if you specify characters in @reserved_chars_allowed
2745 * they are not escaped. This is useful for the ‘reserved’ characters
2746 * in the URI specification, since those are allowed unescaped in some
2747 * portions of a URI.
2748 *
2749 * Though technically incorrect, this will also allow escaping nul
2750 * bytes as `%``00`.
2751 *
2752 * Returns: (not nullable) (transfer full): an escaped version of @unescaped.
2753 * The returned string should be freed when no longer needed.
2754 *
2755 * Since: 2.66
2756 */
2757gchar *
2758g_uri_escape_bytes (const guint8 *unescaped,
2759 gsize length,
2760 const gchar *reserved_chars_allowed)
2761{
2762 GString *string;
2763
2764 g_return_val_if_fail (unescaped != NULL, NULL);
2765
2766 string = g_string_sized_new (dfl_size: length * 1.25);
2767
2768 _uri_encoder (out: string, start: unescaped, length,
2769 reserved_chars_allowed, FALSE);
2770
2771 return g_string_free (string, FALSE);
2772}
2773
2774static gssize
2775g_uri_scheme_length (const gchar *uri)
2776{
2777 const gchar *p;
2778
2779 p = uri;
2780 if (!g_ascii_isalpha (*p))
2781 return -1;
2782 p++;
2783 while (g_ascii_isalnum (*p) || *p == '.' || *p == '+' || *p == '-')
2784 p++;
2785
2786 if (p > uri && *p == ':')
2787 return p - uri;
2788
2789 return -1;
2790}
2791
2792/**
2793 * g_uri_parse_scheme:
2794 * @uri: a valid URI.
2795 *
2796 * Gets the scheme portion of a URI string.
2797 * [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3) decodes the scheme
2798 * as:
2799 * |[
2800 * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
2801 * ]|
2802 * Common schemes include `file`, `https`, `svn+ssh`, etc.
2803 *
2804 * Returns: (transfer full) (nullable): The ‘scheme’ component of the URI, or
2805 * %NULL on error. The returned string should be freed when no longer needed.
2806 *
2807 * Since: 2.16
2808 **/
2809gchar *
2810g_uri_parse_scheme (const gchar *uri)
2811{
2812 gssize len;
2813
2814 g_return_val_if_fail (uri != NULL, NULL);
2815
2816 len = g_uri_scheme_length (uri);
2817 return len == -1 ? NULL : g_strndup (str: uri, n: len);
2818}
2819
2820/**
2821 * g_uri_peek_scheme:
2822 * @uri: a valid URI.
2823 *
2824 * Gets the scheme portion of a URI string.
2825 * [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3) decodes the scheme
2826 * as:
2827 * |[
2828 * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
2829 * ]|
2830 * Common schemes include `file`, `https`, `svn+ssh`, etc.
2831 *
2832 * Unlike g_uri_parse_scheme(), the returned scheme is normalized to
2833 * all-lowercase and does not need to be freed.
2834 *
2835 * Returns: (transfer none) (nullable): The ‘scheme’ component of the URI, or
2836 * %NULL on error. The returned string is normalized to all-lowercase, and
2837 * interned via g_intern_string(), so it does not need to be freed.
2838 *
2839 * Since: 2.66
2840 **/
2841const gchar *
2842g_uri_peek_scheme (const gchar *uri)
2843{
2844 gssize len;
2845 gchar *lower_scheme;
2846 const gchar *scheme;
2847
2848 g_return_val_if_fail (uri != NULL, NULL);
2849
2850 len = g_uri_scheme_length (uri);
2851 if (len == -1)
2852 return NULL;
2853
2854 lower_scheme = g_ascii_strdown (str: uri, len);
2855 scheme = g_intern_string (string: lower_scheme);
2856 g_free (mem: lower_scheme);
2857
2858 return scheme;
2859}
2860
2861G_DEFINE_QUARK (g-uri-quark, g_uri_error)
2862

source code of gtk/subprojects/glib/glib/guri.c