[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