[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