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

Generated by: LCOV version 1.14