[glib] Added method g_network_address_parse_uri()



commit 63105d1074ce54fdda9c81bb0353abc9b0a24d62
Author: Nicolas Dufresne <nicolas dufresne collabora co uk>
Date:   Tue Aug 10 15:23:37 2010 -0400

    Added method g_network_address_parse_uri()
    
    This method allow creating a network address from a URI. If no port is
    found in the URI, the default_port parameter will be used. Note that new
    property scheme is there for future TLS implementation.
    
    Reviewed-by: Dan Winship <danw gnome org>

 docs/reference/gio/gio-sections.txt |    2 +
 gio/gio.symbols                     |    2 +
 gio/gnetworkaddress.c               |  296 +++++++++++++++++++++++++++++++++++
 gio/gnetworkaddress.h               |    4 +
 4 files changed, 304 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 202b689..92f074e 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1662,7 +1662,9 @@ GNetworkAddress
 g_network_address_new
 g_network_address_get_hostname
 g_network_address_get_port
+g_network_address_get_scheme
 g_network_address_parse
+g_network_address_parse_uri
 <SUBSECTION Standard>
 GNetworkAddressClass
 GNetworkAddressPrivate
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 2a579b6..e538c1b 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1212,8 +1212,10 @@ g_srv_target_list_sort
 g_network_address_get_type G_GNUC_CONST
 g_network_address_get_hostname
 g_network_address_get_port
+g_network_address_get_scheme
 g_network_address_new
 g_network_address_parse
+g_network_address_parse_uri
 #endif
 #endif
 
diff --git a/gio/gnetworkaddress.c b/gio/gnetworkaddress.c
index 0093be7..77a82ee 100644
--- a/gio/gnetworkaddress.c
+++ b/gio/gnetworkaddress.c
@@ -63,12 +63,14 @@ struct _GNetworkAddressPrivate {
   gchar *hostname;
   guint16 port;
   GList *sockaddrs;
+  gchar *scheme;
 };
 
 enum {
   PROP_0,
   PROP_HOSTNAME,
   PROP_PORT,
+  PROP_SCHEME,
 };
 
 static void g_network_address_set_property (GObject      *object,
@@ -93,6 +95,7 @@ g_network_address_finalize (GObject *object)
   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
 
   g_free (addr->priv->hostname);
+  g_free (addr->priv->scheme);
 
   if (addr->priv->sockaddrs)
     {
@@ -133,6 +136,15 @@ g_network_address_class_init (GNetworkAddressClass *klass)
                                                       G_PARAM_READWRITE |
                                                       G_PARAM_CONSTRUCT_ONLY |
                                                       G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SCHEME,
+                                   g_param_spec_string ("scheme",
+                                                        P_("Scheme"),
+                                                        P_("URI Scheme"),
+                                                        NULL,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -167,6 +179,12 @@ g_network_address_set_property (GObject      *object,
       addr->priv->port = g_value_get_uint (value);
       break;
 
+    case PROP_SCHEME:
+      if (addr->priv->scheme)
+        g_free (addr->priv->scheme);
+      addr->priv->scheme = g_value_dup_string (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -192,6 +210,10 @@ g_network_address_get_property (GObject    *object,
       g_value_set_uint (value, addr->priv->port);
       break;
 
+    case PROP_SCHEME:
+      g_value_set_string (value, addr->priv->scheme);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -396,6 +418,262 @@ g_network_address_parse (const gchar  *host_and_port,
   return connectable;
 }
 
+/* Allowed characters outside alphanumeric for unreserved. */
+#define G_URI_OTHER_UNRESERVED "-._~"
+
+/* This or something equivalent will eventually go into glib/guri.h */
+static gboolean
+_g_uri_parse_authority (const char  *uri,
+		        char       **host,
+		        guint16     *port,
+		        char       **userinfo)
+{
+  char *tmp_str;
+  const char *start, *p;
+  char c;
+
+  g_return_val_if_fail (uri != NULL, FALSE);
+
+  if (host)
+    *host = NULL;
+
+  if (port)
+    *port = 0;
+
+  if (userinfo)
+    *userinfo = NULL;
+
+  /* From RFC 3986 Decodes:
+   * URI          = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+   * hier-part    = "//" authority path-abempty
+   * path-abempty = *( "/" segment )
+   * authority    = [ userinfo "@" ] host [ ":" port ]
+   */
+
+  /* Check we have a valid scheme */
+  tmp_str = g_uri_parse_scheme (uri);
+
+  if (tmp_str == NULL)
+    return FALSE;
+
+  g_free (tmp_str);
+
+  /* Decode hier-part:
+   *  hier-part   = "//" authority path-abempty
+   */
+  p = uri;
+  start = strstr (p, "//");
+
+  if (start == NULL)
+    return FALSE;
+
+  start += 2;
+  p = strchr (start, '@');
+
+  if (p != NULL)
+    {
+      /* Decode userinfo:
+       * userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
+       * unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
+       * pct-encoded   = "%" HEXDIG HEXDIG
+       */
+      while (1)
+	{
+	  c = *p++;
+
+	  if (c == '@')
+	    break;
+
+	  /* pct-encoded */
+	  if (c == '%')
+	    {
+	      if (!(g_ascii_isxdigit (p[0]) ||
+		    g_ascii_isxdigit (p[1])))
+		return FALSE;
+
+	      p++;
+
+	      continue;
+	    }
+
+	  /* unreserved /  sub-delims / : */
+	  if (!(g_ascii_isalnum(c) ||
+		strchr (G_URI_OTHER_UNRESERVED, c) ||
+		strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
+		c == ':'))
+	    return FALSE;
+	}
+
+      if (userinfo)
+	*userinfo = g_strndup (start, p - start - 1);
+
+      start = p;
+    }
+  else
+    {
+      p = start;
+    }
+
+
+  /* decode host:
+   * host          = IP-literal / IPv4address / reg-name
+   * reg-name      = *( unreserved / pct-encoded / sub-delims )
+   */
+
+  /* If IPv6 or IPvFuture */
+  if (*p == '[')
+    {
+      while (1)
+	{
+	  c = *p++;
+
+	  if (c == ']')
+	    break;
+
+	  /* unreserved /  sub-delims */
+	  if (!(g_ascii_isalnum(c) ||
+		strchr (G_URI_OTHER_UNRESERVED, c) ||
+		strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
+		c == ':' ||
+		c == '.'))
+	    goto error;
+	}
+    }
+  else
+    {
+      while (1)
+	{
+	  c = *p++;
+
+	  if (c == ':' ||
+	      c == '/' ||
+	      c == '?' ||
+	      c == '#' ||
+	      c == '\0')
+	    break;
+
+	  /* pct-encoded */
+	  if (c == '%')
+	    {
+	      if (!(g_ascii_isxdigit (p[0]) ||
+		    g_ascii_isxdigit (p[1])))
+		goto error;
+
+	      p++;
+
+	      continue;
+	    }
+
+	  /* unreserved /  sub-delims */
+	  if (!(g_ascii_isalnum(c) ||
+		strchr (G_URI_OTHER_UNRESERVED, c) ||
+		strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c)))
+	    goto error;
+	}
+    }
+
+  if (host)
+    *host = g_uri_unescape_segment (start, p - 1, NULL);
+
+  if (c == ':')
+    {
+      /* Decode pot:
+       *  port          = *DIGIT
+       */
+      guint tmp = 0;
+
+      while (1)
+	{
+	  c = *p++;
+
+	  if (c == '/' ||
+	      c == '?' ||
+	      c == '#' ||
+	      c == '\0')
+	    break;
+
+	  if (!g_ascii_isdigit (c))
+	    goto error;
+
+	  tmp = (tmp * 10) + (c - '0');
+
+	  if (tmp > 65535)
+	    goto error;
+	}
+      if (port)
+	*port = (guint16) tmp;
+    }
+
+  return TRUE;
+
+error:
+  if (host && *host)
+    {
+      g_free (*host);
+      *host = NULL;
+    }
+
+  if (userinfo && *userinfo)
+    {
+      g_free (*userinfo);
+      *userinfo = NULL;
+    }
+
+  return FALSE;
+}
+
+/**
+ * g_network_address_parse_uri:
+ * @uri: the hostname and optionally a port
+ * @default_port: The default port if none is found in the URI
+ * @error: a pointer to a #GError, or %NULL
+ *
+ * Creates a new #GSocketConnectable for connecting to the given
+ * @uri. May fail and return %NULL in case parsing @uri fails.
+ *
+ * Using this rather than g_network_address_new() or
+ * g_network_address_parse_host() allows #GSocketClient to determine
+ * when to use application-specific proxy protocols.
+ *
+ * Return value: the new #GNetworkAddress, or %NULL on error
+ *
+ * Since: 2.26
+ */
+GSocketConnectable *
+g_network_address_parse_uri (const gchar  *uri,
+    			     guint16       default_port,
+			     GError      **error)
+{
+  GSocketConnectable *conn;
+  gchar *scheme;
+  gchar *hostname;
+  guint16 port;
+
+  if (!_g_uri_parse_authority (uri, &hostname, &port, NULL))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+		   "Invalid URI '%s'",
+		   uri);
+      return NULL;
+    }
+
+  if (port == 0)
+    port = default_port;
+
+  scheme = g_uri_parse_scheme (uri);
+
+  conn = g_object_new (G_TYPE_NETWORK_ADDRESS,
+                       "hostname", hostname,
+                       "port", port,
+                       "scheme", scheme,
+                       NULL);
+
+  g_free (scheme);
+  g_free (hostname);
+
+  return conn;
+}
+
 /**
  * g_network_address_get_hostname:
  * @addr: a #GNetworkAddress
@@ -433,6 +711,24 @@ g_network_address_get_port (GNetworkAddress *addr)
   return addr->priv->port;
 }
 
+/**
+ * g_network_address_get_scheme:
+ * @addr: a #GNetworkAddress
+ *
+ * Gets @addr's scheme
+ *
+ * Return value: @addr's scheme (%NULL if not built from URI)
+ *
+ * Since: 2.26
+ */
+const gchar *
+g_network_address_get_scheme (GNetworkAddress *addr)
+{
+  g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
+
+  return addr->priv->scheme;
+}
+
 #define G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (_g_network_address_address_enumerator_get_type ())
 #define G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, GNetworkAddressAddressEnumerator))
 
diff --git a/gio/gnetworkaddress.h b/gio/gnetworkaddress.h
index 0111f70..af16321 100644
--- a/gio/gnetworkaddress.h
+++ b/gio/gnetworkaddress.h
@@ -60,8 +60,12 @@ GSocketConnectable *g_network_address_new          (const gchar      *hostname,
 GSocketConnectable *g_network_address_parse        (const gchar      *host_and_port,
 						    guint16           default_port,
 						    GError          **error);
+GSocketConnectable *g_network_address_parse_uri    (const gchar      *uri,
+    						    guint16           default_port,
+						    GError          **error);
 const gchar        *g_network_address_get_hostname (GNetworkAddress  *addr);
 guint16             g_network_address_get_port     (GNetworkAddress  *addr);
+const gchar        *g_network_address_get_scheme   (GNetworkAddress  *addr);
 
 
 G_END_DECLS



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