[telepathy-gabble/master] rename file-transfer-channel.[ch] to ft-channel.[ch]
Guillaume Desmottes
guillaume.desmottes at collabora.co.uk
Mon Mar 30 05:14:00 PDT 2009
---
src/Makefile.am | 4 +-
src/file-transfer-channel.c | 1666 -------------------------------------------
src/file-transfer-channel.h | 78 --
src/ft-channel.c | 1666 +++++++++++++++++++++++++++++++++++++++++++
src/ft-channel.h | 78 ++
src/ft-manager.c | 2 +-
6 files changed, 1747 insertions(+), 1747 deletions(-)
delete mode 100644 src/file-transfer-channel.c
delete mode 100644 src/file-transfer-channel.h
create mode 100644 src/ft-channel.c
create mode 100644 src/ft-channel.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 49d5810..195d687 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -49,8 +49,8 @@ libgabble_convenience_la_SOURCES = \
disco.c \
error.c \
error.h \
- file-transfer-channel.c \
- file-transfer-channel.h \
+ ft-channel.c \
+ ft-channel.h \
ft-manager.c \
ft-manager.h \
gabble.c \
diff --git a/src/file-transfer-channel.c b/src/file-transfer-channel.c
deleted file mode 100644
index ee9516c..0000000
--- a/src/file-transfer-channel.c
+++ /dev/null
@@ -1,1666 +0,0 @@
-/*
- * file-transfer-channel.c - Source for GabbleFileTransferChannel
- * Copyright (C) 2009 Collabora Ltd.
- * @author: Guillaume Desmottes <guillaume.desmottes at collabora.co.uk>
- *
- * 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 <glib/gstdio.h>
-#include <dbus/dbus-glib.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#define DEBUG_FLAG GABBLE_DEBUG_FT
-#include "debug.h"
-
-#include <loudmouth/loudmouth.h>
-
-#include <gibber/gibber-listener.h>
-#include <gibber/gibber-transport.h>
-
-#include "bytestream-factory.h"
-#include "connection.h"
-#include "file-transfer-channel.h"
-#include "gabble-signals-marshal.h"
-#include "namespaces.h"
-#include "presence-cache.h"
-#include "util.h"
-
-#include <telepathy-glib/channel-iface.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/gtypes.h>
-#include <telepathy-glib/svc-generic.h>
-#include <telepathy-glib/svc-channel.h>
-
-static void channel_iface_init (gpointer g_iface, gpointer iface_data);
-static void file_transfer_iface_init (gpointer g_iface, gpointer iface_data);
-
-G_DEFINE_TYPE_WITH_CODE (GabbleFileTransferChannel, gabble_file_transfer_channel,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
- tp_dbus_properties_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL);
- G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_FILE_TRANSFER,
- file_transfer_iface_init);
-);
-
-#define CHECK_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0')
-
-#define GABBLE_UNDEFINED_FILE_SIZE G_MAXUINT64
-
-static const char *gabble_file_transfer_channel_interfaces[] = { NULL };
-
-/* properties */
-enum
-{
- PROP_OBJECT_PATH = 1,
-
- /* org.freedesktop.Telepathy.Channel D-Bus properties */
- PROP_CHANNEL_TYPE,
- PROP_INTERFACES,
- PROP_HANDLE,
- PROP_TARGET_ID,
- PROP_HANDLE_TYPE,
- PROP_REQUESTED,
- PROP_INITIATOR_HANDLE,
- PROP_INITIATOR_ID,
-
- PROP_CHANNEL_DESTROYED,
- PROP_CHANNEL_PROPERTIES,
-
- /* org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties */
- PROP_STATE,
- PROP_CONTENT_TYPE,
- PROP_FILENAME,
- PROP_SIZE,
- PROP_CONTENT_HASH_TYPE,
- PROP_CONTENT_HASH,
- PROP_DESCRIPTION,
- PROP_DATE,
- PROP_AVAILABLE_SOCKET_TYPES,
- PROP_TRANSFERRED_BYTES,
- PROP_INITIAL_OFFSET,
-
- PROP_CONNECTION,
- PROP_BYTESTREAM,
- LAST_PROPERTY
-};
-
-/* private structure */
-struct _GabbleFileTransferChannelPrivate {
- gboolean dispose_has_run;
- gboolean closed;
- gchar *object_path;
- TpHandle handle;
- GabbleConnection *connection;
- GTimeVal last_transferred_bytes_emitted;
- guint progress_timer;
- gchar *socket_path;
- TpHandle initiator;
- gboolean remote_accepted;
-
- GabbleBytestreamIface *bytestream;
- GibberListener *listener;
- GibberTransport *transport;
-
- /* properties */
- TpFileTransferState state;
- gchar *content_type;
- gchar *filename;
- guint64 size;
- TpFileHashType content_hash_type;
- gchar *content_hash;
- gchar *description;
- GHashTable *available_socket_types;
- guint64 transferred_bytes;
- guint64 initial_offset;
- guint64 date;
-};
-
-static void set_bytestream (GabbleFileTransferChannel *self,
- GabbleBytestreamIface *bytestream);
-
-static void
-gabble_file_transfer_channel_do_close (GabbleFileTransferChannel *self)
-{
- if (self->priv->closed)
- return;
-
- DEBUG ("Emitting closed signal for %s", self->priv->object_path);
- tp_svc_channel_emit_closed (self);
- self->priv->closed = TRUE;
-}
-
-static void
-gabble_file_transfer_channel_init (GabbleFileTransferChannel *obj)
-{
- obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
- GABBLE_TYPE_FILE_TRANSFER_CHANNEL, GabbleFileTransferChannelPrivate);
-
- /* allocate any data required by the object here */
- obj->priv->object_path = NULL;
- obj->priv->connection = NULL;
-}
-
-static void gabble_file_transfer_channel_set_state (
- TpSvcChannelTypeFileTransfer *iface, TpFileTransferState state,
- TpFileTransferStateChangeReason reason);
-
-static void
-gabble_file_transfer_channel_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
- TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->connection;
-
- switch (property_id)
- {
- case PROP_OBJECT_PATH:
- g_value_set_string (value, self->priv->object_path);
- break;
- case PROP_CHANNEL_TYPE:
- g_value_set_static_string (value,
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER);
- break;
- case PROP_HANDLE_TYPE:
- g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
- break;
- case PROP_TARGET_ID:
- {
- TpHandleRepoIface *repo = tp_base_connection_get_handles (base_conn,
- TP_HANDLE_TYPE_CONTACT);
-
- g_value_set_string (value, tp_handle_inspect (repo,
- self->priv->handle));
- }
- break;
- case PROP_HANDLE:
- g_value_set_uint (value, self->priv->handle);
- break;
- case PROP_REQUESTED:
- g_value_set_boolean (value, (self->priv->initiator ==
- base_conn->self_handle));
- break;
- case PROP_INITIATOR_HANDLE:
- g_value_set_uint (value, self->priv->initiator);
- break;
- case PROP_INITIATOR_ID:
- {
- TpHandleRepoIface *repo = tp_base_connection_get_handles (
- base_conn, TP_HANDLE_TYPE_CONTACT);
-
- g_assert (self->priv->initiator != 0);
- g_value_set_string (value,
- tp_handle_inspect (repo, self->priv->initiator));
- }
- break;
- case PROP_CONNECTION:
- g_value_set_object (value, self->priv->connection);
- break;
- case PROP_INTERFACES:
- g_value_set_boxed (value, gabble_file_transfer_channel_interfaces);
- break;
- case PROP_STATE:
- g_value_set_uint (value, self->priv->state);
- break;
- case PROP_CONTENT_TYPE:
- g_value_set_string (value, self->priv->content_type);
- break;
- case PROP_FILENAME:
- g_value_set_string (value, self->priv->filename);
- break;
- case PROP_SIZE:
- g_value_set_uint64 (value, self->priv->size);
- break;
- case PROP_CONTENT_HASH_TYPE:
- g_value_set_uint (value, self->priv->content_hash_type);
- break;
- case PROP_CONTENT_HASH:
- g_value_set_string (value, self->priv->content_hash);
- break;
- case PROP_DESCRIPTION:
- g_value_set_string (value, self->priv->description);
- break;
- case PROP_AVAILABLE_SOCKET_TYPES:
- g_value_set_boxed (value, self->priv->available_socket_types);
- break;
- case PROP_TRANSFERRED_BYTES:
- g_value_set_uint64 (value, self->priv->transferred_bytes);
- break;
- case PROP_INITIAL_OFFSET:
- g_value_set_uint64 (value, self->priv->initial_offset);
- break;
- case PROP_DATE:
- g_value_set_uint64 (value, self->priv->date);
- break;
- case PROP_CHANNEL_DESTROYED:
- g_value_set_boolean (value, self->priv->closed);
- break;
- case PROP_CHANNEL_PROPERTIES:
- g_value_take_boxed (value,
- tp_dbus_properties_mixin_make_properties_hash (object,
- TP_IFACE_CHANNEL, "ChannelType",
- TP_IFACE_CHANNEL, "Interfaces",
- TP_IFACE_CHANNEL, "TargetHandle",
- TP_IFACE_CHANNEL, "TargetID",
- TP_IFACE_CHANNEL, "TargetHandleType",
- TP_IFACE_CHANNEL, "Requested",
- TP_IFACE_CHANNEL, "InitiatorHandle",
- TP_IFACE_CHANNEL, "InitiatorID",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "State",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentType",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Filename",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Size",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHashType",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHash",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Description",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Date",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "AvailableSocketTypes",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "TransferredBytes",
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "InitialOffset",
- NULL));
- break;
- case PROP_BYTESTREAM:
- g_value_set_object (value, self->priv->bytestream);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-gabble_file_transfer_channel_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
-
- switch (property_id)
- {
- case PROP_OBJECT_PATH:
- g_free (self->priv->object_path);
- self->priv->object_path = g_value_dup_string (value);
- break;
- case PROP_HANDLE:
- self->priv->handle = g_value_get_uint (value);
- break;
- case PROP_CONNECTION:
- self->priv->connection = g_value_get_object (value);
- break;
- case PROP_HANDLE_TYPE:
- g_assert (g_value_get_uint (value) == 0
- || g_value_get_uint (value) == TP_HANDLE_TYPE_CONTACT);
- break;
- case PROP_CHANNEL_TYPE:
- /* these properties are writable in the interface, but not actually
- * meaningfully changeable on this channel, so we do nothing */
- break;
- case PROP_STATE:
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (object),
- g_value_get_uint (value),
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
- break;
- case PROP_CONTENT_TYPE:
- g_free (self->priv->content_type);
- self->priv->content_type = g_value_dup_string (value);
- break;
- case PROP_FILENAME:
- g_free (self->priv->filename);
- self->priv->filename = g_value_dup_string (value);
- break;
- case PROP_SIZE:
- self->priv->size = g_value_get_uint64 (value);
- break;
- case PROP_CONTENT_HASH_TYPE:
- self->priv->content_hash_type = g_value_get_uint (value);
- break;
- case PROP_CONTENT_HASH:
- g_free (self->priv->content_hash);
- self->priv->content_hash = g_value_dup_string (value);
- break;
- case PROP_DESCRIPTION:
- g_free (self->priv->description);
- self->priv->description = g_value_dup_string (value);
- break;
- case PROP_INITIATOR_HANDLE:
- self->priv->initiator = g_value_get_uint (value);
- g_assert (self->priv->initiator != 0);
- break;
- case PROP_DATE:
- self->priv->date = g_value_get_uint64 (value);
- break;
- case PROP_INITIAL_OFFSET:
- self->priv->initial_offset = g_value_get_uint64 (value);
- break;
- case PROP_BYTESTREAM:
- set_bytestream (self,
- GABBLE_BYTESTREAM_IFACE (g_value_get_object (value)));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-free_array (GArray *array)
-{
- g_array_free (array, TRUE);
-}
-
-static void
-connection_presences_updated_cb (GabblePresenceCache *cache,
- GArray *handles,
- GabbleFileTransferChannel *self)
-{
- guint i;
-
- for (i = 0; i < handles->len ; i++)
- {
- TpHandle handle;
-
- handle = g_array_index (handles, TpHandle, i);
- if (handle == self->priv->handle)
- {
- GabblePresence *presence;
-
- presence = gabble_presence_cache_get (
- self->priv->connection->presence_cache, handle);
-
- if (presence == NULL || presence->status < GABBLE_PRESENCE_XA)
- {
- /* Contact is disconnected */
- if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
- self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
- {
- DEBUG ("peer disconnected. FileTransfer is cancelled");
-
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_CANCELLED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
- }
- }
- }
- }
-}
-
-static GObject *
-gabble_file_transfer_channel_constructor (GType type,
- guint n_props,
- GObjectConstructParam *props)
-{
- GObject *obj;
- GabbleFileTransferChannel *self;
- DBusGConnection *bus;
- TpBaseConnection *base_conn;
- TpHandleRepoIface *contact_repo;
- GArray *unix_access;
- TpSocketAccessControl access_control;
-
- /* Parent constructor chain */
- obj = G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->
- constructor (type, n_props, props);
- self = GABBLE_FILE_TRANSFER_CHANNEL (obj);
- base_conn = TP_BASE_CONNECTION (self->priv->connection);
-
- /* Ref the target and initiator handles; they can't be reffed in
- * _set_property as we may not have the TpConnection at that point.
- */
- contact_repo = tp_base_connection_get_handles (base_conn,
- TP_HANDLE_TYPE_CONTACT);
- tp_handle_ref (contact_repo, self->priv->handle);
- tp_handle_ref (contact_repo, self->priv->initiator);
-
- self->priv->object_path = g_strdup_printf ("%s/FileTransferChannel/%p",
- base_conn->object_path, self);
-
- /* Connect to the bus */
- bus = tp_get_bus ();
- dbus_g_connection_register_g_object (bus, self->priv->object_path, obj);
-
- /* Initialise the available socket types hash table */
- self->priv->available_socket_types = g_hash_table_new_full (g_direct_hash,
- g_direct_equal, NULL, (GDestroyNotify) free_array);
-
- /* Socket_Address_Type_Unix */
- unix_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
- 1);
- access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
- g_array_append_val (unix_access, access_control);
- g_hash_table_insert (self->priv->available_socket_types,
- GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), unix_access);
-
- gabble_signal_connect_weak (self->priv->connection->presence_cache,
- "presences-updated", G_CALLBACK (connection_presences_updated_cb), obj);
-
- DEBUG ("New FT channel created: %s (contact: %s, initiator: %s, "
- "file: \"%s\", size: %" G_GUINT64_FORMAT ")",
- self->priv->object_path,
- tp_handle_inspect (contact_repo, self->priv->handle),
- tp_handle_inspect (contact_repo, self->priv->initiator),
- self->priv->filename, self->priv->size);
-
- return obj;
-}
-
-static void gabble_file_transfer_channel_dispose (GObject *object);
-static void gabble_file_transfer_channel_finalize (GObject *object);
-
-static void
-gabble_file_transfer_channel_class_init (
- GabbleFileTransferChannelClass *gabble_file_transfer_channel_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (
- gabble_file_transfer_channel_class);
- GParamSpec *param_spec;
-
- static TpDBusPropertiesMixinPropImpl channel_props[] = {
- { "TargetHandleType", "handle-type", NULL },
- { "TargetHandle", "handle", NULL },
- { "TargetID", "target-id", NULL },
- { "ChannelType", "channel-type", NULL },
- { "Interfaces", "interfaces", NULL },
- { "Requested", "requested", NULL },
- { "InitiatorHandle", "initiator-handle", NULL },
- { "InitiatorID", "initiator-id", NULL },
- { NULL }
- };
-
- static TpDBusPropertiesMixinPropImpl file_props[] = {
- { "State", "state", NULL },
- { "ContentType", "content-type", NULL },
- { "Filename", "filename", NULL },
- { "Size", "size", NULL },
- { "ContentHashType", "content-hash-type", NULL },
- { "ContentHash", "content-hash", NULL },
- { "Description", "description", NULL },
- { "AvailableSocketTypes", "available-socket-types", NULL },
- { "TransferredBytes", "transferred-bytes", NULL },
- { "InitialOffset", "initial-offset", NULL },
- { "Date", "date", NULL },
- { NULL }
- };
-
- static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
- { TP_IFACE_CHANNEL,
- tp_dbus_properties_mixin_getter_gobject_properties,
- NULL,
- channel_props
- },
- { TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
- tp_dbus_properties_mixin_getter_gobject_properties,
- NULL,
- file_props
- },
- { NULL }
- };
-
- g_type_class_add_private (gabble_file_transfer_channel_class,
- sizeof (GabbleFileTransferChannelPrivate));
-
- object_class->dispose = gabble_file_transfer_channel_dispose;
- object_class->finalize = gabble_file_transfer_channel_finalize;
-
- object_class->constructor = gabble_file_transfer_channel_constructor;
- object_class->get_property = gabble_file_transfer_channel_get_property;
- object_class->set_property = gabble_file_transfer_channel_set_property;
-
- g_object_class_override_property (object_class, PROP_OBJECT_PATH,
- "object-path");
- g_object_class_override_property (object_class, PROP_CHANNEL_TYPE,
- "channel-type");
- g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
- "handle-type");
- g_object_class_override_property (object_class, PROP_HANDLE, "handle");
- g_object_class_override_property (object_class, PROP_CHANNEL_DESTROYED,
- "channel-destroyed");
- g_object_class_override_property (object_class, PROP_CHANNEL_PROPERTIES,
- "channel-properties");
-
- param_spec = g_param_spec_string ("target-id", "Target JID",
- "The string obtained by inspecting this channel's handle",
- NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec);
-
- param_spec = g_param_spec_boolean ("requested", "Requested?",
- "True if this channel was requested by the local user",
- FALSE,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_REQUESTED, param_spec);
-
- param_spec = g_param_spec_uint ("initiator-handle", "Initiator's handle",
- "The contact who initiated the channel",
- 0, G_MAXUINT32, 0,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE,
- param_spec);
-
- param_spec = g_param_spec_string ("initiator-id", "Initiator's bare JID",
- "The string obtained by inspecting the initiator-handle",
- NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INITIATOR_ID,
- param_spec);
-
- param_spec = g_param_spec_object ("connection",
- "GabbleConnection object",
- "Gabble Connection that owns the"
- "connection for this IM channel",
- GABBLE_TYPE_CONNECTION,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
-
- param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
- "Additional Channel.Interface.* interfaces",
- G_TYPE_STRV,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
-
- param_spec = g_param_spec_uint (
- "state",
- "TpFileTransferState state",
- "State of the file transfer in this channel",
- 0,
- NUM_TP_FILE_TRANSFER_STATES,
- TP_FILE_TRANSFER_STATE_NONE,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_STATE, param_spec);
-
- param_spec = g_param_spec_string (
- "content-type",
- "gchar *content-type",
- "ContentType of the file",
- "application/octet-stream",
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
- param_spec);
-
- param_spec = g_param_spec_string (
- "filename",
- "gchar *filename",
- "Name of the file",
- "",
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_FILENAME, param_spec);
-
- param_spec = g_param_spec_uint64 (
- "size",
- "guint size",
- "Size of the file in bytes",
- 0,
- G_MAXUINT64,
- GABBLE_UNDEFINED_FILE_SIZE,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_SIZE, param_spec);
-
- param_spec = g_param_spec_uint (
- "content-hash-type",
- "TpFileHashType content-hash-type",
- "Hash type",
- 0,
- NUM_TP_FILE_HASH_TYPES,
- TP_FILE_HASH_TYPE_NONE,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CONTENT_HASH_TYPE,
- param_spec);
-
- param_spec = g_param_spec_string (
- "content-hash",
- "gchar *content-hash",
- "Hash of the file contents",
- "",
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CONTENT_HASH,
- param_spec);
-
- param_spec = g_param_spec_string (
- "description",
- "gchar *description",
- "Description of the file",
- "",
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_DESCRIPTION, param_spec);
-
- param_spec = g_param_spec_boxed (
- "available-socket-types",
- "GabbleSupportedSocketMap available-socket-types",
- "Available socket types",
- TP_HASH_TYPE_SUPPORTED_SOCKET_MAP,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_AVAILABLE_SOCKET_TYPES,
- param_spec);
-
- param_spec = g_param_spec_uint64 (
- "transferred-bytes",
- "guint64 transferred-bytes",
- "Bytes transferred",
- 0,
- G_MAXUINT64,
- 0,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_TRANSFERRED_BYTES,
- param_spec);
-
- param_spec = g_param_spec_uint64 (
- "initial-offset",
- "guint64 initial_offset",
- "Offset set at the beginning of the transfer",
- 0,
- G_MAXUINT64,
- 0,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INITIAL_OFFSET,
- param_spec);
-
- param_spec = g_param_spec_uint64 (
- "date",
- "Epoch time",
- "the last modification time of the file being transferred",
- 0,
- G_MAXUINT64,
- 0,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_DATE,
- param_spec);
-
- param_spec = g_param_spec_object (
- "bytestream",
- "Object implementing the GabbleBytestreamIface interface",
- "Bytestream object used to send the file",
- G_TYPE_OBJECT,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_BYTESTREAM,
- param_spec);
-
- gabble_file_transfer_channel_class->dbus_props_class.interfaces = \
- prop_interfaces;
- tp_dbus_properties_mixin_class_init (object_class,
- G_STRUCT_OFFSET (GabbleFileTransferChannelClass, dbus_props_class));
-}
-
-void
-gabble_file_transfer_channel_dispose (GObject *object)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (self->priv->connection);
- TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (base_conn,
- TP_HANDLE_TYPE_CONTACT);
-
- if (self->priv->dispose_has_run)
- return;
-
- self->priv->dispose_has_run = TRUE;
-
- tp_handle_unref (handle_repo, self->priv->handle);
- tp_handle_unref (handle_repo, self->priv->initiator);
-
- gabble_file_transfer_channel_do_close (self);
-
- if (self->priv->progress_timer != 0)
- {
- g_source_remove (self->priv->progress_timer);
- self->priv->progress_timer = 0;
- }
-
- if (self->priv->bytestream != NULL)
- {
- g_object_unref (self->priv->bytestream);
- self->priv->bytestream = NULL;
- }
-
- if (self->priv->listener != NULL)
- {
- g_object_unref (self->priv->listener);
- self->priv->listener = NULL;
- }
-
- if (self->priv->transport != NULL)
- {
- g_object_unref (self->priv->transport);
- self->priv->transport = NULL;
- }
-
- /* release any references held by the object here */
-
- if (G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->dispose)
- G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->dispose (object);
-}
-
-static void
-gabble_file_transfer_channel_finalize (GObject *object)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
-
- /* free any data held directly by the object here */
- g_free (self->priv->object_path);
- g_free (self->priv->filename);
- g_free (self->priv->socket_path);
- g_free (self->priv->content_type);
- g_free (self->priv->content_hash);
- g_free (self->priv->description);
- g_hash_table_destroy (self->priv->available_socket_types);
-
- G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->finalize (object);
-}
-
-static void
-close_bytestrean_and_transport (GabbleFileTransferChannel *self)
-{
- if (self->priv->bytestream != NULL)
- {
- gabble_bytestream_iface_close (self->priv->bytestream, NULL);
- g_object_unref (self->priv->bytestream);
- self->priv->bytestream = NULL;
- }
-
- if (self->priv->transport != NULL)
- {
- g_object_unref (self->priv->transport);
- self->priv->transport = NULL;
- }
-}
-
-/**
- * gabble_file_transfer_channel_close
- *
- * Implements DBus method Close
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-gabble_file_transfer_channel_close (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
-
- if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
- self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
- {
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (iface),
- TP_FILE_TRANSFER_STATE_CANCELLED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED);
-
- close_bytestrean_and_transport (self);
- }
-
- gabble_file_transfer_channel_do_close (GABBLE_FILE_TRANSFER_CHANNEL (iface));
- tp_svc_channel_return_from_close (context);
-}
-
-/**
- * gabble_file_transfer_channel_get_channel_type
- *
- * Implements DBus method GetChannelType
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-gabble_file_transfer_channel_get_channel_type (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- tp_svc_channel_return_from_get_channel_type (context,
- TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER);
-}
-
-/**
- * gabble_file_transfer_channel_get_handle
- *
- * Implements DBus method GetHandle
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-gabble_file_transfer_channel_get_handle (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
-
- tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT,
- self->priv->handle);
-}
-
-/**
- * gabble_file_transfer_channel_get_interfaces
- *
- * Implements DBus method GetInterfaces
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-gabble_file_transfer_channel_get_interfaces (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- tp_svc_channel_return_from_get_interfaces (context,
- gabble_file_transfer_channel_interfaces);
-}
-
-static void
-channel_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcChannelClass *klass = (TpSvcChannelClass *) g_iface;
-
-#define IMPLEMENT(x) tp_svc_channel_implement_##x (\
- klass, gabble_file_transfer_channel_##x)
- IMPLEMENT (close);
- IMPLEMENT (get_channel_type);
- IMPLEMENT (get_handle);
- IMPLEMENT (get_interfaces);
-#undef IMPLEMENT
-}
-
-static gboolean setup_local_socket (GabbleFileTransferChannel *self);
-
-static void
-gabble_file_transfer_channel_set_state (
- TpSvcChannelTypeFileTransfer *iface,
- TpFileTransferState state,
- TpFileTransferStateChangeReason reason)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
-
- if (self->priv->state == state)
- return;
-
- self->priv->state = state;
- tp_svc_channel_type_file_transfer_emit_file_transfer_state_changed (iface,
- state, reason);
-}
-
-static gboolean
-check_address_and_access_control (GabbleFileTransferChannel *self,
- TpSocketAddressType address_type,
- TpSocketAccessControl access_control,
- const GValue *access_control_param,
- GError **error)
-{
- GArray *access_arr;
- guint i;
-
- /* Do we support this AddressType? */
- access_arr = g_hash_table_lookup (self->priv->available_socket_types,
- GUINT_TO_POINTER (address_type));
- if (access_arr == NULL)
- {
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
- "AddressType %u is not implemented", address_type);
- return FALSE;
- }
-
- /* Do we support this AccessControl? */
- for (i = 0; i < access_arr->len; i++)
- {
- TpSocketAccessControl control;
-
- control = g_array_index (access_arr, TpSocketAccessControl, i);
- if (control == access_control)
- return TRUE;
- }
-
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
- "AccesControl %u is not implemented with AddressType %u",
- access_control, address_type);
-
- return FALSE;
-}
-
-static void
-bytestream_open (GabbleFileTransferChannel *self)
-{
- if (self->priv->socket_path != NULL)
- {
- /* ProvideFile has already been called. Channel is Open */
- tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
- self->priv->initial_offset);
-
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_OPEN,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
- }
- else
- {
- /* Client has to call ProvideFile to open the channel */
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_ACCEPTED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
- }
-}
-
-static void
-bytestream_closed (GabbleFileTransferChannel *self)
-{
- if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED)
- {
- TpBaseConnection *base_conn = (TpBaseConnection *) \
- self->priv->connection;
- gboolean receiver;
-
- receiver = (self->priv->initiator != base_conn->self_handle);
-
- /* Something did wrong */
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_CANCELLED,
- receiver ?
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR :
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR);
- }
-}
-
-static void
-bytestream_state_changed_cb (GabbleBytestreamIface *bytestream,
- GabbleBytestreamState state,
- gpointer user_data)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
-
- if (state == GABBLE_BYTESTREAM_STATE_OPEN)
- {
- bytestream_open (self);
- }
- else if (state == GABBLE_BYTESTREAM_STATE_CLOSED)
- {
- bytestream_closed (self);
- }
-}
-
-static void
-set_bytestream (GabbleFileTransferChannel *self,
- GabbleBytestreamIface *bytestream)
-{
- if (bytestream == NULL)
- return;
-
- self->priv->bytestream = g_object_ref (bytestream);
-
- gabble_signal_connect_weak (bytestream, "state-changed",
- G_CALLBACK (bytestream_state_changed_cb), G_OBJECT (self));
-}
-
-static void
-bytestream_negotiate_cb (GabbleBytestreamIface *bytestream,
- const gchar *stream_id,
- LmMessage *msg,
- GObject *object,
- gpointer user_data)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
-
- if (bytestream == NULL)
- {
- DEBUG ("receiver refused file offer");
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_CANCELLED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
- return;
- }
-
- DEBUG ("receiver accepted file offer");
-
- set_bytestream (self, bytestream);
-
- self->priv->remote_accepted = TRUE;
-}
-
-gboolean
-gabble_file_transfer_channel_offer_file (GabbleFileTransferChannel *self,
- GError **error)
-{
- GabblePresence *presence;
- gboolean result;
- LmMessage *msg;
- TpHandleRepoIface *contact_repo, *room_repo;
- LmMessageNode *si_node, *file_node, *desc_node;
- const gchar *jid;
- gchar *full_jid, *stream_id, *size_str;
-
- g_assert (!CHECK_STR_EMPTY (self->priv->filename));
- g_assert (self->priv->size != GABBLE_UNDEFINED_FILE_SIZE);
- g_assert (self->priv->bytestream == NULL);
-
- presence = gabble_presence_cache_get (self->priv->connection->presence_cache,
- self->priv->handle);
- if (presence == NULL)
- {
- DEBUG ("can't find contact's presence");
- if (error != NULL)
- g_set_error (error, TP_ERRORS, TP_ERROR_OFFLINE,
- "can't find contact's presence");
-
- return FALSE;
- }
-
- contact_repo = tp_base_connection_get_handles (
- (TpBaseConnection *) self->priv->connection, TP_HANDLE_TYPE_CONTACT);
- room_repo = tp_base_connection_get_handles (
- (TpBaseConnection *) self->priv->connection, TP_HANDLE_TYPE_ROOM);
-
- jid = tp_handle_inspect (contact_repo, self->priv->handle);
-
- if (gabble_get_room_handle_from_jid (room_repo, jid) == 0)
- {
- /* Not a MUC jid, need to get a resource */
- const gchar *resource;
-
- resource = gabble_presence_pick_resource_by_caps (presence,
- PRESENCE_CAP_SI_FILE_TRANSFER);
- if (resource == NULL)
- {
- DEBUG ("contact doesn't have file transfer capabilities");
- if (error != NULL)
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_CAPABLE,
- "contact doesn't have file transfer capabilities");
-
- return FALSE;
- }
-
- full_jid = g_strdup_printf ("%s/%s", jid, resource);
- }
- else
- {
- /* MUC jid, we already have the full jid */
- full_jid = g_strdup (jid);
- }
-
- DEBUG ("Offering file transfer to %s", full_jid);
-
- stream_id = gabble_bytestream_factory_generate_stream_id ();
-
- msg = gabble_bytestream_factory_make_stream_init_iq (full_jid,
- stream_id, NS_FILE_TRANSFER);
-
- si_node = lm_message_node_get_child_with_namespace (msg->node, "si", NS_SI);
- g_assert (si_node != NULL);
-
- size_str = g_strdup_printf ("%" G_GUINT64_FORMAT, self->priv->size);
-
- file_node = lm_message_node_add_child (si_node, "file", NULL);
- lm_message_node_set_attributes (file_node,
- "xmlns", NS_FILE_TRANSFER,
- "name", self->priv->filename,
- "size", size_str,
- "mime-type", self->priv->content_type,
- NULL);
-
- if (self->priv->content_hash != NULL)
- lm_message_node_set_attribute (file_node, "hash", self->priv->content_hash);
-
- if (self->priv->date != 0)
- {
- time_t t;
- struct tm *tm;
- char date_str[21];
-
- t = (time_t) self->priv->date;
- tm = gmtime (&t);
-
- strftime (date_str, sizeof (date_str), "%FT%H:%M:%SZ", tm);
-
- lm_message_node_set_attribute (file_node, "date", date_str);
- }
-
- /* TODO: support initial offset */
-
- desc_node = lm_message_node_add_child (file_node, "desc",
- self->priv->description);
-
- result = gabble_bytestream_factory_negotiate_stream (
- self->priv->connection->bytestream_factory, msg, stream_id,
- bytestream_negotiate_cb, self, G_OBJECT (self), error);
-
- lm_message_unref (msg);
- g_free (stream_id);
- g_free (full_jid);
- g_free (size_str);
-
- return result;
-}
-
-static void
-emit_progress_update (GabbleFileTransferChannel *self)
-{
- TpSvcChannelTypeFileTransfer *iface = \
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self);
-
- g_get_current_time (&self->priv->last_transferred_bytes_emitted);
-
- tp_svc_channel_type_file_transfer_emit_transferred_bytes_changed (
- iface, self->priv->transferred_bytes);
-
- if (self->priv->progress_timer != 0)
- {
- g_source_remove (self->priv->progress_timer);
- self->priv->progress_timer = 0;
- }
-}
-
-static gboolean
-emit_progress_update_cb (gpointer user_data)
-{
- GabbleFileTransferChannel *self = \
- GABBLE_FILE_TRANSFER_CHANNEL (user_data);
-
- emit_progress_update (self);
-
- return FALSE;
-}
-
-static void
-transferred_chunk (GabbleFileTransferChannel *self,
- guint64 count)
-{
- GTimeVal timeval;
- gint interval;
-
- self->priv->transferred_bytes += count;
-
- if (self->priv->transferred_bytes >= self->priv->size)
- {
- /* If the transfer has finished send an update right away */
- emit_progress_update (self);
- return;
- }
-
- if (self->priv->progress_timer != 0)
- {
- /* A progress update signal is already scheduled */
- return;
- }
-
- /* Only emit the TransferredBytes signal if it has been one second since its
- * last emission.
- */
- g_get_current_time (&timeval);
- interval = timeval.tv_sec -
- self->priv->last_transferred_bytes_emitted.tv_sec;
-
- if (interval > 1)
- {
- /* At least more then a second apart, emit right away */
- emit_progress_update (self);
- return;
- }
-
- /* Convert interval to milliseconds and calculate it more precisely */
- interval *= 1000;
-
- interval += (timeval.tv_usec -
- self->priv->last_transferred_bytes_emitted.tv_usec)/1000;
-
- /* Protect against clock skew, if the interval is negative the worst thing
- * that can happen is that we wait an extra second before emitting the signal
- */
- interval = ABS (interval);
-
- if (interval > 1000)
- emit_progress_update (self);
- else
- self->priv->progress_timer = g_timeout_add (1000 - interval,
- emit_progress_update_cb, self);
-}
-
-
-static void
-data_received_cb (GabbleBytestreamIface *stream,
- TpHandle sender,
- GString *data,
- gpointer user_data)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
- GError *error = NULL;
-
- g_assert (self->priv->transport != NULL);
-
- if (!gibber_transport_send (self->priv->transport, (const guint8 *) data->str,
- data->len, &error))
- {
- DEBUG ("sending to transport failed: %s", error->message);
- g_error_free (error);
-
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_CANCELLED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR);
- return;
- }
-
- DEBUG ("received %"G_GSIZE_FORMAT" bytes from bytestream. Writing to socket",
- data->len);
-
- transferred_chunk (self, (guint64) data->len);
-
- if (self->priv->transferred_bytes >= self->priv->size)
- {
- DEBUG ("Received all the file. Transfer is complete");
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_COMPLETED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
- return;
- }
-
- if (!gibber_transport_buffer_is_empty (self->priv->transport))
- {
- /* We don't want to send more data while the buffer isn't empty */
- DEBUG ("file transfer buffer isn't empty. Block the bytestream");
- gabble_bytestream_iface_block_reading (self->priv->bytestream, TRUE);
- }
-}
-
-/**
- * gabble_file_transfer_channel_accept_file
- *
- * Implements D-Bus method AcceptFile
- * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
- */
-static void
-gabble_file_transfer_channel_accept_file (TpSvcChannelTypeFileTransfer *iface,
- guint address_type,
- guint access_control,
- const GValue *access_control_param,
- guint64 offset,
- DBusGMethodInvocation *context)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
- GError *error = NULL;
- GValue out_address = { 0 };
-
- if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
- {
- g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "State is not pending; cannot accept file");
- dbus_g_method_return_error (context, error);
- return;
- }
-
- if (!check_address_and_access_control (self, address_type, access_control,
- access_control_param, &error))
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
-
- if (!setup_local_socket (self))
- {
- DEBUG ("Could not set up local socket");
- g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "Could not set up local socket");
- dbus_g_method_return_error (context, error);
- }
-
- DEBUG ("local socket %s", self->priv->socket_path);
-
- gabble_file_transfer_channel_set_state (iface,
- TP_FILE_TRANSFER_STATE_ACCEPTED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
-
- g_value_init (&out_address, G_TYPE_STRING);
- g_value_set_string (&out_address, self->priv->socket_path);
-
- tp_svc_channel_type_file_transfer_return_from_accept_file (context,
- &out_address);
-
- self->priv->initial_offset = 0;
-
- g_assert (self->priv->bytestream != NULL);
- gabble_signal_connect_weak (self->priv->bytestream, "data-received",
- G_CALLBACK (data_received_cb), G_OBJECT (self));
-
- /* channel state will change to open once the bytestream is open */
- /* TODO: set a function once we support resume */
-
- /* Block the bytestream while the user is not connected to the socket */
- gabble_bytestream_iface_block_reading (self->priv->bytestream, TRUE);
-
- gabble_bytestream_iface_accept (self->priv->bytestream, NULL, NULL);
-
- g_value_unset (&out_address);
-}
-
-/**
- * gabble_file_transfer_channel_provide_file
- *
- * Implements D-Bus method ProvideFile
- * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
- */
-static void
-gabble_file_transfer_channel_provide_file (
- TpSvcChannelTypeFileTransfer *iface,
- guint address_type,
- guint access_control,
- const GValue *access_control_param,
- DBusGMethodInvocation *context)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
- TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->connection;
- GValue out_address = { 0 };
- GError *error = NULL;
-
- if (self->priv->initiator != base_conn->self_handle)
- {
- g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "Channel is not an outgoing transfer");
- dbus_g_method_return_error (context, error);
- return;
- }
-
- if (self->priv->socket_path != NULL)
- {
- g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "ProvideFile has already been called for this channel");
- dbus_g_method_return_error (context, error);
- return;
- }
-
- if (!check_address_and_access_control (self, address_type, access_control,
- access_control_param, &error))
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
-
- if (!setup_local_socket (self))
- {
- DEBUG ("Could not set up local socket");
- g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "Could not set up local socket");
- dbus_g_method_return_error (context, error);
- }
-
- g_value_init (&out_address, G_TYPE_STRING);
- g_value_set_string (&out_address, self->priv->socket_path);
-
- if (self->priv->remote_accepted)
- {
- /* Remote already accepted the file. Channel is Open.
- * If not channel stay Pending. */
- tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
- self->priv->initial_offset);
-
- gabble_file_transfer_channel_set_state (iface,
- TP_FILE_TRANSFER_STATE_OPEN,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
- }
-
- tp_svc_channel_type_file_transfer_return_from_provide_file (context,
- &out_address);
-
- g_value_unset (&out_address);
-}
-
-static void
-file_transfer_iface_init (gpointer g_iface,
- gpointer iface_data)
-{
- TpSvcChannelTypeFileTransferClass *klass =
- (TpSvcChannelTypeFileTransferClass *) g_iface;
-
-#define IMPLEMENT(x) tp_svc_channel_type_file_transfer_implement_##x (\
- klass, gabble_file_transfer_channel_##x)
- IMPLEMENT (accept_file);
- IMPLEMENT (provide_file);
-#undef IMPLEMENT
-}
-
-static const gchar *
-get_local_unix_socket_path (GabbleFileTransferChannel *self)
-{
- gchar *path = NULL;
- gint32 random_int;
- gchar *random_str;
- struct stat buf;
-
- while (TRUE)
- {
- random_int = g_random_int_range (0, G_MAXINT32);
- random_str = g_strdup_printf ("tp-ft-%i", random_int);
- path = g_build_filename (g_get_tmp_dir (), random_str, NULL);
- g_free (random_str);
-
- if (g_stat (path, &buf) != 0)
- break;
-
- g_free (path);
- }
-
- if (self->priv->socket_path)
- g_free (self->priv->socket_path);
-
- self->priv->socket_path = path;
-
- return path;
-}
-
-/*
- * Data is available from the channel so we can send it.
- */
-static void
-transport_handler (GibberTransport *transport,
- GibberBuffer *data,
- gpointer user_data)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
-
- DEBUG ("Data available, writing a %"G_GSIZE_FORMAT" bytes chunk",
- data->length);
-
- if (!gabble_bytestream_iface_send (self->priv->bytestream, data->length,
- (const gchar *) data->data))
- {
- DEBUG ("Sending failed. Closing the bytestream");
- close_bytestrean_and_transport (self);
- return;
- }
-
- transferred_chunk (self, (guint64) data->length);
-
- if (self->priv->transferred_bytes >= self->priv->size)
- {
- DEBUG ("All the file has been sent. Closing the bytestream");
-
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_COMPLETED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
-
- gabble_bytestream_iface_close (self->priv->bytestream, NULL);
- return;
- }
-}
-
-static void
-bytestream_write_blocked_cb (GabbleBytestreamIface *bytestream,
- gboolean blocked,
- GabbleFileTransferChannel *self)
-{
- if (blocked)
- {
- DEBUG ("bytestream blocked, stop to read data from FT socket");
- }
- else
- {
- DEBUG ("bytestream unblocked, restart to read data from FT socket");
- }
-
- gibber_transport_block_receiving (self->priv->transport, blocked);
-
-}
-
-static void
-file_transfer_send (GabbleFileTransferChannel *self)
-{
- gabble_signal_connect_weak (self->priv->bytestream, "write-blocked",
- G_CALLBACK (bytestream_write_blocked_cb), G_OBJECT (self));
- gibber_transport_set_handler (self->priv->transport, transport_handler, self);
-}
-
-static void
-file_transfer_receive (GabbleFileTransferChannel *self)
-{
- /* Client is connected, we can now receive data. Unblock the bytestream */
- g_assert (self->priv->bytestream != NULL);
- gabble_bytestream_iface_block_reading (self->priv->bytestream, FALSE);
-}
-
-static void
-transport_disconnected_cb (GibberTransport *transport,
- GabbleFileTransferChannel *self)
-{
- DEBUG ("transport to local socket has been disconnected");
-
- if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED)
- {
- close_bytestrean_and_transport (self);
-
- gabble_file_transfer_channel_set_state (
- TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
- TP_FILE_TRANSFER_STATE_CANCELLED,
- TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR);
- }
-}
-
-static void
-transport_buffer_empty_cb (GibberTransport *transport,
- GabbleFileTransferChannel *self)
-{
- /* Buffer is empty so we can unblock the buffer if it was blocked */
- DEBUG ("file transfer buffer is empty. Unblock the bytestream");
- gabble_bytestream_iface_block_reading (self->priv->bytestream, FALSE);
-}
-
-/*
- * Some client is connecting to the Unix socket.
- */
-static void
-new_connection_cb (GibberListener *listener,
- GibberTransport *transport,
- struct sockaddr_storage *addr,
- guint size,
- gpointer user_data)
-{
- GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
- gboolean requested;
- TpBaseConnection *base_conn = (TpBaseConnection *) \
- self->priv->connection;
-
- DEBUG ("Client connected to local socket");
-
- self->priv->transport = g_object_ref (transport);
- gabble_signal_connect_weak (transport, "disconnected",
- G_CALLBACK (transport_disconnected_cb), G_OBJECT (self));
- gabble_signal_connect_weak (transport, "buffer-empty",
- G_CALLBACK (transport_buffer_empty_cb), G_OBJECT (self));
-
- requested = (self->priv->initiator == base_conn->self_handle);
-
- if (!requested)
- /* Incoming file transfer */
- file_transfer_receive (self);
- else
- /* Outgoing file transfer */
- file_transfer_send (self);
-
- /* stop listening on local socket */
- g_object_unref (self->priv->listener);
- self->priv->listener = NULL;
-}
-
-static gboolean
-setup_local_socket (GabbleFileTransferChannel *self)
-{
- const gchar *path;
- GError *error = NULL;
-
- path = get_local_unix_socket_path (self);
-
- self->priv->listener = gibber_listener_new ();
-
- /* FIXME: should use the socket type and access control chosen by
- * the user. */
- if (!gibber_listener_listen_socket (self->priv->listener, (gchar *) path,
- FALSE, &error))
- {
- DEBUG ("listen_socket failed: %s", error->message);
- g_error_free (error);
- g_object_unref (self->priv->listener);
- self->priv->listener = NULL;
- return FALSE;
- }
-
- g_signal_connect (self->priv->listener, "new-connection",
- G_CALLBACK (new_connection_cb), self);
-
- return TRUE;
-}
-
-GabbleFileTransferChannel *
-gabble_file_transfer_channel_new (GabbleConnection *conn,
- TpHandle handle,
- TpHandle initiator_handle,
- TpFileTransferState state,
- const gchar *content_type,
- const gchar *filename,
- guint64 size,
- TpFileHashType content_hash_type,
- const gchar *content_hash,
- const gchar *description,
- guint64 date,
- guint64 initial_offset,
- GabbleBytestreamIface *bytestream)
-
-{
- return g_object_new (GABBLE_TYPE_FILE_TRANSFER_CHANNEL,
- "connection", conn,
- "handle", handle,
- "initiator-handle", initiator_handle,
- "state", state,
- "content-type", content_type,
- "filename", filename,
- "size", size,
- "content-hash-type", content_hash_type,
- "content-hash", content_hash,
- "description", description,
- "date", date,
- "initial-offset", initial_offset,
- "bytestream", bytestream,
- NULL);
-}
diff --git a/src/file-transfer-channel.h b/src/file-transfer-channel.h
deleted file mode 100644
index b699a6d..0000000
--- a/src/file-transfer-channel.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * file-transfer-channel.h - Header for GabbleFileTransferChannel
- * Copyright (C) 2009 Collabora Ltd.
- * @author: Guillaume Desmottes <guillaume.desmottes at collabora.co.uk>
- *
- * 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_FILE_TRANSFER_CHANNEL_H__
-#define __GABBLE_FILE_TRANSFER_CHANNEL_H__
-
-#include <glib-object.h>
-
-#include <extensions/_gen/svc.h>
-#include <extensions/_gen/interfaces.h>
-#include <extensions/_gen/enums.h>
-
-G_BEGIN_DECLS
-
-typedef struct _GabbleFileTransferChannel GabbleFileTransferChannel;
-typedef struct _GabbleFileTransferChannelClass GabbleFileTransferChannelClass;
-typedef struct _GabbleFileTransferChannelPrivate GabbleFileTransferChannelPrivate;
-
-struct _GabbleFileTransferChannelClass {
- GObjectClass parent_class;
- TpDBusPropertiesMixinClass dbus_props_class;
-};
-
-struct _GabbleFileTransferChannel {
- GObject parent;
-
- GabbleFileTransferChannelPrivate *priv;
-};
-
-GType gabble_file_transfer_channel_get_type (void);
-
-/* TYPE MACROS */
-#define GABBLE_TYPE_FILE_TRANSFER_CHANNEL \
- (gabble_file_transfer_channel_get_type ())
-#define GABBLE_FILE_TRANSFER_CHANNEL(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, GabbleFileTransferChannel))
-#define GABBLE_FILE_TRANSFER_CHANNEL_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, \
- GabbleFileTransferChannelClass))
-#define GABBLE_IS_FILE_TRANSFER_CHANNEL(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL))
-#define GABBLE_IS_FILE_TRANSFER_CHANNEL_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_FILE_TRANSFER_CHANNEL))
-#define GABBLE_FILE_TRANSFER_CHANNEL_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, \
- GabbleFileTransferChannelClass))
-
-GabbleFileTransferChannel *
-gabble_file_transfer_channel_new (GabbleConnection *conn,
- TpHandle handle, TpHandle initiator_handle, TpFileTransferState state,
- const gchar *content_type, const gchar *filename, guint64 size,
- TpFileHashType content_hash_type, const gchar *content_hash,
- const gchar *description, guint64 date, guint64 initial_offset,
- GabbleBytestreamIface *bytestream);
-
-gboolean gabble_file_transfer_channel_offer_file (
- GabbleFileTransferChannel *self, GError **error);
-
-G_END_DECLS
-
-#endif /* #ifndef __GABBLE_FILE_TRANSFER_CHANNEL_H__*/
diff --git a/src/ft-channel.c b/src/ft-channel.c
new file mode 100644
index 0000000..e684fb8
--- /dev/null
+++ b/src/ft-channel.c
@@ -0,0 +1,1666 @@
+/*
+ * ft-channel.c - Source for GabbleFileTransferChannel
+ * Copyright (C) 2009 Collabora Ltd.
+ * @author: Guillaume Desmottes <guillaume.desmottes at collabora.co.uk>
+ *
+ * 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 <glib/gstdio.h>
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_FT
+#include "debug.h"
+
+#include <loudmouth/loudmouth.h>
+
+#include <gibber/gibber-listener.h>
+#include <gibber/gibber-transport.h>
+
+#include "bytestream-factory.h"
+#include "connection.h"
+#include "ft-channel.h"
+#include "gabble-signals-marshal.h"
+#include "namespaces.h"
+#include "presence-cache.h"
+#include "util.h"
+
+#include <telepathy-glib/channel-iface.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/gtypes.h>
+#include <telepathy-glib/svc-generic.h>
+#include <telepathy-glib/svc-channel.h>
+
+static void channel_iface_init (gpointer g_iface, gpointer iface_data);
+static void file_transfer_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GabbleFileTransferChannel, gabble_file_transfer_channel,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+ tp_dbus_properties_mixin_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_FILE_TRANSFER,
+ file_transfer_iface_init);
+);
+
+#define CHECK_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0')
+
+#define GABBLE_UNDEFINED_FILE_SIZE G_MAXUINT64
+
+static const char *gabble_file_transfer_channel_interfaces[] = { NULL };
+
+/* properties */
+enum
+{
+ PROP_OBJECT_PATH = 1,
+
+ /* org.freedesktop.Telepathy.Channel D-Bus properties */
+ PROP_CHANNEL_TYPE,
+ PROP_INTERFACES,
+ PROP_HANDLE,
+ PROP_TARGET_ID,
+ PROP_HANDLE_TYPE,
+ PROP_REQUESTED,
+ PROP_INITIATOR_HANDLE,
+ PROP_INITIATOR_ID,
+
+ PROP_CHANNEL_DESTROYED,
+ PROP_CHANNEL_PROPERTIES,
+
+ /* org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties */
+ PROP_STATE,
+ PROP_CONTENT_TYPE,
+ PROP_FILENAME,
+ PROP_SIZE,
+ PROP_CONTENT_HASH_TYPE,
+ PROP_CONTENT_HASH,
+ PROP_DESCRIPTION,
+ PROP_DATE,
+ PROP_AVAILABLE_SOCKET_TYPES,
+ PROP_TRANSFERRED_BYTES,
+ PROP_INITIAL_OFFSET,
+
+ PROP_CONNECTION,
+ PROP_BYTESTREAM,
+ LAST_PROPERTY
+};
+
+/* private structure */
+struct _GabbleFileTransferChannelPrivate {
+ gboolean dispose_has_run;
+ gboolean closed;
+ gchar *object_path;
+ TpHandle handle;
+ GabbleConnection *connection;
+ GTimeVal last_transferred_bytes_emitted;
+ guint progress_timer;
+ gchar *socket_path;
+ TpHandle initiator;
+ gboolean remote_accepted;
+
+ GabbleBytestreamIface *bytestream;
+ GibberListener *listener;
+ GibberTransport *transport;
+
+ /* properties */
+ TpFileTransferState state;
+ gchar *content_type;
+ gchar *filename;
+ guint64 size;
+ TpFileHashType content_hash_type;
+ gchar *content_hash;
+ gchar *description;
+ GHashTable *available_socket_types;
+ guint64 transferred_bytes;
+ guint64 initial_offset;
+ guint64 date;
+};
+
+static void set_bytestream (GabbleFileTransferChannel *self,
+ GabbleBytestreamIface *bytestream);
+
+static void
+gabble_file_transfer_channel_do_close (GabbleFileTransferChannel *self)
+{
+ if (self->priv->closed)
+ return;
+
+ DEBUG ("Emitting closed signal for %s", self->priv->object_path);
+ tp_svc_channel_emit_closed (self);
+ self->priv->closed = TRUE;
+}
+
+static void
+gabble_file_transfer_channel_init (GabbleFileTransferChannel *obj)
+{
+ obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
+ GABBLE_TYPE_FILE_TRANSFER_CHANNEL, GabbleFileTransferChannelPrivate);
+
+ /* allocate any data required by the object here */
+ obj->priv->object_path = NULL;
+ obj->priv->connection = NULL;
+}
+
+static void gabble_file_transfer_channel_set_state (
+ TpSvcChannelTypeFileTransfer *iface, TpFileTransferState state,
+ TpFileTransferStateChangeReason reason);
+
+static void
+gabble_file_transfer_channel_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
+ TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->connection;
+
+ switch (property_id)
+ {
+ case PROP_OBJECT_PATH:
+ g_value_set_string (value, self->priv->object_path);
+ break;
+ case PROP_CHANNEL_TYPE:
+ g_value_set_static_string (value,
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER);
+ break;
+ case PROP_HANDLE_TYPE:
+ g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
+ break;
+ case PROP_TARGET_ID:
+ {
+ TpHandleRepoIface *repo = tp_base_connection_get_handles (base_conn,
+ TP_HANDLE_TYPE_CONTACT);
+
+ g_value_set_string (value, tp_handle_inspect (repo,
+ self->priv->handle));
+ }
+ break;
+ case PROP_HANDLE:
+ g_value_set_uint (value, self->priv->handle);
+ break;
+ case PROP_REQUESTED:
+ g_value_set_boolean (value, (self->priv->initiator ==
+ base_conn->self_handle));
+ break;
+ case PROP_INITIATOR_HANDLE:
+ g_value_set_uint (value, self->priv->initiator);
+ break;
+ case PROP_INITIATOR_ID:
+ {
+ TpHandleRepoIface *repo = tp_base_connection_get_handles (
+ base_conn, TP_HANDLE_TYPE_CONTACT);
+
+ g_assert (self->priv->initiator != 0);
+ g_value_set_string (value,
+ tp_handle_inspect (repo, self->priv->initiator));
+ }
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, self->priv->connection);
+ break;
+ case PROP_INTERFACES:
+ g_value_set_boxed (value, gabble_file_transfer_channel_interfaces);
+ break;
+ case PROP_STATE:
+ g_value_set_uint (value, self->priv->state);
+ break;
+ case PROP_CONTENT_TYPE:
+ g_value_set_string (value, self->priv->content_type);
+ break;
+ case PROP_FILENAME:
+ g_value_set_string (value, self->priv->filename);
+ break;
+ case PROP_SIZE:
+ g_value_set_uint64 (value, self->priv->size);
+ break;
+ case PROP_CONTENT_HASH_TYPE:
+ g_value_set_uint (value, self->priv->content_hash_type);
+ break;
+ case PROP_CONTENT_HASH:
+ g_value_set_string (value, self->priv->content_hash);
+ break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, self->priv->description);
+ break;
+ case PROP_AVAILABLE_SOCKET_TYPES:
+ g_value_set_boxed (value, self->priv->available_socket_types);
+ break;
+ case PROP_TRANSFERRED_BYTES:
+ g_value_set_uint64 (value, self->priv->transferred_bytes);
+ break;
+ case PROP_INITIAL_OFFSET:
+ g_value_set_uint64 (value, self->priv->initial_offset);
+ break;
+ case PROP_DATE:
+ g_value_set_uint64 (value, self->priv->date);
+ break;
+ case PROP_CHANNEL_DESTROYED:
+ g_value_set_boolean (value, self->priv->closed);
+ break;
+ case PROP_CHANNEL_PROPERTIES:
+ g_value_take_boxed (value,
+ tp_dbus_properties_mixin_make_properties_hash (object,
+ TP_IFACE_CHANNEL, "ChannelType",
+ TP_IFACE_CHANNEL, "Interfaces",
+ TP_IFACE_CHANNEL, "TargetHandle",
+ TP_IFACE_CHANNEL, "TargetID",
+ TP_IFACE_CHANNEL, "TargetHandleType",
+ TP_IFACE_CHANNEL, "Requested",
+ TP_IFACE_CHANNEL, "InitiatorHandle",
+ TP_IFACE_CHANNEL, "InitiatorID",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "State",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentType",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Filename",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Size",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHashType",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "ContentHash",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Description",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "Date",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "AvailableSocketTypes",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "TransferredBytes",
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "InitialOffset",
+ NULL));
+ break;
+ case PROP_BYTESTREAM:
+ g_value_set_object (value, self->priv->bytestream);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_file_transfer_channel_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
+
+ switch (property_id)
+ {
+ case PROP_OBJECT_PATH:
+ g_free (self->priv->object_path);
+ self->priv->object_path = g_value_dup_string (value);
+ break;
+ case PROP_HANDLE:
+ self->priv->handle = g_value_get_uint (value);
+ break;
+ case PROP_CONNECTION:
+ self->priv->connection = g_value_get_object (value);
+ break;
+ case PROP_HANDLE_TYPE:
+ g_assert (g_value_get_uint (value) == 0
+ || g_value_get_uint (value) == TP_HANDLE_TYPE_CONTACT);
+ break;
+ case PROP_CHANNEL_TYPE:
+ /* these properties are writable in the interface, but not actually
+ * meaningfully changeable on this channel, so we do nothing */
+ break;
+ case PROP_STATE:
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (object),
+ g_value_get_uint (value),
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
+ break;
+ case PROP_CONTENT_TYPE:
+ g_free (self->priv->content_type);
+ self->priv->content_type = g_value_dup_string (value);
+ break;
+ case PROP_FILENAME:
+ g_free (self->priv->filename);
+ self->priv->filename = g_value_dup_string (value);
+ break;
+ case PROP_SIZE:
+ self->priv->size = g_value_get_uint64 (value);
+ break;
+ case PROP_CONTENT_HASH_TYPE:
+ self->priv->content_hash_type = g_value_get_uint (value);
+ break;
+ case PROP_CONTENT_HASH:
+ g_free (self->priv->content_hash);
+ self->priv->content_hash = g_value_dup_string (value);
+ break;
+ case PROP_DESCRIPTION:
+ g_free (self->priv->description);
+ self->priv->description = g_value_dup_string (value);
+ break;
+ case PROP_INITIATOR_HANDLE:
+ self->priv->initiator = g_value_get_uint (value);
+ g_assert (self->priv->initiator != 0);
+ break;
+ case PROP_DATE:
+ self->priv->date = g_value_get_uint64 (value);
+ break;
+ case PROP_INITIAL_OFFSET:
+ self->priv->initial_offset = g_value_get_uint64 (value);
+ break;
+ case PROP_BYTESTREAM:
+ set_bytestream (self,
+ GABBLE_BYTESTREAM_IFACE (g_value_get_object (value)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+free_array (GArray *array)
+{
+ g_array_free (array, TRUE);
+}
+
+static void
+connection_presences_updated_cb (GabblePresenceCache *cache,
+ GArray *handles,
+ GabbleFileTransferChannel *self)
+{
+ guint i;
+
+ for (i = 0; i < handles->len ; i++)
+ {
+ TpHandle handle;
+
+ handle = g_array_index (handles, TpHandle, i);
+ if (handle == self->priv->handle)
+ {
+ GabblePresence *presence;
+
+ presence = gabble_presence_cache_get (
+ self->priv->connection->presence_cache, handle);
+
+ if (presence == NULL || presence->status < GABBLE_PRESENCE_XA)
+ {
+ /* Contact is disconnected */
+ if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
+ self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
+ {
+ DEBUG ("peer disconnected. FileTransfer is cancelled");
+
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_CANCELLED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
+ }
+ }
+ }
+ }
+}
+
+static GObject *
+gabble_file_transfer_channel_constructor (GType type,
+ guint n_props,
+ GObjectConstructParam *props)
+{
+ GObject *obj;
+ GabbleFileTransferChannel *self;
+ DBusGConnection *bus;
+ TpBaseConnection *base_conn;
+ TpHandleRepoIface *contact_repo;
+ GArray *unix_access;
+ TpSocketAccessControl access_control;
+
+ /* Parent constructor chain */
+ obj = G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->
+ constructor (type, n_props, props);
+ self = GABBLE_FILE_TRANSFER_CHANNEL (obj);
+ base_conn = TP_BASE_CONNECTION (self->priv->connection);
+
+ /* Ref the target and initiator handles; they can't be reffed in
+ * _set_property as we may not have the TpConnection at that point.
+ */
+ contact_repo = tp_base_connection_get_handles (base_conn,
+ TP_HANDLE_TYPE_CONTACT);
+ tp_handle_ref (contact_repo, self->priv->handle);
+ tp_handle_ref (contact_repo, self->priv->initiator);
+
+ self->priv->object_path = g_strdup_printf ("%s/FileTransferChannel/%p",
+ base_conn->object_path, self);
+
+ /* Connect to the bus */
+ bus = tp_get_bus ();
+ dbus_g_connection_register_g_object (bus, self->priv->object_path, obj);
+
+ /* Initialise the available socket types hash table */
+ self->priv->available_socket_types = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal, NULL, (GDestroyNotify) free_array);
+
+ /* Socket_Address_Type_Unix */
+ unix_access = g_array_sized_new (FALSE, FALSE, sizeof (TpSocketAccessControl),
+ 1);
+ access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
+ g_array_append_val (unix_access, access_control);
+ g_hash_table_insert (self->priv->available_socket_types,
+ GUINT_TO_POINTER (TP_SOCKET_ADDRESS_TYPE_UNIX), unix_access);
+
+ gabble_signal_connect_weak (self->priv->connection->presence_cache,
+ "presences-updated", G_CALLBACK (connection_presences_updated_cb), obj);
+
+ DEBUG ("New FT channel created: %s (contact: %s, initiator: %s, "
+ "file: \"%s\", size: %" G_GUINT64_FORMAT ")",
+ self->priv->object_path,
+ tp_handle_inspect (contact_repo, self->priv->handle),
+ tp_handle_inspect (contact_repo, self->priv->initiator),
+ self->priv->filename, self->priv->size);
+
+ return obj;
+}
+
+static void gabble_file_transfer_channel_dispose (GObject *object);
+static void gabble_file_transfer_channel_finalize (GObject *object);
+
+static void
+gabble_file_transfer_channel_class_init (
+ GabbleFileTransferChannelClass *gabble_file_transfer_channel_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (
+ gabble_file_transfer_channel_class);
+ GParamSpec *param_spec;
+
+ static TpDBusPropertiesMixinPropImpl channel_props[] = {
+ { "TargetHandleType", "handle-type", NULL },
+ { "TargetHandle", "handle", NULL },
+ { "TargetID", "target-id", NULL },
+ { "ChannelType", "channel-type", NULL },
+ { "Interfaces", "interfaces", NULL },
+ { "Requested", "requested", NULL },
+ { "InitiatorHandle", "initiator-handle", NULL },
+ { "InitiatorID", "initiator-id", NULL },
+ { NULL }
+ };
+
+ static TpDBusPropertiesMixinPropImpl file_props[] = {
+ { "State", "state", NULL },
+ { "ContentType", "content-type", NULL },
+ { "Filename", "filename", NULL },
+ { "Size", "size", NULL },
+ { "ContentHashType", "content-hash-type", NULL },
+ { "ContentHash", "content-hash", NULL },
+ { "Description", "description", NULL },
+ { "AvailableSocketTypes", "available-socket-types", NULL },
+ { "TransferredBytes", "transferred-bytes", NULL },
+ { "InitialOffset", "initial-offset", NULL },
+ { "Date", "date", NULL },
+ { NULL }
+ };
+
+ static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+ { TP_IFACE_CHANNEL,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ channel_props
+ },
+ { TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ file_props
+ },
+ { NULL }
+ };
+
+ g_type_class_add_private (gabble_file_transfer_channel_class,
+ sizeof (GabbleFileTransferChannelPrivate));
+
+ object_class->dispose = gabble_file_transfer_channel_dispose;
+ object_class->finalize = gabble_file_transfer_channel_finalize;
+
+ object_class->constructor = gabble_file_transfer_channel_constructor;
+ object_class->get_property = gabble_file_transfer_channel_get_property;
+ object_class->set_property = gabble_file_transfer_channel_set_property;
+
+ g_object_class_override_property (object_class, PROP_OBJECT_PATH,
+ "object-path");
+ g_object_class_override_property (object_class, PROP_CHANNEL_TYPE,
+ "channel-type");
+ g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
+ "handle-type");
+ g_object_class_override_property (object_class, PROP_HANDLE, "handle");
+ g_object_class_override_property (object_class, PROP_CHANNEL_DESTROYED,
+ "channel-destroyed");
+ g_object_class_override_property (object_class, PROP_CHANNEL_PROPERTIES,
+ "channel-properties");
+
+ param_spec = g_param_spec_string ("target-id", "Target JID",
+ "The string obtained by inspecting this channel's handle",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec);
+
+ param_spec = g_param_spec_boolean ("requested", "Requested?",
+ "True if this channel was requested by the local user",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_REQUESTED, param_spec);
+
+ param_spec = g_param_spec_uint ("initiator-handle", "Initiator's handle",
+ "The contact who initiated the channel",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE,
+ param_spec);
+
+ param_spec = g_param_spec_string ("initiator-id", "Initiator's bare JID",
+ "The string obtained by inspecting the initiator-handle",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_INITIATOR_ID,
+ param_spec);
+
+ param_spec = g_param_spec_object ("connection",
+ "GabbleConnection object",
+ "Gabble Connection that owns the"
+ "connection for this IM channel",
+ GABBLE_TYPE_CONNECTION,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
+
+ param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
+ "Additional Channel.Interface.* interfaces",
+ G_TYPE_STRV,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
+
+ param_spec = g_param_spec_uint (
+ "state",
+ "TpFileTransferState state",
+ "State of the file transfer in this channel",
+ 0,
+ NUM_TP_FILE_TRANSFER_STATES,
+ TP_FILE_TRANSFER_STATE_NONE,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_STATE, param_spec);
+
+ param_spec = g_param_spec_string (
+ "content-type",
+ "gchar *content-type",
+ "ContentType of the file",
+ "application/octet-stream",
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
+ param_spec);
+
+ param_spec = g_param_spec_string (
+ "filename",
+ "gchar *filename",
+ "Name of the file",
+ "",
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_FILENAME, param_spec);
+
+ param_spec = g_param_spec_uint64 (
+ "size",
+ "guint size",
+ "Size of the file in bytes",
+ 0,
+ G_MAXUINT64,
+ GABBLE_UNDEFINED_FILE_SIZE,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_SIZE, param_spec);
+
+ param_spec = g_param_spec_uint (
+ "content-hash-type",
+ "TpFileHashType content-hash-type",
+ "Hash type",
+ 0,
+ NUM_TP_FILE_HASH_TYPES,
+ TP_FILE_HASH_TYPE_NONE,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_CONTENT_HASH_TYPE,
+ param_spec);
+
+ param_spec = g_param_spec_string (
+ "content-hash",
+ "gchar *content-hash",
+ "Hash of the file contents",
+ "",
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_CONTENT_HASH,
+ param_spec);
+
+ param_spec = g_param_spec_string (
+ "description",
+ "gchar *description",
+ "Description of the file",
+ "",
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_DESCRIPTION, param_spec);
+
+ param_spec = g_param_spec_boxed (
+ "available-socket-types",
+ "GabbleSupportedSocketMap available-socket-types",
+ "Available socket types",
+ TP_HASH_TYPE_SUPPORTED_SOCKET_MAP,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_AVAILABLE_SOCKET_TYPES,
+ param_spec);
+
+ param_spec = g_param_spec_uint64 (
+ "transferred-bytes",
+ "guint64 transferred-bytes",
+ "Bytes transferred",
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_TRANSFERRED_BYTES,
+ param_spec);
+
+ param_spec = g_param_spec_uint64 (
+ "initial-offset",
+ "guint64 initial_offset",
+ "Offset set at the beginning of the transfer",
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_INITIAL_OFFSET,
+ param_spec);
+
+ param_spec = g_param_spec_uint64 (
+ "date",
+ "Epoch time",
+ "the last modification time of the file being transferred",
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_DATE,
+ param_spec);
+
+ param_spec = g_param_spec_object (
+ "bytestream",
+ "Object implementing the GabbleBytestreamIface interface",
+ "Bytestream object used to send the file",
+ G_TYPE_OBJECT,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_BYTESTREAM,
+ param_spec);
+
+ gabble_file_transfer_channel_class->dbus_props_class.interfaces = \
+ prop_interfaces;
+ tp_dbus_properties_mixin_class_init (object_class,
+ G_STRUCT_OFFSET (GabbleFileTransferChannelClass, dbus_props_class));
+}
+
+void
+gabble_file_transfer_channel_dispose (GObject *object)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
+ TpBaseConnection *base_conn = TP_BASE_CONNECTION (self->priv->connection);
+ TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (base_conn,
+ TP_HANDLE_TYPE_CONTACT);
+
+ if (self->priv->dispose_has_run)
+ return;
+
+ self->priv->dispose_has_run = TRUE;
+
+ tp_handle_unref (handle_repo, self->priv->handle);
+ tp_handle_unref (handle_repo, self->priv->initiator);
+
+ gabble_file_transfer_channel_do_close (self);
+
+ if (self->priv->progress_timer != 0)
+ {
+ g_source_remove (self->priv->progress_timer);
+ self->priv->progress_timer = 0;
+ }
+
+ if (self->priv->bytestream != NULL)
+ {
+ g_object_unref (self->priv->bytestream);
+ self->priv->bytestream = NULL;
+ }
+
+ if (self->priv->listener != NULL)
+ {
+ g_object_unref (self->priv->listener);
+ self->priv->listener = NULL;
+ }
+
+ if (self->priv->transport != NULL)
+ {
+ g_object_unref (self->priv->transport);
+ self->priv->transport = NULL;
+ }
+
+ /* release any references held by the object here */
+
+ if (G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->dispose)
+ G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->dispose (object);
+}
+
+static void
+gabble_file_transfer_channel_finalize (GObject *object)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (object);
+
+ /* free any data held directly by the object here */
+ g_free (self->priv->object_path);
+ g_free (self->priv->filename);
+ g_free (self->priv->socket_path);
+ g_free (self->priv->content_type);
+ g_free (self->priv->content_hash);
+ g_free (self->priv->description);
+ g_hash_table_destroy (self->priv->available_socket_types);
+
+ G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->finalize (object);
+}
+
+static void
+close_bytestrean_and_transport (GabbleFileTransferChannel *self)
+{
+ if (self->priv->bytestream != NULL)
+ {
+ gabble_bytestream_iface_close (self->priv->bytestream, NULL);
+ g_object_unref (self->priv->bytestream);
+ self->priv->bytestream = NULL;
+ }
+
+ if (self->priv->transport != NULL)
+ {
+ g_object_unref (self->priv->transport);
+ self->priv->transport = NULL;
+ }
+}
+
+/**
+ * gabble_file_transfer_channel_close
+ *
+ * Implements DBus method Close
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_file_transfer_channel_close (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
+
+ if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED &&
+ self->priv->state != TP_FILE_TRANSFER_STATE_CANCELLED)
+ {
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (iface),
+ TP_FILE_TRANSFER_STATE_CANCELLED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_STOPPED);
+
+ close_bytestrean_and_transport (self);
+ }
+
+ gabble_file_transfer_channel_do_close (GABBLE_FILE_TRANSFER_CHANNEL (iface));
+ tp_svc_channel_return_from_close (context);
+}
+
+/**
+ * gabble_file_transfer_channel_get_channel_type
+ *
+ * Implements DBus method GetChannelType
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_file_transfer_channel_get_channel_type (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ tp_svc_channel_return_from_get_channel_type (context,
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER);
+}
+
+/**
+ * gabble_file_transfer_channel_get_handle
+ *
+ * Implements DBus method GetHandle
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_file_transfer_channel_get_handle (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
+
+ tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT,
+ self->priv->handle);
+}
+
+/**
+ * gabble_file_transfer_channel_get_interfaces
+ *
+ * Implements DBus method GetInterfaces
+ * on interface org.freedesktop.Telepathy.Channel
+ */
+static void
+gabble_file_transfer_channel_get_interfaces (TpSvcChannel *iface,
+ DBusGMethodInvocation *context)
+{
+ tp_svc_channel_return_from_get_interfaces (context,
+ gabble_file_transfer_channel_interfaces);
+}
+
+static void
+channel_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ TpSvcChannelClass *klass = (TpSvcChannelClass *) g_iface;
+
+#define IMPLEMENT(x) tp_svc_channel_implement_##x (\
+ klass, gabble_file_transfer_channel_##x)
+ IMPLEMENT (close);
+ IMPLEMENT (get_channel_type);
+ IMPLEMENT (get_handle);
+ IMPLEMENT (get_interfaces);
+#undef IMPLEMENT
+}
+
+static gboolean setup_local_socket (GabbleFileTransferChannel *self);
+
+static void
+gabble_file_transfer_channel_set_state (
+ TpSvcChannelTypeFileTransfer *iface,
+ TpFileTransferState state,
+ TpFileTransferStateChangeReason reason)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
+
+ if (self->priv->state == state)
+ return;
+
+ self->priv->state = state;
+ tp_svc_channel_type_file_transfer_emit_file_transfer_state_changed (iface,
+ state, reason);
+}
+
+static gboolean
+check_address_and_access_control (GabbleFileTransferChannel *self,
+ TpSocketAddressType address_type,
+ TpSocketAccessControl access_control,
+ const GValue *access_control_param,
+ GError **error)
+{
+ GArray *access_arr;
+ guint i;
+
+ /* Do we support this AddressType? */
+ access_arr = g_hash_table_lookup (self->priv->available_socket_types,
+ GUINT_TO_POINTER (address_type));
+ if (access_arr == NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
+ "AddressType %u is not implemented", address_type);
+ return FALSE;
+ }
+
+ /* Do we support this AccessControl? */
+ for (i = 0; i < access_arr->len; i++)
+ {
+ TpSocketAccessControl control;
+
+ control = g_array_index (access_arr, TpSocketAccessControl, i);
+ if (control == access_control)
+ return TRUE;
+ }
+
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
+ "AccesControl %u is not implemented with AddressType %u",
+ access_control, address_type);
+
+ return FALSE;
+}
+
+static void
+bytestream_open (GabbleFileTransferChannel *self)
+{
+ if (self->priv->socket_path != NULL)
+ {
+ /* ProvideFile has already been called. Channel is Open */
+ tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
+ self->priv->initial_offset);
+
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_OPEN,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
+ }
+ else
+ {
+ /* Client has to call ProvideFile to open the channel */
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_ACCEPTED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
+ }
+}
+
+static void
+bytestream_closed (GabbleFileTransferChannel *self)
+{
+ if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED)
+ {
+ TpBaseConnection *base_conn = (TpBaseConnection *) \
+ self->priv->connection;
+ gboolean receiver;
+
+ receiver = (self->priv->initiator != base_conn->self_handle);
+
+ /* Something did wrong */
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_CANCELLED,
+ receiver ?
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR :
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR);
+ }
+}
+
+static void
+bytestream_state_changed_cb (GabbleBytestreamIface *bytestream,
+ GabbleBytestreamState state,
+ gpointer user_data)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
+
+ if (state == GABBLE_BYTESTREAM_STATE_OPEN)
+ {
+ bytestream_open (self);
+ }
+ else if (state == GABBLE_BYTESTREAM_STATE_CLOSED)
+ {
+ bytestream_closed (self);
+ }
+}
+
+static void
+set_bytestream (GabbleFileTransferChannel *self,
+ GabbleBytestreamIface *bytestream)
+{
+ if (bytestream == NULL)
+ return;
+
+ self->priv->bytestream = g_object_ref (bytestream);
+
+ gabble_signal_connect_weak (bytestream, "state-changed",
+ G_CALLBACK (bytestream_state_changed_cb), G_OBJECT (self));
+}
+
+static void
+bytestream_negotiate_cb (GabbleBytestreamIface *bytestream,
+ const gchar *stream_id,
+ LmMessage *msg,
+ GObject *object,
+ gpointer user_data)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
+
+ if (bytestream == NULL)
+ {
+ DEBUG ("receiver refused file offer");
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_CANCELLED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED);
+ return;
+ }
+
+ DEBUG ("receiver accepted file offer");
+
+ set_bytestream (self, bytestream);
+
+ self->priv->remote_accepted = TRUE;
+}
+
+gboolean
+gabble_file_transfer_channel_offer_file (GabbleFileTransferChannel *self,
+ GError **error)
+{
+ GabblePresence *presence;
+ gboolean result;
+ LmMessage *msg;
+ TpHandleRepoIface *contact_repo, *room_repo;
+ LmMessageNode *si_node, *file_node, *desc_node;
+ const gchar *jid;
+ gchar *full_jid, *stream_id, *size_str;
+
+ g_assert (!CHECK_STR_EMPTY (self->priv->filename));
+ g_assert (self->priv->size != GABBLE_UNDEFINED_FILE_SIZE);
+ g_assert (self->priv->bytestream == NULL);
+
+ presence = gabble_presence_cache_get (self->priv->connection->presence_cache,
+ self->priv->handle);
+ if (presence == NULL)
+ {
+ DEBUG ("can't find contact's presence");
+ if (error != NULL)
+ g_set_error (error, TP_ERRORS, TP_ERROR_OFFLINE,
+ "can't find contact's presence");
+
+ return FALSE;
+ }
+
+ contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *) self->priv->connection, TP_HANDLE_TYPE_CONTACT);
+ room_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *) self->priv->connection, TP_HANDLE_TYPE_ROOM);
+
+ jid = tp_handle_inspect (contact_repo, self->priv->handle);
+
+ if (gabble_get_room_handle_from_jid (room_repo, jid) == 0)
+ {
+ /* Not a MUC jid, need to get a resource */
+ const gchar *resource;
+
+ resource = gabble_presence_pick_resource_by_caps (presence,
+ PRESENCE_CAP_SI_FILE_TRANSFER);
+ if (resource == NULL)
+ {
+ DEBUG ("contact doesn't have file transfer capabilities");
+ if (error != NULL)
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_CAPABLE,
+ "contact doesn't have file transfer capabilities");
+
+ return FALSE;
+ }
+
+ full_jid = g_strdup_printf ("%s/%s", jid, resource);
+ }
+ else
+ {
+ /* MUC jid, we already have the full jid */
+ full_jid = g_strdup (jid);
+ }
+
+ DEBUG ("Offering file transfer to %s", full_jid);
+
+ stream_id = gabble_bytestream_factory_generate_stream_id ();
+
+ msg = gabble_bytestream_factory_make_stream_init_iq (full_jid,
+ stream_id, NS_FILE_TRANSFER);
+
+ si_node = lm_message_node_get_child_with_namespace (msg->node, "si", NS_SI);
+ g_assert (si_node != NULL);
+
+ size_str = g_strdup_printf ("%" G_GUINT64_FORMAT, self->priv->size);
+
+ file_node = lm_message_node_add_child (si_node, "file", NULL);
+ lm_message_node_set_attributes (file_node,
+ "xmlns", NS_FILE_TRANSFER,
+ "name", self->priv->filename,
+ "size", size_str,
+ "mime-type", self->priv->content_type,
+ NULL);
+
+ if (self->priv->content_hash != NULL)
+ lm_message_node_set_attribute (file_node, "hash", self->priv->content_hash);
+
+ if (self->priv->date != 0)
+ {
+ time_t t;
+ struct tm *tm;
+ char date_str[21];
+
+ t = (time_t) self->priv->date;
+ tm = gmtime (&t);
+
+ strftime (date_str, sizeof (date_str), "%FT%H:%M:%SZ", tm);
+
+ lm_message_node_set_attribute (file_node, "date", date_str);
+ }
+
+ /* TODO: support initial offset */
+
+ desc_node = lm_message_node_add_child (file_node, "desc",
+ self->priv->description);
+
+ result = gabble_bytestream_factory_negotiate_stream (
+ self->priv->connection->bytestream_factory, msg, stream_id,
+ bytestream_negotiate_cb, self, G_OBJECT (self), error);
+
+ lm_message_unref (msg);
+ g_free (stream_id);
+ g_free (full_jid);
+ g_free (size_str);
+
+ return result;
+}
+
+static void
+emit_progress_update (GabbleFileTransferChannel *self)
+{
+ TpSvcChannelTypeFileTransfer *iface = \
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self);
+
+ g_get_current_time (&self->priv->last_transferred_bytes_emitted);
+
+ tp_svc_channel_type_file_transfer_emit_transferred_bytes_changed (
+ iface, self->priv->transferred_bytes);
+
+ if (self->priv->progress_timer != 0)
+ {
+ g_source_remove (self->priv->progress_timer);
+ self->priv->progress_timer = 0;
+ }
+}
+
+static gboolean
+emit_progress_update_cb (gpointer user_data)
+{
+ GabbleFileTransferChannel *self = \
+ GABBLE_FILE_TRANSFER_CHANNEL (user_data);
+
+ emit_progress_update (self);
+
+ return FALSE;
+}
+
+static void
+transferred_chunk (GabbleFileTransferChannel *self,
+ guint64 count)
+{
+ GTimeVal timeval;
+ gint interval;
+
+ self->priv->transferred_bytes += count;
+
+ if (self->priv->transferred_bytes >= self->priv->size)
+ {
+ /* If the transfer has finished send an update right away */
+ emit_progress_update (self);
+ return;
+ }
+
+ if (self->priv->progress_timer != 0)
+ {
+ /* A progress update signal is already scheduled */
+ return;
+ }
+
+ /* Only emit the TransferredBytes signal if it has been one second since its
+ * last emission.
+ */
+ g_get_current_time (&timeval);
+ interval = timeval.tv_sec -
+ self->priv->last_transferred_bytes_emitted.tv_sec;
+
+ if (interval > 1)
+ {
+ /* At least more then a second apart, emit right away */
+ emit_progress_update (self);
+ return;
+ }
+
+ /* Convert interval to milliseconds and calculate it more precisely */
+ interval *= 1000;
+
+ interval += (timeval.tv_usec -
+ self->priv->last_transferred_bytes_emitted.tv_usec)/1000;
+
+ /* Protect against clock skew, if the interval is negative the worst thing
+ * that can happen is that we wait an extra second before emitting the signal
+ */
+ interval = ABS (interval);
+
+ if (interval > 1000)
+ emit_progress_update (self);
+ else
+ self->priv->progress_timer = g_timeout_add (1000 - interval,
+ emit_progress_update_cb, self);
+}
+
+
+static void
+data_received_cb (GabbleBytestreamIface *stream,
+ TpHandle sender,
+ GString *data,
+ gpointer user_data)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
+ GError *error = NULL;
+
+ g_assert (self->priv->transport != NULL);
+
+ if (!gibber_transport_send (self->priv->transport, (const guint8 *) data->str,
+ data->len, &error))
+ {
+ DEBUG ("sending to transport failed: %s", error->message);
+ g_error_free (error);
+
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_CANCELLED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR);
+ return;
+ }
+
+ DEBUG ("received %"G_GSIZE_FORMAT" bytes from bytestream. Writing to socket",
+ data->len);
+
+ transferred_chunk (self, (guint64) data->len);
+
+ if (self->priv->transferred_bytes >= self->priv->size)
+ {
+ DEBUG ("Received all the file. Transfer is complete");
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_COMPLETED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
+ return;
+ }
+
+ if (!gibber_transport_buffer_is_empty (self->priv->transport))
+ {
+ /* We don't want to send more data while the buffer isn't empty */
+ DEBUG ("file transfer buffer isn't empty. Block the bytestream");
+ gabble_bytestream_iface_block_reading (self->priv->bytestream, TRUE);
+ }
+}
+
+/**
+ * gabble_file_transfer_channel_accept_file
+ *
+ * Implements D-Bus method AcceptFile
+ * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
+ */
+static void
+gabble_file_transfer_channel_accept_file (TpSvcChannelTypeFileTransfer *iface,
+ guint address_type,
+ guint access_control,
+ const GValue *access_control_param,
+ guint64 offset,
+ DBusGMethodInvocation *context)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
+ GError *error = NULL;
+ GValue out_address = { 0 };
+
+ if (self->priv->state != TP_FILE_TRANSFER_STATE_PENDING)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "State is not pending; cannot accept file");
+ dbus_g_method_return_error (context, error);
+ return;
+ }
+
+ if (!check_address_and_access_control (self, address_type, access_control,
+ access_control_param, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ if (!setup_local_socket (self))
+ {
+ DEBUG ("Could not set up local socket");
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "Could not set up local socket");
+ dbus_g_method_return_error (context, error);
+ }
+
+ DEBUG ("local socket %s", self->priv->socket_path);
+
+ gabble_file_transfer_channel_set_state (iface,
+ TP_FILE_TRANSFER_STATE_ACCEPTED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
+
+ g_value_init (&out_address, G_TYPE_STRING);
+ g_value_set_string (&out_address, self->priv->socket_path);
+
+ tp_svc_channel_type_file_transfer_return_from_accept_file (context,
+ &out_address);
+
+ self->priv->initial_offset = 0;
+
+ g_assert (self->priv->bytestream != NULL);
+ gabble_signal_connect_weak (self->priv->bytestream, "data-received",
+ G_CALLBACK (data_received_cb), G_OBJECT (self));
+
+ /* channel state will change to open once the bytestream is open */
+ /* TODO: set a function once we support resume */
+
+ /* Block the bytestream while the user is not connected to the socket */
+ gabble_bytestream_iface_block_reading (self->priv->bytestream, TRUE);
+
+ gabble_bytestream_iface_accept (self->priv->bytestream, NULL, NULL);
+
+ g_value_unset (&out_address);
+}
+
+/**
+ * gabble_file_transfer_channel_provide_file
+ *
+ * Implements D-Bus method ProvideFile
+ * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer
+ */
+static void
+gabble_file_transfer_channel_provide_file (
+ TpSvcChannelTypeFileTransfer *iface,
+ guint address_type,
+ guint access_control,
+ const GValue *access_control_param,
+ DBusGMethodInvocation *context)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (iface);
+ TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->connection;
+ GValue out_address = { 0 };
+ GError *error = NULL;
+
+ if (self->priv->initiator != base_conn->self_handle)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "Channel is not an outgoing transfer");
+ dbus_g_method_return_error (context, error);
+ return;
+ }
+
+ if (self->priv->socket_path != NULL)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "ProvideFile has already been called for this channel");
+ dbus_g_method_return_error (context, error);
+ return;
+ }
+
+ if (!check_address_and_access_control (self, address_type, access_control,
+ access_control_param, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ if (!setup_local_socket (self))
+ {
+ DEBUG ("Could not set up local socket");
+ g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "Could not set up local socket");
+ dbus_g_method_return_error (context, error);
+ }
+
+ g_value_init (&out_address, G_TYPE_STRING);
+ g_value_set_string (&out_address, self->priv->socket_path);
+
+ if (self->priv->remote_accepted)
+ {
+ /* Remote already accepted the file. Channel is Open.
+ * If not channel stay Pending. */
+ tp_svc_channel_type_file_transfer_emit_initial_offset_defined (self,
+ self->priv->initial_offset);
+
+ gabble_file_transfer_channel_set_state (iface,
+ TP_FILE_TRANSFER_STATE_OPEN,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_REQUESTED);
+ }
+
+ tp_svc_channel_type_file_transfer_return_from_provide_file (context,
+ &out_address);
+
+ g_value_unset (&out_address);
+}
+
+static void
+file_transfer_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ TpSvcChannelTypeFileTransferClass *klass =
+ (TpSvcChannelTypeFileTransferClass *) g_iface;
+
+#define IMPLEMENT(x) tp_svc_channel_type_file_transfer_implement_##x (\
+ klass, gabble_file_transfer_channel_##x)
+ IMPLEMENT (accept_file);
+ IMPLEMENT (provide_file);
+#undef IMPLEMENT
+}
+
+static const gchar *
+get_local_unix_socket_path (GabbleFileTransferChannel *self)
+{
+ gchar *path = NULL;
+ gint32 random_int;
+ gchar *random_str;
+ struct stat buf;
+
+ while (TRUE)
+ {
+ random_int = g_random_int_range (0, G_MAXINT32);
+ random_str = g_strdup_printf ("tp-ft-%i", random_int);
+ path = g_build_filename (g_get_tmp_dir (), random_str, NULL);
+ g_free (random_str);
+
+ if (g_stat (path, &buf) != 0)
+ break;
+
+ g_free (path);
+ }
+
+ if (self->priv->socket_path)
+ g_free (self->priv->socket_path);
+
+ self->priv->socket_path = path;
+
+ return path;
+}
+
+/*
+ * Data is available from the channel so we can send it.
+ */
+static void
+transport_handler (GibberTransport *transport,
+ GibberBuffer *data,
+ gpointer user_data)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
+
+ DEBUG ("Data available, writing a %"G_GSIZE_FORMAT" bytes chunk",
+ data->length);
+
+ if (!gabble_bytestream_iface_send (self->priv->bytestream, data->length,
+ (const gchar *) data->data))
+ {
+ DEBUG ("Sending failed. Closing the bytestream");
+ close_bytestrean_and_transport (self);
+ return;
+ }
+
+ transferred_chunk (self, (guint64) data->length);
+
+ if (self->priv->transferred_bytes >= self->priv->size)
+ {
+ DEBUG ("All the file has been sent. Closing the bytestream");
+
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_COMPLETED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_NONE);
+
+ gabble_bytestream_iface_close (self->priv->bytestream, NULL);
+ return;
+ }
+}
+
+static void
+bytestream_write_blocked_cb (GabbleBytestreamIface *bytestream,
+ gboolean blocked,
+ GabbleFileTransferChannel *self)
+{
+ if (blocked)
+ {
+ DEBUG ("bytestream blocked, stop to read data from FT socket");
+ }
+ else
+ {
+ DEBUG ("bytestream unblocked, restart to read data from FT socket");
+ }
+
+ gibber_transport_block_receiving (self->priv->transport, blocked);
+
+}
+
+static void
+file_transfer_send (GabbleFileTransferChannel *self)
+{
+ gabble_signal_connect_weak (self->priv->bytestream, "write-blocked",
+ G_CALLBACK (bytestream_write_blocked_cb), G_OBJECT (self));
+ gibber_transport_set_handler (self->priv->transport, transport_handler, self);
+}
+
+static void
+file_transfer_receive (GabbleFileTransferChannel *self)
+{
+ /* Client is connected, we can now receive data. Unblock the bytestream */
+ g_assert (self->priv->bytestream != NULL);
+ gabble_bytestream_iface_block_reading (self->priv->bytestream, FALSE);
+}
+
+static void
+transport_disconnected_cb (GibberTransport *transport,
+ GabbleFileTransferChannel *self)
+{
+ DEBUG ("transport to local socket has been disconnected");
+
+ if (self->priv->state != TP_FILE_TRANSFER_STATE_COMPLETED)
+ {
+ close_bytestrean_and_transport (self);
+
+ gabble_file_transfer_channel_set_state (
+ TP_SVC_CHANNEL_TYPE_FILE_TRANSFER (self),
+ TP_FILE_TRANSFER_STATE_CANCELLED,
+ TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR);
+ }
+}
+
+static void
+transport_buffer_empty_cb (GibberTransport *transport,
+ GabbleFileTransferChannel *self)
+{
+ /* Buffer is empty so we can unblock the buffer if it was blocked */
+ DEBUG ("file transfer buffer is empty. Unblock the bytestream");
+ gabble_bytestream_iface_block_reading (self->priv->bytestream, FALSE);
+}
+
+/*
+ * Some client is connecting to the Unix socket.
+ */
+static void
+new_connection_cb (GibberListener *listener,
+ GibberTransport *transport,
+ struct sockaddr_storage *addr,
+ guint size,
+ gpointer user_data)
+{
+ GabbleFileTransferChannel *self = GABBLE_FILE_TRANSFER_CHANNEL (user_data);
+ gboolean requested;
+ TpBaseConnection *base_conn = (TpBaseConnection *) \
+ self->priv->connection;
+
+ DEBUG ("Client connected to local socket");
+
+ self->priv->transport = g_object_ref (transport);
+ gabble_signal_connect_weak (transport, "disconnected",
+ G_CALLBACK (transport_disconnected_cb), G_OBJECT (self));
+ gabble_signal_connect_weak (transport, "buffer-empty",
+ G_CALLBACK (transport_buffer_empty_cb), G_OBJECT (self));
+
+ requested = (self->priv->initiator == base_conn->self_handle);
+
+ if (!requested)
+ /* Incoming file transfer */
+ file_transfer_receive (self);
+ else
+ /* Outgoing file transfer */
+ file_transfer_send (self);
+
+ /* stop listening on local socket */
+ g_object_unref (self->priv->listener);
+ self->priv->listener = NULL;
+}
+
+static gboolean
+setup_local_socket (GabbleFileTransferChannel *self)
+{
+ const gchar *path;
+ GError *error = NULL;
+
+ path = get_local_unix_socket_path (self);
+
+ self->priv->listener = gibber_listener_new ();
+
+ /* FIXME: should use the socket type and access control chosen by
+ * the user. */
+ if (!gibber_listener_listen_socket (self->priv->listener, (gchar *) path,
+ FALSE, &error))
+ {
+ DEBUG ("listen_socket failed: %s", error->message);
+ g_error_free (error);
+ g_object_unref (self->priv->listener);
+ self->priv->listener = NULL;
+ return FALSE;
+ }
+
+ g_signal_connect (self->priv->listener, "new-connection",
+ G_CALLBACK (new_connection_cb), self);
+
+ return TRUE;
+}
+
+GabbleFileTransferChannel *
+gabble_file_transfer_channel_new (GabbleConnection *conn,
+ TpHandle handle,
+ TpHandle initiator_handle,
+ TpFileTransferState state,
+ const gchar *content_type,
+ const gchar *filename,
+ guint64 size,
+ TpFileHashType content_hash_type,
+ const gchar *content_hash,
+ const gchar *description,
+ guint64 date,
+ guint64 initial_offset,
+ GabbleBytestreamIface *bytestream)
+
+{
+ return g_object_new (GABBLE_TYPE_FILE_TRANSFER_CHANNEL,
+ "connection", conn,
+ "handle", handle,
+ "initiator-handle", initiator_handle,
+ "state", state,
+ "content-type", content_type,
+ "filename", filename,
+ "size", size,
+ "content-hash-type", content_hash_type,
+ "content-hash", content_hash,
+ "description", description,
+ "date", date,
+ "initial-offset", initial_offset,
+ "bytestream", bytestream,
+ NULL);
+}
diff --git a/src/ft-channel.h b/src/ft-channel.h
new file mode 100644
index 0000000..4442673
--- /dev/null
+++ b/src/ft-channel.h
@@ -0,0 +1,78 @@
+/*
+ * ft-channel.h - Header for GabbleFileTransferChannel
+ * Copyright (C) 2009 Collabora Ltd.
+ * @author: Guillaume Desmottes <guillaume.desmottes at collabora.co.uk>
+ *
+ * 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_FILE_TRANSFER_CHANNEL_H__
+#define __GABBLE_FILE_TRANSFER_CHANNEL_H__
+
+#include <glib-object.h>
+
+#include <extensions/_gen/svc.h>
+#include <extensions/_gen/interfaces.h>
+#include <extensions/_gen/enums.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleFileTransferChannel GabbleFileTransferChannel;
+typedef struct _GabbleFileTransferChannelClass GabbleFileTransferChannelClass;
+typedef struct _GabbleFileTransferChannelPrivate GabbleFileTransferChannelPrivate;
+
+struct _GabbleFileTransferChannelClass {
+ GObjectClass parent_class;
+ TpDBusPropertiesMixinClass dbus_props_class;
+};
+
+struct _GabbleFileTransferChannel {
+ GObject parent;
+
+ GabbleFileTransferChannelPrivate *priv;
+};
+
+GType gabble_file_transfer_channel_get_type (void);
+
+/* TYPE MACROS */
+#define GABBLE_TYPE_FILE_TRANSFER_CHANNEL \
+ (gabble_file_transfer_channel_get_type ())
+#define GABBLE_FILE_TRANSFER_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, GabbleFileTransferChannel))
+#define GABBLE_FILE_TRANSFER_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, \
+ GabbleFileTransferChannelClass))
+#define GABBLE_IS_FILE_TRANSFER_CHANNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL))
+#define GABBLE_IS_FILE_TRANSFER_CHANNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_FILE_TRANSFER_CHANNEL))
+#define GABBLE_FILE_TRANSFER_CHANNEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_FILE_TRANSFER_CHANNEL, \
+ GabbleFileTransferChannelClass))
+
+GabbleFileTransferChannel *
+gabble_file_transfer_channel_new (GabbleConnection *conn,
+ TpHandle handle, TpHandle initiator_handle, TpFileTransferState state,
+ const gchar *content_type, const gchar *filename, guint64 size,
+ TpFileHashType content_hash_type, const gchar *content_hash,
+ const gchar *description, guint64 date, guint64 initial_offset,
+ GabbleBytestreamIface *bytestream);
+
+gboolean gabble_file_transfer_channel_offer_file (
+ GabbleFileTransferChannel *self, GError **error);
+
+G_END_DECLS
+
+#endif /* #ifndef __GABBLE_FILE_TRANSFER_CHANNEL_H__*/
diff --git a/src/ft-manager.c b/src/ft-manager.c
index 6539370..e61821a 100644
--- a/src/ft-manager.c
+++ b/src/ft-manager.c
@@ -33,7 +33,7 @@
#include "namespaces.h"
#include "util.h"
-#include "file-transfer-channel.h"
+#include "ft-channel.h"
#include <telepathy-glib/base-connection.h>
#include <telepathy-glib/channel-factory-iface.h>
--
1.5.6.5
More information about the telepathy-commits
mailing list