[gnome-builder/wip/chergert/perspective] libide: add IdeUri



commit fb57fd47ccad5611a7a8ef6033c5f449794ba3ad
Author: Christian Hergert <chergert redhat com>
Date:   Fri Nov 13 01:01:50 2015 -0800

    libide: add IdeUri
    
    Ported from lingering GUri prototpyes.
    
    This is going to be used to route "loaders" for the workbench. We need
    to cleanup our document/view split, and we can do that if we allow
    perspectives/etc to say they can load something. IdeUri is going to be
    our data type used to determine the "noun" of what to load.

 libide/Makefile.am |    2 +
 libide/ide-uri.c   | 1497 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 libide/ide-uri.h   |  168 ++++++
 3 files changed, 1667 insertions(+), 0 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index cb13488..ca29067 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -188,6 +188,8 @@ libide_1_0_la_public_sources = \
        ide-unsaved-file.h \
        ide-unsaved-files.c \
        ide-unsaved-files.h \
+       ide-uri.c \
+       ide-uri.h \
        ide-view.c \
        ide-view.h \
        ide-vcs-uri.c \
diff --git a/libide/ide-uri.c b/libide/ide-uri.c
new file mode 100644
index 0000000..085558a
--- /dev/null
+++ b/libide/ide-uri.c
@@ -0,0 +1,1497 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 2010-2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ide-uri.h"
+
+/**
+ * SECTION:ide-uri
+ * @short_description: URI-handling utilities
+ * @include: glib.h
+ *
+ * FIXME
+ */
+
+/**
+ * IdeUri:
+ * @scheme: the URI scheme
+ * @user: the user or userinfo component, or %NULL
+ * @password: the password component, or %NULL
+ * @auth_params: the userinfo "params" component, or %NULL
+ * @host: the host component, or %NULL
+ * @port: the port, or %0 if not specified
+ * @path: the path component
+ * @query: the query component, or %NULL
+ * @fragment: the fragment, or %NULL
+ * @uri_string: the string the #IdeUri corresponds to, or %NULL
+ *
+ * A parsed URI. The exact manner in which a URI string is broken down
+ * into a #IdeUri depends on the #IdeUriParseFlags that were used when
+ * creating it.
+ *
+ * @scheme is always set, and always lowercase, even if @uri_string
+ * contains uppercase letters in the scheme.
+ *
+ * @host will be set if @uri_string has an "authority" component (that
+ * is, if the scheme is followed by "://" rather than just ":"). If
+ * the URI was not parsed with %IDE_URI_PARSE_NON_DNS, @host will be
+ * assumed to be an internet hostname (or IP address) and will be
+ * decoded accordingly.
+ *
+ * The generic URI syntax allows a "userinfo" component before the
+ * hostname. Some URI schemes further break the userinfo down into a
+ * username, a password (separated from the username by a colon),
+ * and/or additional parameters (separated by a semicolon). If you
+ * parse the URI with %IDE_URI_PARSE_PASSWORD and/or
+ * %IDE_URI_PARSE_AUTH_PARAMS, then the @password and @auth_params
+ * fields will be filled in (assuming they were present). Otherwise,
+ * the entire userinfo component will be put into the @user field.
+ *
+ * By default, @path, @query, and @fragment are stored undecoded,
+ * because with some schemes (such as "http"), it is possible that the
+ * encoded and unencoded forms of a character (eg,
+ * "<literal>/</literal>" and "<literal>%<!-- -->2F</literal>") may
+ * have different meanings. On the other hand, with schemes that do
+ * not use URIs as protocol elements (such as "ftp"), that sort of
+ * confusion is not possible, and it is always safe (and useful) to
+ * decode the URI fully. You can parse the URI with
+ * %IDE_URI_PARSE_DECODED if you want @path, @query, and @fragment to be
+ * decoded.
+ *
+ * Note however that all of the (string) fields in a #IdeUri are
+ * guaranteed to be valid UTF-8 strings, so if @uri_string contained
+ * encoded non-UTF-8 data, it will normally be left %<!-- -->-encoded
+ * in the corresponding #IdeUri fields, even if the #IdeUriParseFlags
+ * would otherwise call for decoding it. You can use the flag
+ * %IDE_URI_PARSE_UTF8_ONLY to cause this case to be an error instead.
+ */
+
+/**
+ * IdeUriParseFlags:
+ * @IDE_URI_PARSE_STRICT: Parse the URI strictly according to the RFC
+ *     3986 grammar.
+ * @IDE_URI_PARSE_HTML5: Parse the URI according to the HTML5 web
+ *     address parsing rules.
+ * @IDE_URI_PARSE_NO_IRI: Disallow Internationalized URIs; return an
+ *     error if the URI contains non-ASCII characters
+ * @IDE_URI_PARSE_PASSWORD: Split the userinfo into user and password,
+ *     separated by ':'.
+ * @IDE_URI_PARSE_AUTH_PARAMS: Split the userinfo into user/password and
+ *     parameters, separated by ';'.
+ * @IDE_URI_PARSE_NON_DNS: Do not parse the host as a DNS host/IP address.
+ *     (Eg, for smb URIs with NetBIOS hostnames).
+ * @IDE_URI_PARSE_DECODED: Decode even reserved %<!-- -->encoded
+ *     characters in the URI (unless this would result in non-UTF8
+ *     strings). Using this flag means that you cannot reliably
+ *     convert the parsed URI back to string form with
+ *     ide_uri_to_string().
+ * @IDE_URI_PARSE_UTF8_ONLY: Return an error if non-UTF8 characters are
+ *     encountered in the URI.
+ *
+ * Flags that control how a URI string is parsed (or re-parsed).
+ */
+
+struct _IdeUri
+{
+  volatile gint ref_count;
+
+  gchar   *scheme;
+
+  gchar   *user;
+  gchar   *password;
+  gchar   *auth_params;
+
+  gchar   *host;
+  gushort  port;
+
+  gchar   *path;
+  gchar   *query;
+  gchar   *fragment;
+
+  gchar   *encoded_userinfo;
+  gchar   *encoded_host;
+  gchar   *encoded_path;
+  gchar   *encoded_query;
+  gchar   *encoded_fragment;
+};
+
+G_DEFINE_BOXED_TYPE (IdeUri, ide_uri, ide_uri_ref, ide_uri_unref)
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+static gchar *
+uri_decoder (const gchar       *part,
+             gboolean           just_normalize,
+             IdeUriParseFlags   flags,
+             IdeUriError        parse_error,
+             GError           **error)
+{
+  gchar *decoded;
+  guchar *s, *d;
+  const gchar *invalid;
+
+#if 0
+  if (flags & IDE_URI_PARSE_DECODED)
+    just_normalize = FALSE;
+#endif
+
+  decoded = g_malloc (strlen (part) + 1);
+  for (s = (guchar *)part, d = (guchar *)decoded; *s; s++)
+    {
+      if (*s == '%')
+        {
+          if (!g_ascii_isxdigit (s[1]) ||
+              !g_ascii_isxdigit (s[2]))
+            {
+              /* % followed by non-hex; this is an error */
+              if (flags & IDE_URI_PARSE_STRICT)
+                {
+                  g_set_error_literal (error, IDE_URI_ERROR, parse_error,
+                                       _("Invalid %-encoding in URI"));
+                  g_free (decoded);
+                  return FALSE;
+                }
+
+              /* In non-strict mode, just let it through; we *don't*
+               * fix it to "%25", since that might change the way that
+               * the URI's owner would interpret it.
+               */
+              *d++ = *s;
+              continue;
+            }
+
+#if 0
+          c = HEXCHAR (s);
+          if (just_normalize && !ide_uri_char_is_unreserved (c))
+            {
+              /* Leave the % sequence there. */
+              *d++ = *s;
+            }
+          else
+            {
+              *d++ = c;
+              s += 2;
+            }
+#endif
+        }
+      else
+        *d++ = *s;
+    }
+  *d = '\0';
+
+  if (!g_utf8_validate (decoded, (gchar *)d - decoded, &invalid))
+    {
+      GString *tmp;
+      const gchar *p = decoded;
+
+      if (flags & IDE_URI_PARSE_UTF8_ONLY)
+        {
+          g_set_error_literal (error, IDE_URI_ERROR, parse_error,
+                               _("Non-UTF8 characters in URI"));
+          g_free (decoded);
+          return FALSE;
+        }
+
+      tmp = g_string_new (NULL);
+
+      do
+        {
+          g_string_append_len (tmp, p, invalid - p);
+          g_string_append_printf (tmp, "%%%02d", *(const guchar *)invalid);
+          p = invalid + 1;
+        }
+      while (!g_utf8_validate (p, (const gchar *)d - p, &invalid));
+
+      g_string_append (tmp, p);
+
+      g_free (decoded);
+      decoded = g_string_free (tmp, FALSE);
+    }
+
+  return decoded;
+}
+
+static gchar *
+uri_decode (const gchar       *part,
+            IdeUriParseFlags   flags,
+            IdeUriError        parse_error,
+            GError           **error)
+{
+  return uri_decoder (part, FALSE, flags, parse_error, error);
+}
+
+static gchar *
+uri_normalize (const gchar       *part,
+               IdeUriParseFlags   flags,
+               IdeUriError        parse_error,
+               GError           **error)
+{
+  return uri_decoder (part, TRUE, flags, parse_error, error);
+}
+
+/* Does the "Remove Dot Segments" algorithm from section 5.2.4 of RFC
+ * 3986. @path is assumed to start with '/', and is modified in place.
+ */
+static void
+remove_dot_segments (gchar *path)
+{
+  gchar *p, *q;
+
+  /* Remove "./" where "." is a complete segment. */
+  for (p = path + 1; *p; )
+    {
+      if (*(p - 1) == '/' &&
+          *p == '.' && *(p + 1) == '/')
+        memmove (p, p + 2, strlen (p + 2) + 1);
+      else
+        p++;
+    }
+  /* Remove "." at end. */
+  if (p > path + 2 &&
+      *(p - 1) == '.' && *(p - 2) == '/')
+    *(p - 1) = '\0';
+
+  /* Remove "<segment>/../" where <segment> != ".." */
+  for (p = path + 1; *p; )
+    {
+      if (!strncmp (p, "../", 3))
+        {
+          p += 3;
+          continue;
+        }
+      q = strchr (p + 1, '/');
+      if (!q)
+        break;
+      if (strncmp (q, "/../", 4) != 0)
+        {
+          p = q + 1;
+          continue;
+        }
+      memmove (p, q + 4, strlen (q + 4) + 1);
+      p = path + 1;
+    }
+  /* Remove "<segment>/.." at end where <segment> != ".." */
+  q = strrchr (path, '/');
+  if (q && !strcmp (q, "/.."))
+    {
+      p = q - 1;
+      while (p > path && *p != '/')
+        p--;
+      if (strncmp (p, "/../", 4) != 0)
+        *(p + 1) = 0;
+    }
+
+  /* Remove extraneous initial "/.."s */
+  while (!strncmp (path, "/../", 4))
+    memmove (path, path + 3, strlen (path) - 2);
+  if (!strcmp (path, "/.."))
+    path[1] = '\0';
+}
+
+static char *
+uri_cleanup (const char *uri_string)
+{
+  GString *copy;
+  const char *end;
+
+  /* Skip leading whitespace */
+  while (g_ascii_isspace (*uri_string))
+    uri_string++;
+
+  /* Ignore trailing whitespace */
+  end = uri_string + strlen (uri_string);
+  while (end > uri_string && g_ascii_isspace (*(end - 1)))
+    end--;
+
+  /* Copy the rest, encoding unencoded spaces and stripping other whitespace */
+  copy = g_string_sized_new (end - uri_string);
+  while (uri_string < end)
+    {
+      if (*uri_string == ' ')
+        g_string_append (copy, "%20");
+      else if (g_ascii_isspace (*uri_string))
+        ;
+      else
+        g_string_append_c (copy, *uri_string);
+      uri_string++;
+    }
+
+  return g_string_free (copy, FALSE);
+}
+
+gboolean
+parse_host (const gchar       *raw_host,
+            IdeUriParseFlags   flags,
+            gchar            **host,
+            GError           **error)
+{
+  gchar *decoded, *addr;
+
+  if (*raw_host == '[')
+    {
+      int len = strlen (raw_host);
+
+      if (raw_host[len - 1] != ']')
+        {
+          g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_BAD_HOST,
+                       _("Invalid IP literal '%s' in URI"),
+                       raw_host);
+          return FALSE;
+        }
+
+      addr = g_strndup (raw_host + 1, len - 2);
+      /* addr must be an IPv6 address */
+      if (!g_hostname_is_ip_address (addr) || !strchr (addr, ':'))
+        {
+          g_free (addr);
+          g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_BAD_HOST,
+                       _("Invalid IP literal '%s' in URI"),
+                       raw_host);
+          return FALSE;
+        }
+
+      *host = addr;
+      return TRUE;
+    }
+
+  if (g_hostname_is_ip_address (raw_host))
+    {
+      *host = g_strdup (raw_host);
+      return TRUE;
+    }
+
+  decoded = uri_decode (raw_host,
+                        (flags & IDE_URI_PARSE_NON_DNS) ? flags : IDE_URI_PARSE_STRICT,
+                        IDE_URI_ERROR_BAD_HOST, error);
+  if (!decoded)
+    return FALSE;
+
+  if (flags & IDE_URI_PARSE_NON_DNS)
+    {
+      *host = decoded;
+      return TRUE;
+    }
+
+  /* You're not allowed to %-encode an IP address, so if it wasn't
+   * one before, it better not be one now.
+   */
+  if (g_hostname_is_ip_address (decoded))
+    {
+      g_free (decoded);
+      g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_BAD_HOST,
+                   _("Invalid encoded IP literal '%s' in URI"),
+                   raw_host);
+      return FALSE;
+    }
+
+  if (strchr (decoded, '%') || !g_utf8_validate (decoded, -1, NULL))
+    {
+      g_free (decoded);
+      g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_BAD_HOST,
+                   _("Invalid non-ASCII hostname '%s' in URI"),
+                   raw_host);
+      return FALSE;
+    }
+
+  if (!g_hostname_is_non_ascii (decoded))
+    {
+      *host = decoded;
+      return TRUE;
+    }
+
+  if (flags & IDE_URI_PARSE_NO_IRI)
+    {
+      g_free (decoded);
+      g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_BAD_HOST,
+                   _("Non-ASCII hostname '%s' forbidden in this URI"),
+                   decoded);
+      return FALSE;
+    }
+
+  *host = g_hostname_to_ascii (decoded);
+  g_free (decoded);
+  return TRUE;
+}
+
+static gboolean
+parse_port (const gchar  *raw_port,
+            gushort      *port,
+            GError      **error)
+{
+  gchar *end;
+  int parsed_port;
+
+  parsed_port = strtoul (raw_port, &end, 10);
+  if (*end)
+    {
+      g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_BAD_PORT,
+                   _("Could not parse port '%s' in URI"),
+                   raw_port);
+      return FALSE;
+    }
+  else if (parsed_port > 65535)
+    {
+      g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_BAD_PORT,
+                   _("Port '%s' in URI is out of range"),
+                   raw_port);
+      return FALSE;
+    }
+
+  *port = parsed_port;
+  return TRUE;
+}
+
+gboolean
+parse_userinfo (const gchar       *raw_userinfo,
+                IdeUriParseFlags   flags,
+                gchar            **user,
+                gchar            **password,
+                gchar            **auth_params,
+                GError           **error)
+{
+  IdeUriParseFlags userflags = flags & (IDE_URI_PARSE_PASSWORD | IDE_URI_PARSE_AUTH_PARAMS);
+  const gchar *start, *end;
+  gchar *raw_user, *raw_password, *raw_params;
+
+  start = raw_userinfo;
+  if (userflags == (IDE_URI_PARSE_PASSWORD | IDE_URI_PARSE_AUTH_PARAMS))
+    end = start + strcspn (start, ":;");
+  else if (userflags == IDE_URI_PARSE_PASSWORD)
+    end = start + strcspn (start, ":");
+  else if (userflags == IDE_URI_PARSE_AUTH_PARAMS)
+    end = start + strcspn (start, ";");
+  else
+    end = start + strlen (start);
+  raw_user = g_strndup (start, end - start);
+
+  *user = uri_decode (raw_user, flags, IDE_URI_ERROR_BAD_USER, error);
+  g_free (raw_user);
+  if (!*user)
+    return FALSE;
+
+  if (*end == ':')
+    {
+      start = end + 1;
+      if (userflags & IDE_URI_PARSE_AUTH_PARAMS)
+        end = start + strcspn (start, ";");
+      else
+        end = start + strlen (start);
+      raw_password = g_strndup (start, end - start);
+
+      *password = uri_decode (raw_password, flags, IDE_URI_ERROR_BAD_PASSWORD, error);
+      g_free (raw_password);
+      if (!*password)
+        {
+          g_free (*user);
+          *user = NULL;
+          return FALSE;
+        }
+    }
+  else
+    *password = NULL;
+
+  if (*end == ';')
+    {
+      start = end + 1;
+      end = start + strlen (start);
+      raw_params = g_strndup (start, end - start);
+
+      *auth_params = uri_decode (raw_params, flags, IDE_URI_ERROR_BAD_AUTH_PARAMS, error);
+      g_free (raw_params);
+      if (!*auth_params)
+        {
+          g_free (*user);
+          *user = NULL;
+          g_free (*password);
+          *password = NULL;
+          return FALSE;
+        }
+    }
+  else
+    *auth_params = NULL;
+
+  return TRUE;
+}
+
+/**
+ * ide_uri_new:
+ * @uri_string: a string representing an absolute URI
+ * @flags: flags describing how to parse @uri_string
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Parses @uri_string according to @flags. If the result is not a
+ * valid absolute URI, it will be discarded, and an error returned.
+ *
+ * Return value: a new #IdeUri.
+ */
+IdeUri *
+ide_uri_new (const gchar       *uri_string,
+             IdeUriParseFlags   flags,
+             GError           **error)
+{
+  return ide_uri_new_relative (NULL, uri_string, flags, error);
+}
+
+/**
+ * ide_uri_new_relative:
+ * @base_uri: (allow-none): a base URI
+ * @uri_string: a string representing a relative or absolute URI
+ * @flags: flags describing how to parse @uri_string
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Parses @uri_string according to @flags and, if it is a relative
+ * URI, merges it with @base_uri. If the result is not a valid
+ * absolute URI, it will be discarded, and an error returned.
+ *
+ * Return value: a new #IdeUri.
+ */
+IdeUri *
+ide_uri_new_relative (IdeUri            *base_uri,
+                      const gchar       *uri_string,
+                      IdeUriParseFlags   flags,
+                      GError           **error)
+{
+  IdeUri *raw = NULL, *uri = NULL;
+  gchar *cleaned_uri_string = NULL;
+  gchar *raw_port = NULL;
+
+  if (base_uri && !base_uri->scheme)
+    {
+      g_set_error_literal (error, IDE_URI_ERROR, IDE_URI_ERROR_MISC,
+                           _("Base URI is not absolute"));
+      return NULL;
+    }
+
+  uri = g_slice_new0 (IdeUri);
+  uri->ref_count = 1;
+
+  if (!(flags & IDE_URI_PARSE_STRICT) && strpbrk (uri_string, " \t\n\r"))
+    {
+      cleaned_uri_string = uri_cleanup (uri_string);
+      uri_string = cleaned_uri_string;
+    }
+
+  /* We use another IdeUri to store the raw data in, for convenience */
+  raw = g_slice_new0 (IdeUri);
+  raw->ref_count = 1;
+  ide_uri_split (uri_string, (flags & IDE_URI_PARSE_STRICT) != 0,
+               &raw->scheme, &raw->user, &raw->host, &raw_port,
+               &raw->path, &raw->query, &raw->fragment);
+  if (cleaned_uri_string)
+    g_free (cleaned_uri_string);
+
+  if (raw->scheme)
+    uri->scheme = g_ascii_strdown (raw->scheme, -1);
+  else if (!base_uri)
+    {
+      g_set_error_literal (error, IDE_URI_ERROR, IDE_URI_ERROR_MISC,
+                           _("URI is not absolute, and no base URI was provided"));
+      goto fail;
+    }
+
+  if (raw->user)
+    {
+      if (!parse_userinfo (raw->user, flags,
+                           &uri->user, &uri->password, &uri->auth_params,
+                           error))
+        goto fail;
+    }
+
+  if (raw->host)
+    {
+      if (!parse_host (raw->host, flags, &uri->host, error))
+        goto fail;
+    }
+
+  if (raw_port)
+    {
+      if (!parse_port (raw_port, &uri->port, error))
+        goto fail;
+    }
+
+  uri->path = uri_normalize (raw->path, flags, IDE_URI_ERROR_BAD_PATH, error);
+  if (!uri->path)
+    goto fail;
+
+  if (raw->query)
+    {
+      uri->query = uri_normalize (raw->query, flags, IDE_URI_ERROR_BAD_QUERY, error);
+      if (!uri->query)
+        goto fail;
+    }
+
+  if (raw->fragment)
+    {
+      uri->fragment = uri_normalize (raw->fragment, flags, IDE_URI_ERROR_BAD_FRAGMENT, error);
+      if (!uri->fragment)
+        goto fail;
+    }
+
+  if (!uri->scheme && !base_uri)
+    {
+      g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_MISC,
+                   _("Could not parse '%s' as absolute URI"),
+                   uri_string);
+      goto fail;
+    }
+
+  if (base_uri)
+    {
+      /* This is section 5.2.2 of RFC 3986, except that we're doing
+       * it in place in @uri rather than copying from R to T.
+       */
+      if (uri->scheme)
+        remove_dot_segments (uri->path);
+      else
+        {
+          if (uri->host)
+            remove_dot_segments (uri->path);
+          else
+            {
+              if (!*uri->path)
+                {
+                  g_free (uri->path);
+                  uri->path = g_strdup (base_uri->path);
+                  g_free (raw->path);
+                  raw->path = NULL;
+                  if (!uri->query)
+                    uri->query = g_strdup (base_uri->query);
+                }
+              else
+                {
+                  if (*uri->path != '/')
+                    remove_dot_segments (uri->path);
+                  else
+                    {
+                      gchar *newpath, *last;
+
+                      last = strrchr (base_uri->path, '/');
+                      if (last)
+                        {
+                          newpath = g_strdup_printf ("%.*s/%s",
+                                                     (int)(last - base_uri->path),
+                                                     base_uri->path,
+                                                     uri->path);
+                        }
+                      else
+                        newpath = g_strdup_printf ("/%s", uri->path);
+
+                      g_free (uri->path);
+                      uri->path = newpath;
+                      g_free (raw->path);
+                      raw->path = NULL;
+
+                      remove_dot_segments (uri->path);
+                    }
+                }
+
+              uri->user = g_strdup (base_uri->user);
+              uri->password = g_strdup (base_uri->password);
+              uri->auth_params = g_strdup (base_uri->auth_params);
+              uri->host = g_strdup (base_uri->host);
+              uri->port = base_uri->port;
+            }
+        }
+    }
+
+  ide_uri_unref (raw);
+  g_free (raw_port);
+  return uri;
+
+ fail:
+  ide_uri_unref (raw);
+  g_free (raw_port);
+  ide_uri_unref (uri);
+  return NULL;
+}
+
+/**
+ * ide_uri_to_string:
+ * @uri: a #IdeUri
+ * @flags: flags describing how to convert @uri
+ *
+ * Returns a string representing @uri.
+ *
+ * Return value: a string representing @uri, which the caller must free.
+ */
+gchar *
+ide_uri_to_string (IdeUri              *uri,
+                   IdeUriToStringFlags  flags)
+{
+  GString *str;
+
+  g_return_val_if_fail (uri != NULL, NULL);
+
+  str = g_string_new (uri->scheme);
+  g_string_append_c (str, ':');
+
+  if (uri->host)
+    {
+      g_string_append (str, "//");
+      if (uri->encoded_userinfo)
+        {
+          g_string_append (str, uri->encoded_userinfo);
+          g_string_append_c (str, '@');
+        }
+
+      if (uri->host)
+         g_string_append (str, uri->host);
+
+      if (uri->port)
+        g_string_append_printf (str, ":%d", uri->port);
+    }
+
+  if (uri->encoded_path)
+    g_string_append (str, uri->encoded_path);
+
+  if (uri->encoded_query)
+    {
+      g_string_append_c (str, '?');
+      g_string_append (str, uri->encoded_query);
+    }
+  if (uri->encoded_fragment)
+    {
+      g_string_append_c (str, '#');
+      g_string_append (str, uri->encoded_fragment);
+    }
+
+  return g_string_free (str, FALSE);
+}
+
+/**
+ * ide_uri_copy:
+ * @uri: a #IdeUri
+ *
+ * Copies @uri
+ *
+ * Return value: a copy of @uri
+ */
+IdeUri *
+ide_uri_copy (IdeUri *uri)
+{
+  IdeUri *dup;
+
+  g_return_val_if_fail (uri != NULL, NULL);
+
+  dup = g_slice_new0 (IdeUri);
+  dup->ref_count   = 1;
+  dup->scheme      = g_strdup (uri->scheme);
+  dup->user        = g_strdup (uri->user);
+  dup->password    = g_strdup (uri->password);
+  dup->auth_params = g_strdup (uri->auth_params);
+  dup->host        = g_strdup (uri->host);
+  dup->port        = uri->port;
+  dup->path        = g_strdup (uri->path);
+  dup->query       = g_strdup (uri->query);
+  dup->fragment    = g_strdup (uri->fragment);
+
+  return dup;
+}
+
+/**
+ * ide_uri_ref:
+ * @uri: An #IdeUri
+ *
+ * Increments the reference count of @uri by one.
+ *
+ * Returns: (transfer full): uri
+ */
+IdeUri *
+ide_uri_ref (IdeUri *uri)
+{
+  g_return_val_if_fail (uri != NULL, NULL);
+  g_return_val_if_fail (uri->ref_count > 0, NULL);
+
+  g_atomic_int_inc (&uri->ref_count);
+
+  return uri;
+}
+
+/**
+ * ide_uri_unref:
+ * @uri: a #IdeUri
+ *
+ * Decrements the reference count of @uri by 1. If the reference count
+ * reaches zero, the structure will be freed.
+ */
+void
+ide_uri_unref (IdeUri *uri)
+{
+  g_return_if_fail (uri != NULL);
+  g_return_if_fail (uri->ref_count > 0);
+
+  if (g_atomic_int_dec_and_test (&uri->ref_count))
+    {
+      g_free (uri->scheme);
+      g_free (uri->user);
+      g_free (uri->password);
+      g_free (uri->auth_params);
+      g_free (uri->host);
+      g_free (uri->path);
+      g_free (uri->query);
+      g_free (uri->fragment);
+
+      g_slice_free (IdeUri, uri);
+    }
+}
+
+/**
+ * ide_uri_split:
+ * @uri_string: a string containing a relative or absolute URI
+ * @strict: whether to parse @uri_string strictly
+ * @scheme: (out) (allow-none): on return, contains the scheme, or %NULL
+ * @userinfo: (out) (allow-none): on return, contains the userinfo, or %NULL
+ * @host: (out) (allow-none): on return, contains the host, or %NULL
+ * @port: (out) (allow-none): on return, contains the port, or %NULL
+ * @path: (out) (allow-none): on return, contains the path, or %NULL
+ * @query: (out) (allow-none): on return, contains the query, or %NULL
+ * @fragment: (out) (allow-none): on return, contains the fragment, or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Parses @uri_string more-or-less according to the generic grammar of
+ * RFC 3986 ("more" if @strict is %TRUE, "less" if %FALSE), and
+ * outputs the pieces into the provided variables. This is a low-level
+ * method that does not do any pre- or post-processing of @uri_string,
+ * and is "garbage in, garbage out"; it just splits @uri_string into
+ * pieces at the appropriate punctuation characters (consuming
+ * delimiters as appropriate), and returns the pieces. Components that
+ * are not present in @uri_string will be set to %NULL (but note that
+ * the path is always present, though it may be an empty string).
+ */
+void
+ide_uri_split (const gchar  *uri_string,
+               gboolean      strict,
+               gchar       **scheme,
+               gchar       **userinfo,
+               gchar       **host,
+               gchar       **port,
+               gchar       **path,
+               gchar       **query,
+               gchar       **fragment)
+{
+  const gchar *end, *colon, *at, *path_start, *semi, *question;
+  const gchar *p, *bracket, *hostend;
+
+  if (scheme)
+    *scheme = NULL;
+  if (userinfo)
+    *userinfo = NULL;
+  if (host)
+    *host = NULL;
+  if (port)
+    *port = NULL;
+  if (path)
+    *path = NULL;
+  if (query)
+    *query = NULL;
+  if (fragment)
+    *fragment = NULL;
+
+  /* Find scheme: initial [a-z+.-]* substring until ":" */
+  p = uri_string;
+  while (*p && (g_ascii_isalnum (*p) ||
+                *p == '.' || *p == '+' || *p == '-'))
+    p++;
+
+  if (p > uri_string && *p == ':')
+    {
+      if (scheme)
+        *scheme = g_strndup (uri_string, p - uri_string);
+      p++;
+    }
+  else
+    p = uri_string;
+
+  /* Check for authority */
+  if (strncmp (p, "//", 2) == 0)
+    {
+      p += 2;
+
+      path_start = p + strcspn (p, "/?#");
+      at = memchr (p, '@', path_start - p);
+      if (at)
+        {
+          if (!strict)
+            {
+              gchar *next_at;
+
+              /* Any "@"s in the userinfo must be %-encoded, but
+               * people get this wrong sometimes. Since "@"s in the
+               * hostname are unlikely (and also wrong anyway), assume
+               * that if there are extra "@"s, they belong in the
+               * userinfo.
+               */
+              do
+                {
+                  next_at = memchr (at + 1, '@', path_start - (at + 1));
+                  if (next_at)
+                    at = next_at;
+                }
+              while (next_at);
+            }
+
+          if (userinfo)
+            *userinfo = g_strndup (p, at - p);
+          p = at + 1;
+        }
+
+      if (!strict)
+        {
+          semi = strchr (p, ';');
+          if (semi && semi < path_start)
+            {
+              /* Technically, semicolons are allowed in the "host"
+               * production, but no one ever does this, and some
+               * schemes mistakenly use semicolon as a delimiter
+               * marking the start of the path. We have to check this
+               * after checking for userinfo though, because a
+               * semicolon before the "@" must be part of the
+               * userinfo.
+               */
+              path_start = semi;
+            }
+        }
+
+      /* Find host and port. The host may be a bracket-delimited IPv6
+       * address, in which case the colon delimiting the port must come
+       * after the close bracket.
+       */
+      if (*p == '[')
+        {
+          bracket = memchr (p, ']', path_start - p);
+          if (bracket && *(bracket + 1) == ':')
+            colon = bracket + 1;
+          else
+            colon = NULL;
+        }
+      else
+        colon = memchr (p, ':', path_start - p);
+
+      if (host)
+        {
+          hostend = colon ? colon : path_start;
+          *host = g_strndup (p, hostend - p);
+        }
+
+      if (colon && colon != path_start - 1 && port)
+        *port = g_strndup (colon + 1, path_start - (colon + 1));
+
+      p = path_start;
+    }
+
+  /* Find fragment. */
+  end = p + strcspn (p, "#");
+  if (*end == '#')
+    {
+      if (fragment)
+        *fragment = g_strdup (end + 1);
+    }
+
+  /* Find query */
+  question = memchr (p, '?', end - p);
+  if (question)
+    {
+      if (query)
+        *query = g_strndup (question + 1, end - (question + 1));
+      end = question;
+    }
+
+  if (path)
+    *path = g_strndup (p, end - p);
+}
+
+/* This is just a copy of g_str_hash() with g_ascii_toupper()s added */
+static guint
+str_ascii_case_hash (gconstpointer key)
+{
+  const char *p = key;
+  guint h = g_ascii_toupper(*p);
+
+  if (h)
+    {
+      for (p += 1; *p != '\0'; p++)
+        h = (h << 5) - h + g_ascii_toupper(*p);
+    }
+
+  return h;
+}
+
+static gboolean
+str_ascii_case_equal (gconstpointer v1,
+                      gconstpointer v2)
+{
+  const char *string1 = v1;
+  const char *string2 = v2;
+
+  return g_ascii_strcasecmp (string1, string2) == 0;
+}
+
+/**
+ * ide_uri_parse_params:
+ * @params: a string containing "attribute=value" parameters
+ * @length: the length of @params, or -1 if it is NUL-terminated
+ * @separator: the separator character between parameters.
+ *   (usually ';', but sometimes '&')
+ * @case_insentitive: whether to match parameter names case-insensitively
+ *
+ * Many URI schemes include one or more attribute/value pairs
+ * as part of the URI value. This method can be used to parse them
+ * into a hash table.
+ *
+ * The @params string is assumed to still be %<!-- -->-encoded, but
+ * the returned values will be fully decoded. (Thus it is possible
+ * that the returned values may contain '=' or @separator, if the
+ * value was encoded in the input.) Invalid %<!-- -->-encoding is
+ * treated as with the non-%IDE_URI_PARSE_STRICT rules for ide_uri_new().
+ * (However, if @params is the path or query string from a #IdeUri that
+ * was parsed with %IDE_URI_PARSE_STRICT, then you already know that it
+ * does not contain any invalid encoding.)
+ *
+ * Return value: (element-type utf8 utf8): a hash table of
+ * attribute/value pairs. Both names and values will be fully-decoded.
+ * If @params cannot be parsed (eg, it contains two @separator
+ * characters in a row), then %NULL is returned.
+ */
+GHashTable *
+ide_uri_parse_params (const gchar *params,
+                      gssize       length,
+                      gchar        separator,
+                      gboolean     case_insensitive)
+{
+  GHashTable *hash;
+  const char *end, *attr, *attr_end, *value, *value_end;
+  char *copy, *decoded_attr, *decoded_value;
+
+  if (case_insensitive)
+    {
+      hash = g_hash_table_new_full (str_ascii_case_hash,
+                                    str_ascii_case_equal,
+                                    g_free, g_free);
+    }
+  else
+    {
+      hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                    g_free, g_free);
+    }
+
+  if (length == -1)
+    end = params + strlen (params);
+  else
+    end = params + length;
+
+  attr = params;
+  while (attr < end)
+    {
+      value_end = memchr (attr, separator, end - attr);
+      if (!value_end)
+        value_end = end;
+
+      attr_end = memchr (attr, '=', value_end - attr);
+      if (!attr_end)
+        {
+          g_hash_table_destroy (hash);
+          return NULL;
+        }
+      copy = g_strndup (attr, attr_end - attr);
+      decoded_attr = uri_decode (copy, 0, IDE_URI_ERROR_MISC, NULL);
+      g_free (copy);
+      if (!decoded_attr)
+        {
+          g_hash_table_destroy (hash);
+          return NULL;
+        }
+
+      value = attr_end + 1;
+      copy = g_strndup (value, value_end - value);
+      decoded_value = uri_decode (copy, 0, IDE_URI_ERROR_MISC, NULL);
+      g_free (copy);
+      if (!decoded_value)
+        {
+          g_free (decoded_attr);
+          g_hash_table_destroy (hash);
+          return NULL;
+        }
+
+      g_hash_table_insert (hash, decoded_attr, decoded_value);
+      attr = value_end + 1;
+    }
+
+  return hash;
+}
+
+/**
+ * ide_uri_parse_host:
+ * @uri_string: a string containing a network URI
+ * @flags: flags for parsing @uri_string
+ * @scheme: (out): on return, will contain @uri_string's URI scheme
+ * @host: (out): on return, will contain @uri_string's decoded hostname
+ * @port: (out): on return, will contain @uri_string's port, or %0
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Utility function for parsing "network" URIs. This extracts just the
+ * scheme, host, and port from @uri_string. All three out parameters
+ * are mandatory.
+ *
+ * Return value: %TRUE on success, %FALSE on failure.
+ */
+gboolean
+ide_uri_parse_host (const gchar       *uri_string,
+                    IdeUriParseFlags   flags,
+                    gchar            **scheme,
+                    gchar            **host,
+                    gushort           *port,
+                    GError           **error)
+{
+  gchar *raw_scheme, *raw_host, *raw_port;
+
+  ide_uri_split (uri_string, flags & IDE_URI_PARSE_STRICT,
+               &raw_scheme, NULL, &raw_host, &raw_port,
+               NULL, NULL, NULL);
+  if (!raw_host)
+    {
+      g_set_error (error, IDE_URI_ERROR, IDE_URI_ERROR_BAD_HOST,
+                   _("URI '%s' has no host component"),
+                   uri_string);
+      goto fail;
+    }
+
+  if (raw_port)
+    {
+      if (!parse_port (raw_port, port, error))
+        goto fail;
+    }
+  else
+    *port = 0;
+
+  if (!parse_host (raw_host, flags, host, error))
+    goto fail;
+
+  *scheme = raw_scheme;
+  g_free (raw_host);
+  g_free (raw_port);
+  return TRUE;
+
+ fail:
+  g_free (raw_scheme);
+  g_free (raw_host);
+  g_free (raw_port);
+  return FALSE;
+}
+
+/**
+ * ide_uri_get_scheme:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's scheme.
+ *
+ * Return value: @uri's scheme.
+ */
+const gchar *
+ide_uri_get_scheme (IdeUri *uri)
+{
+  return uri->scheme;
+}
+
+/**
+ * ide_uri_set_scheme:
+ * @uri: a #IdeUri
+ * @scheme: the URI scheme
+ *
+ * Sets @uri's scheme to @scheme.
+ */
+void
+ide_uri_set_scheme (IdeUri      *uri,
+                    const gchar *scheme)
+{
+  g_free (uri->scheme);
+  uri->scheme = g_strdup (scheme);
+}
+
+/**
+ * ide_uri_get_user:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's user. If @uri was parsed with %IDE_URI_PARSE_PASSWORD or
+ * %IDE_URI_PARSE_AUTH_PARAMS, this is the string that appears before the
+ * password and parameters in the userinfo. If not, then the entire
+ * userinfo is considered the user.
+ *
+ * Return value: @uri's user.
+ */
+const gchar *
+ide_uri_get_user (IdeUri *uri)
+{
+  return uri->user;
+}
+
+/**
+ * ide_uri_set_user:
+ * @uri: a #IdeUri
+ * @user: the username, or %NULL
+ *
+ * Sets @uri's user to @user. See ide_uri_get_user() for a description
+ * of how this interacts with various parsing flags.
+ */
+void
+ide_uri_set_user (IdeUri      *uri,
+                  const gchar *user)
+{
+  g_free (uri->user);
+  uri->user = g_strdup (user);
+}
+
+/**
+ * ide_uri_get_password:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's password. If @uri was not parsed with
+ * %IDE_URI_PARSE_PASSWORD, this will always be %NULL.
+ *
+ * Return value: @uri's password.
+ */
+const gchar *
+ide_uri_get_password (IdeUri *uri)
+{
+  return uri->password;
+}
+
+/**
+ * ide_uri_set_password:
+ * @uri: a #IdeUri
+ * @password: the password, or %NULL
+ *
+ * Sets @uri's password to @password.
+ */
+void
+ide_uri_set_password (IdeUri      *uri,
+                      const gchar *password)
+{
+  g_free (uri->password);
+  uri->password = g_strdup (password);
+}
+
+/**
+ * ide_uri_get_auth_params:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's authentication parameters. Depending on the URI scheme,
+ * ide_uri_parse_params() may be useful for further parsing this
+ * information.
+ *
+ * Return value: @uri's authentication parameters.
+ */
+const gchar *
+ide_uri_get_auth_params (IdeUri *uri)
+{
+  return uri->auth_params;
+}
+
+/**
+ * ide_uri_set_auth_params:
+ * @uri: a #IdeUri
+ * @auth_params: the authentication parameters, or %NULL
+ *
+ * Sets @uri's authentication parameters to @auth_params.
+ */
+void
+ide_uri_set_auth_params (IdeUri      *uri,
+                         const gchar *auth_params)
+{
+  g_free (uri->auth_params);
+  uri->auth_params = g_strdup (auth_params);
+}
+
+/**
+ * ide_uri_get_host:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's host. If @uri contained an IPv6 address literal, this
+ * value will not include the brackets that are required by the URI
+ * syntax.
+ *
+ * Return value: @uri's host.
+ */
+const gchar *
+ide_uri_get_host (IdeUri *uri)
+{
+  return uri->host;
+}
+
+/**
+ * ide_uri_set_host:
+ * @uri: a #IdeUri
+ * @host: the hostname or IP address, or %NULL
+ *
+ * Sets @uri's host to @host.
+ *
+ * If @host is an IPv6 IP address, it should not include the brackets
+ * required by the URI syntax; they will be added automatically when
+ * converting @uri to a string.
+ */
+void
+ide_uri_set_host (IdeUri      *uri,
+                  const gchar *host)
+{
+  g_free (uri->host);
+  uri->host = g_strdup (host);
+}
+
+/**
+ * ide_uri_get_port:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's port.
+ *
+ * Return value: @uri's port, or %0 if it was unset
+ */
+gushort
+ide_uri_get_port (IdeUri *uri)
+{
+  return uri->port;
+}
+
+/**
+ * ide_uri_set_port:
+ * @uri: a #IdeUri
+ * @port: the port, or %0
+ *
+ * Sets @uri's port to @port. If @port is 0, it will not be output
+ * when calling ide_uri_to_string().
+ */
+void
+ide_uri_set_port (IdeUri  *uri,
+                  gushort  port)
+{
+  uri->port = port;
+}
+
+/**
+ * ide_uri_get_path:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's path, which may contain %<!-- -->-encoding, depending
+ * on the flags with which @uri was parsed.
+ *
+ * Return value: @uri's path.
+ */
+const gchar *
+ide_uri_get_path (IdeUri *uri)
+{
+  return uri->path;
+}
+
+/**
+ * ide_uri_set_path:
+ * @uri: a #IdeUri
+ * @path: the (%<!-- -->-encoded) path
+ *
+ * Sets @uri's path to @path, which is assumed to have been
+ * appropriately %<!-- -->-encoded. In particular, this means that if
+ * you want to include a literal percent sign the path, you must write
+ * it as "%<!-- -->25". That being said, if @path contains an
+ * unencoded '?' or '#' character, it will get encoded, since
+ * otherwise converting @uri to a string and then back to a #IdeUri
+ * again would give a different result.
+ */
+void
+ide_uri_set_path (IdeUri      *uri,
+                  const gchar *path)
+{
+  g_free (uri->path);
+  uri->path = g_strdup (path);
+}
+
+/**
+ * ide_uri_get_query:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's query, which may contain %<!-- -->-encoding, depending
+ * on the flags with which @uri was parsed.
+ *
+ * For queries consisting of a series of "name=value" parameters,
+ * ide_uri_parse_params() may be useful.
+ *
+ * Return value: @uri's query.
+ */
+const gchar *
+ide_uri_get_query (IdeUri *uri)
+{
+  return uri->query;
+}
+
+/**
+ * ide_uri_set_query:
+ * @uri: a #IdeUri
+ * @query: the (%<!-- -->-encoded) query
+ *
+ * Sets @uri's query to @query, which is assumed to have been
+ * %<!-- -->-encoded by the caller. See ide_uri_set_path() for more
+ * details.
+ */
+void
+ide_uri_set_query (IdeUri      *uri,
+                   const gchar *query)
+{
+  g_free (uri->query);
+  uri->query = g_strdup (query);
+}
+
+/**
+ * ide_uri_get_fragment:
+ * @uri: a #IdeUri
+ *
+ * Gets @uri's fragment, which may contain %<!-- -->-encoding,
+ * depending on the flags with which @uri was parsed.
+ *
+ * Return value: @uri's fragment.
+ */
+const gchar *
+ide_uri_get_fragment (IdeUri *uri)
+{
+  return uri->fragment;
+}
+
+/**
+ * ide_uri_set_fragment:
+ * @uri: a #IdeUri
+ * @fragment: the (%<!-- -->-encoded) fragment
+ *
+ * Sets @uri's fragment to @fragment, which is assumed to have been
+ * %<!-- -->-encoded by the caller. See ide_uri_set_path() for more
+ * details.
+ */
+void
+ide_uri_set_fragment (IdeUri      *uri,
+                      const gchar *fragment)
+{
+  g_free (uri->fragment);
+  uri->fragment = g_strdup (fragment);
+}
+
+GQuark
+ide_uri_error_quark (void)
+{
+  return g_quark_from_static_string ("ide-uri-error-quark");
+}
diff --git a/libide/ide-uri.h b/libide/ide-uri.h
new file mode 100644
index 0000000..6438f6e
--- /dev/null
+++ b/libide/ide-uri.h
@@ -0,0 +1,168 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1999-2008 Novell, Inc.
+ * Copyright (C) 2008-2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __IDE_URI_H__
+#define __IDE_URI_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _IdeUri IdeUri;
+
+typedef enum
+{
+  IDE_URI_PARSE_STRICT      = 1 << 0,
+  IDE_URI_PARSE_HTML5       = 1 << 1,
+  IDE_URI_PARSE_NO_IRI      = 1 << 2,
+  IDE_URI_PARSE_PASSWORD    = 1 << 3,
+  IDE_URI_PARSE_AUTH_PARAMS = 1 << 4,
+  IDE_URI_PARSE_NON_DNS     = 1 << 5,
+  IDE_URI_PARSE_DECODED     = 1 << 6,
+  IDE_URI_PARSE_UTF8_ONLY   = 1 << 7
+} IdeUriParseFlags;
+
+IdeUri *       ide_uri_new          (const gchar        *uri_string,
+                                     IdeUriParseFlags    flags,
+                                     GError            **error);
+IdeUri *       ide_uri_new_relative (IdeUri             *base_uri,
+                                     const gchar        *uri_string,
+                                     IdeUriParseFlags    flags,
+                                     GError            **error);
+
+typedef enum
+{
+  IDE_URI_HIDE_PASSWORD    = 1 << 0,
+  IDE_URI_HIDE_AUTH_PARAMS = 1 << 1
+} IdeUriToStringFlags;
+
+#define IDE_TYPE_URI (ide_uri_get_type())
+
+GType        ide_uri_get_type        (void);
+
+char *       ide_uri_to_string       (IdeUri               *uri,
+                                      IdeUriToStringFlags   flags);
+
+IdeUri *     ide_uri_copy            (IdeUri               *uri);
+IdeUri *     ide_uri_ref             (IdeUri               *uri);
+void         ide_uri_unref           (IdeUri               *uri);
+
+const gchar *ide_uri_get_scheme      (IdeUri               *uri);
+void         ide_uri_set_scheme      (IdeUri               *uri,
+                                      const gchar        *scheme);
+
+const gchar *ide_uri_get_user        (IdeUri               *uri);
+void         ide_uri_set_user        (IdeUri               *uri,
+                                      const gchar        *user);
+
+const gchar *ide_uri_get_password    (IdeUri               *uri);
+void         ide_uri_set_password    (IdeUri               *uri,
+                                      const gchar        *password);
+
+const gchar *ide_uri_get_auth_params (IdeUri               *uri);
+void         ide_uri_set_auth_params (IdeUri               *uri,
+                                      const gchar        *auth_params);
+
+const gchar *ide_uri_get_host        (IdeUri               *uri);
+void         ide_uri_set_host        (IdeUri               *uri,
+                                      const gchar        *host);
+
+gushort      ide_uri_get_port        (IdeUri               *uri);
+void         ide_uri_set_port        (IdeUri               *uri,
+                                      gushort             port);
+
+const gchar *ide_uri_get_path        (IdeUri               *uri);
+void         ide_uri_set_path        (IdeUri               *uri,
+                                      const gchar        *path);
+
+const gchar *ide_uri_get_query       (IdeUri               *uri);
+void         ide_uri_set_query       (IdeUri               *uri,
+                                      const gchar        *query);
+
+const gchar *ide_uri_get_fragment    (IdeUri               *uri);
+void         ide_uri_set_fragment    (IdeUri               *uri,
+                                      const gchar        *fragment);
+
+
+void         ide_uri_split           (const gchar        *uri_string,
+                                      gboolean            strict,
+                                      gchar             **scheme,
+                                      gchar             **userinfo,
+                                      gchar             **host,
+                                      gchar             **port,
+                                      gchar             **path,
+                                      gchar             **query,
+                                      gchar             **fragment);
+GHashTable * ide_uri_parse_params    (const gchar        *params,
+                                      gssize              length,
+                                      gchar               separator,
+                                      gboolean            case_insensitive);
+gboolean     ide_uri_parse_host      (const gchar        *uri_string,
+                                      IdeUriParseFlags    flags,
+                                      gchar             **scheme,
+                                      gchar             **host,
+                                      gushort            *port,
+                                      GError            **error);
+
+gchar *      ide_uri_build           (const gchar        *scheme,
+                                      const gchar        *userinfo,
+                                      const gchar        *host,
+                                      const gchar        *port,
+                                      const gchar        *path,
+                                      const gchar        *query,
+                                      const gchar        *fragment);
+
+
+/**
+ * IDE_URI_ERROR:
+ *
+ * Error domain for URI methods. Errors in this domain will be from
+ * the #IdeUriError enumeration. See #GError for information on error
+ * domains.
+ */
+#define IDE_URI_ERROR (ide_uri_error_quark ())
+
+/**
+ * IdeUriError:
+ * @IDE_URI_ERROR_PARSE: URI could not be parsed
+ *
+ * Error codes returned by #IdeUri methods.
+ */
+typedef enum
+{
+  IDE_URI_ERROR_MISC,
+  IDE_URI_ERROR_BAD_SCHEME,
+  IDE_URI_ERROR_BAD_USER,
+  IDE_URI_ERROR_BAD_PASSWORD,
+  IDE_URI_ERROR_BAD_AUTH_PARAMS,
+  IDE_URI_ERROR_BAD_HOST,
+  IDE_URI_ERROR_BAD_PORT,
+  IDE_URI_ERROR_BAD_PATH,
+  IDE_URI_ERROR_BAD_QUERY,
+  IDE_URI_ERROR_BAD_FRAGMENT
+} IdeUriError;
+
+GQuark ide_uri_error_quark (void);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (IdeUri, ide_uri_unref)
+
+G_END_DECLS
+
+#endif /* __IDE_URI_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]