[Telepathy-commits] [telepathy-gabble/master] Add an initial implementation of SOCKS5 bytestreams

Marco Barisione marco at barisione.org
Tue Jan 6 08:41:20 PST 2009


---
 src/Makefile.am          |    2 +
 src/bytestream-factory.c |  233 +++++++++++-
 src/bytestream-factory.h |    6 +
 src/bytestream-socks5.c  |  973 ++++++++++++++++++++++++++++++++++++++++++++++
 src/bytestream-socks5.h  |   75 ++++
 src/namespaces.h         |    1 +
 6 files changed, 1288 insertions(+), 2 deletions(-)
 create mode 100644 src/bytestream-socks5.c
 create mode 100644 src/bytestream-socks5.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 6c363b2..8e87bf4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,6 +28,8 @@ libgabble_convenience_la_SOURCES = \
     bytestream-iface.c \
     bytestream-muc.h \
     bytestream-muc.c \
+    bytestream-socks5.h \
+    bytestream-socks5.c \
     capabilities.h \
     capabilities.c \
     caps-hash.h \
diff --git a/src/bytestream-factory.c b/src/bytestream-factory.c
index 47d3e78..fb06a85 100644
--- a/src/bytestream-factory.c
+++ b/src/bytestream-factory.c
@@ -1,6 +1,6 @@
 /*
  * bytestream-factory.c - Source for GabbleBytestreamFactory
- * Copyright (C) 2007 Collabora Ltd.
+ * 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
@@ -33,6 +33,7 @@
 #include "bytestream-ibb.h"
 #include "bytestream-iface.h"
 #include "bytestream-muc.h"
+#include "bytestream-socks5.h"
 #include "connection.h"
 #include "debug.h"
 #include "namespaces.h"
@@ -108,6 +109,7 @@ struct _GabbleBytestreamFactoryPrivate
   GabbleConnection *conn;
   LmMessageHandler *iq_si_cb;
   LmMessageHandler *iq_ibb_cb;
+  LmMessageHandler *iq_socks5_cb;
   LmMessageHandler *msg_data_cb;
 
   /* SI-initiated bytestreams - data sent by normal messages, IQs used to
@@ -115,6 +117,7 @@ struct _GabbleBytestreamFactoryPrivate
    *
    * BytestreamIdentifier -> GabbleBytestreamIBB */
   GHashTable *ibb_bytestreams;
+  GHashTable *socks5_bytestreams;
 
   /* MUC pseudo-IBB - data sent by groupchat messages, IQs not allowed.
    *
@@ -138,6 +141,10 @@ static LmHandlerResult
 bytestream_factory_iq_ibb_cb (LmMessageHandler *handler, LmConnection *lmconn,
     LmMessage *message, gpointer user_data);
 
+static LmHandlerResult
+bytestream_factory_iq_socks5_cb (LmMessageHandler *handler, LmConnection *lmconn,
+    LmMessage *message, gpointer user_data);
+
 static void
 gabble_bytestream_factory_init (GabbleBytestreamFactory *self)
 {
@@ -151,6 +158,9 @@ gabble_bytestream_factory_init (GabbleBytestreamFactory *self)
 
   priv->muc_bytestreams = g_hash_table_new_full (bytestream_id_hash,
       bytestream_id_equal, bytestream_id_free, g_object_unref);
+
+  priv->socks5_bytestreams = g_hash_table_new_full (bytestream_id_hash,
+      bytestream_id_equal, bytestream_id_free, g_object_unref);
 }
 
 static GObject *
@@ -183,6 +193,11 @@ gabble_bytestream_factory_constructor (GType type,
   lm_connection_register_message_handler (priv->conn->lmconn, priv->iq_ibb_cb,
       LM_MESSAGE_TYPE_IQ, LM_HANDLER_PRIORITY_FIRST);
 
+  priv->iq_socks5_cb = lm_message_handler_new (bytestream_factory_iq_socks5_cb,
+      self, NULL);
+  lm_connection_register_message_handler (priv->conn->lmconn,
+      priv->iq_socks5_cb, LM_MESSAGE_TYPE_IQ, LM_HANDLER_PRIORITY_FIRST);
+
   return obj;
 }
 
@@ -211,12 +226,19 @@ gabble_bytestream_factory_dispose (GObject *object)
       priv->iq_ibb_cb, LM_MESSAGE_TYPE_IQ);
   lm_message_handler_unref (priv->iq_ibb_cb);
 
+  lm_connection_unregister_message_handler (priv->conn->lmconn,
+      priv->iq_socks5_cb, LM_MESSAGE_TYPE_IQ);
+  lm_message_handler_unref (priv->iq_socks5_cb);
+
   g_hash_table_destroy (priv->ibb_bytestreams);
   priv->ibb_bytestreams = NULL;
 
   g_hash_table_destroy (priv->muc_bytestreams);
   priv->muc_bytestreams = NULL;
 
+  g_hash_table_destroy (priv->socks5_bytestreams);
+  priv->socks5_bytestreams = NULL;
+
   if (G_OBJECT_CLASS (gabble_bytestream_factory_parent_class)->dispose)
     G_OBJECT_CLASS (gabble_bytestream_factory_parent_class)->dispose (object);
 }
@@ -311,7 +333,10 @@ remove_bytestream (GabbleBytestreamFactory *self,
     }
   else
     {
-      table = priv->ibb_bytestreams;
+      if (GABBLE_IS_BYTESTREAM_IBB (bytestream))
+        table = priv->ibb_bytestreams;
+      else if (GABBLE_IS_BYTESTREAM_SOCKS5 (bytestream))
+        table = priv->socks5_bytestreams;
     }
 
   if (table == NULL)
@@ -561,6 +586,14 @@ bytestream_factory_iq_si_cb (LmMessageHandler *handler,
               GABBLE_BYTESTREAM_STATE_LOCAL_PENDING);
           break;
         }
+      else if (!tp_strdiff (l->data, NS_BYTESTREAMS))
+        {
+          bytestream = (GabbleBytestreamIface *)
+            gabble_bytestream_factory_create_socks5 (self, peer_handle,
+              stream_id, stream_init_id, peer_resource,
+              GABBLE_BYTESTREAM_STATE_LOCAL_PENDING);
+          break;
+        }
     }
 
   if (bytestream == NULL)
@@ -923,6 +956,160 @@ bytestream_factory_msg_data_cb (LmMessageHandler *handler,
   return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
 }
 
+static gboolean
+handle_socks5_query_iq (GabbleBytestreamFactory *self,
+                        LmMessage *msg)
+{
+  GabbleBytestreamFactoryPrivate *priv =
+    GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self);
+  GabbleBytestreamSocks5 *bytestream;
+  LmMessageNode *query_node;
+  LmMessageNode *child_node;
+  ConstBytestreamIdentifier bsid = { NULL, NULL };
+  const gchar *tmp;
+
+  if (lm_message_get_sub_type (msg) != LM_MESSAGE_SUB_TYPE_SET)
+    return FALSE;
+
+  query_node = lm_message_node_get_child_with_namespace (msg->node,
+      "query", NS_BYTESTREAMS);
+  if (query_node == NULL)
+    return FALSE;
+
+  bsid.jid = lm_message_node_get_attribute (msg->node, "from");
+  if (bsid.jid == NULL)
+    {
+      DEBUG ("got a message without a from field");
+      _gabble_connection_send_iq_error (priv->conn, msg,
+          XMPP_ERROR_BAD_REQUEST, NULL);
+      return TRUE;
+    }
+
+  bsid.stream = lm_message_node_get_attribute (query_node, "sid");
+  if (bsid.stream == NULL)
+    {
+      DEBUG ("SOCKS5 query stanza doesn't contain stream id");
+      _gabble_connection_send_iq_error (priv->conn, msg,
+          XMPP_ERROR_BAD_REQUEST, NULL);
+      return TRUE;
+    }
+
+  bytestream = g_hash_table_lookup (priv->socks5_bytestreams, &bsid);
+  if (bytestream == NULL)
+    {
+      /* We don't accept streams not previously announced using SI */
+      DEBUG ("unknown stream: <%s> from <%s>", bsid.stream, bsid.jid);
+      _gabble_connection_send_iq_error (priv->conn, msg,
+          XMPP_ERROR_BAD_REQUEST, NULL);
+      return TRUE;
+    }
+
+  tmp = lm_message_node_get_attribute (query_node, "mode");
+  if (tmp != NULL && strcmp (tmp, "tcp") != 0)
+    {
+      DEBUG ("non-TCP SOCKS5 bytestreams are not supported");
+      _gabble_connection_send_iq_error (priv->conn, msg,
+          XMPP_ERROR_BAD_REQUEST, NULL);
+      return TRUE;
+    }
+
+  child_node = query_node->children;
+  while (child_node)
+    {
+      if (strcmp (child_node->name, "streamhost") == 0)
+        gabble_bytestream_socks5_add_streamhost (bytestream, child_node);
+
+      child_node = child_node->next;
+    }
+
+  g_object_set (bytestream, "state", GABBLE_BYTESTREAM_STATE_OPEN,
+      NULL);
+
+  gabble_bytestream_socks5_connect_to_streamhost (bytestream, msg);
+
+  return TRUE;
+}
+
+static gboolean
+handle_socks5_close_iq (GabbleBytestreamFactory *self,
+                        LmMessage *msg)
+{
+  GabbleBytestreamFactoryPrivate *priv =
+    GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self);
+  ConstBytestreamIdentifier bsid = { NULL, NULL };
+  GabbleBytestreamSocks5 *bytestream;
+  LmMessageNode *close_node;
+
+  if (lm_message_get_sub_type (msg) != LM_MESSAGE_SUB_TYPE_SET)
+    return FALSE;
+
+  close_node = lm_message_node_get_child_with_namespace (msg->node, "close",
+      NS_BYTESTREAMS);
+  if (close_node == NULL)
+    return FALSE;
+
+  bsid.jid = lm_message_node_get_attribute (msg->node, "from");
+  if (bsid.jid == NULL)
+    {
+      DEBUG ("got a message without a from field");
+      _gabble_connection_send_iq_error (priv->conn, msg,
+          XMPP_ERROR_BAD_REQUEST, "SOCKS5 <close> has no 'from' attribute");
+      return TRUE;
+    }
+
+  bsid.stream = lm_message_node_get_attribute (close_node, "sid");
+  if (bsid.stream == NULL)
+    {
+      DEBUG ("SOCKS5 close stanza doesn't contain stream id");
+      _gabble_connection_send_iq_error (priv->conn, msg,
+          XMPP_ERROR_BAD_REQUEST, "SOCKS5 <close> has no stream ID");
+      return TRUE;
+    }
+
+  bytestream = g_hash_table_lookup (priv->socks5_bytestreams, &bsid);
+  if (bytestream == NULL)
+    {
+      DEBUG ("unknown stream: <%s> from <%s>", bsid.stream, bsid.jid);
+      _gabble_connection_send_iq_error (priv->conn, msg,
+          XMPP_ERROR_ITEM_NOT_FOUND, NULL);
+    }
+  else
+    {
+      DEBUG ("received SOCKS5 close stanza. Bytestream closed");
+
+      g_object_set (bytestream, "state", GABBLE_BYTESTREAM_STATE_CLOSED,
+          NULL);
+
+      _gabble_connection_acknowledge_set_iq (priv->conn, msg);
+    }
+
+  return TRUE;
+}
+
+/**
+ * bytestream_factory_iq_socks5_cb:
+ *
+ * Called by loudmouth when we get an incoming <iq>.
+ * This handler is concerned with SOCKS5 iq's.
+ *
+ */
+static LmHandlerResult
+bytestream_factory_iq_socks5_cb (LmMessageHandler *handler,
+                                 LmConnection *lmconn,
+                                 LmMessage *msg,
+                                 gpointer user_data)
+{
+  GabbleBytestreamFactory *self = GABBLE_BYTESTREAM_FACTORY (user_data);
+
+  if (handle_socks5_query_iq (self, msg))
+    return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+
+  if (handle_socks5_close_iq (self, msg))
+    return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+
+  return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+}
+
 GabbleBytestreamFactory *
 gabble_bytestream_factory_new (GabbleConnection *conn)
 {
@@ -1026,6 +1213,40 @@ gabble_bytestream_factory_create_muc (GabbleBytestreamFactory *self,
   return bytestream;
 }
 
+GabbleBytestreamSocks5 *
+gabble_bytestream_factory_create_socks5 (GabbleBytestreamFactory *self,
+                                         TpHandle peer_handle,
+                                         const gchar *stream_id,
+                                         const gchar *stream_init_id,
+                                         const gchar *peer_resource,
+                                         GabbleBytestreamState state)
+{
+  GabbleBytestreamFactoryPrivate *priv;
+  GabbleBytestreamSocks5 *socks5;
+  BytestreamIdentifier *id;
+
+  g_return_val_if_fail (GABBLE_IS_BYTESTREAM_FACTORY (self), NULL);
+  priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (self);
+
+  socks5 = g_object_new (GABBLE_TYPE_BYTESTREAM_SOCKS5,
+      "connection", priv->conn,
+      "peer-handle", peer_handle,
+      "stream-id", stream_id,
+      "state", state,
+      "stream-init-id", stream_init_id,
+      "peer-resource", peer_resource,
+      NULL);
+
+  g_signal_connect (socks5, "state-changed",
+      G_CALLBACK (bytestream_state_changed_cb), self);
+
+  id = bytestream_id_new (GABBLE_BYTESTREAM_IFACE (socks5));
+  DEBUG ("add private bytestream <%s> from <%s>", id->stream, id->jid);
+  g_hash_table_insert (priv->socks5_bytestreams, id, socks5);
+
+  return socks5;
+}
+
 struct _streaminit_reply_cb_data
 {
   gchar *stream_id;
@@ -1127,6 +1348,14 @@ streaminit_reply_cb (GabbleConnection *conn,
               data->stream_id, NULL, peer_resource,
               GABBLE_BYTESTREAM_STATE_INITIATING));
         }
+      else if (!tp_strdiff (stream_method, NS_BYTESTREAMS))
+        {
+          /* Remote user has accepted the stream */
+          bytestream = GABBLE_BYTESTREAM_IFACE (
+              gabble_bytestream_factory_create_socks5 (self, peer_handle,
+              data->stream_id, NULL, peer_resource,
+              GABBLE_BYTESTREAM_STATE_INITIATING));
+        }
       else
         {
           DEBUG ("Remote user chose an unsupported stream method");
diff --git a/src/bytestream-factory.h b/src/bytestream-factory.h
index 44fa2ef..1fe01bf 100644
--- a/src/bytestream-factory.h
+++ b/src/bytestream-factory.h
@@ -28,6 +28,7 @@
 #include "bytestream-iface.h"
 #include "bytestream-ibb.h"
 #include "bytestream-muc.h"
+#include "bytestream-socks5.h"
 #include "connection.h"
 
 G_BEGIN_DECLS
@@ -80,6 +81,11 @@ GabbleBytestreamMuc *gabble_bytestream_factory_create_muc (
     GabbleBytestreamFactory *fac, TpHandle peer_handle, const gchar *stream_id,
     GabbleBytestreamState state);
 
+GabbleBytestreamSocks5 *gabble_bytestream_factory_create_socks5 (
+    GabbleBytestreamFactory *fac, TpHandle peer_handle, const gchar *stream_id,
+    const gchar *stream_init_id, const gchar *peer_resource,
+    GabbleBytestreamState state);
+
 LmMessage *gabble_bytestream_factory_make_stream_init_iq (
     const gchar *full_jid, const gchar *stream_id, const gchar *profile);
 
diff --git a/src/bytestream-socks5.c b/src/bytestream-socks5.c
new file mode 100644
index 0000000..8087a3d
--- /dev/null
+++ b/src/bytestream-socks5.c
@@ -0,0 +1,973 @@
+/*
+ * bytestream-socks5.c - Source for GabbleBytestreamSocks5
+ * 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 "config.h"
+#include "bytestream-socks5.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <loudmouth/loudmouth.h>
+#include <telepathy-glib/interfaces.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_BYTESTREAM
+
+#include "base64.h"
+#include "bytestream-factory.h"
+#include "bytestream-iface.h"
+#include "connection.h"
+#include "debug.h"
+#include "disco.h"
+#include "gabble-signals-marshal.h"
+#include "namespaces.h"
+#include "util.h"
+
+static void
+bytestream_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GabbleBytestreamSocks5, gabble_bytestream_socks5,
+    G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (GABBLE_TYPE_BYTESTREAM_IFACE,
+      bytestream_iface_init));
+
+/* signals */
+enum
+{
+  DATA_RECEIVED,
+  STATE_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+/* properties */
+enum
+{
+  PROP_CONNECTION = 1,
+  PROP_PEER_HANDLE,
+  PROP_PEER_HANDLE_TYPE,
+  PROP_STREAM_ID,
+  PROP_STREAM_INIT_ID,
+  PROP_PEER_JID,
+  PROP_PEER_RESOURCE,
+  PROP_STATE,
+  PROP_PROTOCOL,
+  LAST_PROPERTY
+};
+
+enum _Socks5State
+{
+  SOCKS5_STATE_INVALID,
+  SOCKS5_STATE_AUTH,
+  SOCKS5_STATE_CONNECT_REQUESTED,
+  SOCKS5_STATE_CONNECTED,
+};
+
+typedef enum _Socks5State Socks5State;
+
+/* SOCKS5 commands */
+#define SOCKS5_VERSION     0x05
+#define SOCKS5_CMD_CONNECT 0x01 
+#define SOCKS5_RESERVED    0x00
+#define SOCKS5_ATYP_DOMAIN 0x03
+#define SOCKS5_STATUS_OK   0x00
+#define SOCKS5_AUTH_NONE   0x00
+
+struct _Streamhost
+{
+  gchar *jid;
+  gchar *host;
+  guint port;
+};
+typedef struct _Streamhost Streamhost;
+
+struct _GabbleBytestreamSocks5Private
+{
+  GabbleConnection *conn;
+  TpHandle peer_handle;
+  gchar *stream_id;
+  gchar *stream_init_id;
+  gchar *peer_resource;
+  GabbleBytestreamState bytestream_state;
+  gchar *peer_jid;
+
+  GSList *streamhosts;
+  LmMessage *msg_for_acknowledge_connection;
+
+  GIOChannel *io_channel;
+  Socks5State socks5_state;
+
+  gint read_watch;
+  GString *read_buffer;
+
+  gint write_watch;
+  GString *write_buffer;
+  gsize write_position;
+
+  gint error_watch;
+
+  gboolean dispose_has_run;
+};
+
+#define GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE(obj) ((obj)->priv)
+
+static void socks5_connect_next (GabbleBytestreamSocks5 *self);
+
+static void gabble_bytestream_socks5_close (GabbleBytestreamIface *iface,
+    GError *error);
+
+static void
+gabble_bytestream_socks5_init (GabbleBytestreamSocks5 *self)
+{
+  GabbleBytestreamSocks5Private *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      GABBLE_TYPE_BYTESTREAM_SOCKS5, GabbleBytestreamSocks5Private);
+
+  self->priv = priv;
+}
+
+static void
+gabble_bytestream_socks5_dispose (GObject *object)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (object);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+      (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT);
+
+  if (priv->dispose_has_run)
+    return;
+
+  priv->dispose_has_run = TRUE;
+
+  tp_handle_unref (contact_repo, priv->peer_handle);
+
+  if (priv->bytestream_state != GABBLE_BYTESTREAM_STATE_CLOSED)
+    {
+      gabble_bytestream_iface_close (GABBLE_BYTESTREAM_IFACE (self), NULL);
+    }
+
+  G_OBJECT_CLASS (gabble_bytestream_socks5_parent_class)->dispose (object);
+}
+
+static void
+gabble_bytestream_socks5_finalize (GObject *object)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (object);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+
+  g_free (priv->stream_id);
+  g_free (priv->stream_init_id);
+  g_free (priv->peer_resource);
+  g_free (priv->peer_jid);
+
+  G_OBJECT_CLASS (gabble_bytestream_socks5_parent_class)->finalize (object);
+}
+
+static void
+gabble_bytestream_socks5_get_property (GObject *object,
+                                       guint property_id,
+                                       GValue *value,
+                                       GParamSpec *pspec)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (object);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+
+  switch (property_id)
+    {
+      case PROP_CONNECTION:
+        g_value_set_object (value, priv->conn);
+        break;
+      case PROP_PEER_HANDLE:
+        g_value_set_uint (value, priv->peer_handle);
+        break;
+      case PROP_PEER_HANDLE_TYPE:
+        g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
+        break;
+      case PROP_STREAM_ID:
+        g_value_set_string (value, priv->stream_id);
+        break;
+      case PROP_STREAM_INIT_ID:
+        g_value_set_string (value, priv->stream_init_id);
+        break;
+      case PROP_PEER_RESOURCE:
+        g_value_set_string (value, priv->peer_resource);
+        break;
+      case PROP_PEER_JID:
+        g_value_set_string (value, priv->peer_jid);
+        break;
+      case PROP_STATE:
+        g_value_set_uint (value, priv->bytestream_state);
+        break;
+      case PROP_PROTOCOL:
+        g_value_set_string (value, NS_BYTESTREAMS);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+gabble_bytestream_socks5_set_property (GObject *object,
+                                       guint property_id,
+                                       const GValue *value,
+                                       GParamSpec *pspec)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (object);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+
+  switch (property_id)
+    {
+      case PROP_CONNECTION:
+        priv->conn = g_value_get_object (value);
+        break;
+      case PROP_PEER_HANDLE:
+        priv->peer_handle = g_value_get_uint (value);
+        break;
+      case PROP_STREAM_ID:
+        g_free (priv->stream_id);
+        priv->stream_id = g_value_dup_string (value);
+        break;
+      case PROP_STREAM_INIT_ID:
+        g_free (priv->stream_init_id);
+        priv->stream_init_id = g_value_dup_string (value);
+        break;
+      case PROP_PEER_RESOURCE:
+        g_free (priv->peer_resource);
+        priv->peer_resource = g_value_dup_string (value);
+        break;
+      case PROP_STATE:
+        if (priv->bytestream_state != g_value_get_uint (value))
+            {
+              priv->bytestream_state = g_value_get_uint (value);
+              g_signal_emit (object, signals[STATE_CHANGED], 0, priv->bytestream_state);
+            }
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static GObject *
+gabble_bytestream_socks5_constructor (GType type,
+                                      guint n_props,
+                                      GObjectConstructParam *props)
+{
+  GObject *obj;
+  GabbleBytestreamSocks5Private *priv;
+  TpHandleRepoIface *contact_repo;
+  const gchar *jid;
+
+  obj = G_OBJECT_CLASS (gabble_bytestream_socks5_parent_class)->
+           constructor (type, n_props, props);
+
+  priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (GABBLE_BYTESTREAM_SOCKS5 (obj));
+
+  g_assert (priv->conn != NULL);
+  g_assert (priv->peer_handle != 0);
+  g_assert (priv->stream_id != NULL);
+
+  contact_repo = tp_base_connection_get_handles (
+      (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT);
+
+  tp_handle_ref (contact_repo, priv->peer_handle);
+
+  jid = tp_handle_inspect (contact_repo, priv->peer_handle);
+
+  if (priv->peer_resource != NULL)
+    priv->peer_jid = g_strdup_printf ("%s/%s", jid, priv->peer_resource);
+  else
+    priv->peer_jid = g_strdup (jid);
+
+  return obj;
+}
+
+static void
+gabble_bytestream_socks5_class_init (
+    GabbleBytestreamSocks5Class *gabble_bytestream_socks5_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (gabble_bytestream_socks5_class);
+  GParamSpec *param_spec;
+
+  g_type_class_add_private (gabble_bytestream_socks5_class,
+      sizeof (GabbleBytestreamSocks5Private));
+
+  object_class->dispose = gabble_bytestream_socks5_dispose;
+  object_class->finalize = gabble_bytestream_socks5_finalize;
+
+  object_class->get_property = gabble_bytestream_socks5_get_property;
+  object_class->set_property = gabble_bytestream_socks5_set_property;
+  object_class->constructor = gabble_bytestream_socks5_constructor;
+
+   g_object_class_override_property (object_class, PROP_CONNECTION,
+      "connection");
+   g_object_class_override_property (object_class, PROP_PEER_HANDLE,
+       "peer-handle");
+   g_object_class_override_property (object_class, PROP_PEER_HANDLE_TYPE,
+       "peer-handle-type");
+   g_object_class_override_property (object_class, PROP_STREAM_ID,
+       "stream-id");
+   g_object_class_override_property (object_class, PROP_PEER_JID,
+       "peer-jid");
+   g_object_class_override_property (object_class, PROP_STATE,
+       "state");
+   g_object_class_override_property (object_class, PROP_PROTOCOL,
+       "protocol");
+
+  param_spec = g_param_spec_string (
+      "peer-resource",
+      "Peer resource",
+      "the resource used by the remote peer during the SI, if any",
+      NULL,
+      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_PEER_RESOURCE,
+      param_spec);
+
+  param_spec = g_param_spec_string (
+      "stream-init-id",
+      "stream init ID",
+      "the iq ID of the SI request, if any",
+      NULL,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_STREAM_INIT_ID,
+      param_spec);
+
+  signals[DATA_RECEIVED] =
+    g_signal_new ("data-received",
+                  G_OBJECT_CLASS_TYPE (gabble_bytestream_socks5_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                  0,
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__UINT_POINTER,
+                  G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
+
+  signals[STATE_CHANGED] =
+    g_signal_new ("state-changed",
+                  G_OBJECT_CLASS_TYPE (gabble_bytestream_socks5_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                  0,
+                  NULL, NULL,
+                  gabble_marshal_VOID__UINT,
+                  G_TYPE_NONE, 1, G_TYPE_UINT);
+}
+
+static gboolean 
+socks5_channel_writable_cb (GIOChannel *source, 
+                            GIOCondition condition,
+                            gpointer data) 
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (data);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  gsize remaining_length = priv->write_buffer->len - priv->write_position;
+  GIOStatus status;
+  gsize bytes_written;
+
+  g_assert (remaining_length > 0);
+
+  status = g_io_channel_write_chars (priv->io_channel,
+      &priv->write_buffer->str [priv->write_position], remaining_length,
+      &bytes_written, NULL);
+
+  remaining_length -= bytes_written;
+  if (remaining_length == 0)
+    {
+      g_string_truncate (priv->write_buffer, 0);
+      priv->write_position = 0;
+      priv->write_watch = 0;
+      return FALSE;
+    }
+
+  priv->write_position += bytes_written;
+
+  if (status != G_IO_STATUS_NORMAL)
+    {
+      /* FIXME handle errors. */
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+socks5_schedule_write (GabbleBytestreamSocks5 *self,
+                       const gchar *msg,
+                       gsize len)
+{
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+
+  g_string_append_len (priv->write_buffer, msg, len);
+
+  if (!priv->write_watch)
+    priv->write_watch = g_io_add_watch (priv->io_channel, G_IO_OUT,
+        socks5_channel_writable_cb, self);
+}
+
+static gsize
+socks5_handle_received_data (GabbleBytestreamSocks5 *self,
+                             GString *string)
+{
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  gchar msg[47] = {'\0'};
+
+  switch (priv->socks5_state)
+    {
+      case SOCKS5_STATE_AUTH:
+        if (string->len < 2)
+          return 0;
+
+        /* FIXME: do proper checking */
+        g_assert (string->str[0] == SOCKS5_VERSION &&
+            string->str[1] == SOCKS5_STATUS_OK);
+
+        msg[0] = SOCKS5_VERSION;
+        msg[1] = SOCKS5_CMD_CONNECT;
+        msg[2] = SOCKS5_RESERVED;
+        msg[3] = SOCKS5_ATYP_DOMAIN;
+        /* Lenght of a hex SHA1 */
+        msg[4] = 40;
+        /* FIXME: send the domainname */
+        /* Port: 0 */
+        msg[45] = 0x00;
+        msg[46] = 0x00;
+
+        socks5_schedule_write (self, msg, 47);
+
+        priv->socks5_state = SOCKS5_STATE_CONNECT_REQUESTED;
+
+        return 2;
+
+      case SOCKS5_STATE_CONNECT_REQUESTED:
+        if (string->len < 2)
+          return 0;
+
+        /* FIXME: handle errors */
+        g_assert (string->str[0] == SOCKS5_VERSION &&
+            string->str[1] == SOCKS5_STATUS_OK);
+
+        priv->socks5_state = SOCKS5_STATE_CONNECTED;
+
+        _gabble_connection_acknowledge_set_iq (priv->conn, priv->msg_for_acknowledge_connection);
+
+        return 2;
+
+      case SOCKS5_STATE_CONNECTED:
+        g_signal_emit (G_OBJECT (self), signals[DATA_RECEIVED], 0, priv->peer_handle, string);
+
+        return string->len;
+
+      default:
+        /* FIXME: handle errors */
+        return string->len;
+    }
+}
+
+static gboolean 
+socks5_channel_readable_cb (GIOChannel *source, 
+                            GIOCondition condition,
+                            gpointer data) 
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (data);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  gsize available_length =
+    priv->read_buffer->allocated_len - priv->read_buffer->len - 1;
+  GIOStatus status;
+  gsize bytes_read;
+  gsize used_bytes;
+
+  if (available_length == 0)
+    {
+      g_string_set_size (priv->read_buffer, priv->read_buffer->len * 2);
+      available_length = priv->read_buffer->allocated_len - priv->read_buffer->len - 1;
+    }
+
+  status = g_io_channel_read_chars (source,
+      &priv->read_buffer->str [priv->read_buffer->len], available_length,
+      &bytes_read, NULL);
+
+  priv->read_buffer->len += bytes_read;
+  priv->read_buffer->str[priv->read_buffer->len] = '\0';
+
+  used_bytes = socks5_handle_received_data (self, priv->read_buffer);
+  g_string_erase (priv->read_buffer, 0, used_bytes);
+
+  return TRUE;
+}
+
+static gboolean 
+socks5_channel_error_cb (GIOChannel *source, 
+                         GIOCondition condition,
+                         gpointer data) 
+{
+  /* FIXME: handle errors */
+  g_critical ("I/O error\n");
+
+  return FALSE;
+}
+
+static gboolean
+socks5_connect (gpointer data)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (data);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  Streamhost* streamhost;
+  struct addrinfo req = {0};
+  struct addrinfo *address_list;
+  struct addrinfo *streamhost_address;
+  gint fd;
+  gint socket_flags;
+  gint res;
+  gchar msg[3];
+
+  if (priv->streamhosts)
+    {
+      streamhost = priv->streamhosts->data;
+    }
+  else
+    {
+      DEBUG ("No more streamhosts to streamhost, closing");
+      gabble_bytestream_socks5_close (GABBLE_BYTESTREAM_IFACE (self), NULL);
+
+      return FALSE;
+    }
+
+  DEBUG ("Trying streamhost %s on port %d", streamhost->host,
+      streamhost->port);
+
+  req.ai_family = AF_UNSPEC;
+  req.ai_socktype = SOCK_STREAM;
+  req.ai_protocol = IPPROTO_TCP;
+
+  if (getaddrinfo (streamhost->host, NULL, &req, &address_list) != 0)
+    {
+      DEBUG ("getaddrinfo on %s failed", streamhost->host);
+      socks5_connect_next (self);
+
+      return FALSE;
+    }
+
+  fd = -1;
+  streamhost_address = address_list;
+
+  while (fd < 0 && streamhost_address) 
+    {
+      ((struct sockaddr_in *) streamhost_address->ai_addr)->sin_port =
+        htons (streamhost->port);
+
+      fd = socket (streamhost_address->ai_family,
+          streamhost_address->ai_socktype, streamhost_address->ai_protocol);
+
+      if (fd >= 0)
+        break;
+
+      streamhost_address = streamhost_address->ai_next;
+    }
+
+
+  if (fd < 0)
+    {
+      gabble_bytestream_socks5_close (GABBLE_BYTESTREAM_IFACE (self), NULL);
+      freeaddrinfo (address_list);
+
+      return FALSE;
+    }
+
+  /* Set non-blocking */
+  socket_flags = fcntl (fd, F_GETFL, 0);
+  fcntl (fd, F_SETFL, socket_flags | O_NONBLOCK);
+  
+  res = connect (fd, (struct sockaddr*)streamhost_address->ai_addr, streamhost_address->ai_addrlen);
+
+  freeaddrinfo (address_list);
+
+  if (res < 0 && errno != EINPROGRESS)
+    {
+      close (fd);
+      socks5_connect_next (self);
+
+      return FALSE;
+    }
+
+  priv->io_channel = g_io_channel_unix_new (fd);
+
+  g_io_channel_set_encoding (priv->io_channel, NULL, NULL);
+  g_io_channel_set_buffered (priv->io_channel, FALSE);
+  g_io_channel_set_close_on_unref (priv->io_channel, TRUE);
+
+  priv->read_watch = g_io_add_watch(priv->io_channel, G_IO_IN,
+      socks5_channel_readable_cb, self);
+  priv->error_watch = g_io_add_watch(priv->io_channel, G_IO_HUP | G_IO_ERR,
+      socks5_channel_error_cb, self);
+
+  g_assert (priv->write_buffer == NULL);
+  priv->write_buffer = g_string_new ("");
+
+  g_assert (priv->read_buffer == NULL);
+  priv->read_buffer = g_string_sized_new (4096);
+
+  priv->socks5_state = SOCKS5_STATE_AUTH;
+
+  msg[0] = SOCKS5_VERSION;
+  /* Number of auth methods we are offering */
+  msg[1] = 1;
+  msg[2] = SOCKS5_AUTH_NONE;
+
+  socks5_schedule_write (self, msg, 3);
+
+  return FALSE;
+}
+
+static void
+socks5_connect_next (GabbleBytestreamSocks5 *self)
+{
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+
+  g_assert (priv->streamhosts != NULL);
+
+  /* FIXME: free the streamhost */
+  priv->streamhosts = g_slist_delete_link (priv->streamhosts, priv->streamhosts);
+
+  socks5_connect (self);
+}
+
+/**
+ * gabble_bytestream_socks5_add_streamhost
+ *
+ * Adds the streamhost as a candidate for connection.
+ */
+void
+gabble_bytestream_socks5_add_streamhost (GabbleBytestreamSocks5 *self,
+                                         LmMessageNode *streamhost_node)
+{
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  const gchar *zeroconf;
+  const gchar *jid;
+  const gchar *host;
+  const gchar *port;
+  guint numeric_port;
+  Streamhost *streamhost;
+
+  g_return_if_fail (strcmp (streamhost_node->name, "streamhost") == 0);
+
+  zeroconf = lm_message_node_get_attribute (streamhost_node, "zeroconf");
+  if (zeroconf != NULL)
+    {
+      DEBUG ("zeroconf streamhosts are not supported");
+      return;
+    }
+
+  jid = lm_message_node_get_attribute (streamhost_node, "jid");
+  if (jid == NULL)
+    {
+      DEBUG ("streamhost doesn't contain a JID");
+      return;
+    }
+
+  host = lm_message_node_get_attribute (streamhost_node, "host");
+  if (host == NULL)
+    {
+      DEBUG ("streamhost doesn't contain a host");
+      return;
+    }
+
+  port = lm_message_node_get_attribute (streamhost_node, "port");
+  if (port == NULL)
+    {
+      DEBUG ("streamhost doesn't contain a port");
+      return;
+    }
+
+  numeric_port = strtoul (port, NULL, 10);
+  if (numeric_port <= 0)
+    {
+      DEBUG ("streamhost contain an invalid port: %s", port);
+      return;
+    }
+
+  DEBUG ("streamhost with jid %s, host %s and port %d added", jid, host,
+      numeric_port);
+
+  streamhost = g_new0 (Streamhost, 1);
+  streamhost->jid = g_strdup (jid);
+  streamhost->host = g_strdup (host);
+  streamhost->port = numeric_port;
+
+  priv->streamhosts = g_slist_append (priv->streamhosts, streamhost);
+}
+
+/**
+ * gabble_bytestream_socks5_connect_to_streamhost
+ *
+ * Try to connect to a streamhost.
+ */
+void
+gabble_bytestream_socks5_connect_to_streamhost (GabbleBytestreamSocks5 *self,
+                                                LmMessage *msg)
+
+{
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+
+  priv->msg_for_acknowledge_connection = lm_message_ref (msg);
+
+  g_idle_add(socks5_connect, self);
+}
+
+/*
+ * gabble_bytestream_socks5_send
+ *
+ * Implements gabble_bytestream_iface_send on GabbleBytestreamIface
+ */
+static gboolean
+gabble_bytestream_socks5_send (GabbleBytestreamIface *iface,
+                               guint len,
+                               const gchar *str)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+
+  if (priv->bytestream_state != GABBLE_BYTESTREAM_STATE_OPEN)
+    {
+      DEBUG ("can't send data through a not open bytestream (state: %d)",
+          priv->bytestream_state);
+      return FALSE;
+    }
+
+  socks5_schedule_write (self, str, len);
+
+  return TRUE;
+}
+
+/*
+ * gabble_bytestream_socks5_accept
+ *
+ * Implements gabble_bytestream_iface_accept on GabbleBytestreamIface
+ */
+static void
+gabble_bytestream_socks5_accept (GabbleBytestreamIface *iface,
+                                 GabbleBytestreamAugmentSiAcceptReply func,
+                                 gpointer user_data)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  LmMessage *msg;
+  LmMessageNode *si;
+
+  if (priv->bytestream_state != GABBLE_BYTESTREAM_STATE_LOCAL_PENDING)
+    {
+      /* The stream was previoulsy or automatically accepted */
+      return;
+    }
+
+  msg = gabble_bytestream_factory_make_accept_iq (priv->peer_jid,
+      priv->stream_init_id, NS_BYTESTREAMS);
+  si = lm_message_node_get_child_with_namespace (msg->node, "si", NS_SI);
+  g_assert (si != NULL);
+
+  if (func != NULL)
+    {
+      /* let the caller add his profile specific data */
+      func (si, user_data);
+    }
+
+  if (_gabble_connection_send (priv->conn, msg, NULL))
+    {
+      DEBUG ("stream %s with %s is now accepted", priv->stream_id,
+          priv->peer_jid);
+      g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_ACCEPTED, NULL);
+    }
+
+  lm_message_unref (msg);
+}
+
+static void
+gabble_bytestream_socks5_decline (GabbleBytestreamSocks5 *self,
+                               GError *error)
+{
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  LmMessage *msg;
+
+  g_return_if_fail (priv->bytestream_state == GABBLE_BYTESTREAM_STATE_LOCAL_PENDING);
+
+  msg = lm_message_build (priv->peer_jid, LM_MESSAGE_TYPE_IQ,
+      '@', "type", "error",
+      '@', "id", priv->stream_init_id,
+      NULL);
+
+  if (error != NULL && error->domain == GABBLE_XMPP_ERROR)
+    {
+      gabble_xmpp_error_to_node (error->code, msg->node, error->message);
+    }
+  else
+    {
+      gabble_xmpp_error_to_node (XMPP_ERROR_FORBIDDEN, msg->node,
+          "Offer Declined");
+    }
+
+  _gabble_connection_send (priv->conn, msg, NULL);
+
+  lm_message_unref (msg);
+
+  g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL);
+}
+
+/*
+ * gabble_bytestream_socks5_close
+ *
+ * Implements gabble_bytestream_iface_close on GabbleBytestreamIface
+ */
+static void
+gabble_bytestream_socks5_close (GabbleBytestreamIface *iface,
+                                GError *error)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+
+  if (priv->bytestream_state == GABBLE_BYTESTREAM_STATE_CLOSED)
+     /* bytestream already closed, do nothing */
+     return;
+
+  if (priv->bytestream_state == GABBLE_BYTESTREAM_STATE_LOCAL_PENDING)
+    {
+      /* Stream was created using SI so we decline the request */
+      gabble_bytestream_socks5_decline (self, error);
+    }
+  else
+    {
+      LmMessage *msg;
+
+      DEBUG ("send Socks5 close stanza");
+
+      if (priv->io_channel)
+        {
+          if (priv->read_watch != 0)
+            g_source_remove (priv->read_watch);
+          if (priv->write_watch != 0)
+            g_source_remove (priv->write_watch);
+          if (priv->error_watch != 0)
+            g_source_remove (priv->error_watch);
+          g_io_channel_unref (priv->io_channel);
+          priv->io_channel = NULL;
+        }
+
+      msg = lm_message_build (priv->peer_jid, LM_MESSAGE_TYPE_IQ,
+          '@', "type", "set",
+          '(', "close", "",
+            '@', "xmlns", NS_BYTESTREAMS,
+            '@', "sid", priv->stream_id,
+          ')', NULL);
+
+      /* We don't really care about the answer as the bytestream
+       * is closed anyway. */
+      _gabble_connection_send_with_reply (priv->conn, msg,
+          NULL, NULL, NULL, NULL);
+
+      lm_message_unref (msg);
+
+      g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL);
+    }
+}
+
+static LmHandlerResult
+socks5_init_reply_cb (GabbleConnection *conn,
+                   LmMessage *sent_msg,
+                   LmMessage *reply_msg,
+                   GObject *obj,
+                   gpointer user_data)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (obj);
+
+  if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_RESULT)
+    {
+      /* yeah, stream initiated */
+      DEBUG ("Socks5 stream initiated");
+      g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_OPEN, NULL);
+    }
+  else
+    {
+      DEBUG ("error during Socks5 initiation");
+      g_object_set (self, "state", GABBLE_BYTESTREAM_STATE_CLOSED, NULL);
+    }
+
+  return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+/*
+ * gabble_bytestream_socks5_initiate
+ *
+ * Implements gabble_bytestream_iface_initiate on GabbleBytestreamIface
+ */
+static gboolean
+gabble_bytestream_socks5_initiate (GabbleBytestreamIface *iface)
+{
+  GabbleBytestreamSocks5 *self = GABBLE_BYTESTREAM_SOCKS5 (iface);
+  GabbleBytestreamSocks5Private *priv = GABBLE_BYTESTREAM_SOCKS5_GET_PRIVATE (self);
+  LmMessage *msg;
+  //gchar *block_size;
+
+  if (priv->bytestream_state != GABBLE_BYTESTREAM_STATE_INITIATING)
+    {
+      DEBUG ("bytestream is not is the initiating state (state %d)",
+          priv->bytestream_state);
+      return FALSE;
+    }
+
+  /* FIXME: send a real address and port. */
+  msg = lm_message_build (priv->peer_jid, LM_MESSAGE_TYPE_IQ,
+      '@', "type", "set",
+      '(', "query", "",
+        '@', "xmlns", NS_BYTESTREAMS,
+        '@', "sid", priv->stream_id,
+        '@', "mode", "tcp",
+        '(', "streamhost", "",
+          '@', "jid", priv->peer_jid,
+          '@', "host", "127.0.0.1",
+          '@', "port", "5086",
+        ')',
+      ')', NULL);
+
+  if (!_gabble_connection_send_with_reply (priv->conn, msg,
+      socks5_init_reply_cb, G_OBJECT (self), NULL, NULL))
+    {
+      DEBUG ("Error when sending Socks5 init stanza");
+
+      lm_message_unref (msg);
+      return FALSE;
+    }
+
+  lm_message_unref (msg);
+
+  return TRUE;
+}
+
+static void
+bytestream_iface_init (gpointer g_iface,
+                       gpointer iface_data)
+{
+  GabbleBytestreamIfaceClass *klass = (GabbleBytestreamIfaceClass *) g_iface;
+
+  klass->initiate = gabble_bytestream_socks5_initiate;
+  klass->send = gabble_bytestream_socks5_send;
+  klass->close = gabble_bytestream_socks5_close;
+  klass->accept = gabble_bytestream_socks5_accept;
+}
diff --git a/src/bytestream-socks5.h b/src/bytestream-socks5.h
new file mode 100644
index 0000000..ef3b186
--- /dev/null
+++ b/src/bytestream-socks5.h
@@ -0,0 +1,75 @@
+/*
+ * bytestream-socks5.h - Header for GabbleBytestreamSocks5
+ * 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_BYTESTREAM_SOCKS5_H__
+#define __GABBLE_BYTESTREAM_SOCKS5_H__
+
+#include <stdlib.h>
+
+#include <glib-object.h>
+#include <loudmouth/loudmouth.h>
+
+#include <telepathy-glib/base-connection.h>
+
+#include "error.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleBytestreamSocks5 GabbleBytestreamSocks5;
+typedef struct _GabbleBytestreamSocks5Class GabbleBytestreamSocks5Class;
+typedef struct _GabbleBytestreamSocks5Private GabbleBytestreamSocks5Private;
+
+struct _GabbleBytestreamSocks5Class {
+  GObjectClass parent_class;
+};
+
+struct _GabbleBytestreamSocks5 {
+  GObject parent;
+
+  GabbleBytestreamSocks5Private *priv;
+};
+
+GType gabble_bytestream_socks5_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_BYTESTREAM_SOCKS5 \
+  (gabble_bytestream_socks5_get_type ())
+#define GABBLE_BYTESTREAM_SOCKS5(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_BYTESTREAM_SOCKS5,\
+                              GabbleBytestreamSocks5))
+#define GABBLE_BYTESTREAM_SOCKS5_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_BYTESTREAM_SOCKS5,\
+                           GabbleBytestreamSocks5Class))
+#define GABBLE_IS_BYTESTREAM_SOCKS5(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_BYTESTREAM_SOCKS5))
+#define GABBLE_IS_BYTESTREAM_SOCKS5_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_BYTESTREAM_SOCKS5))
+#define GABBLE_BYTESTREAM_SOCKS5_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_BYTESTREAM_SOCKS5,\
+                              GabbleBytestreamSocks5Class))
+
+void gabble_bytestream_socks5_add_streamhost (GabbleBytestreamSocks5 *socks5,
+    LmMessageNode *streamhost_node);
+
+void gabble_bytestream_socks5_connect_to_streamhost (GabbleBytestreamSocks5 *socks5,
+    LmMessage *msg);
+
+G_END_DECLS
+
+#endif /* #ifndef __GABBLE_BYTESTREAM_SOCKS5_H__ */
diff --git a/src/namespaces.h b/src/namespaces.h
index ba7a920..cb7e832 100644
--- a/src/namespaces.h
+++ b/src/namespaces.h
@@ -73,5 +73,6 @@
 #define NS_X_DELAY              "jabber:x:delay"
 #define NS_X_CONFERENCE         "jabber:x:conference"
 #define NS_XMPP_STANZAS         "urn:ietf:params:xml:ns:xmpp-stanzas"
+#define NS_BYTESTREAMS          "http://jabber.org/protocol/bytestreams"
 
 #endif /* __GABBLE_NAMESPACES__H__ */
-- 
1.5.6.5




More information about the Telepathy-commits mailing list