[telepathy-gabble/master] added ICE-UDP support based on GoogleP2P support code

Senko Rasic senko.rasic at collabora.co.uk
Mon Jun 29 04:43:02 PDT 2009


---
 src/Makefile.am               |    2 +
 src/jingle-transport-iceudp.c |  559 +++++++++++++++++++++++++++++++++++++++++
 src/jingle-transport-iceudp.h |   65 +++++
 3 files changed, 626 insertions(+), 0 deletions(-)
 create mode 100644 src/jingle-transport-iceudp.c
 create mode 100644 src/jingle-transport-iceudp.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 5bab478..ccb3397 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -75,6 +75,8 @@ libgabble_convenience_la_SOURCES = \
     jingle-transport-google.c \
     jingle-transport-rawudp.h \
     jingle-transport-rawudp.c \
+    jingle-transport-iceudp.h \
+    jingle-transport-iceudp.c \
     jingle-transport-iface.h \
     jingle-transport-iface.c \
     media-channel.h \
diff --git a/src/jingle-transport-iceudp.c b/src/jingle-transport-iceudp.c
new file mode 100644
index 0000000..22f387b
--- /dev/null
+++ b/src/jingle-transport-iceudp.c
@@ -0,0 +1,559 @@
+/*
+ * jingle-transport-iceudp.c - Source for GabbleJingleTransportIceUdp
+ *
+ * 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
+ */
+
+#include "jingle-transport-iceudp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <loudmouth/loudmouth.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#include "connection.h"
+#include "debug.h"
+#include "jingle-content.h"
+#include "jingle-factory.h"
+#include "jingle-session.h"
+#include "namespaces.h"
+#include "util.h"
+
+static void
+transport_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GabbleJingleTransportIceUdp,
+    gabble_jingle_transport_iceudp, G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (GABBLE_TYPE_JINGLE_TRANSPORT_IFACE,
+        transport_iface_init));
+
+/* signal enum */
+enum
+{
+  NEW_CANDIDATES,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* properties */
+enum
+{
+  PROP_CONTENT = 1,
+  PROP_TRANSPORT_NS,
+  PROP_STATE,
+  LAST_PROPERTY
+};
+
+struct _GabbleJingleTransportIceUdpPrivate
+{
+  GabbleJingleContent *content;
+  JingleTransportState state;
+  gchar *transport_ns;
+
+  GList *local_candidates;
+
+  /* A pointer into "local_candidates" list to mark the
+   * candidates that are still not transmitted, or NULL
+   * if all of them are transmitted. */
+
+  GList *pending_candidates;
+  GList *remote_candidates;
+
+  /* Until we have a good way of getting foundation
+   * numbers off farsight, we make sure each candidate
+   * has an unique one. */
+  int foundation_sequence;
+
+  gboolean dispose_has_run;
+};
+
+#define GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE(o) ((o)->priv)
+
+static void transmit_candidates (GabbleJingleTransportIceUdp *transport,
+    GList *candidates);
+
+static void
+gabble_jingle_transport_iceudp_init (GabbleJingleTransportIceUdp *obj)
+{
+  GabbleJingleTransportIceUdpPrivate *priv =
+     G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_JINGLE_TRANSPORT_ICEUDP,
+         GabbleJingleTransportIceUdpPrivate);
+  obj->priv = priv;
+
+  priv->foundation_sequence = 1;
+  priv->dispose_has_run = FALSE;
+}
+
+static void
+gabble_jingle_transport_iceudp_dispose (GObject *object)
+{
+  GabbleJingleTransportIceUdp *trans = GABBLE_JINGLE_TRANSPORT_ICEUDP (object);
+  GabbleJingleTransportIceUdpPrivate *priv = GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE (trans);
+
+  if (priv->dispose_has_run)
+    return;
+
+  DEBUG ("dispose called");
+  priv->dispose_has_run = TRUE;
+
+  jingle_transport_free_candidates (priv->remote_candidates);
+  priv->remote_candidates = NULL;
+
+  jingle_transport_free_candidates (priv->local_candidates);
+  priv->local_candidates = NULL;
+
+  g_free (priv->transport_ns);
+  priv->transport_ns = NULL;
+
+  if (G_OBJECT_CLASS (gabble_jingle_transport_iceudp_parent_class)->dispose)
+    G_OBJECT_CLASS (gabble_jingle_transport_iceudp_parent_class)->dispose (object);
+}
+
+static void
+gabble_jingle_transport_iceudp_get_property (GObject *object,
+                                             guint property_id,
+                                             GValue *value,
+                                             GParamSpec *pspec)
+{
+  GabbleJingleTransportIceUdp *trans = GABBLE_JINGLE_TRANSPORT_ICEUDP (object);
+  GabbleJingleTransportIceUdpPrivate *priv = GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE (trans);
+
+  switch (property_id) {
+    case PROP_CONTENT:
+      g_value_set_object (value, priv->content);
+      break;
+    case PROP_TRANSPORT_NS:
+      g_value_set_string (value, priv->transport_ns);
+      break;
+    case PROP_STATE:
+      g_value_set_uint (value, priv->state);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+static void
+gabble_jingle_transport_iceudp_set_property (GObject *object,
+                                             guint property_id,
+                                             const GValue *value,
+                                             GParamSpec *pspec)
+{
+  GabbleJingleTransportIceUdp *trans = GABBLE_JINGLE_TRANSPORT_ICEUDP (object);
+  GabbleJingleTransportIceUdpPrivate *priv =
+      GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE (trans);
+
+  switch (property_id) {
+    case PROP_CONTENT:
+      priv->content = g_value_get_object (value);
+      break;
+    case PROP_TRANSPORT_NS:
+      g_free (priv->transport_ns);
+      priv->transport_ns = g_value_dup_string (value);
+      break;
+    case PROP_STATE:
+      priv->state = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+static void
+gabble_jingle_transport_iceudp_class_init (GabbleJingleTransportIceUdpClass *cls)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (cls);
+  GParamSpec *param_spec;
+
+  g_type_class_add_private (cls, sizeof (GabbleJingleTransportIceUdpPrivate));
+
+  object_class->get_property = gabble_jingle_transport_iceudp_get_property;
+  object_class->set_property = gabble_jingle_transport_iceudp_set_property;
+  object_class->dispose = gabble_jingle_transport_iceudp_dispose;
+
+  /* property definitions */
+  param_spec = g_param_spec_object ("content", "GabbleJingleContent object",
+                                    "Jingle content object using this transport.",
+                                    GABBLE_TYPE_JINGLE_CONTENT,
+                                    G_PARAM_CONSTRUCT_ONLY |
+                                    G_PARAM_READWRITE |
+                                    G_PARAM_STATIC_NICK |
+                                    G_PARAM_STATIC_BLURB);
+  g_object_class_install_property (object_class, PROP_CONTENT, param_spec);
+
+  param_spec = g_param_spec_string ("transport-ns", "Transport namespace",
+                                    "Namespace identifying the transport type.",
+                                    NULL,
+                                    G_PARAM_CONSTRUCT_ONLY |
+                                    G_PARAM_READWRITE |
+                                    G_PARAM_STATIC_NICK |
+                                    G_PARAM_STATIC_BLURB);
+  g_object_class_install_property (object_class, PROP_TRANSPORT_NS, param_spec);
+
+  param_spec = g_param_spec_uint ("state",
+                                  "Connection state for the transport.",
+                                  "Enum specifying the connection state of the transport.",
+                                  JINGLE_TRANSPORT_STATE_DISCONNECTED,
+                                  JINGLE_TRANSPORT_STATE_CONNECTED,
+                                  JINGLE_TRANSPORT_STATE_DISCONNECTED,
+                                  G_PARAM_READWRITE |
+                                  G_PARAM_STATIC_NAME |
+                                  G_PARAM_STATIC_NICK |
+                                  G_PARAM_STATIC_BLURB);
+  g_object_class_install_property (object_class, PROP_STATE, param_spec);
+
+  /* signal definitions */
+  signals[NEW_CANDIDATES] = g_signal_new (
+    "new-candidates",
+    G_TYPE_FROM_CLASS (cls),
+    G_SIGNAL_RUN_LAST,
+    0,
+    NULL, NULL,
+    g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+}
+
+#define SET_BAD_REQ(txt...) g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, txt)
+#define SET_OUT_ORDER(txt...) g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_JINGLE_OUT_OF_ORDER, txt)
+#define SET_CONFLICT(txt...) g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_CONFLICT, txt)
+
+static void
+parse_candidates (GabbleJingleTransportIface *obj,
+    LmMessageNode *transport_node, GError **error)
+{
+  GabbleJingleTransportIceUdp *t = GABBLE_JINGLE_TRANSPORT_ICEUDP (obj);
+  GabbleJingleTransportIceUdpPrivate *priv =
+    GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE (t);
+  GList *candidates = NULL;
+  LmMessageNode *node;
+
+  DEBUG ("called");
+
+  for (node = transport_node->children; node; node = node->next)
+    {
+      const gchar *name, *address, *user, *pass, *str;
+      guint port, net, gen, component = 1;
+      gdouble pref;
+      JingleTransportProtocol proto;
+      JingleCandidateType ctype;
+      JingleCandidate *c;
+
+      if (tp_strdiff (lm_message_node_get_name (node), "candidate"))
+          continue;
+
+      /* ICEUDP doesn't use rtp/rtcp naming */
+      name = "";
+
+      address = lm_message_node_get_attribute (node, "ip");
+      if (address == NULL)
+          break;
+
+      str = lm_message_node_get_attribute (node, "port");
+      if (str == NULL)
+          break;
+      port = atoi (str);
+
+      str = lm_message_node_get_attribute (node, "protocol");
+      if (str == NULL)
+          break;
+
+      if (!tp_strdiff (str, "udp"))
+        {
+          proto = JINGLE_TRANSPORT_PROTOCOL_UDP;
+        }
+      else
+        {
+          /* unknown protocol */
+          DEBUG ("unknown protocol: %s", str);
+          break;
+        }
+
+      str = lm_message_node_get_attribute (node, "priority");
+      if (str == NULL)
+          break;
+
+      pref = g_ascii_strtod (str, NULL);
+
+      str = lm_message_node_get_attribute (node, "type");
+      if (str == NULL)
+          break;
+
+      if (!tp_strdiff (str, "host"))
+        {
+          ctype = JINGLE_CANDIDATE_TYPE_LOCAL;
+        }
+      else if (!tp_strdiff (str, "srflx"))
+        {
+          ctype = JINGLE_CANDIDATE_TYPE_STUN;
+        }
+      else if (!tp_strdiff (str, "relay"))
+        {
+          ctype = JINGLE_CANDIDATE_TYPE_RELAY;
+        }
+      else
+        {
+          /* unknown candidate type */
+          DEBUG ("unknown candidate type: %s", str);
+          break;
+        }
+
+      user = lm_message_node_get_attribute (transport_node, "ufrag");
+      if (user == NULL)
+          break;
+
+      pass = lm_message_node_get_attribute (transport_node, "pwd");
+      if (pass == NULL)
+          break;
+
+      str = lm_message_node_get_attribute (node, "network");
+      if (str == NULL)
+          break;
+      net = atoi (str);
+
+      str = lm_message_node_get_attribute (node, "generation");
+      if (str == NULL)
+          break;
+      gen = atoi (str);
+
+      str = lm_message_node_get_attribute (node, "component");
+      if (str != NULL)
+          component = atoi (str);
+
+      c = jingle_candidate_new (component, address, port, proto,
+          pref, ctype, user, pass, net, gen);
+
+      candidates = g_list_append (candidates, c);
+    }
+
+  if (node != NULL)
+    {
+      gchar *dump = lm_message_node_to_string (node);
+      DEBUG ("error on node: %s\nreturning error", dump);
+      g_free (dump);
+      /* rollback these */
+      jingle_transport_free_candidates (candidates);
+      SET_BAD_REQ ("invalid candidate");
+      return;
+    }
+
+  DEBUG ("emitting %d new remote candidates", g_list_length (candidates));
+
+  g_signal_emit (obj, signals[NEW_CANDIDATES], 0, candidates);
+
+  /* append them to the known remote candidates */
+  priv->remote_candidates = g_list_concat (priv->remote_candidates, candidates);
+}
+
+static void
+transmit_candidates (GabbleJingleTransportIceUdp *transport, GList *candidates)
+{
+  GabbleJingleTransportIceUdpPrivate *priv =
+    GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE (transport);
+  JingleDialect dialect;
+  GList *li;
+  LmMessage *msg;
+  LmMessageNode *trans_node, *sess_node;
+
+  msg = gabble_jingle_session_new_message (priv->content->session,
+    JINGLE_ACTION_TRANSPORT_INFO, &sess_node);
+
+  g_object_get (priv->content->session, "dialect", &dialect, NULL);
+
+  if (dialect == JINGLE_DIALECT_GTALK3)
+    {
+      trans_node = sess_node;
+    }
+  else if (dialect == JINGLE_DIALECT_GTALK4)
+    {
+      trans_node = lm_message_node_add_child (sess_node, "transport", NULL);
+      lm_message_node_set_attribute (trans_node, "xmlns", NS_JINGLE_TRANSPORT_ICEUDP);
+    }
+  else
+    {
+      const gchar *cname, *cns;
+
+      g_object_get (GABBLE_JINGLE_CONTENT (priv->content),
+          "name", &cname, "content-ns", &cns, NULL);
+
+      /* TODO: probably copy this to -google.c */
+      gabble_jingle_content_produce_node (priv->content, sess_node, FALSE);
+      trans_node = lm_message_node_get_child_any_ns (sess_node, "content");
+
+      /* .. and the <transport> node */
+      trans_node = lm_message_node_add_child (trans_node, "transport", NULL);
+      lm_message_node_set_attribute (trans_node, "xmlns", priv->transport_ns);
+    }
+
+  for (li = candidates; li; li = li->next)
+    {
+      JingleCandidate *c = (JingleCandidate *) li->data;
+      gchar port_str[16], pref_str[16], comp_str[16], found_str[16],
+          *type_str, *proto_str;
+      LmMessageNode *cnode;
+
+      sprintf (port_str, "%d", c->port);
+      sprintf (pref_str, "%d", (int) c->preference);
+      sprintf (comp_str, "%d", c->component);
+      sprintf (found_str, "%d", priv->foundation_sequence++);
+
+      switch (c->type) {
+        case JINGLE_CANDIDATE_TYPE_LOCAL:
+          type_str = "host";
+          break;
+        case JINGLE_CANDIDATE_TYPE_STUN:
+          type_str = "srflx";
+          break;
+        case JINGLE_CANDIDATE_TYPE_RELAY:
+          type_str = "relay";
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+
+      switch (c->protocol) {
+        case JINGLE_TRANSPORT_PROTOCOL_UDP:
+          proto_str = "udp";
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+
+      lm_message_node_set_attributes (trans_node,
+          "ufrag", c->username,
+          "pwd", c->password,
+          NULL);
+
+      cnode = lm_message_node_add_child (trans_node, "candidate", NULL);
+      lm_message_node_set_attributes (cnode,
+          "ip", c->address,
+          "port", port_str,
+          "priority", pref_str,
+          "protocol", proto_str,
+          "type", type_str,
+          "component", comp_str,
+          "foundation", found_str,
+          "name", "rtp",
+          "network", "0",
+          "generation", "0",
+          NULL);
+    }
+
+  _gabble_connection_send (priv->content->conn, msg, NULL);
+}
+
+/* Takes in a list of slice-allocated JingleCandidate structs */
+static void
+add_candidates (GabbleJingleTransportIface *obj, GList *new_candidates)
+{
+  GabbleJingleTransportIceUdp *transport =
+    GABBLE_JINGLE_TRANSPORT_ICEUDP (obj);
+  GabbleJingleTransportIceUdpPrivate *priv =
+    GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE (transport);
+  JingleContentState state;
+
+  g_object_get (priv->content, "state", &state, NULL);
+
+  if (state > JINGLE_CONTENT_STATE_EMPTY)
+    {
+      DEBUG ("content already signalled, transmitting candidates");
+      transmit_candidates (transport, new_candidates);
+      priv->pending_candidates = NULL;
+    }
+  else
+    {
+      DEBUG ("content not signalled yet, waiting with candidates");
+
+      /* if we already have pending candidates, the new ones will
+       * be in the local_candidates list after them. but these
+       * are the first pending ones, we must mark them. */
+      if (priv->pending_candidates == NULL)
+        priv->pending_candidates = new_candidates;
+  }
+
+  priv->local_candidates = g_list_concat (priv->local_candidates,
+      new_candidates);
+}
+
+static void
+retransmit_candidates (GabbleJingleTransportIface *obj, gboolean all)
+{
+  GabbleJingleTransportIceUdp *transport =
+    GABBLE_JINGLE_TRANSPORT_ICEUDP (obj);
+  GabbleJingleTransportIceUdpPrivate *priv =
+    GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE (transport);
+
+  if (all)
+    {
+      /* for gtalk3, we might have to retransmit everything */
+      transmit_candidates (transport, priv->local_candidates);
+      priv->pending_candidates = NULL;
+    }
+  else
+    {
+      /* in case content was ready after we wanted to transmit
+       * them originally, we are called to retranmit them */
+      if (priv->pending_candidates != NULL) {
+          transmit_candidates (transport, priv->pending_candidates);
+          priv->pending_candidates = NULL;
+      }
+    }
+}
+
+static GList *
+get_remote_candidates (GabbleJingleTransportIface *iface)
+{
+  GabbleJingleTransportIceUdp *transport =
+    GABBLE_JINGLE_TRANSPORT_ICEUDP (iface);
+  GabbleJingleTransportIceUdpPrivate *priv =
+    GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_PRIVATE (transport);
+
+  return priv->remote_candidates;
+}
+
+static JingleTransportType
+get_transport_type (void)
+{
+  return JINGLE_TRANSPORT_ICE_UDP;
+}
+
+static void
+transport_iface_init (gpointer g_iface, gpointer iface_data)
+{
+  GabbleJingleTransportIfaceClass *klass = (GabbleJingleTransportIfaceClass *) g_iface;
+
+  klass->parse_candidates = parse_candidates;
+  klass->add_candidates = add_candidates;
+  klass->retransmit_candidates = retransmit_candidates;
+  klass->get_remote_candidates = get_remote_candidates;
+  klass->get_transport_type = get_transport_type;
+}
+
+void
+jingle_transport_iceudp_register (GabbleJingleFactory *factory)
+{
+  gabble_jingle_factory_register_transport (factory,
+      NS_JINGLE_TRANSPORT_ICEUDP,
+      GABBLE_TYPE_JINGLE_TRANSPORT_ICEUDP);
+}
+
diff --git a/src/jingle-transport-iceudp.h b/src/jingle-transport-iceudp.h
new file mode 100644
index 0000000..8cfeacf
--- /dev/null
+++ b/src/jingle-transport-iceudp.h
@@ -0,0 +1,65 @@
+/*
+ * jingle-transport-iceudp.h - Header for GabbleJingleTransportIceUdp
+ * 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 __JINGLE_TRANSPORT_ICEUDP_H__
+#define __JINGLE_TRANSPORT_ICEUDP_H__
+
+#include <glib-object.h>
+#include <loudmouth/loudmouth.h>
+
+#include "types.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleJingleTransportIceUdpClass GabbleJingleTransportIceUdpClass;
+
+GType gabble_jingle_transport_iceudp_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_JINGLE_TRANSPORT_ICEUDP \
+  (gabble_jingle_transport_iceudp_get_type ())
+#define GABBLE_JINGLE_TRANSPORT_ICEUDP(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_TRANSPORT_ICEUDP, \
+                              GabbleJingleTransportIceUdp))
+#define GABBLE_JINGLE_TRANSPORT_ICEUDP_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_JINGLE_TRANSPORT_ICEUDP, \
+                           GabbleJingleTransportIceUdpClass))
+#define GABBLE_IS_JINGLE_TRANSPORT_ICEUDP(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_TRANSPORT_ICEUDP))
+#define GABBLE_IS_JINGLE_TRANSPORT_ICEUDP_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_JINGLE_TRANSPORT_ICEUDP))
+#define GABBLE_JINGLE_TRANSPORT_ICEUDP_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_JINGLE_TRANSPORT_ICEUDP, \
+                              GabbleJingleTransportIceUdpClass))
+
+struct _GabbleJingleTransportIceUdpClass {
+    GObjectClass parent_class;
+};
+
+typedef struct _GabbleJingleTransportIceUdpPrivate GabbleJingleTransportIceUdpPrivate;
+
+struct _GabbleJingleTransportIceUdp {
+    GObject parent;
+    GabbleJingleTransportIceUdpPrivate *priv;
+};
+
+void jingle_transport_iceudp_register (GabbleJingleFactory *factory);
+
+#endif /* __JINGLE_TRANSPORT_ICEUDP_H__ */
+
-- 
1.5.6.5




More information about the telepathy-commits mailing list