[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