[Telepathy-commits] [telepathy-gabble/master] Move hash functions in a new file caps-hash.[ch]

Alban Crequy alban.crequy at collabora.co.uk
Tue Aug 19 10:52:32 PDT 2008


20080509171001-a41c0-717a00624ff62250c40a772840f88419b64df9dc.gz
---
 src/Makefile.am         |    2 +
 src/caps-hash.c         |  345 +++++++++++++++++++++++++++++++++++++++++++++++
 src/caps-hash.h         |   31 +++++
 src/gabble-connection.c |    2 +
 src/presence-cache.c    |    1 +
 src/presence.c          |  299 ----------------------------------------
 src/presence.h          |    4 -
 7 files changed, 381 insertions(+), 303 deletions(-)
 create mode 100644 src/caps-hash.c
 create mode 100644 src/caps-hash.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 84f689e..68b53a6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,6 +23,8 @@ libgabble_convenience_la_our_sources = \
     base64.c \
     capabilities.h \
     capabilities.c \
+    caps-hash.h \
+    caps-hash.c \
     bytestream-factory.h \
     bytestream-factory.c \
     bytestream-ibb.h \
diff --git a/src/caps-hash.c b/src/caps-hash.c
new file mode 100644
index 0000000..a8e0edf
--- /dev/null
+++ b/src/caps-hash.c
@@ -0,0 +1,345 @@
+/*
+ * caps-hash.c - Computing verification string hash (XEP-0115 v1.5)
+ * Copyright (C) 2008 Collabora Ltd.
+ *
+ * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/* Computing verification string hash (XEP-0115 v1.5)
+ *
+ * Gabble does not do anything with dataforms (XEP-0128) included in
+ * capabilities.  However, it needs to parse them in order to compute the hash
+ * according to XEP-0115.
+ */
+
+#include "caps-hash.h"
+
+#include <string.h>
+#include <glib.h>
+#include <time.h>
+
+#include "base64.h"
+#include "capabilities.h"
+#include "presence.h"
+#include "presence-cache.h"
+#include "namespaces.h"
+#include "sha1/sha1.h"
+#include "util.h"
+
+#include "config.h"
+#define DEBUG_FLAG GABBLE_DEBUG_PRESENCE
+#include "debug.h"
+
+struct _dataform_field {
+  gchar *fieldname;
+  GPtrArray *values;
+};
+
+struct _dataform {
+  gchar *form_type;
+  GPtrArray *fields;
+};
+
+
+static gint
+feature_strcmp (gconstpointer a, gconstpointer b)
+{
+  gchar *left = *(gchar **) a;
+  gchar *right = *(gchar **) b;
+
+  return strcmp (left, right);
+}
+
+static gint
+fields_cmp (gconstpointer a, gconstpointer b)
+{
+  struct _dataform_field *left = *(struct _dataform_field **) a;
+  struct _dataform_field *right = *(struct _dataform_field **) b;
+
+  return strcmp (left->fieldname, right->fieldname);
+}
+
+static gint
+dataforms_cmp (gconstpointer a, gconstpointer b)
+{
+  struct _dataform *left = *(struct _dataform **) a;
+  struct _dataform *right = *(struct _dataform **) b;
+
+  return strcmp (left->form_type, right->form_type);
+}
+
+static void
+_free_field (gpointer data, gpointer user_data)
+{
+  struct _dataform_field *field = data;
+
+  g_free (field->fieldname);
+  g_ptr_array_foreach (field->values, (GFunc) g_free, NULL);
+
+  g_slice_free1 (sizeof (struct _dataform_field), field);
+}
+
+static void
+_free_form (gpointer data, gpointer user_data)
+{
+  struct _dataform *form = data;
+
+  g_free (form->form_type);
+
+  g_ptr_array_foreach (form->fields, (GFunc) _free_field, NULL);
+
+  g_slice_free1 (sizeof (struct _dataform), form);
+}
+
+static void
+gabble_presence_free_xep0115_hash (
+    GPtrArray *features,
+    GPtrArray *identities,
+    GPtrArray *dataforms)
+{
+  g_ptr_array_foreach (features, (GFunc) g_free, NULL);
+  g_ptr_array_foreach (identities, (GFunc) g_free, NULL);
+  g_ptr_array_foreach (dataforms, (GFunc) _free_form, NULL);
+
+  g_ptr_array_free (features, TRUE);
+  g_ptr_array_free (identities, TRUE);
+  g_ptr_array_free (dataforms, TRUE);
+}
+
+static gchar *
+gabble_presence_compute_xep0115_hash (
+    GPtrArray *features,
+    GPtrArray *identities,
+    GPtrArray *dataforms)
+{
+  GString *s;
+  gchar *str;
+  gchar sha1[SHA1_HASH_SIZE];
+  unsigned int i, j, k;
+  gchar *encoded;
+
+  g_ptr_array_sort (identities, feature_strcmp);
+  g_ptr_array_sort (features, feature_strcmp);
+  g_ptr_array_sort (dataforms, dataforms_cmp);
+
+  s = g_string_new ("");
+
+  for (i = 0 ; i < identities->len ; i++)
+    {
+      s = g_string_append (s, g_ptr_array_index (identities, i));
+      s = g_string_append (s, "<");
+    }
+
+  for (i = 0 ; i < features->len ; i++)
+    {
+      s = g_string_append (s, g_ptr_array_index (features, i));
+      s = g_string_append (s, "<");
+    }
+
+  for (i = 0 ; i < dataforms->len ; i++)
+    {
+      struct _dataform *form = g_ptr_array_index (features, i);
+
+      s = g_string_append (s, form->form_type);
+      s = g_string_append (s, "<");
+
+      g_ptr_array_sort (form->fields, fields_cmp);
+
+      for (j = 0 ; j < form->fields->len ; j++)
+        {
+          struct _dataform_field *field = g_ptr_array_index (form->fields, j);
+
+          s = g_string_append (s, field->fieldname);
+          s = g_string_append (s, "<");
+
+          g_ptr_array_sort (field->values, fields_cmp);
+
+          for (k = 0 ; k < field->values->len ; k++)
+            {
+              s = g_string_append (s, g_ptr_array_index (field->values, k));
+              s = g_string_append (s, "<");
+            }
+        }
+    }
+
+  str = g_string_free (s, FALSE);
+  DEBUG ("caps string: '%s'\n", str);
+  sha1_bin (str, strlen (str), (guchar *) sha1);
+  encoded = base64_encode (SHA1_HASH_SIZE, sha1, FALSE);
+  DEBUG ("caps base64: '%s'\n", encoded);
+
+  return encoded;
+}
+
+/**
+ * Compute the hash as defined by the XEP-0115 from a received LmMessageNode
+ *
+ * Returns: the hash. The called must free the returned hash with g_free().
+ */
+gchar *
+gabble_presence_compute_xep0115_hash_from_lm_node (LmMessageNode *node)
+{
+  GPtrArray *features = g_ptr_array_new ();
+  GPtrArray *identities = g_ptr_array_new ();
+  GPtrArray *dataforms = g_ptr_array_new ();
+  LmMessageNode *child;
+  gchar *str;
+
+  for (child = node->children; NULL != child; child = child->next)
+    {
+      if (g_str_equal (child->name, "identity"))
+        {
+          const gchar *category;
+          const gchar *name;
+          const gchar *type;
+          const gchar *xmllang;
+
+          category = lm_message_node_get_attribute (child, "category");
+          name = lm_message_node_get_attribute (child, "name");
+          type = lm_message_node_get_attribute (child, "type");
+          xmllang = lm_message_node_get_attribute (child, "xml:lang");
+
+          if (NULL == category)
+            continue;
+          if (NULL == name)
+            name = "";
+          if (NULL == type)
+            type = "";
+          if (NULL == xmllang)
+            xmllang = "";
+
+          g_ptr_array_add (identities,
+              (gpointer) g_strdup_printf ("%s/%s/%s/%s",
+                  category, type, xmllang, name));
+        }
+      else if (g_str_equal (child->name, "feature"))
+        {
+          const gchar *var;
+          var = lm_message_node_get_attribute (child, "var");
+
+          if (NULL == var)
+            continue;
+
+          g_ptr_array_add (features, (gpointer) g_strdup (var));
+        }
+      else if (g_str_equal (child->name, "x"))
+        {
+          const gchar *xmlns;
+          const gchar *type;
+          LmMessageNode *x_child;
+          struct _dataform *form;
+
+          xmlns = lm_message_node_get_attribute (child, "xmlns");
+          type = lm_message_node_get_attribute (child, "type");
+
+          if (! g_str_equal (xmlns, "jabber:x:data"))
+            continue;
+
+          if (! g_str_equal (type, "result"))
+            continue;
+
+          form = g_slice_new0 (struct _dataform);
+
+          for (x_child = child->children;
+               NULL != x_child;
+               x_child = x_child->next)
+            {
+              const gchar *var;
+              LmMessageNode *value_child;
+              struct _dataform_field *field;
+
+              if (! g_str_equal (x_child->name, "field"))
+                continue;
+
+              var = lm_message_node_get_attribute (x_child, "var");
+
+              if (NULL == var)
+                continue;
+
+              field = g_slice_new0 (struct _dataform_field);
+
+              for (value_child = x_child->children;
+                   NULL != value_child;
+                   value_child = value_child->next)
+                {
+                  const gchar *content;
+
+                  if (! g_str_equal (value_child->name, "value"))
+                    continue;
+
+                  if (g_str_equal (var, "FORM_TYPE"))
+                    {
+                      form->form_type = g_strdup (var);
+                    }
+                  else
+                    {
+                      content = lm_message_node_get_value (value_child);
+                      g_ptr_array_add (field->values,
+                          (gpointer) g_strdup (content));
+                    }
+                }
+
+              g_ptr_array_add (form->fields, (gpointer) field);
+            }
+
+          g_ptr_array_add (dataforms, (gpointer) form);
+        }
+    }
+
+  str = gabble_presence_compute_xep0115_hash (features, identities, dataforms);
+
+  gabble_presence_free_xep0115_hash (features, identities, dataforms);
+
+  return str;
+}
+
+
+/**
+ * Compute our hash as defined by the XEP-0115.
+ *
+ * Returns: the hash. The called must free the returned hash with g_free().
+ */
+gchar *
+gabble_presence_compute_xep0115_hash_from_self_presence (GabbleConnection *self)
+{
+  GabblePresence *presence = self->self_presence;
+  GSList *features_list = capabilities_get_features (presence->caps);
+  GPtrArray *features = g_ptr_array_new ();
+  GPtrArray *identities = g_ptr_array_new ();
+  GPtrArray *dataforms = g_ptr_array_new ();
+  gchar *str;
+  GSList *i;
+
+  /* get our features list  */
+  for (i = features_list; NULL != i; i = i->next)
+    {
+      const Feature *feat = (const Feature *) i->data;
+      g_ptr_array_add (features, (gpointer) g_strdup (feat->ns));
+    }
+
+  /* XEP-0030 requires at least 1 identity. We don't need more. */
+  g_ptr_array_add (identities,
+      (gpointer) g_strdup ("client/pc//" PACKAGE_STRING));
+
+  /* Gabble does not use dataforms, let 'dataforms' be empty */
+
+  str = gabble_presence_compute_xep0115_hash (features, identities, dataforms);
+
+  gabble_presence_free_xep0115_hash (features, identities, dataforms);
+  g_slist_free (features_list);
+
+  return str;
+}
+
diff --git a/src/caps-hash.h b/src/caps-hash.h
new file mode 100644
index 0000000..cc9c3c4
--- /dev/null
+++ b/src/caps-hash.h
@@ -0,0 +1,31 @@
+/*
+ * caps-hash.h - Headers for computing verification string hash (XEP-0115 v1.5)
+ * Copyright (C) 2008 Collabora Ltd.
+ *
+ * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#ifndef __CAPS_HASH_H__
+#define __CAPS_HASH_H__
+
+#include <loudmouth/loudmouth.h>
+#include "gabble-connection.h"
+
+gchar *gabble_presence_compute_xep0115_hash_from_lm_node (LmMessageNode *node);
+gchar *gabble_presence_compute_xep0115_hash_from_self_presence (
+    GabbleConnection *self);
+
+#endif /* __CAPS_HASH_H__ */
diff --git a/src/gabble-connection.c b/src/gabble-connection.c
index e41eaa0..6eb718d 100644
--- a/src/gabble-connection.c
+++ b/src/gabble-connection.c
@@ -46,6 +46,7 @@
 
 #include "bytestream-factory.h"
 #include "capabilities.h"
+#include "caps-hash.h"
 #include "conn-aliasing.h"
 #include "conn-avatars.h"
 #include "conn-presence.h"
@@ -1266,6 +1267,7 @@ connection_shut_down (TpBaseConnection *base)
     }
 }
 
+
 /**
  * _gabble_connection_signal_own_presence:
  * @self: A #GabbleConnection
diff --git a/src/presence-cache.c b/src/presence-cache.c
index 898abf4..59759e5 100644
--- a/src/presence-cache.c
+++ b/src/presence-cache.c
@@ -30,6 +30,7 @@ be enough */
 
 #include <telepathy-glib/intset.h>
 
+#include "caps-hash.h"
 #include "debug.h"
 #include "disco.h" /* \o\ \o/ /o/ */
 #include "namespaces.h"
diff --git a/src/presence.c b/src/presence.c
index fb7c6bd..cb60622 100644
--- a/src/presence.c
+++ b/src/presence.c
@@ -24,11 +24,8 @@
 #include <glib.h>
 #include <time.h>
 
-#include "base64.h"
-#include "capabilities.h"
 #include "presence-cache.h"
 #include "namespaces.h"
-#include "sha1/sha1.h"
 #include "util.h"
 
 #include "config.h"
@@ -469,299 +466,3 @@ gabble_presence_dump (GabblePresence *presence)
   return g_string_free (ret, FALSE);
 }
 
-static gint
-feature_strcmp (gconstpointer a, gconstpointer b)
-{
-  gchar *left = *(gchar **) a;
-  gchar *right = *(gchar **) b;
-
-  return strcmp (left, right);
-}
-
-struct _dataform_field {
-  gchar *fieldname;
-  GPtrArray *values;
-};
-
-struct _dataform {
-  gchar *form_type;
-  GPtrArray *fields;
-};
-
-static gint
-fields_cmp (gconstpointer a, gconstpointer b)
-{
-  struct _dataform_field *left = *(struct _dataform_field **) a;
-  struct _dataform_field *right = *(struct _dataform_field **) b;
-
-  return strcmp (left->fieldname, right->fieldname);
-}
-
-static gint
-dataforms_cmp (gconstpointer a, gconstpointer b)
-{
-  struct _dataform *left = *(struct _dataform **) a;
-  struct _dataform *right = *(struct _dataform **) b;
-
-  return strcmp (left->form_type, right->form_type);
-}
-
-static gchar *
-gabble_presence_compute_xep0115_hash (
-    GPtrArray *features,
-    GPtrArray *identities,
-    GPtrArray *dataforms)
-{
-  GString *s;
-  gchar *str;
-  gchar sha1[SHA1_HASH_SIZE];
-  unsigned int i, j, k;
-  gchar *encoded;
-
-  g_ptr_array_sort (identities, feature_strcmp);
-  g_ptr_array_sort (features, feature_strcmp);
-  g_ptr_array_sort (dataforms, dataforms_cmp);
-
-  s = g_string_new ("");
-
-  for (i = 0 ; i < identities->len ; i++)
-    {
-      s = g_string_append (s, g_ptr_array_index (identities, i));
-      s = g_string_append (s, "<");
-    }
-
-  for (i = 0 ; i < features->len ; i++)
-    {
-      s = g_string_append (s, g_ptr_array_index (features, i));
-      s = g_string_append (s, "<");
-    }
-
-  for (i = 0 ; i < dataforms->len ; i++)
-    {
-      struct _dataform *form = g_ptr_array_index (features, i);
-
-      s = g_string_append (s, form->form_type);
-      s = g_string_append (s, "<");
-
-      g_ptr_array_sort (form->fields, fields_cmp);
-
-      for (j = 0 ; j < form->fields->len ; j++)
-        {
-          struct _dataform_field *field = g_ptr_array_index (form->fields, j);
-
-          s = g_string_append (s, field->fieldname);
-          s = g_string_append (s, "<");
-
-          g_ptr_array_sort (field->values, fields_cmp);
-
-          for (k = 0 ; k < field->values->len ; k++)
-            {
-              s = g_string_append (s, g_ptr_array_index (field->values, k));
-              s = g_string_append (s, "<");
-            }
-        }
-    }
-
-  str = g_string_free (s, FALSE);
-  DEBUG ("caps string: '%s'\n", str);
-  sha1_bin (str, strlen (str), (guchar *) sha1);
-  encoded = base64_encode (SHA1_HASH_SIZE, sha1, FALSE);
-  DEBUG ("caps base64: '%s'\n", encoded);
-
-  return encoded;
-}
-
-static void
-_free_field (gpointer data, gpointer user_data)
-{
-  struct _dataform_field *field = data;
-
-  g_free (field->fieldname);
-  g_ptr_array_foreach (field->values, (GFunc) g_free, NULL);
-
-  g_slice_free1 (sizeof (struct _dataform_field), field);
-}
-
-static void
-_free_form (gpointer data, gpointer user_data)
-{
-  struct _dataform *form = data;
-
-  g_free (form->form_type);
-
-  g_ptr_array_foreach (form->fields, (GFunc) _free_field, NULL);
-
-  g_slice_free1 (sizeof (struct _dataform), form);
-}
-
-static void
-gabble_presence_free_xep0115_hash (
-    GPtrArray *features,
-    GPtrArray *identities,
-    GPtrArray *dataforms)
-{
-  g_ptr_array_foreach (features, (GFunc) g_free, NULL);
-  g_ptr_array_foreach (identities, (GFunc) g_free, NULL);
-  g_ptr_array_foreach (dataforms, (GFunc) _free_form, NULL);
-
-  g_ptr_array_free (features, TRUE);
-  g_ptr_array_free (identities, TRUE);
-  g_ptr_array_free (dataforms, TRUE);
-}
-
-/**
- *
- * Compute the hash as defined by the XEP-0115
- *
- * Returns: the hash. The called must free the returned hash.
- */
-gchar *
-gabble_presence_compute_xep0115_hash_from_lm_node (LmMessageNode *node)
-{
-  GPtrArray *features = g_ptr_array_new ();
-  GPtrArray *identities = g_ptr_array_new ();
-  GPtrArray *dataforms = g_ptr_array_new ();
-  LmMessageNode *child;
-  gchar *str;
-
-  for (child = node->children; NULL != child; child = child->next)
-    {
-      if (g_str_equal (child->name, "identity"))
-        {
-          const gchar *category;
-          const gchar *name;
-          const gchar *type;
-          const gchar *xmllang;
-
-          category = lm_message_node_get_attribute (child, "category");
-          name = lm_message_node_get_attribute (child, "name");
-          type = lm_message_node_get_attribute (child, "type");
-          xmllang = lm_message_node_get_attribute (child, "xml:lang");
-
-          if (NULL == category)
-            continue;
-          if (NULL == name)
-            name = "";
-          if (NULL == type)
-            type = "";
-          if (NULL == xmllang)
-            xmllang = "";
-
-          g_ptr_array_add (identities,
-              (gpointer) g_strdup_printf ("%s/%s/%s/%s",
-                  category, type, xmllang, name));
-        }
-      else if (g_str_equal (child->name, "feature"))
-        {
-          const gchar *var;
-          var = lm_message_node_get_attribute (child, "var");
-
-          if (NULL == var)
-            continue;
-
-          g_ptr_array_add (features, (gpointer) g_strdup (var));
-        }
-      else if (g_str_equal (child->name, "x"))
-        {
-          const gchar *xmlns;
-          const gchar *type;
-          LmMessageNode *x_child;
-          struct _dataform *form;
-
-          xmlns = lm_message_node_get_attribute (child, "xmlns");
-          type = lm_message_node_get_attribute (child, "type");
-
-          if (! g_str_equal (xmlns, "jabber:x:data"))
-            continue;
-
-          if (! g_str_equal (type, "result"))
-            continue;
-
-          form = g_slice_new0 (struct _dataform);
-
-          for (x_child = child->children;
-               NULL != x_child;
-               x_child = x_child->next)
-            {
-              const gchar *var;
-              LmMessageNode *value_child;
-              struct _dataform_field *field;
-
-              if (! g_str_equal (x_child->name, "field"))
-                continue;
-
-              var = lm_message_node_get_attribute (x_child, "var");
-
-              if (NULL == var)
-                continue;
-
-              field = g_slice_new0 (struct _dataform_field);
-
-              for (value_child = x_child->children;
-                   NULL != value_child;
-                   value_child = value_child->next)
-                {
-                  const gchar *content;
-
-                  if (! g_str_equal (value_child->name, "value"))
-                    continue;
-
-                  if (g_str_equal (var, "FORM_TYPE"))
-                    {
-                      form->form_type = g_strdup (var);
-                    }
-                  else
-                    {
-                      content = lm_message_node_get_value (value_child);
-                      g_ptr_array_add (field->values,
-                          (gpointer) g_strdup (content));
-                    }
-                }
-
-              g_ptr_array_add (form->fields, (gpointer) field);
-            }
-
-          g_ptr_array_add (dataforms, (gpointer) form);
-        }
-    }
-
-  str = gabble_presence_compute_xep0115_hash (features, identities, dataforms);
-
-  gabble_presence_free_xep0115_hash (features, identities, dataforms);
-
-  return str;
-}
-
-
-gchar *
-gabble_presence_compute_xep0115_hash_from_self_presence (GabbleConnection *self)
-{
-  GabblePresence *presence = self->self_presence;
-  GSList *features_list = capabilities_get_features (presence->caps);
-  GPtrArray *features = g_ptr_array_new ();
-  GPtrArray *identities = g_ptr_array_new ();
-  GPtrArray *dataforms = g_ptr_array_new ();
-  gchar *str;
-  GSList *i;
-
-  /* get our features list  */
-  for (i = features_list; NULL != i; i = i->next)
-    {
-      const Feature *feat = (const Feature *) i->data;
-      g_ptr_array_add (features, (gpointer) g_strdup (feat->ns));
-    }
-
-  /* XEP-0030 requires at least 1 identity. We don't need more. */
-  g_ptr_array_add (identities,
-      (gpointer) g_strdup ("client/pc//" PACKAGE_STRING));
-
-  /* Gabble does not use dataforms, let 'dataforms' be empty */
-
-  str = gabble_presence_compute_xep0115_hash (features, identities, dataforms);
-
-  gabble_presence_free_xep0115_hash (features, identities, dataforms);
-  g_slist_free (features_list);
-
-  return str;
-}
-
diff --git a/src/presence.h b/src/presence.h
index d6f2327..a5a56bf 100644
--- a/src/presence.h
+++ b/src/presence.h
@@ -91,10 +91,6 @@ LmMessage *gabble_presence_as_message (GabblePresence *presence,
 gchar *gabble_presence_dump (GabblePresence *presence);
 
 
-gchar *gabble_presence_compute_xep0115_hash_from_lm_node (LmMessageNode *node);
-gchar *gabble_presence_compute_xep0115_hash_from_self_presence (
-    GabbleConnection *self);
-
 G_END_DECLS
 
 #endif /* __GABBLE_PRESENCE_H__ */
-- 
1.5.6.3




More information about the Telepathy-commits mailing list