LCOV - code coverage report
Current view: top level - glib - guri.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 100.0 % 813 813
Test Date: 2026-01-13 05:17:23 Functions: 100.0 % 58 58
Branches: - 0 0

             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                 :         276 : g_uri_clear (GUri *uri)
     234                 :             : {
     235                 :         276 :   g_free (uri->scheme);
     236                 :         276 :   g_free (uri->userinfo);
     237                 :         276 :   g_free (uri->host);
     238                 :         276 :   g_free (uri->path);
     239                 :         276 :   g_free (uri->query);
     240                 :         276 :   g_free (uri->fragment);
     241                 :         276 :   g_free (uri->user);
     242                 :         276 :   g_free (uri->password);
     243                 :         276 :   g_free (uri->auth_params);
     244                 :         276 : }
     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                 :         277 : g_uri_unref (GUri *uri)
     259                 :             : {
     260                 :         277 :   g_return_if_fail (uri != NULL);
     261                 :             : 
     262                 :         277 :   g_atomic_rc_box_release_full (uri, (GDestroyNotify)g_uri_clear);
     263                 :             : }
     264                 :             : 
     265                 :             : static gboolean
     266                 :       17913 : g_uri_char_is_unreserved (gchar ch)
     267                 :             : {
     268                 :       17913 :   if (g_ascii_isalnum (ch))
     269                 :       14535 :     return TRUE;
     270                 :        3378 :   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                 :       14395 : 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                 :       14395 :   if (!(flags & G_URI_FLAGS_ENCODED))
     293                 :        2855 :     just_normalize = FALSE;
     294                 :             : 
     295                 :       14395 :   decoded = g_string_sized_new (length + 1);
     296                 :      216210 :   for (s = start, end = s + length; s < end; s++)
     297                 :             :     {
     298                 :      201831 :       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                 :      201300 :       else if (www_form && *s == '+')
     345                 :          12 :         g_string_append_c (decoded, ' ');
     346                 :             :       /* Normalize any illegal characters. */
     347                 :      201288 :       else if (just_normalize && (!g_ascii_isgraph (*s)))
     348                 :          22 :         g_string_append_printf (decoded, "%%%02X", (guchar)*s);
     349                 :             :       else
     350                 :      201266 :         g_string_append_c (decoded, *s);
     351                 :             :     }
     352                 :             : 
     353                 :       14379 :   len = decoded->len;
     354                 :       14379 :   g_assert (len >= 0);
     355                 :             : 
     356                 :       17231 :   if (!(flags & G_URI_FLAGS_ENCODED) &&
     357                 :        2852 :       !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                 :       14355 :   if (out)
     366                 :       13078 :     *out = g_string_free (decoded, FALSE);
     367                 :             :   else
     368                 :        1277 :     g_string_free (decoded, TRUE);
     369                 :             : 
     370                 :       14355 :   return len;
     371                 :             : }
     372                 :             : 
     373                 :             : static gboolean
     374                 :         686 : 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                 :         686 :   return uri_decoder (out, illegal_chars, start, length, FALSE, www_form, flags,
     384                 :         686 :                       parse_error, error) != -1;
     385                 :             : }
     386                 :             : 
     387                 :             : static gboolean
     388                 :        2365 : uri_normalize (gchar       **out,
     389                 :             :                const gchar  *start,
     390                 :             :                gsize         length,
     391                 :             :                GUriFlags     flags,
     392                 :             :                GUriError     parse_error,
     393                 :             :                GError      **error)
     394                 :             : {
     395                 :        2365 :   return uri_decoder (out, NULL, start, length, TRUE, FALSE, flags,
     396                 :        2365 :                       parse_error, error) != -1;
     397                 :             : }
     398                 :             : 
     399                 :             : static gboolean
     400                 :       17889 : is_valid (guchar       c,
     401                 :             :           const gchar *reserved_chars_allowed)
     402                 :             : {
     403                 :       17889 :   if (g_uri_char_is_unreserved (c))
     404                 :       16184 :     return TRUE;
     405                 :             : 
     406                 :        1705 :   if (reserved_chars_allowed && strchr (reserved_chars_allowed, c))
     407                 :         694 :     return TRUE;
     408                 :             : 
     409                 :        1011 :   return FALSE;
     410                 :             : }
     411                 :             : 
     412                 :             : void
     413                 :        2106 : _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                 :        2106 :   const guchar *p = start;
     421                 :        2106 :   const guchar *end = p + length;
     422                 :             : 
     423                 :       20006 :   while (p < end)
     424                 :             :     {
     425                 :       17900 :       gunichar multibyte_utf8_char = 0;
     426                 :             : 
     427                 :       17900 :       if (allow_utf8 && *p >= 0x80)
     428                 :          14 :         multibyte_utf8_char = g_utf8_get_char_validated ((gchar *)p, end - p);
     429                 :             : 
     430                 :       17900 :       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                 :       17889 :       else if (is_valid (*p, reserved_chars_allowed))
     438                 :             :         {
     439                 :       16878 :           g_string_append_c (out, *p);
     440                 :       16878 :           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                 :        2106 : }
     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                 :        1510 : parse_host (const gchar  *start,
     548                 :             :             gsize         length,
     549                 :             :             GUriFlags     flags,
     550                 :             :             gchar       **out,
     551                 :             :             GError      **error)
     552                 :             : {
     553                 :        1510 :   gchar *decoded = NULL, *host;
     554                 :        1510 :   gchar *addr = NULL;
     555                 :             : 
     556                 :        1510 :   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                 :        1282 :   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                 :         463 :   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                 :         461 :   flags &= ~G_URI_FLAGS_ENCODED;
     584                 :         461 :   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                 :         460 :   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                 :         459 :   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                 :         453 :       host = g_steal_pointer (&decoded);
     615                 :             :     }
     616                 :             : 
     617                 :        1491 :  ok:
     618                 :        1491 :   if (out)
     619                 :         868 :     *out = g_steal_pointer (&host);
     620                 :        1491 :   g_free (host);
     621                 :        1491 :   g_free (decoded);
     622                 :             : 
     623                 :        1491 :   return TRUE;
     624                 :             : }
     625                 :             : 
     626                 :             : static gboolean
     627                 :        1166 : 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                 :        1166 :   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                 :        1165 :   parsed_port = strtoul (start, &end, 10);
     648                 :        1165 :   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                 :        1162 :   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                 :        1161 :   if (out)
     664                 :         634 :     *out = parsed_port;
     665                 :        1161 :   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                 :          71 : should_normalize_empty_path (const char *scheme)
     759                 :             : {
     760                 :          71 :   const char * const schemes[] = { "https", "http", "wss", "ws" };
     761                 :             :   gsize i;
     762                 :         259 :   for (i = 0; i < G_N_ELEMENTS (schemes); ++i)
     763                 :             :     {
     764                 :         221 :       if (!strcmp (schemes[i], scheme))
     765                 :          33 :         return TRUE;
     766                 :             :     }
     767                 :          38 :   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                 :          64 : g_uri_get_default_scheme_port (const char *scheme)
     805                 :             : {
     806                 :          64 :   if (strcmp (scheme, "http") == 0 || strcmp (scheme, "ws") == 0)
     807                 :          10 :     return 80;
     808                 :             : 
     809                 :          54 :   if (strcmp (scheme, "https") == 0 || strcmp (scheme, "wss") == 0)
     810                 :           4 :     return 443;
     811                 :             : 
     812                 :          50 :   if (strcmp (scheme, "ftp") == 0)
     813                 :           3 :     return 21;
     814                 :             : 
     815                 :          47 :   if (strstr (scheme, "socks") == scheme)
     816                 :          10 :     return 1080;
     817                 :             : 
     818                 :          37 :   return -1;
     819                 :             : }
     820                 :             : 
     821                 :             : static gboolean
     822                 :        1778 : 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                 :        1778 :   gchar *cleaned_uri_string = NULL;
     839                 :        1778 :   gchar *normalized_scheme = NULL;
     840                 :             : 
     841                 :        1778 :   if (scheme)
     842                 :        1723 :     *scheme = NULL;
     843                 :        1778 :   if (userinfo)
     844                 :         272 :     *userinfo = NULL;
     845                 :        1778 :   if (user)
     846                 :         553 :     *user = NULL;
     847                 :        1778 :   if (password)
     848                 :         553 :     *password = NULL;
     849                 :        1778 :   if (auth_params)
     850                 :         525 :     *auth_params = NULL;
     851                 :        1778 :   if (host)
     852                 :        1134 :     *host = NULL;
     853                 :        1778 :   if (port)
     854                 :        1159 :     *port = -1;
     855                 :        1778 :   if (path)
     856                 :         568 :     *path = NULL;
     857                 :        1778 :   if (query)
     858                 :         531 :     *query = NULL;
     859                 :        1778 :   if (fragment)
     860                 :         530 :     *fragment = NULL;
     861                 :             : 
     862                 :        1778 :   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                 :        1778 :   p = uri_string;
     870                 :        8057 :   while (*p && (g_ascii_isalpha (*p) ||
     871                 :        1557 :                (p > uri_string && (g_ascii_isdigit (*p) ||
     872                 :        1547 :                                    *p == '.' || *p == '+' || *p == '-'))))
     873                 :        6279 :     p++;
     874                 :             : 
     875                 :        1778 :   if (p > uri_string && *p == ':')
     876                 :             :     {
     877                 :        1415 :       normalized_scheme = g_ascii_strdown (uri_string, p - uri_string);
     878                 :        1415 :       if (scheme)
     879                 :        1360 :         *scheme = g_steal_pointer (&normalized_scheme);
     880                 :        1415 :       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                 :        1778 :   if (strncmp (p, "//", 2) == 0)
     891                 :             :     {
     892                 :        1517 :       p += 2;
     893                 :             : 
     894                 :        1517 :       path_start = p + strcspn (p, "/?#");
     895                 :        1517 :       at = memchr (p, '@', path_start - p);
     896                 :        1517 :       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                 :        1510 :       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                 :        1510 :       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                 :        1282 :         colon = memchr (p, ':', path_start - p);
     964                 :             : 
     965                 :        1510 :       hostend = colon ? colon : path_start;
     966                 :        1510 :       if (!parse_host (p, hostend - p, flags, host, error))
     967                 :          19 :         goto fail;
     968                 :             : 
     969                 :        1491 :       if (colon && colon != path_start - 1)
     970                 :             :         {
     971                 :        1166 :           p = colon + 1;
     972                 :        1166 :           if (!parse_port (p, path_start - p, port, error))
     973                 :           5 :             goto fail;
     974                 :             :         }
     975                 :             : 
     976                 :        1486 :       p = path_start;
     977                 :             :     }
     978                 :             : 
     979                 :             :   /* Find fragment. */
     980                 :        1747 :   end = p + strcspn (p, "#");
     981                 :        1747 :   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                 :        1746 :   question = memchr (p, '?', end - p);
     991                 :        1746 :   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                 :        1745 :   if (!uri_normalize (path, p, end - p,
    1001                 :        1745 :                       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                 :        1743 :   if (flags & G_URI_FLAGS_SCHEME_NORMALIZE && ((scheme && *scheme) || normalized_scheme))
    1007                 :             :     {
    1008                 :          49 :       const char *scheme_str = scheme && *scheme ? *scheme : normalized_scheme;
    1009                 :             : 
    1010                 :          49 :       if (should_normalize_empty_path (scheme_str) && path && !**path)
    1011                 :             :         {
    1012                 :          17 :           g_free (*path);
    1013                 :          17 :           *path = g_strdup ("/");
    1014                 :             :         }
    1015                 :             : 
    1016                 :          49 :       if (port && *port == -1)
    1017                 :          31 :         *port = g_uri_get_default_scheme_port (scheme_str);
    1018                 :             :     }
    1019                 :             : 
    1020                 :        1743 :   g_free (normalized_scheme);
    1021                 :        1743 :   g_free (cleaned_uri_string);
    1022                 :        1743 :   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                 :         602 : 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                 :         602 :   gchar *my_scheme = NULL, *my_host = NULL;
    1206                 :             : 
    1207                 :         602 :   g_return_val_if_fail (uri_string != NULL, FALSE);
    1208                 :         602 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    1209                 :             : 
    1210                 :         602 :   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                 :         591 :   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                 :         586 :   if (scheme)
    1237                 :         527 :     *scheme = g_steal_pointer (&my_scheme);
    1238                 :         586 :   if (host)
    1239                 :         585 :     *host = g_steal_pointer (&my_host);
    1240                 :             : 
    1241                 :         586 :   g_free (my_scheme);
    1242                 :         586 :   g_free (my_host);
    1243                 :             : 
    1244                 :         586 :   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                 :         574 : g_uri_is_valid (const gchar  *uri_string,
    1268                 :             :                 GUriFlags     flags,
    1269                 :             :                 GError      **error)
    1270                 :             : {
    1271                 :         574 :   gchar *my_scheme = NULL;
    1272                 :             : 
    1273                 :         574 :   g_return_val_if_fail (uri_string != NULL, FALSE);
    1274                 :         574 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    1275                 :             : 
    1276                 :         574 :   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                 :         566 :   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                 :         561 :   g_free (my_scheme);
    1291                 :             : 
    1292                 :         561 :   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                 :         239 : 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                 :         239 :   gchar *input = path;
    1309                 :         239 :   gchar *output = path;
    1310                 :             : 
    1311                 :         239 :   if (!*path)
    1312                 :          33 :     return;
    1313                 :             : 
    1314                 :         768 :   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                 :         562 :       if (strncmp (input, "../", 3) == 0)
    1320                 :           2 :         input += 3;
    1321                 :         560 :       else if (strncmp (input, "./", 2) == 0)
    1322                 :           3 :         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                 :         557 :       else if (strncmp (input, "/./", 3) == 0)
    1329                 :          18 :         input += 2;
    1330                 :         539 :       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                 :         535 :       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                 :         492 :       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                 :         483 :       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                 :         479 :           *output++ = *input++;
    1378                 :        1316 :           while (*input && *input != '/')
    1379                 :         837 :             *output++ = *input++;
    1380                 :             :         }
    1381                 :             :     }
    1382                 :         206 :   *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                 :         138 : g_uri_parse (const gchar  *uri_string,
    1401                 :             :              GUriFlags     flags,
    1402                 :             :              GError      **error)
    1403                 :             : {
    1404                 :         138 :   g_return_val_if_fail (uri_string != NULL, NULL);
    1405                 :         138 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    1406                 :             : 
    1407                 :         138 :   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                 :         266 : g_uri_parse_relative (GUri         *base_uri,
    1428                 :             :                       const gchar  *uri_ref,
    1429                 :             :                       GUriFlags     flags,
    1430                 :             :                       GError      **error)
    1431                 :             : {
    1432                 :         266 :   GUri *uri = NULL;
    1433                 :             : 
    1434                 :         266 :   g_return_val_if_fail (uri_ref != NULL, NULL);
    1435                 :         266 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    1436                 :         266 :   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                 :         266 :   uri = g_atomic_rc_box_new0 (GUri);
    1441                 :         266 :   uri->flags = flags;
    1442                 :             : 
    1443                 :         266 :   if (!g_uri_split_internal (uri_ref, flags,
    1444                 :         266 :                              &uri->scheme, &uri->userinfo,
    1445                 :         266 :                              &uri->user, &uri->password, &uri->auth_params,
    1446                 :         266 :                              &uri->host, &uri->port,
    1447                 :         266 :                              &uri->path, &uri->query, &uri->fragment,
    1448                 :             :                              error))
    1449                 :             :     {
    1450                 :          10 :       g_uri_unref (uri);
    1451                 :          10 :       return NULL;
    1452                 :             :     }
    1453                 :             : 
    1454                 :         256 :   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                 :         245 :   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                 :         139 :       remove_dot_segments (uri->path);
    1536                 :             :     }
    1537                 :             : 
    1538                 :             :   /* Fix up the invalid cases from
    1539                 :             :    * https://datatracker.ietf.org/doc/html/rfc3986#section-3, as otherwise
    1540                 :             :    * calling g_uri_to_string() on this URI will fail. These can be caused by
    1541                 :             :    * remove_dot_segments(), e.g. `data:/.//` gets normalised to `data://` whose
    1542                 :             :    * path is invalid given the lack of an authority. */
    1543                 :         245 :   if (uri->host == NULL && uri->path[0] == '/' && uri->path[1] == '/')
    1544                 :             :     {
    1545                 :           2 :       char *new_path = g_strconcat ("/.", uri->path, NULL);
    1546                 :           2 :       g_free (uri->path);
    1547                 :           2 :       uri->path = g_steal_pointer (&new_path);
    1548                 :             :     }
    1549                 :             : 
    1550                 :         245 :   return g_steal_pointer (&uri);
    1551                 :             : }
    1552                 :             : 
    1553                 :             : /**
    1554                 :             :  * g_uri_resolve_relative:
    1555                 :             :  * @base_uri_string: (nullable): a string representing a base URI
    1556                 :             :  * @uri_ref: a string representing a relative or absolute URI
    1557                 :             :  * @flags: flags describing how to parse @uri_ref
    1558                 :             :  * @error: #GError for error reporting, or %NULL to ignore.
    1559                 :             :  *
    1560                 :             :  * Parses @uri_ref according to @flags and, if it is a
    1561                 :             :  * [relative URI](#relative-and-absolute-uris), resolves it relative to
    1562                 :             :  * @base_uri_string. If the result is not a valid absolute URI, it will be
    1563                 :             :  * discarded, and an error returned.
    1564                 :             :  *
    1565                 :             :  * (If @base_uri_string is %NULL, this just returns @uri_ref, or
    1566                 :             :  * %NULL if @uri_ref is invalid or not absolute.)
    1567                 :             :  *
    1568                 :             :  * Return value: (transfer full): the resolved URI string,
    1569                 :             :  * or NULL on error.
    1570                 :             :  *
    1571                 :             :  * Since: 2.66
    1572                 :             :  */
    1573                 :             : gchar *
    1574                 :          55 : g_uri_resolve_relative (const gchar  *base_uri_string,
    1575                 :             :                         const gchar  *uri_ref,
    1576                 :             :                         GUriFlags     flags,
    1577                 :             :                         GError      **error)
    1578                 :             : {
    1579                 :             :   GUri *base_uri, *resolved_uri;
    1580                 :             :   gchar *resolved_uri_string;
    1581                 :             : 
    1582                 :          55 :   g_return_val_if_fail (uri_ref != NULL, NULL);
    1583                 :          55 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    1584                 :             : 
    1585                 :          55 :   flags |= G_URI_FLAGS_ENCODED;
    1586                 :             : 
    1587                 :          55 :   if (base_uri_string)
    1588                 :             :     {
    1589                 :          53 :       base_uri = g_uri_parse (base_uri_string, flags, error);
    1590                 :          53 :       if (!base_uri)
    1591                 :           2 :         return NULL;
    1592                 :             :     }
    1593                 :             :   else
    1594                 :           2 :     base_uri = NULL;
    1595                 :             : 
    1596                 :          53 :   resolved_uri = g_uri_parse_relative (base_uri, uri_ref, flags, error);
    1597                 :          53 :   if (base_uri)
    1598                 :          51 :     g_uri_unref (base_uri);
    1599                 :          53 :   if (!resolved_uri)
    1600                 :           1 :     return NULL;
    1601                 :             : 
    1602                 :          52 :   resolved_uri_string = g_uri_to_string (resolved_uri);
    1603                 :          52 :   g_uri_unref (resolved_uri);
    1604                 :          52 :   return g_steal_pointer (&resolved_uri_string);
    1605                 :             : }
    1606                 :             : 
    1607                 :             : /* userinfo as a whole can contain sub-delims + ":", but split-out
    1608                 :             :  * user can't contain ":" or ";", and split-out password can't contain
    1609                 :             :  * ";".
    1610                 :             :  */
    1611                 :             : #define USERINFO_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO
    1612                 :             : #define USER_ALLOWED_CHARS "!$&'()*+,="
    1613                 :             : #define PASSWORD_ALLOWED_CHARS "!$&'()*+,=:"
    1614                 :             : #define AUTH_PARAMS_ALLOWED_CHARS USERINFO_ALLOWED_CHARS
    1615                 :             : #define IP_ADDR_ALLOWED_CHARS ":"
    1616                 :             : #define HOST_ALLOWED_CHARS G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS
    1617                 :             : #define PATH_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH
    1618                 :             : #define QUERY_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH "?"
    1619                 :             : #define FRAGMENT_ALLOWED_CHARS G_URI_RESERVED_CHARS_ALLOWED_IN_PATH "?"
    1620                 :             : 
    1621                 :             : static gchar *
    1622                 :         879 : g_uri_join_internal (GUriFlags    flags,
    1623                 :             :                      const gchar *scheme,
    1624                 :             :                      gboolean     userinfo,
    1625                 :             :                      const gchar *user,
    1626                 :             :                      const gchar *password,
    1627                 :             :                      const gchar *auth_params,
    1628                 :             :                      const gchar *host,
    1629                 :             :                      gint         port,
    1630                 :             :                      const gchar *path,
    1631                 :             :                      const gchar *query,
    1632                 :             :                      const gchar *fragment)
    1633                 :             : {
    1634                 :         879 :   gboolean encoded = (flags & G_URI_FLAGS_ENCODED);
    1635                 :             :   GString *str;
    1636                 :         879 :   char *normalized_scheme = NULL;
    1637                 :             : 
    1638                 :             :   /* Restrictions on path prefixes. See:
    1639                 :             :    * https://tools.ietf.org/html/rfc3986#section-3
    1640                 :             :    */
    1641                 :         879 :   g_return_val_if_fail (path != NULL, NULL);
    1642                 :         879 :   g_return_val_if_fail (host == NULL || (path[0] == '\0' || path[0] == '/'), NULL);
    1643                 :         879 :   g_return_val_if_fail (host != NULL || (path[0] != '/' || path[1] != '/'), NULL);
    1644                 :             : 
    1645                 :             :   /* Arbitrarily chosen default size which should handle most average length
    1646                 :             :    * URIs. This should avoid a few reallocations of the buffer in most cases.
    1647                 :             :    * It’s 1B shorter than a power of two, since GString will add a
    1648                 :             :    * nul-terminator byte. */
    1649                 :         879 :   str = g_string_sized_new (127);
    1650                 :             : 
    1651                 :         879 :   if (scheme)
    1652                 :             :     {
    1653                 :             :       g_string_append (str, scheme);
    1654                 :             :       g_string_append_c (str, ':');
    1655                 :             :     }
    1656                 :             : 
    1657                 :         879 :   if (flags & G_URI_FLAGS_SCHEME_NORMALIZE && scheme && ((host && port != -1) || path[0] == '\0'))
    1658                 :          26 :     normalized_scheme = g_ascii_strdown (scheme, -1);
    1659                 :             : 
    1660                 :         879 :   if (host)
    1661                 :             :     {
    1662                 :         731 :       g_string_append (str, "//");
    1663                 :             : 
    1664                 :         731 :       if (user)
    1665                 :             :         {
    1666                 :          79 :           if (encoded)
    1667                 :             :             g_string_append (str, user);
    1668                 :             :           else
    1669                 :             :             {
    1670                 :          77 :               if (userinfo)
    1671                 :           4 :                 g_string_append_uri_escaped (str, user, USERINFO_ALLOWED_CHARS, TRUE);
    1672                 :             :               else
    1673                 :             :                 /* Encode ':' and ';' regardless of whether we have a
    1674                 :             :                  * password or auth params, since it may be parsed later
    1675                 :             :                  * under the assumption that it does.
    1676                 :             :                  */
    1677                 :          73 :                 g_string_append_uri_escaped (str, user, USER_ALLOWED_CHARS, TRUE);
    1678                 :             :             }
    1679                 :             : 
    1680                 :          79 :           if (password)
    1681                 :             :             {
    1682                 :             :               g_string_append_c (str, ':');
    1683                 :          41 :               if (encoded)
    1684                 :             :                 g_string_append (str, password);
    1685                 :             :               else
    1686                 :          40 :                 g_string_append_uri_escaped (str, password,
    1687                 :             :                                              PASSWORD_ALLOWED_CHARS, TRUE);
    1688                 :             :             }
    1689                 :             : 
    1690                 :          79 :           if (auth_params)
    1691                 :             :             {
    1692                 :             :               g_string_append_c (str, ';');
    1693                 :          41 :               if (encoded)
    1694                 :             :                 g_string_append (str, auth_params);
    1695                 :             :               else
    1696                 :          40 :                 g_string_append_uri_escaped (str, auth_params,
    1697                 :             :                                              AUTH_PARAMS_ALLOWED_CHARS, TRUE);
    1698                 :             :             }
    1699                 :             : 
    1700                 :             :           g_string_append_c (str, '@');
    1701                 :             :         }
    1702                 :             : 
    1703                 :         731 :       if (strchr (host, ':') && g_hostname_is_ip_address (host))
    1704                 :             :         {
    1705                 :             :           g_string_append_c (str, '[');
    1706                 :          91 :           if (encoded)
    1707                 :             :             g_string_append (str, host);
    1708                 :             :           else
    1709                 :          90 :             g_string_append_uri_escaped (str, host, IP_ADDR_ALLOWED_CHARS, TRUE);
    1710                 :          91 :           g_string_append_c (str, ']');
    1711                 :             :         }
    1712                 :             :       else
    1713                 :             :         {
    1714                 :         640 :           if (encoded)
    1715                 :             :             g_string_append (str, host);
    1716                 :             :           else
    1717                 :         593 :             g_string_append_uri_escaped (str, host, HOST_ALLOWED_CHARS, TRUE);
    1718                 :             :         }
    1719                 :             : 
    1720                 :         731 :       if (port != -1 && (!normalized_scheme || normalize_port (normalized_scheme, port) != -1))
    1721                 :         551 :         g_string_append_printf (str, ":%d", port);
    1722                 :             :     }
    1723                 :             : 
    1724                 :         879 :   if (path[0] == '\0' && normalized_scheme && should_normalize_empty_path (normalized_scheme))
    1725                 :          16 :     g_string_append (str, "/");
    1726                 :         871 :   else if (encoded || flags & G_URI_FLAGS_ENCODED_PATH)
    1727                 :          57 :     g_string_append (str, path);
    1728                 :             :   else
    1729                 :         814 :     g_string_append_uri_escaped (str, path, PATH_ALLOWED_CHARS, TRUE);
    1730                 :             : 
    1731                 :         879 :   g_free (normalized_scheme);
    1732                 :             : 
    1733                 :         879 :   if (query)
    1734                 :             :     {
    1735                 :             :       g_string_append_c (str, '?');
    1736                 :         159 :       if (encoded || flags & G_URI_FLAGS_ENCODED_QUERY)
    1737                 :          10 :         g_string_append (str, query);
    1738                 :             :       else
    1739                 :         149 :         g_string_append_uri_escaped (str, query, QUERY_ALLOWED_CHARS, TRUE);
    1740                 :             :     }
    1741                 :         879 :   if (fragment)
    1742                 :             :     {
    1743                 :             :       g_string_append_c (str, '#');
    1744                 :         154 :       if (encoded || flags & G_URI_FLAGS_ENCODED_FRAGMENT)
    1745                 :           8 :         g_string_append (str, fragment);
    1746                 :             :       else
    1747                 :         146 :         g_string_append_uri_escaped (str, fragment, FRAGMENT_ALLOWED_CHARS, TRUE);
    1748                 :             :     }
    1749                 :             : 
    1750                 :         879 :   return g_string_free (str, FALSE);
    1751                 :             : }
    1752                 :             : 
    1753                 :             : /**
    1754                 :             :  * g_uri_join:
    1755                 :             :  * @flags: flags describing how to build the URI string
    1756                 :             :  * @scheme: (nullable): the URI scheme, or %NULL
    1757                 :             :  * @userinfo: (nullable): the userinfo component, or %NULL
    1758                 :             :  * @host: (nullable): the host component, or %NULL
    1759                 :             :  * @port: the port, or `-1`
    1760                 :             :  * @path: (not nullable): the path component
    1761                 :             :  * @query: (nullable): the query component, or %NULL
    1762                 :             :  * @fragment: (nullable): the fragment, or %NULL
    1763                 :             :  *
    1764                 :             :  * Joins the given components together according to @flags to create
    1765                 :             :  * an absolute URI string. @path may not be %NULL (though it may be the empty
    1766                 :             :  * string).
    1767                 :             :  *
    1768                 :             :  * When @host is present, @path must either be empty or begin with a slash (`/`)
    1769                 :             :  * character. When @host is not present, @path cannot begin with two slash
    1770                 :             :  * characters (`//`). See
    1771                 :             :  * [RFC 3986, section 3](https://tools.ietf.org/html/rfc3986#section-3).
    1772                 :             :  *
    1773                 :             :  * See also g_uri_join_with_user(), which allows specifying the
    1774                 :             :  * components of the ‘userinfo’ separately.
    1775                 :             :  *
    1776                 :             :  * %G_URI_FLAGS_HAS_PASSWORD and %G_URI_FLAGS_HAS_AUTH_PARAMS are ignored if set
    1777                 :             :  * in @flags.
    1778                 :             :  *
    1779                 :             :  * Return value: (not nullable) (transfer full): an absolute URI string
    1780                 :             :  *
    1781                 :             :  * Since: 2.66
    1782                 :             :  */
    1783                 :             : gchar *
    1784                 :         612 : g_uri_join (GUriFlags    flags,
    1785                 :             :             const gchar *scheme,
    1786                 :             :             const gchar *userinfo,
    1787                 :             :             const gchar *host,
    1788                 :             :             gint         port,
    1789                 :             :             const gchar *path,
    1790                 :             :             const gchar *query,
    1791                 :             :             const gchar *fragment)
    1792                 :             : {
    1793                 :         612 :   g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
    1794                 :         612 :   g_return_val_if_fail (path != NULL, NULL);
    1795                 :             : 
    1796                 :         612 :   return g_uri_join_internal (flags,
    1797                 :             :                               scheme,
    1798                 :             :                               TRUE, userinfo, NULL, NULL,
    1799                 :             :                               host,
    1800                 :             :                               port,
    1801                 :             :                               path,
    1802                 :             :                               query,
    1803                 :             :                               fragment);
    1804                 :             : }
    1805                 :             : 
    1806                 :             : /**
    1807                 :             :  * g_uri_join_with_user:
    1808                 :             :  * @flags: flags describing how to build the URI string
    1809                 :             :  * @scheme: (nullable): the URI scheme, or %NULL
    1810                 :             :  * @user: (nullable): the user component of the userinfo, or %NULL
    1811                 :             :  * @password: (nullable): the password component of the userinfo, or
    1812                 :             :  *   %NULL
    1813                 :             :  * @auth_params: (nullable): the auth params of the userinfo, or
    1814                 :             :  *   %NULL
    1815                 :             :  * @host: (nullable): the host component, or %NULL
    1816                 :             :  * @port: the port, or `-1`
    1817                 :             :  * @path: (not nullable): the path component
    1818                 :             :  * @query: (nullable): the query component, or %NULL
    1819                 :             :  * @fragment: (nullable): the fragment, or %NULL
    1820                 :             :  *
    1821                 :             :  * Joins the given components together according to @flags to create
    1822                 :             :  * an absolute URI string. @path may not be %NULL (though it may be the empty
    1823                 :             :  * string).
    1824                 :             :  *
    1825                 :             :  * In contrast to g_uri_join(), this allows specifying the components
    1826                 :             :  * of the ‘userinfo’ separately. It otherwise behaves the same.
    1827                 :             :  *
    1828                 :             :  * %G_URI_FLAGS_HAS_PASSWORD and %G_URI_FLAGS_HAS_AUTH_PARAMS are ignored if set
    1829                 :             :  * in @flags.
    1830                 :             :  *
    1831                 :             :  * Return value: (not nullable) (transfer full): an absolute URI string
    1832                 :             :  *
    1833                 :             :  * Since: 2.66
    1834                 :             :  */
    1835                 :             : gchar *
    1836                 :         267 : g_uri_join_with_user (GUriFlags    flags,
    1837                 :             :                       const gchar *scheme,
    1838                 :             :                       const gchar *user,
    1839                 :             :                       const gchar *password,
    1840                 :             :                       const gchar *auth_params,
    1841                 :             :                       const gchar *host,
    1842                 :             :                       gint         port,
    1843                 :             :                       const gchar *path,
    1844                 :             :                       const gchar *query,
    1845                 :             :                       const gchar *fragment)
    1846                 :             : {
    1847                 :         267 :   g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
    1848                 :         267 :   g_return_val_if_fail (path != NULL, NULL);
    1849                 :             : 
    1850                 :         267 :   return g_uri_join_internal (flags,
    1851                 :             :                               scheme,
    1852                 :             :                               FALSE, user, password, auth_params,
    1853                 :             :                               host,
    1854                 :             :                               port,
    1855                 :             :                               path,
    1856                 :             :                               query,
    1857                 :             :                               fragment);
    1858                 :             : }
    1859                 :             : 
    1860                 :             : /**
    1861                 :             :  * g_uri_build:
    1862                 :             :  * @flags: flags describing how to build the #GUri
    1863                 :             :  * @scheme: (not nullable): the URI scheme
    1864                 :             :  * @userinfo: (nullable): the userinfo component, or %NULL
    1865                 :             :  * @host: (nullable): the host component, or %NULL
    1866                 :             :  * @port: the port, or `-1`
    1867                 :             :  * @path: (not nullable): the path component
    1868                 :             :  * @query: (nullable): the query component, or %NULL
    1869                 :             :  * @fragment: (nullable): the fragment, or %NULL
    1870                 :             :  *
    1871                 :             :  * Creates a new #GUri from the given components according to @flags.
    1872                 :             :  *
    1873                 :             :  * See also g_uri_build_with_user(), which allows specifying the
    1874                 :             :  * components of the "userinfo" separately.
    1875                 :             :  *
    1876                 :             :  * Return value: (not nullable) (transfer full): a new #GUri
    1877                 :             :  *
    1878                 :             :  * Since: 2.66
    1879                 :             :  */
    1880                 :             : GUri *
    1881                 :           3 : g_uri_build (GUriFlags    flags,
    1882                 :             :              const gchar *scheme,
    1883                 :             :              const gchar *userinfo,
    1884                 :             :              const gchar *host,
    1885                 :             :              gint         port,
    1886                 :             :              const gchar *path,
    1887                 :             :              const gchar *query,
    1888                 :             :              const gchar *fragment)
    1889                 :             : {
    1890                 :             :   GUri *uri;
    1891                 :             : 
    1892                 :           3 :   g_return_val_if_fail (scheme != NULL, NULL);
    1893                 :           3 :   g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
    1894                 :           3 :   g_return_val_if_fail (path != NULL, NULL);
    1895                 :             : 
    1896                 :           3 :   uri = g_atomic_rc_box_new0 (GUri);
    1897                 :           3 :   uri->flags = flags;
    1898                 :           3 :   uri->scheme = g_ascii_strdown (scheme, -1);
    1899                 :           3 :   uri->userinfo = g_strdup (userinfo);
    1900                 :           3 :   uri->host = g_strdup (host);
    1901                 :           3 :   uri->port = port;
    1902                 :           3 :   uri->path = g_strdup (path);
    1903                 :           3 :   uri->query = g_strdup (query);
    1904                 :           3 :   uri->fragment = g_strdup (fragment);
    1905                 :             : 
    1906                 :           3 :   return g_steal_pointer (&uri);
    1907                 :             : }
    1908                 :             : 
    1909                 :             : /**
    1910                 :             :  * g_uri_build_with_user:
    1911                 :             :  * @flags: flags describing how to build the #GUri
    1912                 :             :  * @scheme: (not nullable): the URI scheme
    1913                 :             :  * @user: (nullable): the user component of the userinfo, or %NULL
    1914                 :             :  * @password: (nullable): the password component of the userinfo, or %NULL
    1915                 :             :  * @auth_params: (nullable): the auth params of the userinfo, or %NULL
    1916                 :             :  * @host: (nullable): the host component, or %NULL
    1917                 :             :  * @port: the port, or `-1`
    1918                 :             :  * @path: (not nullable): the path component
    1919                 :             :  * @query: (nullable): the query component, or %NULL
    1920                 :             :  * @fragment: (nullable): the fragment, or %NULL
    1921                 :             :  *
    1922                 :             :  * Creates a new #GUri from the given components according to @flags
    1923                 :             :  * (%G_URI_FLAGS_HAS_PASSWORD is added unconditionally). The @flags must be
    1924                 :             :  * coherent with the passed values, in particular use `%`-encoded values with
    1925                 :             :  * %G_URI_FLAGS_ENCODED.
    1926                 :             :  *
    1927                 :             :  * In contrast to g_uri_build(), this allows specifying the components
    1928                 :             :  * of the ‘userinfo’ field separately. Note that @user must be non-%NULL
    1929                 :             :  * if either @password or @auth_params is non-%NULL.
    1930                 :             :  *
    1931                 :             :  * Return value: (not nullable) (transfer full): a new #GUri
    1932                 :             :  *
    1933                 :             :  * Since: 2.66
    1934                 :             :  */
    1935                 :             : GUri *
    1936                 :           7 : g_uri_build_with_user (GUriFlags    flags,
    1937                 :             :                        const gchar *scheme,
    1938                 :             :                        const gchar *user,
    1939                 :             :                        const gchar *password,
    1940                 :             :                        const gchar *auth_params,
    1941                 :             :                        const gchar *host,
    1942                 :             :                        gint         port,
    1943                 :             :                        const gchar *path,
    1944                 :             :                        const gchar *query,
    1945                 :             :                        const gchar *fragment)
    1946                 :             : {
    1947                 :             :   GUri *uri;
    1948                 :             :   GString *userinfo;
    1949                 :             : 
    1950                 :           7 :   g_return_val_if_fail (scheme != NULL, NULL);
    1951                 :           7 :   g_return_val_if_fail (password == NULL || user != NULL, NULL);
    1952                 :           7 :   g_return_val_if_fail (auth_params == NULL || user != NULL, NULL);
    1953                 :           7 :   g_return_val_if_fail (port >= -1 && port <= 65535, NULL);
    1954                 :           7 :   g_return_val_if_fail (path != NULL, NULL);
    1955                 :             : 
    1956                 :           7 :   uri = g_atomic_rc_box_new0 (GUri);
    1957                 :           7 :   uri->flags = flags | G_URI_FLAGS_HAS_PASSWORD;
    1958                 :           7 :   uri->scheme = g_ascii_strdown (scheme, -1);
    1959                 :           7 :   uri->user = g_strdup (user);
    1960                 :           7 :   uri->password = g_strdup (password);
    1961                 :           7 :   uri->auth_params = g_strdup (auth_params);
    1962                 :           7 :   uri->host = g_strdup (host);
    1963                 :           7 :   uri->port = port;
    1964                 :           7 :   uri->path = g_strdup (path);
    1965                 :           7 :   uri->query = g_strdup (query);
    1966                 :           7 :   uri->fragment = g_strdup (fragment);
    1967                 :             : 
    1968                 :           7 :   if (user)
    1969                 :             :     {
    1970                 :           6 :       userinfo = g_string_new (user);
    1971                 :           6 :       if (password)
    1972                 :             :         {
    1973                 :             :           g_string_append_c (userinfo, ':');
    1974                 :           5 :           g_string_append (userinfo, uri->password);
    1975                 :             :         }
    1976                 :           6 :       if (auth_params)
    1977                 :             :         {
    1978                 :             :           g_string_append_c (userinfo, ';');
    1979                 :           5 :           g_string_append (userinfo, uri->auth_params);
    1980                 :             :         }
    1981                 :           6 :       uri->userinfo = g_string_free (userinfo, FALSE);
    1982                 :             :     }
    1983                 :             : 
    1984                 :           7 :   return g_steal_pointer (&uri);
    1985                 :             : }
    1986                 :             : 
    1987                 :             : /**
    1988                 :             :  * g_uri_to_string:
    1989                 :             :  * @uri: a #GUri
    1990                 :             :  *
    1991                 :             :  * Returns a string representing @uri.
    1992                 :             :  *
    1993                 :             :  * This is not guaranteed to return a string which is identical to the
    1994                 :             :  * string that @uri was parsed from. However, if the source URI was
    1995                 :             :  * syntactically correct (according to RFC 3986), and it was parsed
    1996                 :             :  * with %G_URI_FLAGS_ENCODED, then g_uri_to_string() is guaranteed to return
    1997                 :             :  * a string which is at least semantically equivalent to the source
    1998                 :             :  * URI (according to RFC 3986).
    1999                 :             :  *
    2000                 :             :  * If @uri might contain sensitive details, such as authentication parameters,
    2001                 :             :  * or private data in its query string, and the returned string is going to be
    2002                 :             :  * logged, then consider using g_uri_to_string_partial() to redact parts.
    2003                 :             :  *
    2004                 :             :  * Return value: (not nullable) (transfer full): a string representing @uri,
    2005                 :             :  *     which the caller must free.
    2006                 :             :  *
    2007                 :             :  * Since: 2.66
    2008                 :             :  */
    2009                 :             : gchar *
    2010                 :         130 : g_uri_to_string (GUri *uri)
    2011                 :             : {
    2012                 :         130 :   g_return_val_if_fail (uri != NULL, NULL);
    2013                 :             : 
    2014                 :         130 :   return g_uri_to_string_partial (uri, G_URI_HIDE_NONE);
    2015                 :             : }
    2016                 :             : 
    2017                 :             : /**
    2018                 :             :  * g_uri_to_string_partial:
    2019                 :             :  * @uri: a #GUri
    2020                 :             :  * @flags: flags describing what parts of @uri to hide
    2021                 :             :  *
    2022                 :             :  * Returns a string representing @uri, subject to the options in
    2023                 :             :  * @flags. See g_uri_to_string() and #GUriHideFlags for more details.
    2024                 :             :  *
    2025                 :             :  * Return value: (not nullable) (transfer full): a string representing
    2026                 :             :  *     @uri, which the caller must free.
    2027                 :             :  *
    2028                 :             :  * Since: 2.66
    2029                 :             :  */
    2030                 :             : gchar *
    2031                 :         136 : g_uri_to_string_partial (GUri          *uri,
    2032                 :             :                          GUriHideFlags  flags)
    2033                 :             : {
    2034                 :         136 :   gboolean hide_user = (flags & G_URI_HIDE_USERINFO);
    2035                 :         136 :   gboolean hide_password = (flags & (G_URI_HIDE_USERINFO | G_URI_HIDE_PASSWORD));
    2036                 :         136 :   gboolean hide_auth_params = (flags & (G_URI_HIDE_USERINFO | G_URI_HIDE_AUTH_PARAMS));
    2037                 :         136 :   gboolean hide_query = (flags & G_URI_HIDE_QUERY);
    2038                 :         136 :   gboolean hide_fragment = (flags & G_URI_HIDE_FRAGMENT);
    2039                 :             : 
    2040                 :         136 :   g_return_val_if_fail (uri != NULL, NULL);
    2041                 :             : 
    2042                 :         136 :   if (uri->flags & (G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_HAS_AUTH_PARAMS))
    2043                 :             :     {
    2044                 :          24 :       return g_uri_join_with_user (uri->flags,
    2045                 :           8 :                                    uri->scheme,
    2046                 :             :                                    hide_user ? NULL : uri->user,
    2047                 :             :                                    hide_password ? NULL : uri->password,
    2048                 :             :                                    hide_auth_params ? NULL : uri->auth_params,
    2049                 :           8 :                                    uri->host,
    2050                 :             :                                    uri->port,
    2051                 :           8 :                                    uri->path,
    2052                 :             :                                    hide_query ? NULL : uri->query,
    2053                 :             :                                    hide_fragment ? NULL : uri->fragment);
    2054                 :             :     }
    2055                 :             : 
    2056                 :         256 :   return g_uri_join (uri->flags,
    2057                 :         128 :                      uri->scheme,
    2058                 :             :                      hide_user ? NULL : uri->userinfo,
    2059                 :         128 :                      uri->host,
    2060                 :             :                      uri->port,
    2061                 :         128 :                      uri->path,
    2062                 :             :                      hide_query ? NULL : uri->query,
    2063                 :             :                      hide_fragment ? NULL : uri->fragment);
    2064                 :             : }
    2065                 :             : 
    2066                 :             : /* This is just a copy of g_str_hash() with g_ascii_toupper() added */
    2067                 :             : static guint
    2068                 :           6 : str_ascii_case_hash (gconstpointer v)
    2069                 :             : {
    2070                 :             :   const signed char *p;
    2071                 :           6 :   guint32 h = 5381;
    2072                 :             : 
    2073                 :          18 :   for (p = v; *p != '\0'; p++)
    2074                 :          12 :     h = (h << 5) + h + g_ascii_toupper (*p);
    2075                 :             : 
    2076                 :           6 :   return h;
    2077                 :             : }
    2078                 :             : 
    2079                 :             : static gboolean
    2080                 :           4 : str_ascii_case_equal (gconstpointer v1,
    2081                 :             :                       gconstpointer v2)
    2082                 :             : {
    2083                 :           4 :   const gchar *string1 = v1;
    2084                 :           4 :   const gchar *string2 = v2;
    2085                 :             : 
    2086                 :           4 :   return g_ascii_strcasecmp (string1, string2) == 0;
    2087                 :             : }
    2088                 :             : 
    2089                 :             : /**
    2090                 :             :  * GUriParamsIter:
    2091                 :             :  *
    2092                 :             :  * Many URI schemes include one or more attribute/value pairs as part of the URI
    2093                 :             :  * value. For example `scheme://server/path?query=string&is=there` has two
    2094                 :             :  * attributes – `query=string` and `is=there` – in its query part.
    2095                 :             :  *
    2096                 :             :  * A #GUriParamsIter structure represents an iterator that can be used to
    2097                 :             :  * iterate over the attribute/value pairs of a URI query string. #GUriParamsIter
    2098                 :             :  * structures are typically allocated on the stack and then initialized with
    2099                 :             :  * g_uri_params_iter_init(). See the documentation for g_uri_params_iter_init()
    2100                 :             :  * for a usage example.
    2101                 :             :  *
    2102                 :             :  * Since: 2.66
    2103                 :             :  */
    2104                 :             : typedef struct
    2105                 :             : {
    2106                 :             :   GUriParamsFlags flags;
    2107                 :             :   const gchar    *attr;
    2108                 :             :   const gchar    *end;
    2109                 :             :   guint8          sep_table[256]; /* 1 = index is a separator; 0 otherwise */
    2110                 :             : } RealIter;
    2111                 :             : 
    2112                 :             : G_STATIC_ASSERT (sizeof (GUriParamsIter) == sizeof (RealIter));
    2113                 :             : G_STATIC_ASSERT (G_ALIGNOF (GUriParamsIter) >= G_ALIGNOF (RealIter));
    2114                 :             : 
    2115                 :             : /**
    2116                 :             :  * g_uri_params_iter_init:
    2117                 :             :  * @iter: an uninitialized #GUriParamsIter
    2118                 :             :  * @params: a `%`-encoded string containing `attribute=value`
    2119                 :             :  *   parameters
    2120                 :             :  * @length: the length of @params, or `-1` if it is nul-terminated
    2121                 :             :  * @separators: the separator byte character set between parameters. (usually
    2122                 :             :  *   `&`, but sometimes `;` or both `&;`). Note that this function works on
    2123                 :             :  *   bytes not characters, so it can't be used to delimit UTF-8 strings for
    2124                 :             :  *   anything but ASCII characters. You may pass an empty set, in which case
    2125                 :             :  *   no splitting will occur.
    2126                 :             :  * @flags: flags to modify the way the parameters are handled.
    2127                 :             :  *
    2128                 :             :  * Initializes an attribute/value pair iterator.
    2129                 :             :  *
    2130                 :             :  * The iterator keeps pointers to the @params and @separators arguments, those
    2131                 :             :  * variables must thus outlive the iterator and not be modified during the
    2132                 :             :  * iteration.
    2133                 :             :  *
    2134                 :             :  * If %G_URI_PARAMS_WWW_FORM is passed in @flags, `+` characters in the param
    2135                 :             :  * string will be replaced with spaces in the output. For example, `foo=bar+baz`
    2136                 :             :  * will give attribute `foo` with value `bar baz`. This is commonly used on the
    2137                 :             :  * web (the `https` and `http` schemes only), but is deprecated in favour of
    2138                 :             :  * the equivalent of encoding spaces as `%20`.
    2139                 :             :  *
    2140                 :             :  * Unlike with g_uri_parse_params(), %G_URI_PARAMS_CASE_INSENSITIVE has no
    2141                 :             :  * effect if passed to @flags for g_uri_params_iter_init(). The caller is
    2142                 :             :  * responsible for doing their own case-insensitive comparisons.
    2143                 :             :  *
    2144                 :             :  * |[<!-- language="C" -->
    2145                 :             :  * GUriParamsIter iter;
    2146                 :             :  * GError *error = NULL;
    2147                 :             :  * gchar *unowned_attr, *unowned_value;
    2148                 :             :  *
    2149                 :             :  * g_uri_params_iter_init (&iter, "foo=bar&baz=bar&Foo=frob&baz=bar2", -1, "&", G_URI_PARAMS_NONE);
    2150                 :             :  * while (g_uri_params_iter_next (&iter, &unowned_attr, &unowned_value, &error))
    2151                 :             :  *   {
    2152                 :             :  *     g_autofree gchar *attr = g_steal_pointer (&unowned_attr);
    2153                 :             :  *     g_autofree gchar *value = g_steal_pointer (&unowned_value);
    2154                 :             :  *     // do something with attr and value; this code will be called 4 times
    2155                 :             :  *     // for the params string in this example: once with attr=foo and value=bar,
    2156                 :             :  *     // then with baz/bar, then Foo/frob, then baz/bar2.
    2157                 :             :  *   }
    2158                 :             :  * if (error)
    2159                 :             :  *   // handle parsing error
    2160                 :             :  * ]|
    2161                 :             :  *
    2162                 :             :  * Since: 2.66
    2163                 :             :  */
    2164                 :             : void
    2165                 :          79 : g_uri_params_iter_init (GUriParamsIter *iter,
    2166                 :             :                         const gchar    *params,
    2167                 :             :                         gssize          length,
    2168                 :             :                         const gchar    *separators,
    2169                 :             :                         GUriParamsFlags flags)
    2170                 :             : {
    2171                 :          79 :   RealIter *ri = (RealIter *)iter;
    2172                 :             :   const gchar *s;
    2173                 :             : 
    2174                 :          79 :   g_return_if_fail (iter != NULL);
    2175                 :          79 :   g_return_if_fail (length == 0 || params != NULL);
    2176                 :          79 :   g_return_if_fail (length >= -1);
    2177                 :          79 :   g_return_if_fail (separators != NULL);
    2178                 :             : 
    2179                 :          79 :   ri->flags = flags;
    2180                 :             : 
    2181                 :          79 :   if (length == -1)
    2182                 :          65 :     ri->end = params + strlen (params);
    2183                 :             :   else
    2184                 :          14 :     ri->end = params + length;
    2185                 :             : 
    2186                 :          79 :   memset (ri->sep_table, FALSE, sizeof (ri->sep_table));
    2187                 :         158 :   for (s = separators; *s != '\0'; ++s)
    2188                 :          79 :     ri->sep_table[*(guchar *)s] = TRUE;
    2189                 :             : 
    2190                 :          79 :   ri->attr = params;
    2191                 :             : }
    2192                 :             : 
    2193                 :             : /**
    2194                 :             :  * g_uri_params_iter_next:
    2195                 :             :  * @iter: an initialized #GUriParamsIter
    2196                 :             :  * @attribute: (out) (nullable) (optional) (transfer full): on return, contains
    2197                 :             :  *     the attribute, or %NULL.
    2198                 :             :  * @value: (out) (nullable) (optional) (transfer full): on return, contains
    2199                 :             :  *     the value, or %NULL.
    2200                 :             :  * @error: #GError for error reporting, or %NULL to ignore.
    2201                 :             :  *
    2202                 :             :  * Advances @iter and retrieves the next attribute/value. %FALSE is returned if
    2203                 :             :  * an error has occurred (in which case @error is set), or if the end of the
    2204                 :             :  * iteration is reached (in which case @attribute and @value are set to %NULL
    2205                 :             :  * and the iterator becomes invalid). If %TRUE is returned,
    2206                 :             :  * g_uri_params_iter_next() may be called again to receive another
    2207                 :             :  * attribute/value pair.
    2208                 :             :  *
    2209                 :             :  * Note that the same @attribute may be returned multiple times, since URIs
    2210                 :             :  * allow repeated attributes.
    2211                 :             :  *
    2212                 :             :  * Returns: %FALSE if the end of the parameters has been reached or an error was
    2213                 :             :  *     encountered. %TRUE otherwise.
    2214                 :             :  *
    2215                 :             :  * Since: 2.66
    2216                 :             :  */
    2217                 :             : gboolean
    2218                 :         176 : g_uri_params_iter_next (GUriParamsIter *iter,
    2219                 :             :                         gchar         **attribute,
    2220                 :             :                         gchar         **value,
    2221                 :             :                         GError        **error)
    2222                 :             : {
    2223                 :         176 :   RealIter *ri = (RealIter *)iter;
    2224                 :             :   const gchar *attr_end, *val, *val_end;
    2225                 :             :   gchar *decoded_attr, *decoded_value;
    2226                 :         176 :   gboolean www_form = ri->flags & G_URI_PARAMS_WWW_FORM;
    2227                 :         176 :   GUriFlags decode_flags = G_URI_FLAGS_NONE;
    2228                 :             : 
    2229                 :         176 :   g_return_val_if_fail (iter != NULL, FALSE);
    2230                 :         176 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    2231                 :             : 
    2232                 :             :   /* Pre-clear these in case of failure or finishing. */
    2233                 :         176 :   if (attribute)
    2234                 :         118 :     *attribute = NULL;
    2235                 :         176 :   if (value)
    2236                 :         118 :     *value = NULL;
    2237                 :             : 
    2238                 :         176 :   if (ri->attr >= ri->end)
    2239                 :          55 :     return FALSE;
    2240                 :             : 
    2241                 :         121 :   if (ri->flags & G_URI_PARAMS_PARSE_RELAXED)
    2242                 :           6 :     decode_flags |= G_URI_FLAGS_PARSE_RELAXED;
    2243                 :             : 
    2244                 :             :   /* Check if each character in @attr is a separator, by indexing by the
    2245                 :             :    * character value into the @sep_table, which has value 1 stored at an
    2246                 :             :    * index if that index is a separator. */
    2247                 :        3579 :   for (val_end = ri->attr; val_end < ri->end; val_end++)
    2248                 :        3506 :     if (ri->sep_table[*(guchar *)val_end])
    2249                 :          48 :       break;
    2250                 :             : 
    2251                 :         121 :   attr_end = memchr (ri->attr, '=', val_end - ri->attr);
    2252                 :         121 :   if (!attr_end)
    2253                 :             :     {
    2254                 :          12 :       g_set_error_literal (error, G_URI_ERROR, G_URI_ERROR_FAILED,
    2255                 :             :                            _("Missing ‘=’ and parameter value"));
    2256                 :          12 :       return FALSE;
    2257                 :             :     }
    2258                 :         109 :   if (!uri_decode (&decoded_attr, NULL, ri->attr, attr_end - ri->attr,
    2259                 :             :                    www_form, decode_flags, G_URI_ERROR_FAILED, error))
    2260                 :             :     {
    2261                 :           6 :       return FALSE;
    2262                 :             :     }
    2263                 :             : 
    2264                 :         103 :   val = attr_end + 1;
    2265                 :         103 :   if (!uri_decode (&decoded_value, NULL, val, val_end - val,
    2266                 :             :                    www_form, decode_flags, G_URI_ERROR_FAILED, error))
    2267                 :             :     {
    2268                 :           6 :       g_free (decoded_attr);
    2269                 :           6 :       return FALSE;
    2270                 :             :     }
    2271                 :             : 
    2272                 :          97 :   if (attribute)
    2273                 :          65 :     *attribute = g_steal_pointer (&decoded_attr);
    2274                 :          97 :   if (value)
    2275                 :          65 :     *value = g_steal_pointer (&decoded_value);
    2276                 :             : 
    2277                 :          97 :   g_free (decoded_attr);
    2278                 :          97 :   g_free (decoded_value);
    2279                 :             : 
    2280                 :          97 :   ri->attr = val_end + 1;
    2281                 :          97 :   return TRUE;
    2282                 :             : }
    2283                 :             : 
    2284                 :             : /**
    2285                 :             :  * g_uri_parse_params:
    2286                 :             :  * @params: a `%`-encoded string containing `attribute=value`
    2287                 :             :  *   parameters
    2288                 :             :  * @length: the length of @params, or `-1` if it is nul-terminated
    2289                 :             :  * @separators: the separator byte character set between parameters. (usually
    2290                 :             :  *   `&`, but sometimes `;` or both `&;`). Note that this function works on
    2291                 :             :  *   bytes not characters, so it can't be used to delimit UTF-8 strings for
    2292                 :             :  *   anything but ASCII characters. You may pass an empty set, in which case
    2293                 :             :  *   no splitting will occur.
    2294                 :             :  * @flags: flags to modify the way the parameters are handled.
    2295                 :             :  * @error: #GError for error reporting, or %NULL to ignore.
    2296                 :             :  *
    2297                 :             :  * Many URI schemes include one or more attribute/value pairs as part of the URI
    2298                 :             :  * value. This method can be used to parse them into a hash table. When an
    2299                 :             :  * attribute has multiple occurrences, the last value is the final returned
    2300                 :             :  * value. If you need to handle repeated attributes differently, use
    2301                 :             :  * #GUriParamsIter.
    2302                 :             :  *
    2303                 :             :  * The @params string is assumed to still be `%`-encoded, but the returned
    2304                 :             :  * values will be fully decoded. (Thus it is possible that the returned values
    2305                 :             :  * may contain `=` or @separators, if the value was encoded in the input.)
    2306                 :             :  * Invalid `%`-encoding is treated as with the %G_URI_FLAGS_PARSE_RELAXED
    2307                 :             :  * rules for g_uri_parse(). (However, if @params is the path or query string
    2308                 :             :  * from a #GUri that was parsed without %G_URI_FLAGS_PARSE_RELAXED and
    2309                 :             :  * %G_URI_FLAGS_ENCODED, then you already know that it does not contain any
    2310                 :             :  * invalid encoding.)
    2311                 :             :  *
    2312                 :             :  * %G_URI_PARAMS_WWW_FORM is handled as documented for g_uri_params_iter_init().
    2313                 :             :  *
    2314                 :             :  * If %G_URI_PARAMS_CASE_INSENSITIVE is passed to @flags, attributes will be
    2315                 :             :  * compared case-insensitively, so a params string `attr=123&Attr=456` will only
    2316                 :             :  * return a single attribute–value pair, `Attr=456`. Case will be preserved in
    2317                 :             :  * the returned attributes.
    2318                 :             :  *
    2319                 :             :  * If @params cannot be parsed (for example, it contains two @separators
    2320                 :             :  * characters in a row), then @error is set and %NULL is returned.
    2321                 :             :  *
    2322                 :             :  * Return value: (transfer full) (element-type utf8 utf8):
    2323                 :             :  *     A hash table of attribute/value pairs, with both names and values
    2324                 :             :  *     fully-decoded; or %NULL on error.
    2325                 :             :  *
    2326                 :             :  * Since: 2.66
    2327                 :             :  */
    2328                 :             : GHashTable *
    2329                 :          27 : g_uri_parse_params (const gchar     *params,
    2330                 :             :                     gssize           length,
    2331                 :             :                     const gchar     *separators,
    2332                 :             :                     GUriParamsFlags  flags,
    2333                 :             :                     GError         **error)
    2334                 :             : {
    2335                 :             :   GHashTable *hash;
    2336                 :             :   GUriParamsIter iter;
    2337                 :             :   gchar *attribute, *value;
    2338                 :          27 :   GError *err = NULL;
    2339                 :             : 
    2340                 :          27 :   g_return_val_if_fail (length == 0 || params != NULL, NULL);
    2341                 :          27 :   g_return_val_if_fail (length >= -1, NULL);
    2342                 :          27 :   g_return_val_if_fail (separators != NULL, NULL);
    2343                 :          27 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    2344                 :             : 
    2345                 :          27 :   if (flags & G_URI_PARAMS_CASE_INSENSITIVE)
    2346                 :             :     {
    2347                 :           2 :       hash = g_hash_table_new_full (str_ascii_case_hash,
    2348                 :             :                                     str_ascii_case_equal,
    2349                 :             :                                     g_free, g_free);
    2350                 :             :     }
    2351                 :             :   else
    2352                 :             :     {
    2353                 :          25 :       hash = g_hash_table_new_full (g_str_hash, g_str_equal,
    2354                 :             :                                     g_free, g_free);
    2355                 :             :     }
    2356                 :             : 
    2357                 :          27 :   g_uri_params_iter_init (&iter, params, length, separators, flags);
    2358                 :             : 
    2359                 :          60 :   while (g_uri_params_iter_next (&iter, &attribute, &value, &err))
    2360                 :          33 :     g_hash_table_insert (hash, attribute, value);
    2361                 :             : 
    2362                 :          27 :   if (err)
    2363                 :             :     {
    2364                 :           8 :       g_propagate_error (error, g_steal_pointer (&err));
    2365                 :           8 :       g_hash_table_destroy (hash);
    2366                 :           8 :       return NULL;
    2367                 :             :     }
    2368                 :             : 
    2369                 :          19 :   return g_steal_pointer (&hash);
    2370                 :             : }
    2371                 :             : 
    2372                 :             : /**
    2373                 :             :  * g_uri_get_scheme:
    2374                 :             :  * @uri: a #GUri
    2375                 :             :  *
    2376                 :             :  * Gets @uri's scheme. Note that this will always be all-lowercase,
    2377                 :             :  * regardless of the string or strings that @uri was created from.
    2378                 :             :  *
    2379                 :             :  * Return value: (not nullable): @uri's scheme.
    2380                 :             :  *
    2381                 :             :  * Since: 2.66
    2382                 :             :  */
    2383                 :             : const gchar *
    2384                 :         114 : g_uri_get_scheme (GUri *uri)
    2385                 :             : {
    2386                 :         114 :   g_return_val_if_fail (uri != NULL, NULL);
    2387                 :             : 
    2388                 :         114 :   return uri->scheme;
    2389                 :             : }
    2390                 :             : 
    2391                 :             : /**
    2392                 :             :  * g_uri_get_userinfo:
    2393                 :             :  * @uri: a #GUri
    2394                 :             :  *
    2395                 :             :  * Gets @uri's userinfo, which may contain `%`-encoding, depending on
    2396                 :             :  * the flags with which @uri was created.
    2397                 :             :  *
    2398                 :             :  * Return value: (nullable): @uri's userinfo.
    2399                 :             :  *
    2400                 :             :  * Since: 2.66
    2401                 :             :  */
    2402                 :             : const gchar *
    2403                 :         118 : g_uri_get_userinfo (GUri *uri)
    2404                 :             : {
    2405                 :         118 :   g_return_val_if_fail (uri != NULL, NULL);
    2406                 :             : 
    2407                 :         118 :   return uri->userinfo;
    2408                 :             : }
    2409                 :             : 
    2410                 :             : /**
    2411                 :             :  * g_uri_get_user:
    2412                 :             :  * @uri: a #GUri
    2413                 :             :  *
    2414                 :             :  * Gets the ‘username’ component of @uri's userinfo, which may contain
    2415                 :             :  * `%`-encoding, depending on the flags with which @uri was created.
    2416                 :             :  * If @uri was not created with %G_URI_FLAGS_HAS_PASSWORD or
    2417                 :             :  * %G_URI_FLAGS_HAS_AUTH_PARAMS, this is the same as g_uri_get_userinfo().
    2418                 :             :  *
    2419                 :             :  * Return value: (nullable): @uri's user.
    2420                 :             :  *
    2421                 :             :  * Since: 2.66
    2422                 :             :  */
    2423                 :             : const gchar *
    2424                 :           2 : g_uri_get_user (GUri *uri)
    2425                 :             : {
    2426                 :           2 :   g_return_val_if_fail (uri != NULL, NULL);
    2427                 :             : 
    2428                 :           2 :   return uri->user;
    2429                 :             : }
    2430                 :             : 
    2431                 :             : /**
    2432                 :             :  * g_uri_get_password:
    2433                 :             :  * @uri: a #GUri
    2434                 :             :  *
    2435                 :             :  * Gets @uri's password, which may contain `%`-encoding, depending on
    2436                 :             :  * the flags with which @uri was created. (If @uri was not created
    2437                 :             :  * with %G_URI_FLAGS_HAS_PASSWORD then this will be %NULL.)
    2438                 :             :  *
    2439                 :             :  * Return value: (nullable): @uri's password.
    2440                 :             :  *
    2441                 :             :  * Since: 2.66
    2442                 :             :  */
    2443                 :             : const gchar *
    2444                 :           2 : g_uri_get_password (GUri *uri)
    2445                 :             : {
    2446                 :           2 :   g_return_val_if_fail (uri != NULL, NULL);
    2447                 :             : 
    2448                 :           2 :   return uri->password;
    2449                 :             : }
    2450                 :             : 
    2451                 :             : /**
    2452                 :             :  * g_uri_get_auth_params:
    2453                 :             :  * @uri: a #GUri
    2454                 :             :  *
    2455                 :             :  * Gets @uri's authentication parameters, which may contain
    2456                 :             :  * `%`-encoding, depending on the flags with which @uri was created.
    2457                 :             :  * (If @uri was not created with %G_URI_FLAGS_HAS_AUTH_PARAMS then this will
    2458                 :             :  * be %NULL.)
    2459                 :             :  *
    2460                 :             :  * Depending on the URI scheme, g_uri_parse_params() may be useful for
    2461                 :             :  * further parsing this information.
    2462                 :             :  *
    2463                 :             :  * Return value: (nullable): @uri's authentication parameters.
    2464                 :             :  *
    2465                 :             :  * Since: 2.66
    2466                 :             :  */
    2467                 :             : const gchar *
    2468                 :           1 : g_uri_get_auth_params (GUri *uri)
    2469                 :             : {
    2470                 :           1 :   g_return_val_if_fail (uri != NULL, NULL);
    2471                 :             : 
    2472                 :           1 :   return uri->auth_params;
    2473                 :             : }
    2474                 :             : 
    2475                 :             : /**
    2476                 :             :  * g_uri_get_host:
    2477                 :             :  * @uri: a #GUri
    2478                 :             :  *
    2479                 :             :  * Gets @uri's host. This will never have `%`-encoded characters,
    2480                 :             :  * unless it is non-UTF-8 (which can only be the case if @uri was
    2481                 :             :  * created with %G_URI_FLAGS_NON_DNS).
    2482                 :             :  *
    2483                 :             :  * If @uri contained an IPv6 address literal, this value will be just
    2484                 :             :  * that address, without the brackets around it that are necessary in
    2485                 :             :  * the string form of the URI. Note that in this case there may also
    2486                 :             :  * be a scope ID attached to the address. Eg, `fe80::1234%``em1` (or
    2487                 :             :  * `fe80::1234%``25em1` if the string is still encoded).
    2488                 :             :  *
    2489                 :             :  * Return value: (nullable): @uri's host.
    2490                 :             :  *
    2491                 :             :  * Since: 2.66
    2492                 :             :  */
    2493                 :             : const gchar *
    2494                 :         114 : g_uri_get_host (GUri *uri)
    2495                 :             : {
    2496                 :         114 :   g_return_val_if_fail (uri != NULL, NULL);
    2497                 :             : 
    2498                 :         114 :   return uri->host;
    2499                 :             : }
    2500                 :             : 
    2501                 :             : /**
    2502                 :             :  * g_uri_get_port:
    2503                 :             :  * @uri: a #GUri
    2504                 :             :  *
    2505                 :             :  * Gets @uri's port.
    2506                 :             :  *
    2507                 :             :  * Return value: @uri's port, or `-1` if no port was specified.
    2508                 :             :  *
    2509                 :             :  * Since: 2.66
    2510                 :             :  */
    2511                 :             : gint
    2512                 :         138 : g_uri_get_port (GUri *uri)
    2513                 :             : {
    2514                 :         138 :   g_return_val_if_fail (uri != NULL, -1);
    2515                 :             : 
    2516                 :         138 :   if (uri->port == -1 && uri->flags & G_URI_FLAGS_SCHEME_NORMALIZE)
    2517                 :           5 :     return g_uri_get_default_scheme_port (uri->scheme);
    2518                 :             : 
    2519                 :         133 :   return uri->port;
    2520                 :             : }
    2521                 :             : 
    2522                 :             : /**
    2523                 :             :  * g_uri_get_path:
    2524                 :             :  * @uri: a #GUri
    2525                 :             :  *
    2526                 :             :  * Gets @uri's path, which may contain `%`-encoding, depending on the
    2527                 :             :  * flags with which @uri was created.
    2528                 :             :  *
    2529                 :             :  * Return value: (not nullable): @uri's path.
    2530                 :             :  *
    2531                 :             :  * Since: 2.66
    2532                 :             :  */
    2533                 :             : const gchar *
    2534                 :         138 : g_uri_get_path (GUri *uri)
    2535                 :             : {
    2536                 :         138 :   g_return_val_if_fail (uri != NULL, NULL);
    2537                 :             : 
    2538                 :         138 :   return uri->path;
    2539                 :             : }
    2540                 :             : 
    2541                 :             : /**
    2542                 :             :  * g_uri_get_query:
    2543                 :             :  * @uri: a #GUri
    2544                 :             :  *
    2545                 :             :  * Gets @uri's query, which may contain `%`-encoding, depending on the
    2546                 :             :  * flags with which @uri was created.
    2547                 :             :  *
    2548                 :             :  * For queries consisting of a series of `name=value` parameters,
    2549                 :             :  * #GUriParamsIter or g_uri_parse_params() may be useful.
    2550                 :             :  *
    2551                 :             :  * Return value: (nullable): @uri's query.
    2552                 :             :  *
    2553                 :             :  * Since: 2.66
    2554                 :             :  */
    2555                 :             : const gchar *
    2556                 :         114 : g_uri_get_query (GUri *uri)
    2557                 :             : {
    2558                 :         114 :   g_return_val_if_fail (uri != NULL, NULL);
    2559                 :             : 
    2560                 :         114 :   return uri->query;
    2561                 :             : }
    2562                 :             : 
    2563                 :             : /**
    2564                 :             :  * g_uri_get_fragment:
    2565                 :             :  * @uri: a #GUri
    2566                 :             :  *
    2567                 :             :  * Gets @uri's fragment, which may contain `%`-encoding, depending on
    2568                 :             :  * the flags with which @uri was created.
    2569                 :             :  *
    2570                 :             :  * Return value: (nullable): @uri's fragment.
    2571                 :             :  *
    2572                 :             :  * Since: 2.66
    2573                 :             :  */
    2574                 :             : const gchar *
    2575                 :         114 : g_uri_get_fragment (GUri *uri)
    2576                 :             : {
    2577                 :         114 :   g_return_val_if_fail (uri != NULL, NULL);
    2578                 :             : 
    2579                 :         114 :   return uri->fragment;
    2580                 :             : }
    2581                 :             : 
    2582                 :             : 
    2583                 :             : /**
    2584                 :             :  * g_uri_get_flags:
    2585                 :             :  * @uri: a #GUri
    2586                 :             :  *
    2587                 :             :  * Gets @uri's flags set upon construction.
    2588                 :             :  *
    2589                 :             :  * Return value: @uri's flags.
    2590                 :             :  *
    2591                 :             :  * Since: 2.66
    2592                 :             :  **/
    2593                 :             : GUriFlags
    2594                 :           2 : g_uri_get_flags (GUri *uri)
    2595                 :             : {
    2596                 :           2 :   g_return_val_if_fail (uri != NULL, G_URI_FLAGS_NONE);
    2597                 :             : 
    2598                 :           2 :   return uri->flags;
    2599                 :             : }
    2600                 :             : 
    2601                 :             : /**
    2602                 :             :  * g_uri_unescape_segment:
    2603                 :             :  * @escaped_string: (nullable): A string, may be %NULL
    2604                 :             :  * @escaped_string_end: (nullable): Pointer to end of @escaped_string,
    2605                 :             :  *   may be %NULL
    2606                 :             :  * @illegal_characters: (nullable): An optional string of illegal
    2607                 :             :  *   characters not to be allowed, may be %NULL
    2608                 :             :  *
    2609                 :             :  * Unescapes a segment of an escaped string.
    2610                 :             :  *
    2611                 :             :  * If any of the characters in @illegal_characters or the NUL
    2612                 :             :  * character appears as an escaped character in @escaped_string, then
    2613                 :             :  * that is an error and %NULL will be returned. This is useful if you
    2614                 :             :  * want to avoid for instance having a slash being expanded in an
    2615                 :             :  * escaped path element, which might confuse pathname handling.
    2616                 :             :  *
    2617                 :             :  * Note: `NUL` byte is not accepted in the output, in contrast to
    2618                 :             :  * g_uri_unescape_bytes().
    2619                 :             :  *
    2620                 :             :  * Returns: (nullable): an unescaped version of @escaped_string,
    2621                 :             :  * or %NULL on error. The returned string should be freed when no longer
    2622                 :             :  * needed.  As a special case if %NULL is given for @escaped_string, this
    2623                 :             :  * function will return %NULL.
    2624                 :             :  *
    2625                 :             :  * Since: 2.16
    2626                 :             :  **/
    2627                 :             : gchar *
    2628                 :       11334 : g_uri_unescape_segment (const gchar *escaped_string,
    2629                 :             :                         const gchar *escaped_string_end,
    2630                 :             :                         const gchar *illegal_characters)
    2631                 :             : {
    2632                 :             :   gchar *unescaped;
    2633                 :             :   gsize length;
    2634                 :             :   gssize decoded_len;
    2635                 :             : 
    2636                 :       11334 :   if (!escaped_string)
    2637                 :           1 :     return NULL;
    2638                 :             : 
    2639                 :       11333 :   if (escaped_string_end)
    2640                 :       11254 :     length = escaped_string_end - escaped_string;
    2641                 :             :   else
    2642                 :          79 :     length = strlen (escaped_string);
    2643                 :             : 
    2644                 :       11333 :   decoded_len = uri_decoder (&unescaped,
    2645                 :             :                              illegal_characters,
    2646                 :             :                              escaped_string, length,
    2647                 :             :                              FALSE, FALSE,
    2648                 :             :                              G_URI_FLAGS_ENCODED,
    2649                 :             :                              0, NULL);
    2650                 :       11333 :   if (decoded_len < 0)
    2651                 :           7 :     return NULL;
    2652                 :             : 
    2653                 :       11326 :   if (memchr (unescaped, '\0', decoded_len))
    2654                 :             :     {
    2655                 :           3 :       g_free (unescaped);
    2656                 :           3 :       return NULL;
    2657                 :             :     }
    2658                 :             : 
    2659                 :       11323 :   return unescaped;
    2660                 :             : }
    2661                 :             : 
    2662                 :             : /**
    2663                 :             :  * g_uri_unescape_string:
    2664                 :             :  * @escaped_string: an escaped string to be unescaped.
    2665                 :             :  * @illegal_characters: (nullable): a string of illegal characters
    2666                 :             :  *   not to be allowed, or %NULL.
    2667                 :             :  *
    2668                 :             :  * Unescapes a whole escaped string.
    2669                 :             :  *
    2670                 :             :  * If any of the characters in @illegal_characters or the NUL
    2671                 :             :  * character appears as an escaped character in @escaped_string, then
    2672                 :             :  * that is an error and %NULL will be returned. This is useful if you
    2673                 :             :  * want to avoid for instance having a slash being expanded in an
    2674                 :             :  * escaped path element, which might confuse pathname handling.
    2675                 :             :  *
    2676                 :             :  * Returns: (nullable): an unescaped version of @escaped_string.
    2677                 :             :  * The returned string should be freed when no longer needed.
    2678                 :             :  *
    2679                 :             :  * Since: 2.16
    2680                 :             :  **/
    2681                 :             : gchar *
    2682                 :          79 : g_uri_unescape_string (const gchar *escaped_string,
    2683                 :             :                        const gchar *illegal_characters)
    2684                 :             : {
    2685                 :          79 :   return g_uri_unescape_segment (escaped_string, NULL, illegal_characters);
    2686                 :             : }
    2687                 :             : 
    2688                 :             : /**
    2689                 :             :  * g_uri_escape_string:
    2690                 :             :  * @unescaped: the unescaped input string.
    2691                 :             :  * @reserved_chars_allowed: (nullable): a string of reserved
    2692                 :             :  *   characters that are allowed to be used, or %NULL.
    2693                 :             :  * @allow_utf8: %TRUE if the result can include UTF-8 characters.
    2694                 :             :  *
    2695                 :             :  * Escapes a string for use in a URI.
    2696                 :             :  *
    2697                 :             :  * Normally all characters that are not "unreserved" (i.e. ASCII
    2698                 :             :  * alphanumerical characters plus dash, dot, underscore and tilde) are
    2699                 :             :  * escaped. But if you specify characters in @reserved_chars_allowed
    2700                 :             :  * they are not escaped. This is useful for the "reserved" characters
    2701                 :             :  * in the URI specification, since those are allowed unescaped in some
    2702                 :             :  * portions of a URI.
    2703                 :             :  *
    2704                 :             :  * Returns: (not nullable): an escaped version of @unescaped. The
    2705                 :             :  * returned string should be freed when no longer needed.
    2706                 :             :  *
    2707                 :             :  * Since: 2.16
    2708                 :             :  **/
    2709                 :             : gchar *
    2710                 :          26 : g_uri_escape_string (const gchar *unescaped,
    2711                 :             :                      const gchar *reserved_chars_allowed,
    2712                 :             :                      gboolean     allow_utf8)
    2713                 :             : {
    2714                 :             :   GString *s;
    2715                 :             : 
    2716                 :          26 :   g_return_val_if_fail (unescaped != NULL, NULL);
    2717                 :             : 
    2718                 :          26 :   s = g_string_sized_new ((size_t) (strlen (unescaped) * 1.25));
    2719                 :             : 
    2720                 :          26 :   g_string_append_uri_escaped (s, unescaped, reserved_chars_allowed, allow_utf8);
    2721                 :             : 
    2722                 :          26 :   return g_string_free (s, FALSE);
    2723                 :             : }
    2724                 :             : 
    2725                 :             : /**
    2726                 :             :  * g_uri_unescape_bytes:
    2727                 :             :  * @escaped_string: A URI-escaped string
    2728                 :             :  * @length: the length (in bytes) of @escaped_string to escape, or `-1` if it
    2729                 :             :  *   is nul-terminated.
    2730                 :             :  * @illegal_characters: (nullable): a string of illegal characters
    2731                 :             :  *   not to be allowed, or %NULL.
    2732                 :             :  * @error: #GError for error reporting, or %NULL to ignore.
    2733                 :             :  *
    2734                 :             :  * Unescapes a segment of an escaped string as binary data.
    2735                 :             :  *
    2736                 :             :  * Note that in contrast to g_uri_unescape_string(), this does allow
    2737                 :             :  * nul bytes to appear in the output.
    2738                 :             :  *
    2739                 :             :  * If any of the characters in @illegal_characters appears as an escaped
    2740                 :             :  * character in @escaped_string, then that is an error and %NULL will be
    2741                 :             :  * returned. This is useful if you want to avoid for instance having a slash
    2742                 :             :  * being expanded in an escaped path element, which might confuse pathname
    2743                 :             :  * handling.
    2744                 :             :  *
    2745                 :             :  * Returns: (transfer full): an unescaped version of @escaped_string
    2746                 :             :  *     or %NULL on error (if decoding failed, using %G_URI_ERROR_FAILED error
    2747                 :             :  *     code). The returned #GBytes should be unreffed when no longer needed.
    2748                 :             :  *
    2749                 :             :  * Since: 2.66
    2750                 :             :  **/
    2751                 :             : GBytes *
    2752                 :          11 : g_uri_unescape_bytes (const gchar *escaped_string,
    2753                 :             :                       gssize       length,
    2754                 :             :                       const char *illegal_characters,
    2755                 :             :                       GError     **error)
    2756                 :             : {
    2757                 :             :   gchar *buf;
    2758                 :             :   gssize unescaped_length;
    2759                 :             : 
    2760                 :          11 :   g_return_val_if_fail (escaped_string != NULL, NULL);
    2761                 :          11 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    2762                 :             : 
    2763                 :          11 :   if (length == -1)
    2764                 :           5 :     length = strlen (escaped_string);
    2765                 :             : 
    2766                 :          11 :   unescaped_length = uri_decoder (&buf,
    2767                 :             :                                   illegal_characters,
    2768                 :             :                                   escaped_string, length,
    2769                 :             :                                   FALSE,
    2770                 :             :                                   FALSE,
    2771                 :             :                                   G_URI_FLAGS_ENCODED,
    2772                 :             :                                   G_URI_ERROR_FAILED, error);
    2773                 :          11 :   if (unescaped_length == -1)
    2774                 :           6 :     return NULL;
    2775                 :             : 
    2776                 :           5 :   return g_bytes_new_take (buf, unescaped_length);
    2777                 :             : }
    2778                 :             : 
    2779                 :             : /**
    2780                 :             :  * g_uri_escape_bytes:
    2781                 :             :  * @unescaped: (array length=length): the unescaped input data.
    2782                 :             :  * @length: the length of @unescaped
    2783                 :             :  * @reserved_chars_allowed: (nullable): a string of reserved
    2784                 :             :  *   characters that are allowed to be used, or %NULL.
    2785                 :             :  *
    2786                 :             :  * Escapes arbitrary data for use in a URI.
    2787                 :             :  *
    2788                 :             :  * Normally all characters that are not ‘unreserved’ (i.e. ASCII
    2789                 :             :  * alphanumerical characters plus dash, dot, underscore and tilde) are
    2790                 :             :  * escaped. But if you specify characters in @reserved_chars_allowed
    2791                 :             :  * they are not escaped. This is useful for the ‘reserved’ characters
    2792                 :             :  * in the URI specification, since those are allowed unescaped in some
    2793                 :             :  * portions of a URI.
    2794                 :             :  *
    2795                 :             :  * Though technically incorrect, this will also allow escaping nul
    2796                 :             :  * bytes as `%``00`.
    2797                 :             :  *
    2798                 :             :  * Returns: (not nullable) (transfer full): an escaped version of @unescaped.
    2799                 :             :  *     The returned string should be freed when no longer needed.
    2800                 :             :  *
    2801                 :             :  * Since: 2.66
    2802                 :             :  */
    2803                 :             : gchar *
    2804                 :           2 : g_uri_escape_bytes (const guint8 *unescaped,
    2805                 :             :                     gsize         length,
    2806                 :             :                     const gchar  *reserved_chars_allowed)
    2807                 :             : {
    2808                 :             :   GString *string;
    2809                 :             : 
    2810                 :           2 :   g_return_val_if_fail (unescaped != NULL, NULL);
    2811                 :             : 
    2812                 :           2 :   string = g_string_sized_new ((size_t) (length * 1.25));
    2813                 :             : 
    2814                 :           2 :   _uri_encoder (string, unescaped, length,
    2815                 :             :                reserved_chars_allowed, FALSE);
    2816                 :             : 
    2817                 :           2 :   return g_string_free (string, FALSE);
    2818                 :             : }
    2819                 :             : 
    2820                 :             : static gssize
    2821                 :         848 : g_uri_scheme_length (const gchar *uri)
    2822                 :             : {
    2823                 :             :   const gchar *p;
    2824                 :             : 
    2825                 :         848 :   p = uri;
    2826                 :         848 :   if (!g_ascii_isalpha (*p))
    2827                 :          19 :     return -1;
    2828                 :         829 :   p++;
    2829                 :        4694 :   while (g_ascii_isalnum (*p) || *p == '.' || *p == '+' || *p == '-')
    2830                 :        3865 :     p++;
    2831                 :             : 
    2832                 :         829 :   if (p > uri && *p == ':')
    2833                 :         816 :     return p - uri;
    2834                 :             : 
    2835                 :          13 :   return -1;
    2836                 :             : }
    2837                 :             : 
    2838                 :             : /**
    2839                 :             :  * g_uri_parse_scheme:
    2840                 :             :  * @uri: a valid URI.
    2841                 :             :  *
    2842                 :             :  * Gets the scheme portion of a URI string.
    2843                 :             :  * [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3) decodes the scheme
    2844                 :             :  * as:
    2845                 :             :  * |[
    2846                 :             :  * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
    2847                 :             :  * ]|
    2848                 :             :  * Common schemes include `file`, `https`, `svn+ssh`, etc.
    2849                 :             :  *
    2850                 :             :  * Returns: (transfer full) (nullable): The ‘scheme’ component of the URI, or
    2851                 :             :  *     %NULL on error. The returned string should be freed when no longer needed.
    2852                 :             :  *
    2853                 :             :  * Since: 2.16
    2854                 :             :  **/
    2855                 :             : gchar *
    2856                 :         844 : g_uri_parse_scheme (const gchar *uri)
    2857                 :             : {
    2858                 :             :   gssize len;
    2859                 :             : 
    2860                 :         844 :   g_return_val_if_fail (uri != NULL, NULL);
    2861                 :             : 
    2862                 :         844 :   len = g_uri_scheme_length (uri);
    2863                 :         844 :   return len == -1 ? NULL : g_strndup (uri, len);
    2864                 :             : }
    2865                 :             : 
    2866                 :             : /**
    2867                 :             :  * g_uri_peek_scheme:
    2868                 :             :  * @uri: a valid URI.
    2869                 :             :  *
    2870                 :             :  * Gets the scheme portion of a URI string.
    2871                 :             :  * [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3) decodes the scheme
    2872                 :             :  * as:
    2873                 :             :  * |[
    2874                 :             :  * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
    2875                 :             :  * ]|
    2876                 :             :  * Common schemes include `file`, `https`, `svn+ssh`, etc.
    2877                 :             :  *
    2878                 :             :  * Unlike g_uri_parse_scheme(), the returned scheme is normalized to
    2879                 :             :  * all-lowercase and does not need to be freed.
    2880                 :             :  *
    2881                 :             :  * Returns: (transfer none) (nullable): The ‘scheme’ component of the URI, or
    2882                 :             :  *     %NULL on error. The returned string is normalized to all-lowercase, and
    2883                 :             :  *     interned via g_intern_string(), so it does not need to be freed.
    2884                 :             :  *
    2885                 :             :  * Since: 2.66
    2886                 :             :  **/
    2887                 :             : const gchar *
    2888                 :           4 : g_uri_peek_scheme (const gchar *uri)
    2889                 :             : {
    2890                 :             :   gssize len;
    2891                 :             :   gchar *lower_scheme;
    2892                 :             :   const gchar *scheme;
    2893                 :             : 
    2894                 :           4 :   g_return_val_if_fail (uri != NULL, NULL);
    2895                 :             : 
    2896                 :           4 :   len = g_uri_scheme_length (uri);
    2897                 :           4 :   if (len == -1)
    2898                 :           2 :     return NULL;
    2899                 :             : 
    2900                 :           2 :   lower_scheme = g_ascii_strdown (uri, len);
    2901                 :           2 :   scheme = g_intern_string (lower_scheme);
    2902                 :           2 :   g_free (lower_scheme);
    2903                 :             : 
    2904                 :           2 :   return scheme;
    2905                 :             : }
    2906                 :             : 
    2907                 :         163 : G_DEFINE_QUARK (g-uri-quark, g_uri_error)
        

Generated by: LCOV version 2.0-1