[Telepathy-commits] [telepathy-sofiasip/master] WIP: reimplemented URI normalization with use of GRegex

Mikhail Zabaluev mikhail.zabaluev at nokia.com
Tue Oct 7 02:09:14 PDT 2008


---
 src/sip-connection-helpers.c |  257 +++++++++++++----------------------------
 src/sip-connection-helpers.h |   10 +-
 2 files changed, 89 insertions(+), 178 deletions(-)

diff --git a/src/sip-connection-helpers.c b/src/sip-connection-helpers.c
index 7bf84b0..7889e5a 100644
--- a/src/sip-connection-helpers.c
+++ b/src/sip-connection-helpers.c
@@ -653,247 +653,154 @@ tpsip_conn_discover_stun_server (TpsipConnection *conn)
 }
 
 static gboolean
-priv_is_user_unreserved (gchar x)
+priv_is_host (const gchar* str)
 {
-    switch (x)
-      {
-        case '-':
-        case '_':
-        case '.':
-        case '!':
-        case '~':
-        case '*':
-        case '\'':
-        case '(':
-        case ')':
-        case '&':
-        case '=':
-        case '+':
-        case '$':
-        case ',':
-        case ':':
-        case '?':
-        case ';':
-        case '/':
-          return TRUE;
-        default:
-          return g_ascii_isalnum (x);
-      }
-}
-
-static gboolean
-priv_is_host (gchar x)
-{
-    switch (x)
-      {
-        case '.':
-        case '-':
-          return TRUE;
-        default:
-          return g_ascii_isalnum (x);
-      }
-}
-
-static gchar *
-priv_user_encode (su_home_t *home, const gchar *string)
-{
-  const gchar *a;
-  gchar *b;
-  gchar *res = su_zalloc (home, strlen (string) * 3 + 1);
+  static GRegex *host_regex = NULL;
 
-  g_return_val_if_fail (res != NULL, NULL);
+#define DOMAIN "[[:alnum:]]([-[:alnum:]]*[[:alnum:]])?"
+#define TLD "[[:alpha:]]([-[:alnum:]]*[[:alnum:]])?"
 
-  a = string;
-  b = res;
-  while (*a)
+  if (host_regex == NULL)
     {
-      if (priv_is_user_unreserved (*a))
-        {
-          *b++ = *a++;
-        }
-      else
-        {
-          snprintf (b, 4, "%%%02x", (guint) *a);
-          ++a;
-          b += 3;
-        }
-    }
+      GError *error = NULL;
 
-  return res;
-}
-
-/* unescape characters that don't need escaping */
-static gchar *
-priv_user_decode (su_home_t *home, const gchar *string)
-{
-    const gchar *a;
-    gchar *b;
-    gchar *res = su_zalloc (home, strlen (string) + 1);
+      host_regex = g_regex_new ("^("
+            "("DOMAIN"\\.)*"TLD"\\.?|"                  /* host name */
+            "[[:digit:]]{1,3}(\\.[[:digit:]]{1,3}){3}|" /* IPv4 address */
+            "\\[[[:xdigit:]:.]\\]"                      /* IPv6 address, sloppily */
+          ")$",
+          G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &error);
 
-    g_return_val_if_fail (res != NULL, NULL);
+      if (error != NULL)
+        g_error ("failed to compile the host regex: %s", error->message);
+    }
 
-    a = string;
-    b = res;
-    while (*a)
-      {
-        if ((a[0] == '%') && g_ascii_isxdigit(a[1]) && g_ascii_isxdigit(a[2]))
-          {
-            gchar x = (gchar) (g_ascii_xdigit_value(a[1]) * 16
-                               + g_ascii_xdigit_value(a[2]));
-            if (priv_is_user_unreserved (x))
-              {
-                *b++ = x;
-                a += 3;
-                continue;
-              }
-          }
-        *b++ = *a++;
-      }
+#undef DOMAIN
+#undef TLD
 
-    return res;
+  return g_regex_match (host_regex, str, 0, NULL);
 }
 
 static gboolean
-priv_is_tel_num (const gchar *string)
+priv_is_tel_num (const gchar *str)
 {
-  const gchar *pc;
-  gboolean has_digits = FALSE;
+  static GRegex *tel_num_regex = NULL;
 
-  g_return_val_if_fail (string != NULL, FALSE);
-
-  /* skip the initial whitespace */
-  pc = string + strspn (string, " \t");
+  if (tel_num_regex == NULL)
+    {
+      GError *error = NULL;
 
-  /* the leading '+' is acceptable */
-  if (*pc == '+')
-    ++pc;
+      tel_num_regex = g_regex_new (
+          "^\\s*[\\+(]?\\s*[[:digit:]][-.[:digit:]()\\s]*$",
+          G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &error);
 
-  /* only digits, delimiters and inline whitespace */
-  while (*pc)
-    {
-      if (g_ascii_isdigit (*pc))
-        has_digits = TRUE;
-      else
-        switch (*pc)
-          {
-          case ' ':
-          case '\t':
-          case '-':
-          case '.':
-          case '(':
-          case ')':
-            break;
-          default:
-            return FALSE;
-          }
-      ++pc;
+      if (error != NULL)
+        g_error ("failed to compile the telephone number regex: %s", error->message);
     }
 
-  return has_digits;
+  return g_regex_match (tel_num_regex, str, 0, NULL);
 }
 
+/* Strip the non-essential characters from a string regarded as
+ * a telephone number */
 static gchar *
-priv_strip_whitespace (su_home_t *home, const gchar *string)
+priv_strip_tel_num (const gchar *fuzzy)
 {
-  const gchar *a;
-  gchar *b;
-  gchar *res = su_zalloc (home, strlen (string) + 1);
+  static GRegex *cruft_regex = NULL;
 
-  g_return_val_if_fail (res != NULL, NULL);
-
-  b = res;
-  for (a = string; *a; a++)
+  if (cruft_regex == NULL)
     {
-      if (!g_ascii_isspace (*a))
-        *b++ = *a;
+      GError *error = NULL;
+
+      cruft_regex = g_regex_new ("[^+[:digit:]]+",
+          G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &error);
+
+      if (error != NULL)
+        g_error ("failed to compile the non-essential telephone number cruft regex: %s", error->message);
     }
-  *b = '\0';
 
-  return res;
+  return g_regex_replace_literal (cruft_regex, fuzzy, -1, 0, "", 0, NULL);
 }
 
+#define TPSIP_RESERVED_CHARS_ALLOWED_IN_USERNAME "!*'()&=+$,;?/"
+
 gchar *
-tpsip_conn_normalize_uri (TpsipConnection *conn,
+tpsip_handle_normalize (TpHandleRepoIface *repo,
                         const gchar *sipuri,
+                        gpointer context,
                         GError **error)
 {
+  TpsipConnection *conn = TPSIP_CONNECTION (context);
   TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (conn);
-  su_home_t home[1] = { SU_HOME_INIT (home) };
-  url_t *url = NULL;;
+  const url_t *base_url = priv->account_url;
+  su_home_t home[1] = { SU_HOME_INIT(home) };
+  url_t *url;
   gchar *retval = NULL;
-  char *c, *str;
+  char *c;
 
   url = url_make (home, sipuri);
 
-  /* we got username or phone number, local to our domain */
-  if ((url == NULL) ||
-      ((url->url_scheme == NULL) && (url->url_user == NULL)))
+  if (url == NULL ||
+      (url->url_scheme == NULL && url->url_user == NULL))
     {
-      if ((priv->account_url == NULL) || (priv->account_url->url_host == NULL))
+      /* we got username or phone number, local to our domain */
+      gchar *user;
+
+      if (base_url == NULL || base_url->url_host == NULL)
         {
-          g_debug ("local uri specified and we don't know local domain yet");
+          g_warning ("bare name given, but no account URL is set");
           goto error;
         }
 
       if (priv_is_tel_num (sipuri))
         {
+          user = priv_strip_tel_num (sipuri);
           url = url_format (home, "sip:%s@%s;user=phone",
-              priv_strip_whitespace (home, sipuri),
-              priv->account_url->url_host);
+              user, base_url->url_host);
         }
       else
         {
+          user = g_uri_escape_string (sipuri,
+              TPSIP_RESERVED_CHARS_ALLOWED_IN_USERNAME, FALSE);
           url = url_format (home, "sip:%s@%s",
-              priv_user_encode (home, sipuri),
-              priv->account_url->url_host);
+              user, base_url->url_host);
         }
+
+      g_free (user);
+
       if (!url) goto error;
     }
-  else
+
+  if (url_sanitize (url) != 0) goto error;
+
+  /* scheme should've been set by now */
+  if (url->url_scheme == NULL || (url->url_scheme[0] == 0))
+    goto error;
+
+  /* Check that if we have '@', the username isn't empty.
+   * Note that we rely on Sofia-SIP to canonize the user name */
+  if (url->url_user)
     {
-      if ((url != NULL) && (url->url_user != NULL))
-        {
-          url->url_user = (char *) priv_user_decode (home, url->url_user);
-        }
+      if (url->url_user[0] == 0) goto error;
     }
 
-  if (url_sanitize (url)) goto error;
-
-  /* scheme and host should've been set by now */
-  if (!url->url_scheme || (url->url_scheme[0] == 0) ||
-      !url->url_host || (url->url_host[0] == 0))
+  /* host should be set and valid */
+  if (url->url_host == NULL || !priv_is_host (url->url_host))
       goto error;
 
+  /* convert host to lowercase */
   for (c = (char *) url->url_host; *c; c++)
     {
-      /* check for illegal characters */
-      if (!priv_is_host (*c))
-          goto error;
-
-      /* convert host to lowercase */
       *c = g_ascii_tolower (*c);
     }
-  /* check that the hostname isn't empty */
-  if (c == url->url_host) goto error;
 
-  /* check that if we have '@', the username isn't empty */
-  if (url->url_user)
-    {
-      if (url->url_user[0] == 0) goto error;
-    }
-
-  str = url_as_string (home, url);
-  if (NULL == str) goto error;
-
-  retval = g_strdup (str);
+  retval = g_strdup (url_as_string (home, url));
 
 error:
-  if (NULL == retval)
+  if (retval == NULL)
       g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
           "invalid SIP URI");
 
-  /* success */
   su_home_deinit (home);
   return retval;
 }
-
diff --git a/src/sip-connection-helpers.h b/src/sip-connection-helpers.h
index fca2888..3a27201 100644
--- a/src/sip-connection-helpers.h
+++ b/src/sip-connection-helpers.h
@@ -61,9 +61,13 @@ void tpsip_conn_save_event (TpsipConnection *conn,
  * SIP URI helpers *
  ***********************************************************************/
 
-gchar * tpsip_conn_normalize_uri (TpsipConnection *conn,
-                                  const gchar *sipuri,
-                                  GError **error);
+gchar * tpsip_handle_normalize (TpHandleRepoIface *repo,
+                                const gchar *sipuri,
+                                gpointer context,
+                                GError **error);
+
+const url_t* tpsip_handle_inspect_url (TpHandleRepoIface *repo,
+                                       TpHandle handle);
 
 G_END_DECLS
 
-- 
1.5.6.5




More information about the Telepathy-commits mailing list