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