[Telepathy-commits] [telepathy-gabble/master] first attempt at refactoring jingle signalling support into separate subsystem
Senko Rasic
senko at phyrexia.lan
Tue Dec 2 04:33:55 PST 2008
---
src/jingle-content.c | 435 ++++++++++++++++++
src/jingle-content.h | 89 ++++
src/jingle-description-iface.c | 122 +++++
src/jingle-description-iface.h | 60 +++
src/jingle-factory.c | 386 ++++++++++++++++
src/jingle-factory.h | 143 ++++++
src/jingle-media-rtp.c | 520 +++++++++++++++++++++
src/jingle-media-rtp.h | 64 +++
src/jingle-session.c | 976 ++++++++++++++++++++++++++++++++++++++++
src/jingle-session.h | 72 +++
src/jingle-transport-google.c | 548 ++++++++++++++++++++++
src/jingle-transport-google.h | 62 +++
src/jingle-transport-iface.c | 124 +++++
src/jingle-transport-iface.h | 64 +++
14 files changed, 3665 insertions(+), 0 deletions(-)
create mode 100644 src/jingle-content.c
create mode 100644 src/jingle-content.h
create mode 100644 src/jingle-description-iface.c
create mode 100644 src/jingle-description-iface.h
create mode 100644 src/jingle-factory.c
create mode 100644 src/jingle-factory.h
create mode 100644 src/jingle-media-rtp.c
create mode 100644 src/jingle-media-rtp.h
create mode 100644 src/jingle-session.c
create mode 100644 src/jingle-session.h
create mode 100644 src/jingle-transport-google.c
create mode 100644 src/jingle-transport-google.h
create mode 100644 src/jingle-transport-iface.c
create mode 100644 src/jingle-transport-iface.h
diff --git a/src/jingle-content.c b/src/jingle-content.c
new file mode 100644
index 0000000..93bb192
--- /dev/null
+++ b/src/jingle-content.c
@@ -0,0 +1,435 @@
+/*
+ * gabble-jingle-session.c - Source for GabbleJingleContent
+ * 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-content.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <loudmouth/loudmouth.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#include "debug.h"
+#include "gabble-connection.h"
+#include "util.h"
+#include "namespaces.h"
+#include "jingle-factory.h"
+#include "jingle-session.h"
+#include "jingle-transport-iface.h"
+
+G_DEFINE_TYPE(GabbleJingleContent, gabble_jingle_content, G_TYPE_OBJECT);
+
+/* signal enum */
+enum
+{
+ LAST_SIGNAL
+};
+
+// FIXME static guint signals[LAST_SIGNAL] = {0};
+
+/* properties */
+enum
+{
+ PROP_CONNECTION = 1,
+ PROP_FACTORY,
+ PROP_SESSION,
+ PROP_NAME,
+ PROP_SENDERS,
+ PROP_STATE,
+ LAST_PROPERTY
+};
+
+typedef struct _GabbleJingleContentPrivate GabbleJingleContentPrivate;
+struct _GabbleJingleContentPrivate
+{
+ GabbleConnection *conn;
+ GabbleJingleFactory *factory;
+ GabbleJingleSession *session;
+
+ gchar *name;
+ gchar *creator;
+ gboolean created_by_initiator;
+ JingleContentState state;
+ JingleContentSenders senders;
+
+ GabbleJingleDescriptionIface *description;
+ GabbleJingleTransportIface *transport;
+
+ gboolean dispose_has_run;
+};
+
+#define GABBLE_JINGLE_CONTENT_GET_PRIVATE(o)\
+ ((GabbleJingleContentPrivate*)((o)->priv))
+
+/* lookup tables */
+
+static const gchar *content_senders_table[] = {
+ "initiator",
+ "responder",
+ "both",
+ NULL
+};
+
+static void
+gabble_jingle_content_init (GabbleJingleContent *obj)
+{
+ GabbleJingleContentPrivate *priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_JINGLE_CONTENT,
+ GabbleJingleContentPrivate);
+ obj->priv = priv;
+
+ priv->state = JINGLE_CONTENT_STATE_EMPTY;
+ priv->created_by_initiator = TRUE;
+ priv->dispose_has_run = FALSE;
+}
+
+static void
+gabble_jingle_content_dispose (GObject *object)
+{
+ GabbleJingleContent *sess = GABBLE_JINGLE_CONTENT (object);
+ GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (sess);
+
+ if (priv->dispose_has_run)
+ return;
+
+ DEBUG ("dispose called");
+ priv->dispose_has_run = TRUE;
+
+ if (priv->description)
+ g_object_unref (priv->description);
+ priv->description = NULL;
+
+ if (priv->transport)
+ g_object_unref (priv->transport);
+ priv->transport = NULL;
+
+ g_free (priv->name);
+ priv->name = NULL;
+
+ g_free (priv->creator);
+ priv->creator = NULL;
+
+ if (G_OBJECT_CLASS (gabble_jingle_content_parent_class)->dispose)
+ G_OBJECT_CLASS (gabble_jingle_content_parent_class)->dispose (object);
+}
+
+static void
+gabble_jingle_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleContent *self = GABBLE_JINGLE_CONTENT (object);
+ GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (self);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->conn);
+ break;
+ case PROP_FACTORY:
+ g_value_set_object (value, priv->factory);
+ break;
+ case PROP_SESSION:
+ g_value_set_object (value, priv->session);
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, priv->name);
+ break;
+ case PROP_SENDERS:
+ g_value_set_uint (value, priv->senders);
+ 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_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleContent *self = GABBLE_JINGLE_CONTENT (object);
+ GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (self);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ priv->conn = g_value_get_object (value);
+ break;
+ case PROP_FACTORY:
+ priv->factory = g_value_get_object (value);
+ break;
+ case PROP_SESSION:
+ priv->factory = g_value_get_object (value);
+ break;
+ case PROP_SENDERS:
+ priv->senders = g_value_get_uint (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_content_class_init (GabbleJingleContentClass *cls)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (cls, sizeof (GabbleJingleContentPrivate));
+
+ object_class->get_property = gabble_jingle_content_get_property;
+ object_class->set_property = gabble_jingle_content_set_property;
+ object_class->dispose = gabble_jingle_content_dispose;
+
+ /* property definitions */
+ param_spec = g_param_spec_object ("connection", "GabbleConnection object",
+ "Gabble connection object used for exchanging "
+ "messages.",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_object ("factory", "GabbleJingleFactory object",
+ "Jingle factory object that has transport "
+ "and description namespace handlers.",
+ GABBLE_TYPE_JINGLE_FACTORY,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_object ("session", "GabbleJingleSession object",
+ "Jingle session object that owns this content.",
+ GABBLE_TYPE_JINGLE_SESSION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_string ("name", "Content name",
+ "A unique content name in the session.",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_NAME, param_spec);
+
+
+ param_spec = g_param_spec_uint ("senders", "Stream senders",
+ "Valid senders for the stream.",
+ 0, G_MAXUINT32, JINGLE_CONTENT_STATE_NEW,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_STATE, param_spec);
+
+ param_spec = g_param_spec_uint ("state", "Content state",
+ "The current state that the content is in.",
+ 0, G_MAXUINT32, JINGLE_CONTENT_STATE_NEW,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_STATE, param_spec);
+
+ /* signal definitions */
+}
+
+#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)
+
+void
+gabble_jingle_content_parse_add (GabbleJingleContent *c,
+ LmMessageNode *content_node, gboolean google_mode, GError **error)
+{
+ GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (c);
+ const gchar *name, *creator, *senders, *xmlns;
+ LmMessageNode *desc_node, *trans_node;
+ JingleDescriptionMaker dmaker;
+ GabbleJingleDescriptionIface *desc = NULL;
+ JingleTransportMaker tmaker;
+ GabbleJingleTransportIface *trans = NULL;
+
+ desc_node = lm_message_node_get_child (content_node, "description");
+ trans_node = lm_message_node_get_child (content_node, "transport");
+ creator = lm_message_node_get_attribute (content_node, "creator");
+ name = lm_message_node_get_attribute (content_node, "name");
+ senders = lm_message_node_get_attribute (content_node, "senders");
+
+ if (desc_node == NULL)
+ {
+ SET_BAD_REQ ("content description is missing");
+ return;
+ }
+
+ xmlns = lm_message_node_get_attribute (desc_node, "xmlns");
+ dmaker = g_hash_table_lookup (priv->factory->descriptions, xmlns);
+
+ if (dmaker == NULL)
+ {
+ SET_BAD_REQ ("unsupported content description");
+ return;
+ }
+
+ if (!google_mode)
+ {
+ if ((trans_node == NULL) || (creator == NULL) || (name == NULL))
+ {
+ SET_BAD_REQ ("missing required content attributes or elements");
+ return;
+ }
+ }
+ else
+ {
+ /* explicit is better than implicit */
+ if (creator == NULL)
+ creator = "initiator";
+
+ if (name == NULL)
+ name = "audio";
+ }
+
+ if (trans_node)
+ {
+ xmlns = lm_message_node_get_attribute (trans_node, "xmlns");
+ tmaker = g_hash_table_lookup (priv->factory->transports, NULL);
+ }
+ else
+ {
+ /* older gtalk assumes google-p2p */
+ g_object_set (priv->session, "dialect", JINGLE_DIALECT_GTALK3, NULL);
+ tmaker = g_hash_table_lookup (priv->factory->transports, NULL);
+ }
+
+ priv->created_by_initiator = (!tp_strdiff (creator, "initiator"));
+ priv->senders = _string_to_enum (content_senders_table, senders);
+ if (priv->senders == JINGLE_CONTENT_SENDERS_NONE)
+ {
+ SET_BAD_REQ ("invalid content senders in stream");
+ return;
+ }
+
+ desc = dmaker (c);
+ trans = tmaker (c);
+
+ gabble_jingle_transport_iface_parse (trans, trans_node, error);
+ if (*error)
+ {
+ g_object_unref (desc);
+ g_object_unref (trans);
+ return;
+ }
+
+ gabble_jingle_description_iface_parse (desc, desc_node, error);
+ if (*error)
+ {
+ g_object_unref (desc);
+ g_object_unref (trans);
+ return;
+ }
+
+ g_assert (priv->description == NULL);
+ priv->description = desc;
+
+ g_assert (priv->transport == NULL);
+ priv->transport = trans;
+
+ g_assert (priv->name == NULL);
+ priv->name = g_strdup (name);
+
+ g_assert (priv->creator == NULL);
+ priv->creator = g_strdup (creator);
+
+ priv->state = JINGLE_CONTENT_STATE_NEW;
+
+ return;
+}
+
+void
+gabble_jingle_content_produce_node (GabbleJingleContent *c,
+ LmMessageNode *parent, gboolean full)
+{
+ GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (c);
+ LmMessageNode *content_node;
+ JingleDialect dialect;
+
+ g_object_get (priv->session, "dialect", &dialect, NULL);
+
+ if ((dialect == JINGLE_DIALECT_GTALK3) ||
+ (dialect == JINGLE_DIALECT_GTALK4))
+ {
+ /* content-* isn't used in GTalk anyways, so we always have to include
+ * the full content description */
+ g_assert (full == TRUE);
+
+ content_node = parent;
+ }
+ else
+ {
+ content_node = lm_message_node_add_child (parent, "content", NULL);
+ lm_message_node_set_attributes (content_node,
+ "creator", priv->creator,
+ "name", priv->name,
+ "senders", _enum_to_string (content_senders_table, priv->senders));
+ }
+
+ if (!full)
+ return;
+
+ gabble_jingle_description_iface_produce (priv->description, content_node);
+ gabble_jingle_transport_iface_produce (priv->transport, content_node);
+}
+
+void
+gabble_jingle_content_update_senders (GabbleJingleContent *c,
+ LmMessageNode *content_node, GError **error)
+{
+ GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (c);
+ JingleContentSenders senders;
+
+ senders = _string_to_enum (content_senders_table,
+ lm_message_node_get_attribute (content_node, "senders"));
+
+ if (senders == JINGLE_CONTENT_SENDERS_NONE)
+ {
+ SET_BAD_REQ ("invalid content senders in stream");
+ return;
+ }
+
+ priv->senders = senders;
+ g_object_notify ((GObject *) c, "senders");
+}
+
diff --git a/src/jingle-content.h b/src/jingle-content.h
new file mode 100644
index 0000000..e423493
--- /dev/null
+++ b/src/jingle-content.h
@@ -0,0 +1,89 @@
+/*
+ * jingle-content.h - Header for GabbleJingleContent
+ * 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_CONTENT_H__
+#define __JINGLE_CONTENT_H__
+
+#include <glib-object.h>
+#include <loudmouth/loudmouth.h>
+#include "gabble-types.h"
+#include "jingle-factory.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ JINGLE_CONTENT_STATE_EMPTY = 0,
+ JINGLE_CONTENT_STATE_NEW,
+ JINGLE_CONTENT_STATE_SENT,
+ JINGLE_CONTENT_STATE_ACKNOWLEDGED,
+ JINGLE_CONTENT_STATE_REMOVING
+} JingleContentState;
+
+struct _JingleCandidate {
+ gchar *address;
+ int port;
+ int generation;
+
+ JingleTransportProtocol protocol;
+ int preference;
+ JingleCandidateType type;
+ gchar *username;
+ gchar *password;
+ int network;
+};
+
+typedef struct _GabbleJingleContentClass GabbleJingleContentClass;
+
+GType gabble_jingle_content_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_JINGLE_CONTENT \
+ (gabble_jingle_content_get_type ())
+#define GABBLE_JINGLE_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_CONTENT, \
+ GabbleJingleContent))
+#define GABBLE_JINGLE_CONTENT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_JINGLE_CONTENT, \
+ GabbleJingleContentClass))
+#define GABBLE_IS_JINGLE_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_CONTENT))
+#define GABBLE_IS_JINGLE_CONTENT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_JINGLE_CONTENT))
+#define GABBLE_JINGLE_CONTENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_JINGLE_CONTENT, \
+ GabbleJingleContentClass))
+
+struct _GabbleJingleContentClass {
+ GObjectClass parent_class;
+};
+
+struct _GabbleJingleContent {
+ GObject parent;
+ gpointer priv;
+};
+
+void gabble_jingle_content_parse_add (GabbleJingleContent *c,
+ LmMessageNode *content_node, gboolean google_mode, GError **error);
+void gabble_jingle_content_update_senders (GabbleJingleContent *c,
+ LmMessageNode *content_node, GError **error);
+void gabble_jingle_content_produce_node (GabbleJingleContent *c,
+ LmMessageNode *parent, gboolean full);
+
+#endif /* __JINGLE_CONTENT_H__ */
+
diff --git a/src/jingle-description-iface.c b/src/jingle-description-iface.c
new file mode 100644
index 0000000..50479ad
--- /dev/null
+++ b/src/jingle-description-iface.c
@@ -0,0 +1,122 @@
+/*
+ * jingle-description-iface.c - Source for GabbleJingleDescription interface
+ * Copyright (C) 2007-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-description-iface.h"
+#include "gabble-connection.h"
+#include "jingle-session.h"
+#include <glib.h>
+
+void
+gabble_jingle_description_iface_produce (GabbleJingleDescriptionIface *self,
+ LmMessageNode *node)
+{
+ void (*virtual_method)(GabbleJingleDescriptionIface *,
+ LmMessageNode *) =
+ GABBLE_JINGLE_DESCRIPTION_IFACE_GET_CLASS (self)->produce;
+
+ g_assert (virtual_method != NULL);
+ virtual_method (self, node);
+}
+
+void
+gabble_jingle_description_iface_parse (GabbleJingleDescriptionIface *self,
+ LmMessageNode *node, GError **error)
+{
+ void (*virtual_method)(GabbleJingleDescriptionIface *,
+ LmMessageNode *, GError **) =
+ GABBLE_JINGLE_DESCRIPTION_IFACE_GET_CLASS (self)->parse;
+
+ g_assert (virtual_method != NULL);
+ virtual_method (self, node, error);
+}
+
+static void
+gabble_jingle_description_iface_base_init (gpointer klass)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ GParamSpec *param_spec;
+
+ param_spec = g_param_spec_object (
+ "connection",
+ "GabbleConnection object",
+ "Gabble connection object that owns this jingle description object.",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_interface_install_property (klass, param_spec);
+
+ param_spec = g_param_spec_object (
+ "session",
+ "GabbleJingleSession object",
+ "Jingle session that's using this jingle description object.",
+ GABBLE_TYPE_JINGLE_SESSION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_interface_install_property (klass, param_spec);
+
+ param_spec = g_param_spec_uint (
+ "handle",
+ "Handle",
+ "The TpHandle associated with the jingle descriptions channel that"
+ "owns this jingle description object.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_interface_install_property (klass, param_spec);
+
+ initialized = TRUE;
+ }
+}
+
+GType
+gabble_jingle_description_iface_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof (GabbleJingleDescriptionIfaceClass),
+ gabble_jingle_description_iface_base_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL /* instance_init */
+ };
+
+ type = g_type_register_static (G_TYPE_INTERFACE, "GabbleJingleDescriptionIface",
+ &info, 0);
+ }
+
+ return type;
+}
diff --git a/src/jingle-description-iface.h b/src/jingle-description-iface.h
new file mode 100644
index 0000000..35c2ae9
--- /dev/null
+++ b/src/jingle-description-iface.h
@@ -0,0 +1,60 @@
+/*
+ * jingle_description-iface.h - Header for GabbleJingleDescription interface
+ * Copyright (C) 2007-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 __GABBLE_JINGLE_DESCRIPTION_IFACE_H__
+#define __GABBLE_JINGLE_DESCRIPTION_IFACE_H__
+
+#include <glib-object.h>
+
+#include "gabble-types.h"
+#include <loudmouth/loudmouth.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleJingleDescriptionIface GabbleJingleDescriptionIface;
+typedef struct _GabbleJingleDescriptionIfaceClass GabbleJingleDescriptionIfaceClass;
+
+struct _GabbleJingleDescriptionIfaceClass {
+ GTypeInterface parent;
+
+ void (*produce) (GabbleJingleDescriptionIface*, LmMessageNode*);
+ void (*parse) (GabbleJingleDescriptionIface*, LmMessageNode*, GError **error);
+};
+
+GType gabble_jingle_description_iface_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_JINGLE_DESCRIPTION_IFACE \
+ (gabble_jingle_description_iface_get_type ())
+#define GABBLE_JINGLE_DESCRIPTION_IFACE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_DESCRIPTION_IFACE, GabbleJingleDescriptionIface))
+#define GABBLE_IS_JINGLE_DESCRIPTION_IFACE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_DESCRIPTION_IFACE))
+#define GABBLE_JINGLE_DESCRIPTION_IFACE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GABBLE_TYPE_JINGLE_DESCRIPTION_IFACE,\
+ GabbleJingleDescriptionIfaceClass))
+
+void gabble_jingle_description_iface_parse (GabbleJingleDescriptionIface *,
+ LmMessageNode *, GError **);
+void gabble_jingle_description_iface_produce (GabbleJingleDescriptionIface *,
+ LmMessageNode *);
+
+G_END_DECLS
+
+#endif /* #ifndef __GABBLE_JINGLE_DESCRIPTION_IFACE_H__ */
diff --git a/src/jingle-factory.c b/src/jingle-factory.c
new file mode 100644
index 0000000..dce7778
--- /dev/null
+++ b/src/jingle-factory.c
@@ -0,0 +1,386 @@
+/*
+ * jingle-factory.c - Support for XEP-0166 (Jingle)
+ *
+ * Copyright (C) 2006-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-factory.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <loudmouth/loudmouth.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#include "debug.h"
+#include "gabble-connection.h"
+#include "util.h"
+#include "namespaces.h"
+#include "jingle-session.h"
+
+#include <loudmouth/loudmouth.h>
+
+
+G_DEFINE_TYPE(GabbleJingleFactory, gabble_jingle_factory, G_TYPE_OBJECT);
+
+/* signal enum */
+enum
+{
+ NEW_SESSION,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* properties */
+enum
+{
+ PROP_CONNECTION = 1,
+ LAST_PROPERTY
+};
+
+
+typedef struct _GabbleJingleFactoryPrivate GabbleJingleFactoryPrivate;
+struct _GabbleJingleFactoryPrivate
+{
+ GabbleConnection *conn;
+ LmMessageHandler *jingle_cb;
+ GHashTable *sessions;
+
+ gboolean dispose_has_run;
+};
+
+#define GABBLE_JINGLE_FACTORY_GET_PRIVATE(o)\
+ ((GabbleJingleFactoryPrivate*)((o)->priv))
+
+static LmHandlerResult
+jingle_cb (LmMessageHandler *handler, LmConnection *lmconn,
+ LmMessage *message, gpointer user_data);
+static GabbleJingleSession *create_session (GabbleJingleFactory *fac,
+ const gchar *sid, TpHandle peer);
+
+static void
+gabble_jingle_factory_init (GabbleJingleFactory *obj)
+{
+ GabbleJingleFactoryPrivate *priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_JINGLE_FACTORY,
+ GabbleJingleFactoryPrivate);
+ obj->priv = priv;
+
+ priv->sessions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+
+ obj->transports = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, NULL);
+
+ obj->descriptions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, NULL);
+
+ priv->jingle_cb = NULL;
+
+ priv->conn = NULL;
+ priv->dispose_has_run = FALSE;
+}
+
+static void
+gabble_jingle_factory_dispose (GObject *object)
+{
+ GabbleJingleFactory *fac = GABBLE_JINGLE_FACTORY (object);
+ GabbleJingleFactoryPrivate *priv = GABBLE_JINGLE_FACTORY_GET_PRIVATE (fac);
+
+ if (priv->dispose_has_run)
+ return;
+
+ DEBUG ("dispose called");
+ priv->dispose_has_run = TRUE;
+
+ g_hash_table_destroy (priv->sessions);
+ priv->sessions = NULL;
+
+ g_hash_table_destroy (fac->descriptions);
+ fac->descriptions = NULL;
+
+ g_hash_table_destroy (fac->transports);
+ fac->descriptions = NULL;
+
+ lm_connection_unregister_message_handler (priv->conn->lmconn,
+ priv->jingle_cb, LM_MESSAGE_TYPE_IQ);
+
+ if (G_OBJECT_CLASS (gabble_jingle_factory_parent_class)->dispose)
+ G_OBJECT_CLASS (gabble_jingle_factory_parent_class)->dispose (object);
+}
+
+static void
+gabble_jingle_factory_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleFactory *chan = GABBLE_JINGLE_FACTORY (object);
+ GabbleJingleFactoryPrivate *priv = GABBLE_JINGLE_FACTORY_GET_PRIVATE (chan);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->conn);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_jingle_factory_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleFactory *chan = GABBLE_JINGLE_FACTORY (object);
+ GabbleJingleFactoryPrivate *priv = GABBLE_JINGLE_FACTORY_GET_PRIVATE (chan);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ priv->conn = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gabble_jingle_factory_constructor (GType type,
+ guint n_props,
+ GObjectConstructParam *props)
+{
+ GObject *obj;
+ GabbleJingleFactory *self;
+ GabbleJingleFactoryPrivate *priv;
+
+ obj = G_OBJECT_CLASS (gabble_jingle_factory_parent_class)->
+ constructor (type, n_props, props);
+
+ self = GABBLE_JINGLE_FACTORY (obj);
+ priv = GABBLE_JINGLE_FACTORY_GET_PRIVATE (self);
+
+ priv->jingle_cb = lm_message_handler_new (jingle_cb, self, NULL);
+ lm_connection_register_message_handler (priv->conn->lmconn,
+ priv->jingle_cb, LM_MESSAGE_TYPE_IQ, LM_HANDLER_PRIORITY_FIRST);
+
+ return obj;
+}
+
+static void
+gabble_jingle_factory_class_init (GabbleJingleFactoryClass *cls)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (cls, sizeof (GabbleJingleFactoryPrivate));
+
+ object_class->constructor = gabble_jingle_factory_constructor;
+ object_class->get_property = gabble_jingle_factory_get_property;
+ object_class->set_property = gabble_jingle_factory_set_property;
+ object_class->dispose = gabble_jingle_factory_dispose;
+
+ param_spec = g_param_spec_object ("connection", "GabbleConnection object",
+ "Gabble connection object that uses this Jingle Factory object",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ /* signal definitions */
+
+ signals[NEW_SESSION] = g_signal_new ("new-session",
+ G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
+
+static gboolean
+sid_in_use (GabbleJingleFactory *factory, const gchar *sid)
+{
+ GabbleJingleFactoryPrivate *priv = GABBLE_JINGLE_FACTORY_GET_PRIVATE (factory);
+ gpointer key, value;
+
+ return g_hash_table_lookup_extended (priv->sessions, sid, &key, &value);
+}
+
+static gchar *
+get_unique_sid (GabbleJingleFactory *factory)
+{
+ GabbleJingleFactoryPrivate *priv = GABBLE_JINGLE_FACTORY_GET_PRIVATE (factory);
+ guint32 val;
+ gchar *sid = NULL;
+ gboolean unique = FALSE;
+
+ while (!unique)
+ {
+ val = g_random_int_range (1000000, G_MAXINT);
+
+ g_free (sid);
+ sid = g_strdup_printf ("%u", val);
+
+ unique = !sid_in_use (factory, sid);
+ }
+
+ g_hash_table_insert (priv->sessions, sid, NULL);
+
+ return sid;
+}
+
+static void
+register_session (GabbleJingleFactory *factory,
+ const gchar *sid,
+ GabbleJingleSession *sess)
+{
+ GabbleJingleFactoryPrivate *priv = GABBLE_JINGLE_FACTORY_GET_PRIVATE (factory);
+ gchar *sid_copy;
+
+ if (sid == NULL)
+ {
+ sid_copy = get_unique_sid (factory);
+ }
+ else
+ {
+ sid_copy = g_strdup (sid);
+ }
+
+ g_hash_table_insert (priv->sessions, sid_copy, sess);
+}
+
+void
+_jingle_factory_unregister_session (GabbleJingleFactory *factory,
+ const gchar *sid)
+{
+ GabbleJingleFactoryPrivate *priv = GABBLE_JINGLE_FACTORY_GET_PRIVATE (factory);
+ g_hash_table_remove (priv->sessions, sid);
+}
+
+static LmHandlerResult
+jingle_cb (LmMessageHandler *handler,
+ LmConnection *lmconn,
+ LmMessage *msg,
+ gpointer user_data)
+{
+ GabbleJingleFactory *self = GABBLE_JINGLE_FACTORY (user_data);
+ GabbleJingleFactoryPrivate *priv =
+ GABBLE_JINGLE_FACTORY_GET_PRIVATE (self);
+ GError *error = NULL;
+ const gchar *sid;
+ GabbleJingleSession *sess;
+ gboolean new_session = FALSE;
+
+ /* try and validate the message */
+ sid = gabble_jingle_session_parse (NULL, msg, &error);
+ if (sid == NULL)
+ {
+ if (error)
+ goto REQUEST_ERROR;
+
+ /* else, it's not for us, bail out */
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ sess = g_hash_table_lookup (priv->sessions, sid);
+ if (sess == NULL)
+ {
+ new_session = TRUE;
+ sess = create_session (self, sid, 0);
+ }
+
+ /* now act on the message */
+ gabble_jingle_session_parse (sess, msg, &error);
+
+ if (!error)
+ {
+ if (new_session)
+ {
+ g_signal_emit (self, signals[NEW_SESSION], 0, sess);
+ }
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+
+ /* on parse error */
+ g_object_unref (sess);
+
+REQUEST_ERROR:
+ _gabble_connection_send_iq_error (
+ priv->conn, msg, error->code, error->message);
+
+ g_error_free (error);
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+/*
+ * If sid is set to NULL a unique sid is generated and
+ * the "local-initiator" property of the newly created
+ * GabbleJingleSession is set to true.
+ */
+static GabbleJingleSession *
+create_session (GabbleJingleFactory *fac,
+ const gchar *sid, TpHandle peer)
+{
+ GabbleJingleFactoryPrivate *priv =
+ GABBLE_JINGLE_FACTORY_GET_PRIVATE (fac);
+ GabbleJingleSession *sess;
+ gboolean local_initiator = TRUE;
+
+ if (sid != NULL)
+ {
+ g_assert (NULL == g_hash_table_lookup (priv->sessions, sid));
+ local_initiator = FALSE;
+ }
+
+ sess = g_object_new (GABBLE_TYPE_JINGLE_SESSION,
+ "session-id", sid,
+ "connection", priv->conn,
+ "local-initiator", local_initiator,
+ "peer", peer,
+ NULL);
+
+ register_session (fac, sid, sess);
+ return sess;
+}
+
+GabbleJingleSession *
+gabble_jingle_factory_initiate_session (GabbleJingleFactory *fac,
+ TpHandle peer)
+{
+ return create_session (fac, NULL, peer);
+}
+
+void
+gabble_jingle_factory_register_transport (GabbleJingleFactory *factory,
+ gchar *namespace, JingleTransportMaker maker)
+{
+ g_hash_table_insert (factory->transports, namespace, maker);
+}
+
+void
+gabble_jingle_factory_register_description (GabbleJingleFactory *factory,
+ gchar *namespace, JingleDescriptionMaker maker)
+{
+ g_hash_table_insert (factory->descriptions, namespace, maker);
+}
+
diff --git a/src/jingle-factory.h b/src/jingle-factory.h
new file mode 100644
index 0000000..25bc0cd
--- /dev/null
+++ b/src/jingle-factory.h
@@ -0,0 +1,143 @@
+/*
+ * jingle-factory.h - Header for GabbleJingleFactory
+ * 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_FACTORY_H__
+#define __JINGLE_FACTORY_H__
+
+#include <glib-object.h>
+
+#include "gabble-types.h"
+#include "jingle-transport-iface.h"
+#include "jingle-description-iface.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ /* not a jingle message */
+ JINGLE_DIALECT_ERROR,
+ /* old libjingle3 gtalk variant */
+ JINGLE_DIALECT_GTALK3,
+ /* new gtalk variant */
+ JINGLE_DIALECT_GTALK4,
+ /* jingle in the old 0.15 version days */
+ JINGLE_DIALECT_V015,
+ /* current jingle standard */
+ JINGLE_DIALECT_V026
+} JingleDialect;
+
+typedef enum {
+ JINGLE_STATE_PENDING_CREATED,
+ JINGLE_STATE_PENDING_INITIATE_SENT,
+ JINGLE_STATE_PENDING_INITIATED,
+ JINGLE_STATE_PENDING_ACCEPT_SENT,
+ JINGLE_STATE_PENDING_ACTIVE,
+ JINGLE_STATE_ENDED
+} JingleState;
+
+typedef enum {
+ JINGLE_ACTION_UNKNOWN = -1,
+ JINGLE_ACTION_CONTENT_ACCEPT = 0,
+ JINGLE_ACTION_CONTENT_ADD,
+ JINGLE_ACTION_CONTENT_MODIFY,
+ JINGLE_ACTION_CONTENT_REMOVE,
+ JINGLE_ACTION_CONTENT_REPLACE,
+ JINGLE_ACTION_SESSION_ACCEPT,
+ JINGLE_ACTION_SESSION_INFO,
+ JINGLE_ACTION_SESSION_INITIATE,
+ JINGLE_ACTION_SESSION_TERMINATE,
+ JINGLE_ACTION_TRANSPORT_INFO,
+} JingleAction;
+
+typedef enum {
+ JINGLE_CONTENT_SENDERS_NONE = -1,
+ JINGLE_CONTENT_SENDERS_INITIATOR = 0,
+ JINGLE_CONTENT_SENDERS_RESPONDER,
+ JINGLE_CONTENT_SENDERS_BOTH
+} JingleContentSenders;
+
+typedef enum {
+ JINGLE_TRANSPORT_UNKNOWN = -1,
+ JINGLE_TRANSPORT_GOOGLE_P2P = 0,
+ JINGLE_TRANSPORT_RAW_UDP,
+ JINGLE_TRANSPORT_ICE
+} JingleTransportType;
+
+typedef enum {
+ JINGLE_TRANSPORT_PROTOCOL_UDP,
+ JINGLE_TRANSPORT_PROTOCOL_TCP
+} JingleTransportProtocol;
+
+typedef enum {
+ JINGLE_CANDIDATE_TYPE_LOCAL,
+ JINGLE_CANDIDATE_TYPE_DERIVED,
+ JINGLE_CANDIDATE_TYPE_STUN,
+ JINGLE_CANDIDATE_TYPE_RELAY
+} JingleCandidateType;
+
+typedef struct _GabbleJingleFactoryClass GabbleJingleFactoryClass;
+
+GType gabble_jingle_factory_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_JINGLE_FACTORY \
+ (gabble_jingle_factory_get_type ())
+#define GABBLE_JINGLE_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_FACTORY, \
+ GabbleJingleFactory))
+#define GABBLE_JINGLE_FACTORY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_JINGLE_FACTORY, \
+ GabbleJingleFactoryClass))
+#define GABBLE_IS_JINGLE_FACTORY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_FACTORY))
+#define GABBLE_IS_JINGLE_FACTORY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_JINGLE_FACTORY))
+#define GABBLE_JINGLE_FACTORY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_JINGLE_FACTORY, \
+ GabbleJingleFactoryClass))
+
+struct _GabbleJingleFactoryClass {
+ GObjectClass parent_class;
+};
+
+typedef GabbleJingleTransportIface * (*JingleTransportMaker) (GabbleJingleContent *c);
+typedef GabbleJingleDescriptionIface * (*JingleDescriptionMaker) (GabbleJingleContent *c);
+
+struct _GabbleJingleFactory {
+ GObject parent;
+
+ GHashTable *descriptions;
+ GHashTable *transports;
+
+ gpointer priv;
+};
+
+void gabble_jingle_factory_register_transport (GabbleJingleFactory *factory,
+ gchar *namespace, JingleTransportMaker maker);
+void gabble_jingle_factory_register_description (GabbleJingleFactory *factory,
+ gchar *namespace, JingleDescriptionMaker maker);
+void _jingle_factory_unregister_session (GabbleJingleFactory *factory,
+ const gchar *sid);
+
+GabbleJingleSession *gabble_jingle_factory_initiate_session (GabbleJingleFactory
+ *fac, TpHandle peer);
+
+G_END_DECLS;
+
+#endif /* __JINGLE_FACTORY_H__ */
+
diff --git a/src/jingle-media-rtp.c b/src/jingle-media-rtp.c
new file mode 100644
index 0000000..6591bc5
--- /dev/null
+++ b/src/jingle-media-rtp.c
@@ -0,0 +1,520 @@
+/*
+ * jingle-media-rtp.c - Source for GabbleJingleMediaRtp
+ *
+ * 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-media-rtp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <loudmouth/loudmouth.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#include "debug.h"
+#include "gabble-connection.h"
+#include "util.h"
+#include "namespaces.h"
+#include "jingle-factory.h"
+#include "jingle-session.h"
+#include "jingle-content.h"
+
+static void
+description_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GabbleJingleMediaRtp,
+ gabble_jingle_media_rtp, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GABBLE_TYPE_JINGLE_DESCRIPTION_IFACE,
+ description_iface_init));
+
+/* signal enum */
+enum
+{
+ NEW_CANDIDATE,
+ LAST_SIGNAL
+};
+
+// static guint signals[LAST_SIGNAL] = {0};
+
+/* properties */
+enum
+{
+ PROP_CONNECTION,
+ PROP_SESSION,
+ PROP_CONTENT,
+ PROP_MEDIA_TYPE,
+ LAST_PROPERTY
+};
+
+typedef enum {
+ JINGLE_MEDIA_TYPE_NONE = -1,
+ JINGLE_MEDIA_TYPE_AUDIO = 0,
+ JINGLE_MEDIA_TYPE_VIDEO
+} JingleMediaType;
+
+typedef enum {
+ JINGLE_MEDIA_PROFILE_RTP_AVP,
+} JingleMediaProfile;
+
+typedef struct {
+ guchar id;
+ gchar *name;
+ guint clockrate;
+ guint channels;
+} JingleCodec;
+
+typedef struct _GabbleJingleMediaRtpPrivate GabbleJingleMediaRtpPrivate;
+struct _GabbleJingleMediaRtpPrivate
+{
+ GabbleConnection *conn;
+ GabbleJingleSession *session;
+ GabbleJingleContent *content;
+
+ GList *local_codecs;
+ // GList *remote_codecs;
+ JingleMediaType media_type;
+ gboolean dispose_has_run;
+};
+
+#define GABBLE_JINGLE_MEDIA_RTP_GET_PRIVATE(o)\
+ ((GabbleJingleMediaRtpPrivate*)((o)->priv))
+
+static void
+gabble_jingle_media_rtp_init (GabbleJingleMediaRtp *obj)
+{
+ GabbleJingleMediaRtpPrivate *priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_JINGLE_MEDIA_RTP,
+ GabbleJingleMediaRtpPrivate);
+ obj->priv = priv;
+
+ priv->dispose_has_run = FALSE;
+}
+
+static void
+_free_codecs (GList *codecs)
+{
+ while (codecs != NULL)
+ {
+ JingleCodec *p = (JingleCodec *) codecs->data;
+
+ g_free (p->name);
+ g_free (p);
+
+ codecs = g_list_remove (codecs, p);
+ }
+}
+
+static void
+gabble_jingle_media_rtp_dispose (GObject *object)
+{
+ GabbleJingleMediaRtp *trans = GABBLE_JINGLE_MEDIA_RTP (object);
+ GabbleJingleMediaRtpPrivate *priv = GABBLE_JINGLE_MEDIA_RTP_GET_PRIVATE (trans);
+
+ if (priv->dispose_has_run)
+ return;
+
+ DEBUG ("dispose called");
+ priv->dispose_has_run = TRUE;
+
+ // _free_codecs (priv->remote_codecs);
+ // priv->remote_codecs = NULL;
+
+ _free_codecs (priv->local_codecs);
+ priv->local_codecs = NULL;
+
+ if (G_OBJECT_CLASS (gabble_jingle_media_rtp_parent_class)->dispose)
+ G_OBJECT_CLASS (gabble_jingle_media_rtp_parent_class)->dispose (object);
+}
+
+static void
+gabble_jingle_media_rtp_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleMediaRtp *trans = GABBLE_JINGLE_MEDIA_RTP (object);
+ GabbleJingleMediaRtpPrivate *priv = GABBLE_JINGLE_MEDIA_RTP_GET_PRIVATE (trans);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->conn);
+ break;
+ case PROP_SESSION:
+ g_value_set_object (value, priv->session);
+ break;
+ case PROP_CONTENT:
+ g_value_set_object (value, priv->content);
+ break;
+ case PROP_MEDIA_TYPE:
+ g_value_set_uint (value, priv->media_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_jingle_media_rtp_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleMediaRtp *trans = GABBLE_JINGLE_MEDIA_RTP (object);
+ GabbleJingleMediaRtpPrivate *priv =
+ GABBLE_JINGLE_MEDIA_RTP_GET_PRIVATE (trans);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ priv->conn = g_value_get_object (value);
+ break;
+ case PROP_SESSION:
+ priv->session = g_value_get_object (value);
+ break;
+ case PROP_CONTENT:
+ priv->content = g_value_get_object (value);
+ break;
+ case PROP_MEDIA_TYPE:
+ priv->media_type = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_jingle_media_rtp_class_init (GabbleJingleMediaRtpClass *cls)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (cls, sizeof (GabbleJingleMediaRtpPrivate));
+
+ object_class->get_property = gabble_jingle_media_rtp_get_property;
+ object_class->set_property = gabble_jingle_media_rtp_set_property;
+ object_class->dispose = gabble_jingle_media_rtp_dispose;
+
+ /* property definitions */
+ param_spec = g_param_spec_object ("connection", "GabbleConnection object",
+ "Gabble connection object used for exchanging "
+ "messages.",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_object ("session", "GabbleJingleSession object",
+ "The session using this transport object.",
+ GABBLE_TYPE_JINGLE_SESSION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_SESSION, param_spec);
+
+ 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_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);
+
+ /* signal definitions */
+}
+
+static GabbleJingleDescriptionIface *
+new_description (GabbleJingleContent *content)
+{
+ GabbleJingleMediaRtp *self;
+ GabbleJingleSession *sess;
+ GabbleConnection *conn;
+
+ g_object_get (content, "connection", &conn,
+ "session", &sess, NULL);
+
+ self = g_object_new (GABBLE_TYPE_JINGLE_MEDIA_RTP,
+ "connection", conn,
+ "session", sess,
+ "content", content,
+ NULL);
+
+ return GABBLE_JINGLE_DESCRIPTION_IFACE (self);
+}
+
+#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_description (GabbleJingleDescriptionIface *iface,
+ LmMessageNode *desc_node, GError **error)
+{
+ GabbleJingleMediaRtp *self = GABBLE_JINGLE_MEDIA_RTP (iface);
+ GabbleJingleMediaRtpPrivate *priv = GABBLE_JINGLE_MEDIA_RTP_GET_PRIVATE (self);
+ JingleMediaType mtype = JINGLE_MEDIA_TYPE_NONE;
+ gboolean google_mode = FALSE;
+ GList *codecs = NULL;
+ LmMessageNode *node;
+
+ if (lm_message_node_has_namespace (desc_node, NS_JINGLE_RTP_TMP, NULL))
+ {
+ const gchar *type = lm_message_node_get_attribute (desc_node, "media");
+
+ if (type == NULL)
+ {
+ SET_BAD_REQ("missing required media type attribute");
+ return;
+ }
+
+ if (!tp_strdiff (type, "audio"))
+ mtype = JINGLE_MEDIA_TYPE_AUDIO;
+ else if (!tp_strdiff (type, "video"))
+ mtype = JINGLE_MEDIA_TYPE_VIDEO;
+ }
+ else if (lm_message_node_has_namespace (desc_node,
+ NS_JINGLE_DESCRIPTION_AUDIO, NULL))
+ {
+ mtype = JINGLE_MEDIA_TYPE_AUDIO;
+ }
+ else if (lm_message_node_has_namespace (desc_node,
+ NS_JINGLE_DESCRIPTION_VIDEO, NULL))
+ {
+ mtype = JINGLE_MEDIA_TYPE_VIDEO;
+ }
+ else if (lm_message_node_has_namespace (desc_node,
+ NS_GOOGLE_SESSION_PHONE, NULL))
+ {
+ mtype = JINGLE_MEDIA_TYPE_AUDIO;
+ google_mode = TRUE;
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ /* FIXME: we ignore "profile" attribute */
+
+ for (node = desc_node->children; node; node = node->next)
+ {
+ JingleCodec *p;
+ const char *txt;
+ guchar id;
+ const gchar *name;
+ guint clockrate, channels;
+
+ if (tp_strdiff (node->name, "payload-type"))
+ continue;
+
+ txt = lm_message_node_get_attribute (node, "id");
+ if (txt == NULL)
+ break;
+
+ id = atoi (txt);
+
+ name = lm_message_node_get_attribute (node, "name");
+ if (name == NULL)
+ name = "";
+
+ /* xep-0167 v0.22, gtalk libjingle 0.3/0.4 use "clockrate" */
+ txt = lm_message_node_get_attribute (node, "clockrate");
+ /* older jingle rtp used "rate" ? */
+ if (txt == NULL)
+ txt = lm_message_node_get_attribute (node, "rate");
+
+ if (txt != NULL)
+ {
+ clockrate = atoi (txt);
+ }
+ else
+ {
+ clockrate = 0;
+ }
+
+ txt = lm_message_node_get_attribute (node, "channels");
+ if (txt != NULL)
+ {
+ channels = atoi (txt);
+ }
+ else
+ {
+ channels = 1;
+ }
+
+ /* FIXME: do we need "bitrate" param? never seen it in use */
+
+ p = g_new0 (JingleCodec, 1);
+ p->id = id;
+ p->name = (gchar *) name;
+ p->clockrate = clockrate;
+ p->channels = channels;
+
+ codecs = g_list_append (codecs, p);
+ }
+
+ if (node != NULL)
+ {
+ /* rollback these */
+ while (codecs != NULL)
+ {
+ JingleCodec *p = codecs->data;
+
+ g_free (codecs->data);
+ codecs = g_list_remove (codecs, p);
+ }
+
+ SET_BAD_REQ ("invalid payload");
+ return;
+ }
+
+ priv->media_type = mtype;
+
+ g_signal_emit_by_name (priv->content, "remote-codecs", codecs);
+
+ /* append them to the known remote codecs */
+ // priv->remote_codecs = g_list_concat (priv->remote_codecs, codecs);
+}
+
+static void
+produce_node (GabbleJingleDescriptionIface *obj, LmMessageNode *content_node)
+{
+ GabbleJingleMediaRtp *desc =
+ GABBLE_JINGLE_MEDIA_RTP (obj);
+ GabbleJingleMediaRtpPrivate *priv =
+ GABBLE_JINGLE_MEDIA_RTP_GET_PRIVATE (desc);
+ LmMessageNode *desc_node;
+ GList *li;
+ JingleDialect dialect;
+ const gchar *xmlns = NULL;
+
+ g_object_get (priv->session, "dialect", &dialect, NULL);
+
+ desc_node = lm_message_node_add_child (content_node, "description", NULL);
+
+ switch (dialect) {
+ case JINGLE_DIALECT_GTALK3:
+ case JINGLE_DIALECT_GTALK4:
+ g_assert (priv->media_type == JINGLE_MEDIA_TYPE_AUDIO);
+ xmlns = NS_GOOGLE_SESSION_PHONE;
+ break;
+ case JINGLE_DIALECT_V015:
+ if (priv->media_type == JINGLE_MEDIA_TYPE_AUDIO)
+ xmlns = NS_JINGLE_DESCRIPTION_AUDIO;
+ else if (priv->media_type == JINGLE_MEDIA_TYPE_VIDEO)
+ xmlns = NS_JINGLE_DESCRIPTION_VIDEO;
+ else
+ g_assert_not_reached ();
+ break;
+ case JINGLE_DIALECT_V026:
+ xmlns = "urn:xmpp:tmp:jingle:apps:rtp";
+ if (priv->media_type == JINGLE_MEDIA_TYPE_AUDIO)
+ lm_message_node_set_attribute (desc_node, "media", "audio");
+ else if (priv->media_type == JINGLE_MEDIA_TYPE_VIDEO)
+ lm_message_node_set_attribute (desc_node, "media", "video");
+ else
+ g_assert_not_reached ();
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ lm_message_node_set_attribute (desc_node, "xmlns", xmlns);
+
+ for (li = priv->local_codecs; li; li = li->next)
+ {
+ LmMessageNode *pt_node;
+ gchar buf[16];
+ JingleCodec *p = li->data;
+
+ pt_node = lm_message_node_add_child (desc_node, "payload-type", NULL);
+
+ /* id: required */
+ sprintf (buf, "%d", p->id);
+ lm_message_node_set_attribute (pt_node, "id", buf);
+
+ /* name: optional */
+ if (*p->name != '\0')
+ {
+ lm_message_node_set_attribute (pt_node, "name", p->name);
+ }
+
+ /* clock rate: optional */
+ if (p->clockrate != 0)
+ {
+ const gchar *attname = "clockrate";
+
+ if (dialect == JINGLE_DIALECT_V015)
+ attname = "rate";
+
+ sprintf (buf, "%u", p->clockrate);
+ lm_message_node_set_attribute (pt_node, attname, buf);
+ }
+
+ if (p->channels != 0)
+ {
+ sprintf (buf, "%u", p->channels);
+ lm_message_node_set_attribute (pt_node, "channels", buf);
+ }
+ }
+}
+
+static void
+description_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ GabbleJingleDescriptionIfaceClass *klass = (GabbleJingleDescriptionIfaceClass *) g_iface;
+
+ klass->parse = parse_description;
+ klass->produce = produce_node;
+ // klass->add_codecs = add_codecs;
+}
+
+void
+jingle_media_rtp_register (GabbleJingleFactory *factory)
+{
+ /* Current (v0.25) Jingle draft URI */
+ gabble_jingle_factory_register_description (factory,
+ NS_JINGLE_RTP_TMP, new_description);
+
+ /* Old Jingle audio/video namespaces */
+ gabble_jingle_factory_register_description (factory,
+ NS_JINGLE_DESCRIPTION_AUDIO, new_description);
+
+ gabble_jingle_factory_register_description (factory,
+ NS_JINGLE_DESCRIPTION_VIDEO, new_description);
+
+ /* GTalk audio call namespace */
+ gabble_jingle_factory_register_description (factory,
+ NS_GOOGLE_SESSION_PHONE, new_description);
+}
+
diff --git a/src/jingle-media-rtp.h b/src/jingle-media-rtp.h
new file mode 100644
index 0000000..37fe1e5
--- /dev/null
+++ b/src/jingle-media-rtp.h
@@ -0,0 +1,64 @@
+/*
+ * jingle-media-rtp.h - Header for GabbleJingleMediaRtp
+ * 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_MEDIA_RTP_H__
+#define __JINGLE_MEDIA_RTP_H__
+
+#include <glib-object.h>
+#include <loudmouth/loudmouth.h>
+#include "gabble-types.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleJingleMediaRtpClass GabbleJingleMediaRtpClass;
+
+GType gabble_jingle_media_rtp_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_JINGLE_MEDIA_RTP \
+ (gabble_jingle_media_rtp_get_type ())
+#define GABBLE_JINGLE_MEDIA_RTP(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_MEDIA_RTP, \
+ GabbleJingleMediaRtp))
+#define GABBLE_JINGLE_MEDIA_RTP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_JINGLE_MEDIA_RTP, \
+ GabbleJingleMediaRtpClass))
+#define GABBLE_IS_JINGLE_MEDIA_RTP(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_MEDIA_RTP))
+#define GABBLE_IS_JINGLE_MEDIA_RTP_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_JINGLE_MEDIA_RTP))
+#define GABBLE_JINGLE_MEDIA_RTP_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_JINGLE_MEDIA_RTP, \
+ GabbleJingleMediaRtpClass))
+
+struct _GabbleJingleMediaRtpClass {
+ GObjectClass parent_class;
+};
+
+struct _GabbleJingleMediaRtp {
+ GObject parent;
+ gpointer priv;
+};
+
+const gchar *gabble_jingle_media_rtp_parse (GabbleJingleMediaRtp *sess,
+ LmMessage *message, GError **error);
+void jingle_media_rtp_register (GabbleJingleFactory *factory);
+
+#endif /* __JINGLE_MEDIA_RTP_H__ */
+
diff --git a/src/jingle-session.c b/src/jingle-session.c
new file mode 100644
index 0000000..6548649
--- /dev/null
+++ b/src/jingle-session.c
@@ -0,0 +1,976 @@
+/*
+ * gabble-jingle-session.c - Source for GabbleJingleSession
+ * 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-session.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <loudmouth/loudmouth.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#include "debug.h"
+#include "gabble-connection.h"
+#include "util.h"
+#include "namespaces.h"
+#include "jingle-factory.h"
+#include "jingle-content.h"
+
+#include "gabble-media-session.h"
+
+G_DEFINE_TYPE(GabbleJingleSession, gabble_jingle_session, G_TYPE_OBJECT);
+
+/* signal enum */
+enum
+{
+ FOO,
+ LAST_SIGNAL
+};
+
+// FIXME static guint signals[LAST_SIGNAL] = {0};
+
+/* properties */
+enum
+{
+ PROP_CONNECTION = 1,
+ PROP_FACTORY,
+ PROP_SESSION_ID,
+ PROP_PEER,
+ PROP_PEER_RESOURCE,
+ PROP_LOCAL_INITIATOR,
+ PROP_STATE,
+ PROP_DIALECT,
+ LAST_PROPERTY
+};
+
+typedef struct _GabbleJingleSessionPrivate GabbleJingleSessionPrivate;
+struct _GabbleJingleSessionPrivate
+{
+ GabbleConnection *conn;
+ GabbleJingleFactory *factory;
+
+ TpHandle peer;
+ gchar *peer_resource;
+ gchar *peer_jid;
+ gchar *initiator;
+ gboolean local_initiator;
+ GHashTable *contents;
+ JingleDialect dialect;
+ JingleState state;
+ gchar *sid;
+
+ gboolean locally_accepted;
+
+ gboolean dispose_has_run;
+};
+
+#define GABBLE_JINGLE_SESSION_GET_PRIVATE(o)\
+ ((GabbleJingleSessionPrivate*)((o)->priv))
+
+/* lookup tables */
+
+static const gchar *action_table[] = {
+ "content-accept",
+ "content-add",
+ "content-modify",
+ "content-remove",
+ "content-replace",
+ "session-accept",
+ "session-info",
+ "session-initiate",
+ "session-terminate",
+ "transport-info",
+ NULL
+};
+
+typedef struct {
+ JingleState state;
+ JingleAction *actions;
+} JingleStateActions;
+
+static JingleAction allowed_actions[6][8] = {
+ /* JS_STATE_PENDING_CREATED */
+ { JINGLE_ACTION_SESSION_INITIATE, JINGLE_ACTION_UNKNOWN },
+ /* JS_STATE_PENDING_INITIATE_SENT */
+ { JINGLE_ACTION_UNKNOWN },
+ /* JS_STATE_PENDING_INITIATED */
+ { JINGLE_ACTION_SESSION_ACCEPT, JINGLE_ACTION_SESSION_TERMINATE,
+ JINGLE_ACTION_TRANSPORT_INFO,
+ JINGLE_ACTION_CONTENT_MODIFY, JINGLE_ACTION_CONTENT_ACCEPT,
+ JINGLE_ACTION_CONTENT_REMOVE, JINGLE_ACTION_UNKNOWN },
+ /* JS_STATE_PENDING_ACCEPT_SENT */
+ { JINGLE_ACTION_UNKNOWN },
+ /* JS_STATE_ACTIVE */
+ { JINGLE_ACTION_CONTENT_MODIFY, JINGLE_ACTION_CONTENT_ADD,
+ JINGLE_ACTION_CONTENT_REMOVE, JINGLE_ACTION_CONTENT_REPLACE,
+ JINGLE_ACTION_SESSION_INFO, JINGLE_ACTION_TRANSPORT_INFO,
+ JINGLE_ACTION_SESSION_TERMINATE, JINGLE_ACTION_UNKNOWN },
+ /* JS_STATE_ENDED */
+ { JINGLE_ACTION_UNKNOWN }
+};
+
+static void
+gabble_jingle_session_init (GabbleJingleSession *obj)
+{
+ GabbleJingleSessionPrivate *priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_JINGLE_SESSION,
+ GabbleJingleSessionPrivate);
+ obj->priv = priv;
+
+ priv->contents = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+
+ priv->locally_accepted = FALSE;
+ priv->dispose_has_run = FALSE;
+}
+
+static void
+gabble_jingle_session_dispose (GObject *object)
+{
+ GabbleJingleSession *sess = GABBLE_JINGLE_SESSION (object);
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT);
+
+ if (priv->dispose_has_run)
+ return;
+
+ DEBUG ("dispose called");
+ priv->dispose_has_run = TRUE;
+
+ _jingle_factory_unregister_session (priv->factory, priv->sid);
+
+ g_hash_table_destroy (priv->contents);
+ priv->contents = NULL;
+
+ if (priv->peer)
+ {
+ tp_handle_unref (contact_repo, priv->peer);
+ priv->peer = 0;
+ }
+
+ g_free (priv->sid);
+ priv->sid = NULL;
+
+ g_free (priv->peer_resource);
+ priv->peer_resource = NULL;
+
+ g_free (priv->peer_jid);
+ priv->peer_jid = NULL;
+
+ g_free (priv->initiator);
+ priv->initiator = NULL;
+
+ if (G_OBJECT_CLASS (gabble_jingle_session_parent_class)->dispose)
+ G_OBJECT_CLASS (gabble_jingle_session_parent_class)->dispose (object);
+}
+
+static void
+gabble_jingle_session_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleSession *sess = GABBLE_JINGLE_SESSION (object);
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->conn);
+ break;
+ case PROP_FACTORY:
+ g_value_set_object (value, priv->factory);
+ break;
+ case PROP_SESSION_ID:
+ g_value_set_string (value, priv->sid);
+ break;
+ case PROP_LOCAL_INITIATOR:
+ g_value_set_boolean (value, priv->local_initiator);
+ break;
+ case PROP_PEER:
+ g_value_set_uint (value, priv->peer);
+ break;
+ case PROP_PEER_RESOURCE:
+ g_value_set_string (value, priv->peer_resource);
+ break;
+ case PROP_STATE:
+ g_value_set_uint (value, priv->state);
+ break;
+ case PROP_DIALECT:
+ g_value_set_uint (value, priv->dialect);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_jingle_session_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleSession *sess = GABBLE_JINGLE_SESSION (object);
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ priv->conn = g_value_get_object (value);
+ break;
+ case PROP_FACTORY:
+ priv->factory = g_value_get_object (value);
+ break;
+ case PROP_LOCAL_INITIATOR:
+ priv->local_initiator = g_value_get_boolean (value);
+ break;
+ case PROP_DIALECT:
+ priv->dialect = g_value_get_uint (value);
+ break;
+ case PROP_PEER:
+ priv->peer = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_jingle_session_class_init (GabbleJingleSessionClass *cls)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (cls, sizeof (GabbleJingleSessionPrivate));
+
+ object_class->get_property = gabble_jingle_session_get_property;
+ object_class->set_property = gabble_jingle_session_set_property;
+ object_class->dispose = gabble_jingle_session_dispose;
+
+ /* property definitions */
+ param_spec = g_param_spec_object ("connection", "GabbleConnection object",
+ "Gabble connection object used for exchanging "
+ "messages.",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_object ("factory", "GabbleJingleFactory object",
+ "Jingle factory object that owns this "
+ "jingle session.",
+ GABBLE_TYPE_JINGLE_FACTORY,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_string ("session-id", "Session ID",
+ "A unique session identifier used "
+ "throughout all communication.",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_SESSION_ID, param_spec);
+
+ param_spec = g_param_spec_boolean ("local-initiator", "Session initiator",
+ "Specifies if local end initiated the session.",
+ TRUE,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_LOCAL_INITIATOR, param_spec);
+
+ param_spec = g_param_spec_uint ("peer", "Session peer",
+ "The TpHandle representing the contact "
+ "with whom this session communicates.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PEER, param_spec);
+
+ param_spec = g_param_spec_string ("peer-resource",
+ "Session peer's resource",
+ "The resource of the contact "
+ "with whom this session communicates, "
+ "if applicable",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PEER_RESOURCE,
+ param_spec);
+
+ param_spec = g_param_spec_uint ("state", "Session state",
+ "The current state that the session is in.",
+ 0, G_MAXUINT32, JS_STATE_PENDING_CREATED,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_STATE, param_spec);
+
+ param_spec = g_param_spec_uint ("dialect", "Jingle dialect",
+ "Jingle dialect used for this session.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PEER, param_spec);
+
+
+ /* signal definitions */
+}
+
+typedef void (*HandlerFunc)(GabbleJingleSession *sess,
+ LmMessageNode *node, GError **error);
+typedef void (*ContentHandlerFunc)(GabbleJingleSession *sess,
+ GabbleJingleContent *c, LmMessageNode *content_node, GError **error);
+
+const gchar *
+_enum_to_string (const gchar **table, gint val)
+{
+ return table[val];
+}
+
+gint
+_string_to_enum (const gchar **table, const gchar *val)
+{
+ gint i;
+
+ if (val == NULL)
+ return -1;
+
+ for (i = 0; table[i] != NULL; i++)
+ {
+ if (!tp_strdiff (val, table[i]))
+ return i;
+ }
+
+ return -1;
+}
+
+static JingleAction
+parse_action (const gchar *txt)
+{
+ /* synonyms, best deal with them right now */
+ if (!tp_strdiff (txt, "initiate"))
+ txt = "session-initiate";
+ else if (!tp_strdiff (txt, "terminate"))
+ txt = "session-terminate";
+ else if (!tp_strdiff (txt, "accept"))
+ txt = "session-accept";
+ else if (!tp_strdiff (txt, "reject"))
+ txt = "session-terminate";
+ else if (!tp_strdiff (txt, "candidates"))
+ txt = "transport-info";
+
+ return (JingleAction) _string_to_enum (action_table, txt);
+}
+
+static const gchar *
+produce_action (JingleAction act, JingleDialect dialect)
+{
+ g_assert (act != JINGLE_ACTION_UNKNOWN);
+
+ if ((dialect == JINGLE_DIALECT_GTALK3) || (dialect == JINGLE_DIALECT_GTALK4))
+ {
+ switch (act) {
+ case JINGLE_ACTION_SESSION_INITIATE:
+ return "initiate";
+ case JINGLE_ACTION_SESSION_TERMINATE:
+ return "terminate";
+ case JINGLE_ACTION_SESSION_ACCEPT:
+ return "accept";
+ case JINGLE_ACTION_TRANSPORT_INFO:
+ return "candidates";
+ default:
+ return _enum_to_string (action_table, act);
+ }
+ }
+
+ return _enum_to_string (action_table, act);
+}
+
+static gboolean
+action_is_allowed (JingleAction action, JingleState state)
+{
+ guint i;
+
+ for (i = 0; allowed_actions[state][i] != JINGLE_ACTION_UNKNOWN; i++)
+ {
+ if (allowed_actions[state][i] == action)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#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
+_foreach_content (GabbleJingleSession *sess, LmMessageNode *node,
+ ContentHandlerFunc func, GError **error)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ GabbleJingleContent *c;
+ LmMessageNode *content_node;
+ const gchar *name;
+
+ for (content_node = node->children;
+ NULL != content_node;
+ content_node = content_node->next)
+ {
+ if (!tp_strdiff (content_node->name, "content"))
+ continue;
+
+ name = lm_message_node_get_attribute (content_node, "name");
+ c = g_hash_table_lookup (priv->contents, name);
+
+ func (sess, c, content_node, error);
+ if (*error)
+ return;
+ }
+}
+
+static void
+_each_content_add (GabbleJingleSession *sess, GabbleJingleContent *c,
+ LmMessageNode *content_node, GError **error)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ const gchar *name = lm_message_node_get_attribute (content_node, "name");
+
+ if (c != NULL)
+ {
+ JingleContentState state;
+
+ /* streams added by the session initiator may replace similarly-named
+ * streams which we are trying to add (but havn't had acknowledged) */
+
+ g_object_get (c, "state", &state, NULL);
+ if (state < JINGLE_CONTENT_STATE_ACKNOWLEDGED)
+ {
+ if (priv->local_initiator)
+ {
+ SET_CONFLICT ("session initiator is creating a stream "
+ "named \"%s\" already", name);
+ return;
+ }
+ }
+ else
+ {
+ SET_CONFLICT ("stream called \"%s\" already exists, rejecting", name);
+ return;
+ }
+
+ }
+
+
+ c = g_object_new (GABBLE_TYPE_JINGLE_CONTENT,
+ "connection", priv->conn,
+ "factory", priv->factory,
+ "session", sess,
+ NULL);
+
+ gabble_jingle_content_parse_add (c, content_node, FALSE, error);
+ if (*error)
+ {
+ g_object_unref (c);
+ return;
+ }
+
+ /* This will override existing stream if it exists. */
+ g_hash_table_replace (priv->contents, g_strdup (name), c);
+}
+
+static void
+_each_content_remove (GabbleJingleSession *sess, GabbleJingleContent *c,
+ LmMessageNode *content_node, GError **error)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ const gchar *name = lm_message_node_get_attribute (content_node, "name");
+
+ if (c == NULL)
+ {
+ SET_BAD_REQ ("stream called \"%s\" doesn't exist", name);
+ return;
+ }
+
+ if (g_hash_table_size (priv->contents) == 1)
+ {
+ SET_BAD_REQ ("unable to remove the last stream in a session");
+ return;
+ }
+
+ /* This should have the effect of shutting the stream down.
+ * FIXME: do we need to have REMOVING state at all? */
+ g_hash_table_remove (priv->contents, name);
+}
+
+static void
+_each_content_modify (GabbleJingleSession *sess, GabbleJingleContent *c,
+ LmMessageNode *content_node, GError **error)
+{
+ const gchar *name = lm_message_node_get_attribute (content_node, "name");
+
+ if (c == NULL)
+ {
+ SET_BAD_REQ ("stream called \"%s\" doesn't exist", name);
+ return;
+ }
+
+ gabble_jingle_content_update_senders (c, content_node, error);
+
+ if (*error)
+ return;
+}
+
+static void
+_each_content_replace (GabbleJingleSession *sess, GabbleJingleContent *c,
+ LmMessageNode *content_node, GError **error)
+{
+ _each_content_remove (sess, c, content_node, error);
+
+ if (*error)
+ return;
+
+ _each_content_add (sess, c, content_node, error);
+}
+
+static void
+on_session_initiate (GabbleJingleSession *sess, LmMessageNode *node,
+ GError **error)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+
+ /* we can't call ourselves at the moment */
+ if (priv->local_initiator)
+ {
+ // FIXME: terminate session here, plzkthxbai
+ // jingle_session_terminate (sess, FALSE, JINGLE_REASON_BUSY);
+ return;
+ }
+
+ if ((priv->dialect == JINGLE_DIALECT_GTALK3) ||
+ (priv->dialect == JINGLE_DIALECT_GTALK4))
+ {
+ /* in this case we implicitly have just one content */
+ _each_content_add (sess, NULL, node, error);
+ }
+ else
+ {
+ _foreach_content (sess, node, _each_content_add, error);
+ }
+}
+
+static void
+on_content_add (GabbleJingleSession *sess, LmMessageNode *node,
+ GError **error)
+{
+ _foreach_content (sess, node, _each_content_add, error);
+}
+
+static void
+on_content_modify (GabbleJingleSession *sess, LmMessageNode *node,
+ GError **error)
+{
+ _foreach_content (sess, node, _each_content_modify, error);
+}
+
+static void
+on_content_remove (GabbleJingleSession *sess, LmMessageNode *node,
+ GError **error)
+{
+ _foreach_content (sess, node, _each_content_remove, error);
+}
+
+static void
+on_content_replace (GabbleJingleSession *sess, LmMessageNode *node,
+ GError **error)
+{
+ _foreach_content (sess, node, _each_content_replace, error);
+}
+
+static void
+on_content_accept (GabbleJingleSession *sess, LmMessageNode *node,
+ GError **error)
+{
+ // FIXME
+ // _foreach_content (sess, node, _each_content_replace, error);
+}
+
+
+static HandlerFunc handlers[] = {
+ on_content_accept,
+ on_content_add,
+ on_content_modify,
+ on_content_remove,
+ on_content_replace,
+ NULL, /* jingle_on_session_accept */
+ NULL, /* jingle_on_session_info */
+ on_session_initiate,
+ NULL, /* jingle_on_session_terminate */
+ NULL /* jingle_on_transport_info */
+};
+
+static void
+jingle_state_machine_dance (GabbleJingleSession *sess, JingleAction action,
+ LmMessageNode *node, GError **error)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+
+ if (!action_is_allowed (action, priv->state))
+ {
+ g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_JINGLE_OUT_OF_ORDER,
+ "action \"%s\" not allowed in current state",
+ _enum_to_string (action_table, action));
+ return;
+ }
+
+ handlers[action] (sess, node, error);
+}
+
+
+const gchar *
+gabble_jingle_session_parse (GabbleJingleSession *sess, LmMessage *message, GError **error)
+{
+ TpHandleRepoIface *contact_repo;
+ GabbleJingleSessionPrivate *priv;
+ LmMessageNode *iq_node, *session_node;
+ const gchar *actxt, *sid, *from, *to, *resource;
+ JingleDialect dialect;
+ JingleAction action;
+ const gchar *initiator, *responder;
+ gboolean google_mode = FALSE;
+
+ /* all jingle actions are sets */
+ if (LM_MESSAGE_SUB_TYPE_SET != lm_message_get_sub_type (message))
+ return FALSE;
+
+ iq_node = lm_message_get_node (message);
+
+ /* IQ from/to can come in handy */
+ from = lm_message_node_get_attribute (iq_node, "from");
+ if (from == NULL)
+ return NULL;
+
+ to = lm_message_node_get_attribute (iq_node, "to");
+ if (to == FALSE)
+ return NULL;
+
+ /* first, we try standard jingle */
+ session_node = lm_message_node_get_child_with_namespace (iq_node,
+ "jingle", NS_JINGLE026);
+
+ if (session_node != NULL)
+ {
+ dialect = JINGLE_DIALECT_V026;
+ }
+ else
+ {
+ /* then, we try a bit older jingle version */
+ session_node = lm_message_node_get_child_with_namespace (iq_node,
+ "jingle", NS_JINGLE015);
+
+ if (session_node != NULL)
+ {
+ dialect = JINGLE_DIALECT_V015;
+ }
+ else
+ {
+ /* next, we try googletalk */
+ session_node = lm_message_node_get_child_with_namespace (iq_node,
+ "session", NS_GOOGLE_SESSION);
+
+ /* we can't distinguish between libjingle 0.3 and libjingle0.4 at this
+ * point, assume the better case */
+ if (session_node != NULL)
+ {
+ dialect = JINGLE_DIALECT_GTALK4;
+ google_mode = TRUE;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+
+ if (google_mode)
+ {
+ actxt = lm_message_node_get_attribute (session_node, "type");
+ sid = lm_message_node_get_attribute (session_node, "id");
+ }
+ else
+ {
+ actxt = lm_message_node_get_attribute (session_node, "action");
+ sid = lm_message_node_get_attribute (session_node, "sid");
+ }
+
+ if (actxt == NULL)
+ {
+ SET_BAD_REQ ("session action not found");
+ return NULL;
+ }
+
+ if (sid == NULL)
+ {
+ SET_BAD_REQ ("session id not found");
+ return NULL;
+ }
+
+ action = parse_action (actxt);
+ if (action == JINGLE_ACTION_UNKNOWN)
+ {
+ SET_BAD_REQ ("unknown session action");
+ return NULL;
+ }
+
+ initiator = lm_message_node_get_attribute (session_node, "initiator");
+ if (initiator == NULL)
+ {
+ SET_BAD_REQ ("session initiator not found");
+ return NULL;
+ }
+
+ resource = strchr (from, '/');
+ if (resource == NULL || *resource == '\0')
+ {
+ SET_BAD_REQ ("sender with no resource");
+ return NULL;
+ }
+
+ /* this one is not required, so it can be NULL */
+ responder = lm_message_node_get_attribute (session_node, "responder");
+
+ /* if we were just validating, return successfully now */
+ if (sess == NULL)
+ return sid;
+
+ priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT);
+
+ if (!action_is_allowed (action, priv->state))
+ {
+ SET_OUT_ORDER ("action \"%s\" not allowed in current state", actxt);
+ return NULL;
+ }
+
+ priv->peer = tp_handle_ensure (contact_repo, from, NULL, NULL);
+ if (priv->peer == 0)
+ {
+ SET_BAD_REQ ("unable to get sender handle");
+ return NULL;
+ }
+
+ /* if we just created the session, fill in the data */
+ if (priv->state == JS_STATE_PENDING_CREATED)
+ {
+ priv->sid = g_strdup (sid);
+ priv->peer_resource = g_strdup (resource);
+ priv->dialect = dialect;
+ priv->peer_jid = g_strdup (from);
+ priv->initiator = g_strdup (initiator);
+ }
+
+ jingle_state_machine_dance (sess, action, session_node, error);
+
+ if (*error)
+ return NULL;
+
+ return sid;
+}
+
+LmMessage *
+gabble_jingle_session_new_message (GabbleJingleSession *sess,
+ JingleAction action, LmMessageNode **sess_node)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ LmMessage *msg;
+ LmMessageNode *iq_node, *session_node;
+ gchar *el = NULL, *ns = NULL;
+ gboolean gtalk_mode = FALSE;
+
+ msg = lm_message_new_with_sub_type (
+ priv->peer_jid,
+ LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_SET);
+
+ iq_node = lm_message_get_node (msg);
+
+ switch (priv->dialect) {
+ case JINGLE_DIALECT_V026:
+ el = "jingle";
+ ns = NS_JINGLE026;
+ break;
+ case JINGLE_DIALECT_V015:
+ el = "jingle";
+ ns = NS_JINGLE015;
+ break;
+ case JINGLE_DIALECT_GTALK3:
+ case JINGLE_DIALECT_GTALK4:
+ el = "session";
+ ns = NS_GOOGLE_SESSION;
+ gtalk_mode = TRUE;
+ break;
+ case JINGLE_DIALECT_ERROR:
+ g_assert_not_reached ();
+
+ }
+
+ session_node = lm_message_node_add_child (iq_node, el, NULL);
+ lm_message_node_set_attributes (session_node,
+ "xmlns", ns,
+ "initiator", priv->initiator,
+ (gtalk_mode) ? "id" : "sid", priv->sid,
+ (gtalk_mode) ? "type" : "action",
+ produce_action (action, priv->dialect),
+ NULL);
+
+ return msg;
+}
+
+static void
+_try_session_accept_check (gpointer key, gpointer data, gpointer user_data)
+{
+ GabbleJingleContent *c = GABBLE_JINGLE_CONTENT (data);
+ gboolean *is_ready = (gboolean *) user_data;
+
+ if (!gabble_jingle_content_is_ready (c))
+ *is_ready = FALSE;
+}
+
+static void
+_try_session_accept_fill (gpointer key, gpointer data, gpointer user_data)
+{
+ GabbleJingleContent *c = GABBLE_JINGLE_CONTENT (data);
+ LmMessageNode *sess_node = user_data;
+
+ gabble_jingle_content_produce_node (c, sess_node, TRUE);
+}
+
+static void
+try_session_accept (GabbleJingleSession *sess)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ LmMessage *msg;
+ LmMessageNode *sess_node;
+
+ gboolean content_ready = TRUE;
+
+ if (!priv->locally_accepted)
+ return;
+
+ g_hash_table_foreach (priv->contents, _try_session_accept_check, &content_ready);
+
+ if ((g_hash_table_size (priv->contents) == 0) || (content_ready == FALSE))
+ return;
+
+ msg = gabble_jingle_session_new_message (sess, JINGLE_ACTION_SESSION_ACCEPT,
+ &sess_node);
+
+ g_hash_table_foreach (priv->contents, _try_session_accept_fill, &sess_node);
+
+ /* FIXME: Actually send the message, change local session state, etc. */
+}
+
+void
+gabble_jingle_session_accept (GabbleJingleSession *sess)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+
+ priv->locally_accepted = TRUE;
+
+ try_session_accept (sess);
+}
+
+void
+gabble_jingle_session_terminate (GabbleJingleSession *sess)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ LmMessage *msg;
+ LmMessageNode *sess_node;
+
+ msg = gabble_jingle_session_new_message (sess, JINGLE_ACTION_SESSION_TERMINATE,
+ &sess_node);
+
+ /* FIXME - unref and cleanup everything, change session state, etc */
+}
+
+void
+gabble_jingle_session_remove_content (GabbleJingleSession *sess,
+ const gchar *content_name)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ GabbleJingleContent *c = g_hash_table_lookup (priv->contents, content_name);
+ LmMessage *msg;
+ LmMessageNode *sess_node;
+
+ g_return_if_fail (c != NULL);
+
+ msg = gabble_jingle_session_new_message (sess, JINGLE_ACTION_CONTENT_REMOVE,
+ &sess_node);
+
+ g_assert (msg != NULL);
+
+ gabble_jingle_content_produce_node (c, sess_node, FALSE);
+ /* FIXME: actually send it, mark it as in removal, etc */
+
+ /* This should g_object_unref the content */
+ g_hash_table_remove (priv->contents, content_name);
+}
+
+void
+gabble_jingle_session_change_direction (GabbleJingleSession *sess,
+ const gchar *content_name, JingleContentSenders senders)
+{
+ GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+ GabbleJingleContent *c = g_hash_table_lookup (priv->contents, content_name);
+ LmMessage *msg;
+ LmMessageNode *sess_node;
+
+ g_return_if_fail (c != NULL);
+
+ g_object_set (c, "senders", senders, NULL);
+
+ msg = gabble_jingle_session_new_message (sess, JINGLE_ACTION_CONTENT_MODIFY,
+ &sess_node);
+
+ g_assert (msg != NULL);
+
+ gabble_jingle_content_produce_node (c, sess_node, FALSE);
+
+ /* FIXME: send the message, mark the nodes as pending change, etc */
+}
+
diff --git a/src/jingle-session.h b/src/jingle-session.h
new file mode 100644
index 0000000..d9d0991
--- /dev/null
+++ b/src/jingle-session.h
@@ -0,0 +1,72 @@
+/*
+ * jingle-session.h - Header for GabbleJingleSession
+ * 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_SESSION_H__
+#define __JINGLE_SESSION_H__
+
+#include <glib-object.h>
+#include <loudmouth/loudmouth.h>
+#include "gabble-types.h"
+#include "jingle-factory.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleJingleSessionClass GabbleJingleSessionClass;
+
+GType gabble_jingle_session_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_JINGLE_SESSION \
+ (gabble_jingle_session_get_type ())
+#define GABBLE_JINGLE_SESSION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_SESSION, \
+ GabbleJingleSession))
+#define GABBLE_JINGLE_SESSION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_JINGLE_SESSION, \
+ GabbleJingleSessionClass))
+#define GABBLE_IS_JINGLE_SESSION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_SESSION))
+#define GABBLE_IS_JINGLE_SESSION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_JINGLE_SESSION))
+#define GABBLE_JINGLE_SESSION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_JINGLE_SESSION, \
+ GabbleJingleSessionClass))
+
+struct _GabbleJingleSessionClass {
+ GObjectClass parent_class;
+};
+
+struct _GabbleJingleSession {
+ GObject parent;
+ gpointer priv;
+};
+
+const gchar *gabble_jingle_session_parse (GabbleJingleSession *sess,
+ LmMessage *message, GError **error);
+LmMessage *gabble_jingle_session_new_message (GabbleJingleSession *sess,
+ JingleAction action, LmMessageNode **sess_node);
+
+const gchar *_enum_to_string (const gchar **table, gint val);
+gint _string_to_enum (const gchar **table, const gchar *val);
+
+void gabble_jingle_session_accept (GabbleJingleSession *sess);
+void gabble_jingle_session_terminate (GabbleJingleSession *sess);
+
+#endif /* __JINGLE_SESSION_H__ */
+
diff --git a/src/jingle-transport-google.c b/src/jingle-transport-google.c
new file mode 100644
index 0000000..11648b0
--- /dev/null
+++ b/src/jingle-transport-google.c
@@ -0,0 +1,548 @@
+/*
+ * jingle-transport-google.c - Source for GabbleJingleTransportGoogle
+ *
+ * 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-google.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <loudmouth/loudmouth.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#include "debug.h"
+#include "gabble-connection.h"
+#include "util.h"
+#include "namespaces.h"
+#include "jingle-factory.h"
+#include "jingle-session.h"
+#include "jingle-content.h"
+
+static void
+transport_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GabbleJingleTransportGoogle,
+ gabble_jingle_transport_google, 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_CONNECTION,
+ PROP_SESSION,
+ PROP_CONTENT,
+ LAST_PROPERTY
+};
+
+typedef struct _GabbleJingleTransportGooglePrivate GabbleJingleTransportGooglePrivate;
+struct _GabbleJingleTransportGooglePrivate
+{
+ GabbleConnection *conn;
+ GabbleJingleSession *session;
+ GabbleJingleContent *content;
+
+ GList *local_candidates;
+ // GList *remote_candidates;
+ gboolean dispose_has_run;
+};
+
+#define GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_PRIVATE(o)\
+ ((GabbleJingleTransportGooglePrivate*)((o)->priv))
+
+static void transmit_candidates (GabbleJingleTransportGoogle *transport,
+ GList *candidates);
+
+static void
+gabble_jingle_transport_google_init (GabbleJingleTransportGoogle *obj)
+{
+ GabbleJingleTransportGooglePrivate *priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_JINGLE_TRANSPORT_GOOGLE,
+ GabbleJingleTransportGooglePrivate);
+ obj->priv = priv;
+
+ priv->dispose_has_run = FALSE;
+}
+
+static void
+_free_candidates (GList *candidates)
+{
+ while (candidates != NULL)
+ {
+ JingleCandidate *c = (JingleCandidate *) candidates->data;
+
+ g_free (c->address);
+ g_free (c->username);
+ g_free (c->password);
+
+ g_free (c);
+
+ candidates = g_list_remove (candidates, c);
+ }
+}
+
+static void
+gabble_jingle_transport_google_dispose (GObject *object)
+{
+ GabbleJingleTransportGoogle *trans = GABBLE_JINGLE_TRANSPORT_GOOGLE (object);
+ GabbleJingleTransportGooglePrivate *priv = GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_PRIVATE (trans);
+
+ if (priv->dispose_has_run)
+ return;
+
+ DEBUG ("dispose called");
+ priv->dispose_has_run = TRUE;
+
+ // _free_candidates (priv->remote_candidates); // FIXME: huge bug, malloc/free hell
+ // priv->remote_candidates = NULL;
+
+ _free_candidates (priv->local_candidates);
+ priv->local_candidates = NULL;
+
+ if (G_OBJECT_CLASS (gabble_jingle_transport_google_parent_class)->dispose)
+ G_OBJECT_CLASS (gabble_jingle_transport_google_parent_class)->dispose (object);
+}
+
+static void
+gabble_jingle_transport_google_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleTransportGoogle *trans = GABBLE_JINGLE_TRANSPORT_GOOGLE (object);
+ GabbleJingleTransportGooglePrivate *priv = GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_PRIVATE (trans);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->conn);
+ break;
+ case PROP_SESSION:
+ g_value_set_object (value, priv->session);
+ break;
+ case PROP_CONTENT:
+ g_value_set_object (value, priv->content);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_jingle_transport_google_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleJingleTransportGoogle *trans = GABBLE_JINGLE_TRANSPORT_GOOGLE (object);
+ GabbleJingleTransportGooglePrivate *priv =
+ GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_PRIVATE (trans);
+
+ switch (property_id) {
+ case PROP_CONNECTION:
+ priv->conn = g_value_get_object (value);
+ break;
+ case PROP_SESSION:
+ priv->session = g_value_get_object (value);
+ break;
+ case PROP_CONTENT:
+ priv->content = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_jingle_transport_google_class_init (GabbleJingleTransportGoogleClass *cls)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (cls);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (cls, sizeof (GabbleJingleTransportGooglePrivate));
+
+ object_class->get_property = gabble_jingle_transport_google_get_property;
+ object_class->set_property = gabble_jingle_transport_google_set_property;
+ object_class->dispose = gabble_jingle_transport_google_dispose;
+
+ /* property definitions */
+ param_spec = g_param_spec_object ("connection", "GabbleConnection object",
+ "Gabble connection object used for exchanging "
+ "messages.",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_object ("session", "GabbleJingleSession object",
+ "The session using this transport object.",
+ GABBLE_TYPE_JINGLE_SESSION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_SESSION, param_spec);
+
+ 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);
+
+ /* 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__UINT, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+}
+
+static GabbleJingleTransportIface *
+new_transport (GabbleJingleContent *content)
+{
+ GabbleJingleTransportGoogle *self;
+ GabbleJingleSession *sess;
+ GabbleConnection *conn;
+
+ g_object_get (content, "connection", &conn,
+ "session", &sess, NULL);
+
+ self = g_object_new (GABBLE_TYPE_JINGLE_TRANSPORT_GOOGLE,
+ "connection", conn,
+ "session", sess,
+ "content", content,
+ NULL);
+
+ return GABBLE_JINGLE_TRANSPORT_IFACE (self);
+}
+
+#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)
+{
+ GabbleJingleTransportGoogle *t = GABBLE_JINGLE_TRANSPORT_GOOGLE (obj);
+ GabbleJingleTransportGooglePrivate *priv =
+ GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_PRIVATE (t);
+ GList *candidates = NULL;
+
+ LmMessageNode *cnode, *node;
+
+ /* special-case for GTalk libjingle0.3 */
+ cnode = lm_message_node_get_child (transport_node, "candidate");
+
+ if (cnode == NULL)
+ {
+ JingleDialect dialect;
+
+ cnode = transport_node;
+
+ g_object_get (priv->session, "dialect", &dialect, NULL);
+
+ if (dialect == JINGLE_DIALECT_GTALK4)
+ {
+ /* FIXME: do we need to do anything more than retransmit
+ * local candidates and mode switch? */
+ g_object_set (priv->session, "dialect",
+ JINGLE_DIALECT_GTALK3, NULL);
+
+ transmit_candidates (t, priv->local_candidates);
+ }
+ }
+
+ for (node = cnode->children; node; node = node->next)
+ {
+ const gchar *name, *address, *user, *pass, *str;
+ guint port, net, gen;
+ gdouble pref;
+ JingleTransportProtocol proto;
+ JingleCandidateType ctype;
+ JingleCandidate *c;
+
+ if (tp_strdiff (node->name, "candidate"))
+ continue;
+
+ name = lm_message_node_get_attribute (node, "name");
+ if (name == NULL || tp_strdiff (name, "rtp"))
+ break;
+
+ address = lm_message_node_get_attribute (node, "address");
+ 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 if (!tp_strdiff (str, "tcp"))
+ {
+ /* candiates on port 443 must be "ssltcp" */
+ if (port == 443)
+ break;
+
+ proto = JINGLE_TRANSPORT_PROTOCOL_TCP;
+ }
+ else if (!tp_strdiff (str, "ssltcp"))
+ {
+ /* "ssltcp" must use port 443 */
+ if (port != 443)
+ break;
+
+ /* we really don't care about "ssltcp" otherwise */
+ proto = JINGLE_TRANSPORT_PROTOCOL_TCP;
+ }
+ else
+ {
+ /* unknown protocol */
+ break;
+ }
+
+ str = lm_message_node_get_attribute (node, "preference");
+ 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, "local"))
+ {
+ ctype = JINGLE_CANDIDATE_TYPE_LOCAL;
+ }
+ else if (!tp_strdiff (str, "stun"))
+ {
+ ctype = JINGLE_CANDIDATE_TYPE_DERIVED;
+ }
+ else if (!tp_strdiff (str, "relay"))
+ {
+ ctype = JINGLE_CANDIDATE_TYPE_RELAY;
+ }
+ else
+ {
+ /* unknown candidate type */
+ break;
+ }
+
+ user = lm_message_node_get_attribute (node, "username");
+ if (user == NULL)
+ break;
+
+ pass = lm_message_node_get_attribute (node, "password");
+ 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);
+
+ c = g_new0 (JingleCandidate, 1);
+ c->address = (gchar *) address;
+ c->port = port;
+ c->protocol = proto;
+ c->preference = pref;
+ c->type = ctype;
+ c->username = (gchar *) user;
+ c->password = (gchar *) pass;
+ c->network = net;
+ c->generation = gen;
+
+ candidates = g_list_append (candidates, c);
+ }
+
+ if (node != NULL)
+ {
+ /* rollback these */
+ while (candidates != NULL)
+ {
+ JingleCandidate *c = candidates->data;
+
+ g_free (candidates->data);
+ candidates = g_list_remove (candidates, c);
+ }
+
+ SET_BAD_REQ ("invalid candidate");
+ return;
+ }
+
+ g_signal_emit_by_name (priv->content, "remote-candidates", candidates);
+
+ /* append them to the known remote candidates */
+
+ /* OK this sucks, do we really need to save it? if we want to save it we
+ * can't borrow the strings, malloc hell ensues */
+ // priv->remote_candidates = g_list_concat (priv->remote_candidates, candidates);
+}
+
+static void
+transmit_candidates (GabbleJingleTransportGoogle *transport, GList *candidates)
+{
+ GabbleJingleTransportGooglePrivate *priv =
+ GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_PRIVATE (transport);
+ JingleDialect dialect;
+ GList *li;
+ LmMessage *msg;
+ LmMessageNode *trans_node, *sess_node;
+
+ msg = jingle_session_new_message (priv->session,
+ JINGLE_ACTION_TRANSPORT_INFO, &sess_node);
+
+ g_object_get (priv->session, "dialect", &dialect, NULL);
+
+ if (dialect == JINGLE_DIALECT_GTALK3)
+ {
+ trans_node = sess_node;
+ }
+ else
+ {
+ trans_node = lm_message_node_add_child (sess_node, "transport",
+ NS_GOOGLE_SESSION);
+ }
+
+ for (li = candidates; li; li = li->next)
+ {
+ JingleCandidate *c = (JingleCandidate *) li->data;
+ gchar port_str[16], pref_str[16], *type_str, *proto_str;
+ LmMessageNode *cnode;
+
+ sprintf (port_str, "%d", c->port);
+ sprintf (pref_str, "%d", c->preference);
+
+ switch (c->type) {
+ case JINGLE_CANDIDATE_TYPE_LOCAL:
+ type_str = "local";
+ break;
+ case JINGLE_CANDIDATE_TYPE_DERIVED:
+ type_str = "derived";
+ 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;
+ case JINGLE_TRANSPORT_PROTOCOL_TCP:
+ if ((c->port == 443) && (c->type == JINGLE_CANDIDATE_TYPE_RELAY))
+ proto_str = "ssltcp";
+ else
+ proto_str = "tcp";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ cnode = lm_message_node_add_child (trans_node, "candidate", NULL);
+ lm_message_node_set_attributes (cnode,
+ "address", c->address,
+ "port", port_str,
+ "username", c->username,
+ "password", c->password,
+ "preference", pref_str,
+ "protocol", proto_str,
+
+ "name", "rtp",
+ "network", "0",
+ "generation", "0",
+ NULL);
+ }
+
+ _gabble_connection_send (priv->conn, msg, NULL);
+}
+
+static void
+add_candidates (GabbleJingleTransportIface *obj, GList *new_candidates)
+{
+ GabbleJingleTransportGoogle *transport =
+ GABBLE_JINGLE_TRANSPORT_GOOGLE (obj);
+ GabbleJingleTransportGooglePrivate *priv =
+ GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_PRIVATE (transport);
+
+ transmit_candidates (transport, new_candidates);
+
+ priv->local_candidates = g_list_concat (priv->local_candidates,
+ new_candidates);
+}
+
+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;
+}
+
+void
+jingle_transport_google_register (GabbleJingleFactory *factory)
+{
+ /* GTalk libjingle0.3 dialect */
+ gabble_jingle_factory_register_transport (factory, NULL,
+ new_transport);
+
+ /* GTalk libjingle0.4 dialect */
+ gabble_jingle_factory_register_transport (factory,
+ NS_GOOGLE_SESSION, new_transport);
+}
+
diff --git a/src/jingle-transport-google.h b/src/jingle-transport-google.h
new file mode 100644
index 0000000..cec5a84
--- /dev/null
+++ b/src/jingle-transport-google.h
@@ -0,0 +1,62 @@
+/*
+ * jingle-transport-google.h - Header for GabbleJingleTransportGoogle
+ * 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_GOOGLE_H__
+#define __JINGLE_TRANSPORT_GOOGLE_H__
+
+#include <glib-object.h>
+#include <loudmouth/loudmouth.h>
+#include "gabble-types.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleJingleTransportGoogleClass GabbleJingleTransportGoogleClass;
+
+GType gabble_jingle_transport_google_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_JINGLE_TRANSPORT_GOOGLE \
+ (gabble_jingle_transport_google_get_type ())
+#define GABBLE_JINGLE_TRANSPORT_GOOGLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_TRANSPORT_GOOGLE, \
+ GabbleJingleTransportGoogle))
+#define GABBLE_JINGLE_TRANSPORT_GOOGLE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_JINGLE_TRANSPORT_GOOGLE, \
+ GabbleJingleTransportGoogleClass))
+#define GABBLE_IS_JINGLE_TRANSPORT_GOOGLE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_TRANSPORT_GOOGLE))
+#define GABBLE_IS_JINGLE_TRANSPORT_GOOGLE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_JINGLE_TRANSPORT_GOOGLE))
+#define GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_JINGLE_TRANSPORT_GOOGLE, \
+ GabbleJingleTransportGoogleClass))
+
+struct _GabbleJingleTransportGoogleClass {
+ GObjectClass parent_class;
+};
+
+struct _GabbleJingleTransportGoogle {
+ GObject parent;
+ gpointer priv;
+};
+
+void jingle_transport_google_register (GabbleJingleFactory *factory);
+
+#endif /* __JINGLE_TRANSPORT_GOOGLE_H__ */
+
diff --git a/src/jingle-transport-iface.c b/src/jingle-transport-iface.c
new file mode 100644
index 0000000..3eb8a30
--- /dev/null
+++ b/src/jingle-transport-iface.c
@@ -0,0 +1,124 @@
+/*
+ * jingle-transport-iface.c - Source for GabbleJingleTransport interface
+ * Copyright (C) 2007-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-iface.h"
+#include "gabble-connection.h"
+#include "jingle-session.h"
+
+#include <glib.h>
+
+void
+gabble_jingle_transport_iface_parse (GabbleJingleTransportIface *self,
+ LmMessageNode *node, GError **error)
+{
+ void (*virtual_method)(GabbleJingleTransportIface *,
+ LmMessageNode *, GError **) =
+ GABBLE_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->parse;
+
+ g_assert (virtual_method != NULL);
+ return virtual_method (self, node, error);
+}
+
+void
+gabble_jingle_transport_iface_produce (GabbleJingleTransportIface *self,
+ LmMessageNode *node)
+{
+ void (*virtual_method)(GabbleJingleTransportIface *,
+ LmMessageNode *) =
+ GABBLE_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->produce;
+
+ g_assert (virtual_method != NULL);
+ return virtual_method (self, node);
+}
+
+void
+gabble_jingle_transport_iface_add_candidates (GabbleJingleTransportIface *self,
+ GList *candidates)
+{
+ void (*virtual_method)(GabbleJingleTransportIface *,
+ GList *) =
+ GABBLE_JINGLE_TRANSPORT_IFACE_GET_CLASS (self)->add_candidates;
+
+ g_assert (virtual_method != NULL);
+ virtual_method (self, candidates);
+}
+
+
+static void
+gabble_jingle_transport_iface_base_init (gpointer klass)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ GParamSpec *param_spec;
+
+ param_spec = g_param_spec_object (
+ "connection",
+ "GabbleConnection object",
+ "Gabble connection object that owns this jingle transport object.",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_interface_install_property (klass, param_spec);
+
+ param_spec = g_param_spec_object (
+ "session",
+ "GabbleJingleSession object",
+ "Jingle session that's using this jingle transport object.",
+ GABBLE_TYPE_JINGLE_SESSION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB);
+ g_object_interface_install_property (klass, param_spec);
+
+ initialized = TRUE;
+ }
+}
+
+GType
+gabble_jingle_transport_iface_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof (GabbleJingleTransportIfaceClass),
+ gabble_jingle_transport_iface_base_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL /* instance_init */
+ };
+
+ type = g_type_register_static (G_TYPE_INTERFACE, "GabbleJingleTransportIface",
+ &info, 0);
+ }
+
+ return type;
+}
+
diff --git a/src/jingle-transport-iface.h b/src/jingle-transport-iface.h
new file mode 100644
index 0000000..d28a4e9
--- /dev/null
+++ b/src/jingle-transport-iface.h
@@ -0,0 +1,64 @@
+/*
+ * jingle_transport-iface.h - Header for GabbleJingleTransport interface
+ * Copyright (C) 2007-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 __GABBLE_JINGLE_TRANSPORT_IFACE_H__
+#define __GABBLE_JINGLE_TRANSPORT_IFACE_H__
+
+#include <glib-object.h>
+
+#include "gabble-types.h"
+#include <loudmouth/loudmouth.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleJingleTransportIface GabbleJingleTransportIface;
+typedef struct _GabbleJingleTransportIfaceClass GabbleJingleTransportIfaceClass;
+
+struct _GabbleJingleTransportIfaceClass {
+ GTypeInterface parent;
+
+ void (*parse) (GabbleJingleTransportIface *,
+ LmMessageNode *, GError **);
+ void (*produce) (GabbleJingleTransportIface *,
+ LmMessageNode *);
+
+ void (*add_candidates) (GabbleJingleTransportIface *,
+ GList *);
+};
+
+GType gabble_jingle_transport_iface_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_JINGLE_TRANSPORT_IFACE \
+ (gabble_jingle_transport_iface_get_type ())
+#define GABBLE_JINGLE_TRANSPORT_IFACE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_JINGLE_TRANSPORT_IFACE, GabbleJingleTransportIface))
+#define GABBLE_IS_JINGLE_TRANSPORT_IFACE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_JINGLE_TRANSPORT_IFACE))
+#define GABBLE_JINGLE_TRANSPORT_IFACE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GABBLE_TYPE_JINGLE_TRANSPORT_IFACE,\
+ GabbleJingleTransportIfaceClass))
+
+void gabble_jingle_transport_iface_parse (GabbleJingleTransportIface *, LmMessageNode *, GError **);
+void gabble_jingle_transport_iface_produce (GabbleJingleTransportIface *, LmMessageNode *);
+void gabble_jingle_transport_iface_add_candidates (GabbleJingleTransportIface *, GList *);
+
+G_END_DECLS
+
+#endif /* #ifndef __GABBLE_JINGLE_TRANSPORT_IFACE_H__ */
--
1.5.6.5
More information about the Telepathy-commits
mailing list