summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Staudinger <robsta@linux.intel.com>2012-02-07 16:12:14 +0100
committerRob Staudinger <robsta@linux.intel.com>2012-02-07 16:12:14 +0100
commit67e829a85220a13fc129fa57989f38c2eb647c99 (patch)
treecbe4ed511da5c830bff1d54170cc2205b6066eca
parent2d0380472367bb5435e0f689b0f5251d0bca3c56 (diff)
IncomingFile: Add API for receiving files
Add new YtsIncomingFile class implementing the YtsFileTransfer interface. This class offers a very simple API to receive a file from a remote service to a GFile, and track progress and errors. Also update the "file-transfer" example to demonstrate and exercise the new API. Tags: feature, api
-rw-r--r--docs/reference/ytstenut/Makefile.am1
-rw-r--r--docs/reference/ytstenut/ytstenut-docs.xml.in1
-rw-r--r--examples/file-transfer.c147
-rw-r--r--ytstenut/Makefile.am3
-rw-r--r--ytstenut/marshal.list2
-rw-r--r--ytstenut/yts-client.c138
-rw-r--r--ytstenut/yts-contact-internal.h4
-rw-r--r--ytstenut/yts-contact.c9
-rw-r--r--ytstenut/yts-file-transfer.c84
-rw-r--r--ytstenut/yts-file-transfer.h4
-rw-r--r--ytstenut/yts-incoming-file-internal.h56
-rw-r--r--ytstenut/yts-incoming-file.c452
-rw-r--r--ytstenut/yts-incoming-file.h64
-rw-r--r--ytstenut/yts-outgoing-file-internal.h3
-rw-r--r--ytstenut/yts-outgoing-file.c221
-rw-r--r--ytstenut/yts-outgoing-file.h8
-rw-r--r--ytstenut/yts-roster-internal.h5
-rw-r--r--ytstenut/yts-roster.c17
-rw-r--r--ytstenut/ytstenut.h1
-rw-r--r--ytstenut/ytstenut.sym4
20 files changed, 1026 insertions, 198 deletions
diff --git a/docs/reference/ytstenut/Makefile.am b/docs/reference/ytstenut/Makefile.am
index 303f53e..8f9c788 100644
--- a/docs/reference/ytstenut/Makefile.am
+++ b/docs/reference/ytstenut/Makefile.am
@@ -61,6 +61,7 @@ IGNORE_HFILES = \
yts-error-message.h \
yts-event-message.h \
yts-factory.h \
+ yts-incoming-file-internal.h \
yts-invocation-message.h \
yts-message.h \
yts-metadata.h \
diff --git a/docs/reference/ytstenut/ytstenut-docs.xml.in b/docs/reference/ytstenut/ytstenut-docs.xml.in
index f80b06c..3ff33ae 100644
--- a/docs/reference/ytstenut/ytstenut-docs.xml.in
+++ b/docs/reference/ytstenut/ytstenut-docs.xml.in
@@ -54,6 +54,7 @@
<xi:include href="xml/yts-error.xml"/>
<xi:include href="xml/yts-event-message.xml"/>
<xi:include href="xml/yts-file-transfer.xml"/>
+ <xi:include href="xml/yts-incoming-file.xml"/>
<xi:include href="xml/yts-invocation-message.xml"/>
<xi:include href="xml/yts-message.xml"/>
<xi:include href="xml/yts-metadata-service.xml"/>
diff --git a/examples/file-transfer.c b/examples/file-transfer.c
index 8758f3a..76c777c 100644
--- a/examples/file-transfer.c
+++ b/examples/file-transfer.c
@@ -31,6 +31,42 @@
#define SERVER_UID "org.freedesktop.ytstenut.FileTransferServer"
#define SERVER_JID "ytstenut1@test.collabora.co.uk0"
+static void
+_transfer_error (YtsFileTransfer *transfer,
+ GError *error,
+ void *data)
+{
+ g_debug ("%s()", __FUNCTION__);
+
+ g_critical ("%s", error->message);
+ g_object_unref (transfer);
+}
+
+static void
+_transfer_notify_progress (YtsFileTransfer *transfer,
+ GParamSpec *pspec,
+ void *data)
+{
+ float progress;
+
+ progress = yts_file_transfer_get_progress (transfer);
+
+ if (progress < 0) {
+
+ /* Error, handled above. */
+
+ } else if (progress <= 1.0) {
+
+ printf ("%.2f .. ", progress);
+ fflush (stdout);
+
+ } else {
+
+ printf ("done\n");
+ g_object_unref (transfer);
+ }
+}
+
/*
* Client
*/
@@ -66,42 +102,6 @@ _client_text_message (YtsClient *client,
}
static void
-_outgoing_error (YtsOutgoingFile *outgoing,
- GError *error,
- void *data)
-{
- g_debug ("%s()", __FUNCTION__);
-
- g_critical ("%s", error->message);
- g_object_unref (outgoing);
-}
-
-static void
-_outgoing_notify_progress (YtsOutgoingFile *outgoing,
- GParamSpec *pspec,
- void *data)
-{
- float progress;
-
- progress = yts_file_transfer_get_progress (YTS_FILE_TRANSFER (outgoing));
-
- if (progress < 0) {
-
- /* Error, handled above. */
-
- } else if (progress <= 1.0) {
-
- printf ("%.2f .. ", progress);
- fflush (stdout);
-
- } else {
-
- printf ("done\n");
- g_object_unref (outgoing);
- }
-}
-
-static void
_client_roster_service_added (YtsRoster *roster,
YtsService *service,
char const *path)
@@ -127,9 +127,9 @@ _client_roster_service_added (YtsRoster *roster,
}
g_signal_connect (outgoing, "error",
- G_CALLBACK (_outgoing_error), NULL);
+ G_CALLBACK (_transfer_error), NULL);
g_signal_connect (outgoing, "notify::progress",
- G_CALLBACK (_outgoing_notify_progress), NULL);
+ G_CALLBACK (_transfer_notify_progress), NULL);
g_object_unref (file);
_is_sent = true;
@@ -175,10 +175,6 @@ run_client (bool p2p,
* Server
*/
-typedef struct {
- YtsService *service;
-} ServerData;
-
static void
_server_authenticated (YtsClient *client,
void *data)
@@ -201,45 +197,44 @@ _server_disconnected (YtsClient *client,
}
static void
-_server_text_message (YtsClient *client,
- char const *text,
- ServerData *self)
+_server_incoming_file (YtsClient *client,
+ YtsService *from_service,
+ GHashTable *properties,
+ YtsIncomingFile *incoming,
+ char const *path)
{
- g_debug ("%s() know client: %s", __FUNCTION__,
- self->service ? "yes" : "no");
+ GFile *file;
+ char const *service_id;
+ GError *error = NULL;
- if (self->service) {
- g_debug ("%s() echoing \"%s\"", __FUNCTION__, text);
- yts_service_send_text (YTS_SERVICE (self->service), text);
- }
-}
+ service_id = yts_service_get_id (from_service);
-static void
-_server_roster_service_added (YtsRoster *roster,
- YtsService *service,
- ServerData *self)
-{
- char const *uid;
+ g_debug ("%s() %s", __FUNCTION__, service_id);
+
+ file = g_file_new_for_commandline_arg (path);
+ if (yts_incoming_file_accept (incoming, file, &error)) {
+
+ g_object_ref (incoming);
- uid = yts_service_get_id (service);
+ g_signal_connect (incoming, "error",
+ G_CALLBACK (_transfer_error), NULL);
+ g_signal_connect (incoming, "notify::progress",
+ G_CALLBACK (_transfer_notify_progress), NULL);
- g_debug ("%s() %s", __FUNCTION__, uid);
+ } else {
- /* FIXME, possible race condition when client sends message before
- * it shows up in our roster? */
- if (0 == g_strcmp0 (uid, CLIENT_UID)) {
- /* Should probably take a weak ref here. */
- self->service = service;
+ g_critical ("%s", error->message);
+ g_clear_error (&error);
+ return;
}
}
static int
-run_server (bool p2p)
+run_server (bool p2p,
+ char const *path)
{
- YtsClient *client;
- YtsRoster *roster;
- GMainLoop *mainloop;
- ServerData self = { NULL, };
+ YtsClient *client;
+ GMainLoop *mainloop;
if (p2p)
client = yts_client_new_p2p (SERVER_UID);
@@ -252,12 +247,9 @@ run_server (bool p2p)
G_CALLBACK (_server_ready), NULL);
g_signal_connect (client, "disconnected",
G_CALLBACK (_server_disconnected), NULL);
- g_signal_connect (client, "text-message",
- G_CALLBACK (_server_text_message), &self);
+ g_signal_connect (client, "incoming-file",
+ G_CALLBACK (_server_incoming_file), (void *) path);
- roster = yts_client_get_roster (client);
- g_signal_connect (roster, "service-added",
- G_CALLBACK (_server_roster_service_added), &self);
yts_client_connect (client);
@@ -302,9 +294,8 @@ main (int argc,
g_message ("Running as client ...");
ret = run_client (p2p, path);
} else if (server) {
- g_message ("Running as server ... not implemented yet");
- /* ret = run_server (p2p); */
- ret = -1;
+ g_message ("Running as server ... ");
+ ret = run_server (p2p, path);
} else {
g_warning ("%s : Not running as server or client, quitting", G_STRLOC);
ret = -1;
diff --git a/ytstenut/Makefile.am b/ytstenut/Makefile.am
index e0b4e05..937165c 100644
--- a/ytstenut/Makefile.am
+++ b/ytstenut/Makefile.am
@@ -20,6 +20,7 @@ libhdr_la_SOURCES = \
$(srcdir)/yts-client.h \
$(srcdir)/yts-contact.h \
$(srcdir)/yts-file-transfer.h \
+ $(srcdir)/yts-incoming-file.h \
$(srcdir)/yts-outgoing-file.h \
$(srcdir)/yts-roster.h \
$(srcdir)/yts-service.h \
@@ -60,6 +61,7 @@ libsrc_la_SOURCES = \
$(srcdir)/yts-error-message.c \
$(srcdir)/yts-event-message.c \
$(srcdir)/yts-factory.c \
+ $(srcdir)/yts-incoming-file.c \
$(srcdir)/yts-invocation-message.c \
$(srcdir)/yts-service-emitter.c \
$(srcdir)/yts-file-transfer.c \
@@ -102,6 +104,7 @@ libprv_la_SOURCES = \
$(srcdir)/yts-contact-internal.h \
$(srcdir)/yts-error.h \
$(srcdir)/yts-factory.h \
+ $(srcdir)/yts-incoming-file-internal.h \
$(srcdir)/yts-metadata-internal.h \
$(srcdir)/yts-outgoing-file-internal.h \
$(srcdir)/yts-proxy-factory.h \
diff --git a/ytstenut/marshal.list b/ytstenut/marshal.list
index 4be0386..62e67e1 100644
--- a/ytstenut/marshal.list
+++ b/ytstenut/marshal.list
@@ -5,11 +5,11 @@ VOID:OBJECT
VOID:POINTER
VOID:UINT
VOID:STRING
-BOOLEAN:STRING,STRING,UINT64,UINT64,OBJECT
VOID:STRING,STRING,BOOLEAN
BOOLEAN:POINTER,UINT
VOID:OBJECT,OBJECT
VOID:OBJECT,OBJECT,OBJECT
+VOID:OBJECT,BOXED,OBJECT
VOID:STRING,BOOLEAN
VOID:STRING,BOXED
VOID:STRING,STRING
diff --git a/ytstenut/yts-client.c b/ytstenut/yts-client.c
index c65eaa6..0ab614e 100644
--- a/ytstenut/yts-client.c
+++ b/ytstenut/yts-client.c
@@ -41,6 +41,8 @@
#include "yts-enum-types.h"
#include "yts-error-message.h"
#include "yts-event-message.h"
+#include "yts-file-transfer.h"
+#include "yts-incoming-file-internal.h"
#include "yts-invocation-message.h"
#include "yts-marshal.h"
#include "yts-metadata-internal.h"
@@ -97,6 +99,7 @@ typedef struct {
TpProxy *tp_debug_proxy;
TpYtsStatus *tp_status;
TpYtsClient *tp_client;
+ TpBaseClient *tp_file_handler;
/* Implemented services */
GHashTable *services;
@@ -598,19 +601,6 @@ yts_client_raw_message (YtsClient *self,
{
}
-static gboolean
-yts_client_stop_accumulator (GSignalInvocationHint *ihint,
- GValue *accumulated,
- const GValue *returned,
- gpointer data)
-{
- gboolean cont = g_value_get_boolean (returned);
-
- g_value_set_boolean (accumulated, cont);
-
- return cont;
-}
-
/*
* Callback for #TpProxy::interface-added: we need to add the signals we
* care for here.
@@ -844,6 +834,55 @@ yts_client_setup_debug (YtsClient *self)
g_free (busname);
}
+static void
+_file_handler_handle_channels (TpSimpleHandler *handler,
+ TpAccount *account,
+ TpConnection *connection,
+ GList *channels,
+ GList *requests_satisfied,
+ gint64 user_action_time,
+ TpHandleChannelsContext *context,
+ void *data)
+{
+ YtsClient *self = YTS_CLIENT (data);
+ YtsClientPrivate *priv = GET_PRIVATE (self);
+ GList *iter;
+
+ for (iter = channels;
+ iter && TP_IS_FILE_TRANSFER_CHANNEL (iter->data);
+ iter = iter->next) {
+
+ TpFileTransferChannel *channel = iter->data;
+ char const *remote_contact_id = tp_channel_get_initiator_identifier (TP_CHANNEL (channel));
+ GHashTable *props = tp_channel_borrow_immutable_properties (TP_CHANNEL (channel));
+ char const *const key = "org.freedesktop.Telepathy.Channel.Interface.FileTransfer.Metadata.ServiceName";
+ char const *remote_service_id = tp_asv_get_string (props, key);
+
+ YtsService *service = yts_roster_find_service_by_id (priv->roster,
+ remote_contact_id,
+ remote_service_id);
+
+ YtsIncomingFile *incoming = yts_incoming_file_new (channel);
+
+ GError *error = NULL;
+
+ if (g_initable_init (G_INITABLE (incoming), NULL, &error)) {
+
+ g_signal_emit (self, signals[INCOMING_FILE], 0,
+ service, props, incoming);
+
+ } else {
+
+ // TODO if (error) {}
+ g_critical ("Handling incoming file failed");
+ }
+
+ g_object_unref (incoming);
+ }
+
+ tp_handle_channels_context_accept (context);
+}
+
static bool
_client_status_foreach_interest_add (YtsClientStatus const *client_status,
char const *capability,
@@ -874,6 +913,8 @@ setup_tp_client (YtsClient *self,
TpAccount *account)
{
YtsClientPrivate *priv = GET_PRIVATE (self);
+ GHashTable *filter;
+ GError *error = NULL;
g_return_if_fail (TP_IS_ACCOUNT (account));
@@ -884,7 +925,37 @@ setup_tp_client (YtsClient *self,
yts_client_setup_debug (self);
}
- /* TODO receive files */
+ /* Incoming file machinery. */
+ priv->tp_file_handler = tp_simple_handler_new_with_factory (
+ tp_proxy_get_factory (priv->tp_am),
+ TRUE, FALSE, priv->service_id,
+ TRUE, _file_handler_handle_channels, self, NULL);
+
+ filter = tp_asv_new (
+ TP_PROP_CHANNEL_CHANNEL_TYPE,
+ G_TYPE_STRING,
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
+
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
+ G_TYPE_UINT,
+ TP_HANDLE_TYPE_CONTACT,
+
+ TP_PROP_CHANNEL_REQUESTED,
+ G_TYPE_BOOLEAN,
+ FALSE,
+
+ TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME,
+ G_TYPE_STRING,
+ priv->service_id,
+
+ NULL);
+
+ tp_base_client_take_handler_filter (priv->tp_file_handler, filter);
+ tp_base_client_register (priv->tp_file_handler, &error);
+ if (error) {
+ g_critical ("%s", error->message);
+ g_clear_error (&error);
+ }
/* Publish capabilities */
yts_client_status_foreach_capability (
@@ -1254,6 +1325,13 @@ yts_client_dispose (GObject *object)
priv->unwanted = NULL;
}
+ if (priv->tp_file_handler)
+ {
+ tp_base_client_unregister (priv->tp_file_handler);
+ g_object_unref (priv->tp_file_handler);
+ priv->tp_file_handler = NULL;
+ }
+
if (priv->tp_conn)
{
tp_cli_connection_call_disconnect (priv->tp_conn,
@@ -1550,8 +1628,7 @@ yts_client_class_init (YtsClientClass *klass)
g_signal_new ("error",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL,
+ 0, NULL, NULL,
yts_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
@@ -1559,16 +1636,14 @@ yts_client_class_init (YtsClientClass *klass)
/**
* YtsClient::incoming-file:
* @self: object which emitted the signal.
- * @from: contact_id of the originator
- * @name: name of the file
- * @size: size of the file
- * @offset: offset into the file,
- * @channel: #TpChannel
+ * @service: #YtsService sending the file.
+ * @properties: an a{sv} #GHashTable containing file properties, see telepathy channel properties.
+ * @incoming: the #YtsIncomingFile that is being sent.
*
* The #YtsClient::incoming-file signal is emitted when the client receives
- * incoming request for a file transfer. The signal closure will
- * kickstart the transfer -- this can be prevented by a connected handler
- * returning %FALSE.
+ * incoming request for a file transfer. To accept the file, the signal
+ * handler needs to call #yts_incoming_file_accept(), otherwise the transfer
+ * will be cancelled.
*
* Since: 0.1
*/
@@ -1576,15 +1651,12 @@ yts_client_class_init (YtsClientClass *klass)
g_signal_new ("incoming-file",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
- 0,
- yts_client_stop_accumulator, NULL,
- yts_marshal_BOOLEAN__STRING_STRING_UINT64_UINT64_OBJECT,
- G_TYPE_BOOLEAN, 5,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_UINT64,
- G_TYPE_UINT64,
- TP_TYPE_CHANNEL);
+ 0, NULL, NULL,
+ yts_marshal_VOID__OBJECT_BOXED_OBJECT,
+ G_TYPE_NONE, 3,
+ YTS_TYPE_SERVICE,
+ G_TYPE_HASH_TABLE,
+ YTS_TYPE_INCOMING_FILE);
}
static void
diff --git a/ytstenut/yts-contact-internal.h b/ytstenut/yts-contact-internal.h
index 64570f4..eafc147 100644
--- a/ytstenut/yts-contact-internal.h
+++ b/ytstenut/yts-contact-internal.h
@@ -59,6 +59,10 @@ void
yts_contact_add_service (YtsContact *contact,
YtsService *service);
+YtsService *const
+yts_contact_find_service_by_id (YtsContact *self,
+ char const *service_id);
+
TpContact *const
yts_contact_get_tp_contact (YtsContact const *self);
diff --git a/ytstenut/yts-contact.c b/ytstenut/yts-contact.c
index b947ffb..badb653 100644
--- a/ytstenut/yts-contact.c
+++ b/ytstenut/yts-contact.c
@@ -399,6 +399,15 @@ yts_contact_add_service (YtsContact *self,
g_signal_emit (self, _signals[SIG_SERVICE_ADDED], 0, service);
}
+YtsService *const
+yts_contact_find_service_by_id (YtsContact *self,
+ char const *service_id)
+{
+ YtsContactPrivate *priv = GET_PRIVATE (self);
+
+ return g_hash_table_lookup (priv->services, service_id);
+}
+
void
yts_contact_remove_service_by_id (YtsContact *self,
const char *service_id)
diff --git a/ytstenut/yts-file-transfer.c b/ytstenut/yts-file-transfer.c
index 7498d83..e6772e8 100644
--- a/ytstenut/yts-file-transfer.c
+++ b/ytstenut/yts-file-transfer.c
@@ -23,12 +23,17 @@
#include "yts-file-transfer.h"
#include "yts-marshal.h"
+/* HACK, include known implementers headers for type checks. */
+#include "yts-incoming-file-internal.h"
+#include "yts-outgoing-file-internal.h"
+
#include "config.h"
G_DEFINE_INTERFACE (YtsFileTransfer, yts_file_transfer, G_TYPE_OBJECT)
/**
- * SECTION:yts-file-transfer
+ * SECTION: yts-file-transfer
+ * @title: YtsFileTransfer
* @short_description: Common interface for file transfers between Ytstenut
* services.
*
@@ -37,22 +42,14 @@ G_DEFINE_INTERFACE (YtsFileTransfer, yts_file_transfer, G_TYPE_OBJECT)
*/
enum {
- SIG_ERROR = 0,
+ SIG_CANCELLED = 0,
+ SIG_ERROR,
LAST_SIGNAL
};
static unsigned _signals[LAST_SIGNAL] = { 0, };
-/**
- * SECTION:yts-file_transfer
- * @short_description: Common interface for file up- and downloads.
- *
- * #YtsFileTransfer is an common interface for the #YtsIncomingFile and
- * #YtsOutgoingFile file transmission classes. A value smaller than 0.0
- * denotes error state.
- */
-
static void
yts_file_transfer_default_init (YtsFileTransferInterface *interface)
{
@@ -63,20 +60,31 @@ yts_file_transfer_default_init (YtsFileTransferInterface *interface)
GParamSpec *pspec;
/**
+ * YtsFileTransfer:file:
+ *
+ * The #GFile instance backing the transfer.
+ *
+ * Since: 0.4
+ */
+ pspec = g_param_spec_object ("file", "", "",
+ G_TYPE_FILE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+ g_object_interface_install_property (interface, pspec);
+
+ /**
* YtsFileTransfer:progress:
*
* Read-only property that holds the file transmission progress. Values range
* from 0.0 at the start of the transfer, to 1.0 upon completion.
+ * Error or cancellation leaves the progress with a value smaller than zero.
*/
pspec = g_param_spec_float ("progress", "", "",
-0.1, 1.1, 0.0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_interface_install_property (interface, pspec);
- /*
- * Signals
- */
-
/**
* YtsFileTransfer::error:
* @self: object which emitted the signal.
@@ -94,7 +102,53 @@ yts_file_transfer_default_init (YtsFileTransferInterface *interface)
yts_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
G_TYPE_ERROR);
+
+ /**
+ * YtsFileTransfer::cancelled:
+ * @self: object which emitted the signal.
+ *
+ * This signal is emitted when remote peer cancelled the file transmission.
+ *
+ * Since: 0.4
+ */
+ _signals[SIG_CANCELLED] = g_signal_new ("cancelled",
+ G_TYPE_FROM_INTERFACE (interface),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ yts_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ }
+}
+
+/**
+ * yts_file_transfer_get_file:
+ * @self: object on which to invoke this method.
+ *
+ * See YtsFileTransfer:file property for details.
+ *
+ * Returns: #GFile instance backing the transfer.
+ */
+GFile *const
+yts_file_transfer_get_file (YtsFileTransfer *self)
+{
+ /* Known subclasses, so hack it up for const return. */
+
+ if (YTS_IS_INCOMING_FILE (self)) {
+
+ return yts_incoming_file_get_file (YTS_INCOMING_FILE (self));
+
+ } else if (YTS_IS_OUTGOING_FILE (self)) {
+
+ return yts_outgoing_file_get_file (YTS_OUTGOING_FILE (self));
+
+ } else {
+
+ g_warning ("Unhandled YtsFileTransfer instance %s in %s",
+ G_OBJECT_TYPE_NAME (self),
+ __FUNCTION__);
}
+
+ return NULL;
}
/**
diff --git a/ytstenut/yts-file-transfer.h b/ytstenut/yts-file-transfer.h
index 6aa482b..b5afb8b 100644
--- a/ytstenut/yts-file-transfer.h
+++ b/ytstenut/yts-file-transfer.h
@@ -22,6 +22,7 @@
#define YTS_FILE_TRANSFER_H
#include <glib-object.h>
+#include <gio/gio.h>
G_BEGIN_DECLS
@@ -48,6 +49,9 @@ typedef struct {
GType
yts_file_transfer_get_type (void) G_GNUC_CONST;
+GFile *const
+yts_file_transfer_get_file (YtsFileTransfer *self);
+
float
yts_file_transfer_get_progress (YtsFileTransfer *self);
diff --git a/ytstenut/yts-incoming-file-internal.h b/ytstenut/yts-incoming-file-internal.h
new file mode 100644
index 0000000..cef2638
--- /dev/null
+++ b/ytstenut/yts-incoming-file-internal.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2012 Intel Corp.
+ *
+ * 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 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Rob Staudinger <robsta@linux.intel.com>
+ */
+
+#ifndef YTS_INCOMING_FILE_INTERNAL_H
+#define YTS_INCOMING_FILE_INTERNAL_H
+
+#include <gio/gio.h>
+#include <telepathy-glib/telepathy-glib.h>
+#include <ytstenut/yts-incoming-file.h>
+
+G_BEGIN_DECLS
+
+#define YTS_INCOMING_FILE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), YTS_TYPE_INCOMING_FILE, YtsIncomingFileClass))
+
+#define YTS_IS_INCOMING_FILE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), YTS_TYPE_INCOMING_FILE))
+
+#define YTS_INCOMING_FILE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), YTS_TYPE_INCOMING_FILE, YtsIncomingFileClass))
+
+struct YtsIncomingFile {
+ GObject parent;
+};
+
+typedef struct {
+ GObjectClass parent;
+} YtsIncomingFileClass;
+
+YtsIncomingFile *
+yts_incoming_file_new (TpFileTransferChannel *tp_channel);
+
+GFile *const
+yts_incoming_file_get_file (YtsIncomingFile *self);
+
+G_END_DECLS
+
+#endif /* YTS_INCOMING_FILE_INTERNAL_H */
+
diff --git a/ytstenut/yts-incoming-file.c b/ytstenut/yts-incoming-file.c
new file mode 100644
index 0000000..fdd4c32
--- /dev/null
+++ b/ytstenut/yts-incoming-file.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright © 2012 Intel Corp.
+ *
+ * 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 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Rob Staudinger <robsta@linux.intel.com>
+ * Using portions from telepathy-ytstenut's server-file-transfer.c, © Intel.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+#include "yts-file-transfer.h"
+#include "yts-incoming-file-internal.h"
+
+#include "config.h"
+
+static void
+_initable_interface_init (GInitableIface *interface);
+
+static void
+_file_transfer_interface_init (YtsFileTransferInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (YtsIncomingFile,
+ yts_incoming_file,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ _initable_interface_init)
+ G_IMPLEMENT_INTERFACE (YTS_TYPE_FILE_TRANSFER,
+ _file_transfer_interface_init))
+
+/**
+ * SECTION: yts-incoming-file
+ * @title: YtsIncomingFile
+ * @short_description: File download implementation.
+ *
+ * #YtsIncomingFile represents an incoming file downlod operation from another
+ * Ytstenut service.
+ *
+ * TODO add cancellation in dispose(), and cancel API. Take care not to touch
+ * self any more after cancellation.
+ */
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), YTS_TYPE_INCOMING_FILE, YtsIncomingFilePrivate))
+
+enum {
+ PROP_0,
+
+ /* YtsFileTransfer */
+ PROP_FILE_TRANSFER_FILE,
+ PROP_FILE_TRANSFER_PROGRESS,
+
+ /* YtsIncomingFile */
+ PROP_TP_CHANNEL
+};
+
+typedef struct {
+
+ /* Properties */
+ GFile *file;
+ float progress;
+
+ /* Data */
+ TpFileTransferChannel *tp_channel;
+ uint64_t size;
+} YtsIncomingFilePrivate;
+
+static gboolean
+_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error);
+
+/*
+ * GInitable interface
+ */
+
+static void
+_initable_interface_init (GInitableIface *interface)
+{
+ static bool _is_initialized = false;
+
+ if (!_is_initialized) {
+ interface->init = _initable_init;
+ _is_initialized = true;
+ }
+}
+
+/*
+ * YtsFileTransfer interface
+ */
+
+static void
+_file_transfer_interface_init (YtsFileTransferInterface *interface)
+{
+ /* Nothing to do here, since this interface is only about properties and
+ * signals. */
+}
+
+/*
+ * YtsIncomingFile
+ */
+
+static void
+set_and_emit_error (YtsIncomingFile *self,
+ GError *error)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (self);
+
+ g_signal_emit_by_name (self, "error", error);
+ priv->progress = -0.1;
+ g_object_notify (G_OBJECT (self), "progress");
+}
+
+static void
+_channel_close (GObject *source,
+ GAsyncResult *result,
+ void *data)
+{
+ /* Object might be in dispose() already, do not touch self/priv any more. */
+ GError *error_in = NULL;
+
+ tp_channel_close_finish (TP_CHANNEL (source), result, &error_in);
+ if (error_in) {
+ g_critical ("Failed to close the file-transfer channel");
+ g_clear_error (&error_in);
+ }
+}
+
+static void
+_channel_notify_state (TpFileTransferChannel *channel,
+ GParamSpec *pspec,
+ YtsIncomingFile *self)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (self);
+ TpFileTransferState state;
+ TpFileTransferStateChangeReason reason;
+ bool close_channel = false;
+
+ state = tp_file_transfer_channel_get_state (channel, &reason);
+
+ if (state == TP_FILE_TRANSFER_STATE_COMPLETED) {
+
+ priv->progress = 1.1;
+ g_object_notify (G_OBJECT (self), "progress");
+ close_channel = true;
+
+ } else if (reason == TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED) {
+
+ g_signal_emit_by_name (self, "cancelled");
+ priv->progress = -0.1;
+ g_object_notify (G_OBJECT (self), "progress");
+ close_channel = true;
+
+ } else if (reason == TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR) {
+
+ GError *error = g_error_new (YTS_INCOMING_FILE_ERROR,
+ YTS_INCOMING_FILE_ERROR_LOCAL,
+ "Transmission failed because of a local error");
+ set_and_emit_error (self, error);
+ g_error_free (error);
+ close_channel = true;
+
+ } else if (reason == TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR) {
+
+ GError *error = g_error_new (YTS_INCOMING_FILE_ERROR,
+ YTS_INCOMING_FILE_ERROR_REMOTE,
+ "Transmission failed because of a remote error");
+ set_and_emit_error (self, error);
+ g_error_free (error);
+ close_channel = true;
+ }
+
+ if (close_channel && priv->tp_channel) {
+ tp_channel_close_async (TP_CHANNEL (priv->tp_channel),
+ _channel_close,
+ self);
+ g_object_unref (priv->tp_channel);
+ priv->tp_channel = NULL;
+ }
+}
+
+static void
+_channel_notify_transferred_bytes (TpFileTransferChannel *channel,
+ GParamSpec *pspec,
+ YtsIncomingFile *self)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (self);
+ float transferred_bytes;
+
+ transferred_bytes = tp_file_transfer_channel_get_transferred_bytes (channel);
+ priv->progress = transferred_bytes / priv->size;
+ g_object_notify (G_OBJECT (self), "progress");
+}
+
+static gboolean
+_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error_out)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (initable);
+
+ if (!TP_IS_FILE_TRANSFER_CHANNEL (priv->tp_channel)) {
+ if (error_out) {
+ *error_out = g_error_new (YTS_INCOMING_FILE_ERROR,
+ YTS_INCOMING_FILE_ERROR_NO_CHANNEL,
+ "No channel to receive file");
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static void
+_get_property (GObject *object,
+ unsigned property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (object);
+
+ switch (property_id) {
+
+ /* YtsFileTransfer */
+
+ case PROP_FILE_TRANSFER_FILE:
+ g_value_set_object (value, priv->file);
+ break;
+ case PROP_FILE_TRANSFER_PROGRESS:
+ g_value_set_float (value, priv->progress);
+ break;
+
+ /* YtsIncomingFile */
+
+ case PROP_TP_CHANNEL:
+ g_value_set_object (value, priv->tp_channel);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+_set_property (GObject *object,
+ unsigned property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (object);
+
+ switch (property_id) {
+
+ /* YtsFileTransfer */
+
+ case PROP_FILE_TRANSFER_FILE:
+ /* Construct-only, but optional, because it's set behind the scenes
+ * in _accept(). */
+ if (g_value_get_object (value))
+ priv->file = g_value_dup_object (value);
+ break;
+
+ /* YtsIncomingFile */
+
+ case PROP_TP_CHANNEL: {
+ /* Construct-only */
+ priv->tp_channel = g_value_dup_object (value);
+ priv->size = tp_file_transfer_channel_get_size (priv->tp_channel);
+ } break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+_dispose (GObject *object)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (object);
+
+ if (priv->tp_channel) {
+ tp_channel_close_async (TP_CHANNEL (priv->tp_channel),
+ _channel_close,
+ object);
+ g_object_unref (priv->tp_channel);
+ priv->tp_channel = NULL;
+ }
+
+ G_OBJECT_CLASS (yts_incoming_file_parent_class)->finalize (object);
+}
+
+static void
+_finalize (GObject *object)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (object);
+
+ if (priv->file) {
+ g_object_unref (priv->file);
+ priv->file = NULL;
+ }
+
+ G_OBJECT_CLASS (yts_incoming_file_parent_class)->finalize (object);
+}
+
+static void
+yts_incoming_file_class_init (YtsIncomingFileClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (YtsIncomingFilePrivate));
+
+ object_class->get_property = _get_property;
+ object_class->set_property = _set_property;
+ object_class->dispose = _dispose;
+ object_class->finalize = _finalize;
+
+ /* YtsFileTransfer properties */
+
+ g_object_class_override_property (object_class,
+ PROP_FILE_TRANSFER_FILE,
+ "file");
+ g_object_class_override_property (object_class,
+ PROP_FILE_TRANSFER_PROGRESS,
+ "progress");
+
+ /* YtsIncomingFile properties */
+
+ pspec = g_param_spec_object ("tp-channel", "", "",
+ TP_TYPE_FILE_TRANSFER_CHANNEL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_TP_CHANNEL, pspec);
+}
+
+static void
+yts_incoming_file_init (YtsIncomingFile *self)
+{
+}
+
+YtsIncomingFile *
+yts_incoming_file_new (TpFileTransferChannel *tp_channel)
+{
+ return g_object_new (YTS_TYPE_INCOMING_FILE,
+ "tp-channel", tp_channel,
+ NULL);
+}
+
+GFile *const
+yts_incoming_file_get_file (YtsIncomingFile *self)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (YTS_IS_INCOMING_FILE (self), NULL);
+
+ return priv->file;
+}
+
+static void
+_channel_accept_file (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ YtsIncomingFile *self = YTS_INCOMING_FILE (data);
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (self);
+ GError *error_in = NULL;
+
+ tp_file_transfer_channel_accept_file_finish (priv->tp_channel,
+ result,
+ &error_in);
+ if (error_in) {
+ GError *error_out = g_error_new (YTS_INCOMING_FILE_ERROR,
+ YTS_INCOMING_FILE_ERROR_ACCEPT_FAILED,
+ "Failed to start file transfer "
+ "(%s)",
+ error_in->message);
+ set_and_emit_error (self, error_out);
+ g_error_free (error_out);
+ g_clear_error (&error_in);
+ }
+}
+
+bool
+yts_incoming_file_accept (YtsIncomingFile *self,
+ GFile *file,
+ GError **error_out)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (YTS_IS_INCOMING_FILE (self), false);
+ g_return_val_if_fail (G_IS_FILE (file), false);
+
+ if (G_IS_FILE (priv->file)) {
+ if (error_out) {
+ *error_out = g_error_new (YTS_INCOMING_FILE_ERROR,
+ YTS_INCOMING_FILE_ERROR_ALREADY_ACCEPTED,
+ "Incoming file has already been accepted");
+ }
+ return false;
+ }
+
+ priv->file = g_object_ref (file);
+ g_object_notify (G_OBJECT (self), "file");
+
+ g_signal_connect (priv->tp_channel, "notify::state",
+ G_CALLBACK (_channel_notify_state), self);
+
+ g_signal_connect (priv->tp_channel, "notify::transferred-bytes",
+ G_CALLBACK (_channel_notify_transferred_bytes), self);
+
+ tp_file_transfer_channel_accept_file_async (priv->tp_channel,
+ priv->file,
+ 0,
+ _channel_accept_file,
+ self);
+
+ return true;
+}
+
+bool
+yts_incoming_file_reject (YtsIncomingFile *self,
+ GError **error)
+{
+ YtsIncomingFilePrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (YTS_IS_INCOMING_FILE (self), false);
+ g_return_val_if_fail (TP_IS_FILE_TRANSFER_CHANNEL (priv->tp_channel), false);
+
+ tp_channel_close_async (TP_CHANNEL (priv->tp_channel),
+ _channel_close,
+ self);
+ g_object_unref (priv->tp_channel);
+ priv->tp_channel = NULL;
+
+ return true;
+}
+
diff --git a/ytstenut/yts-incoming-file.h b/ytstenut/yts-incoming-file.h
new file mode 100644
index 0000000..7b79a4d
--- /dev/null
+++ b/ytstenut/yts-incoming-file.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2012 Intel Corp.
+ *
+ * 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 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Rob Staudinger <robsta@linux.intel.com>
+ */
+
+#ifndef YTS_INCOMING_FILE_H
+#define YTS_INCOMING_FILE_H
+
+#include <stdbool.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define YTS_TYPE_INCOMING_FILE yts_incoming_file_get_type()
+
+#define YTS_INCOMING_FILE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), YTS_TYPE_INCOMING_FILE, YtsIncomingFile))
+
+#define YTS_IS_INCOMING_FILE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YTS_TYPE_INCOMING_FILE))
+
+typedef struct YtsIncomingFile YtsIncomingFile;
+
+GType
+yts_incoming_file_get_type (void) G_GNUC_CONST;
+
+#define YTS_INCOMING_FILE_ERROR g_quark_from_static_string (__FILE__)
+
+enum {
+ YTS_INCOMING_FILE_ERROR_NO_CHANNEL,
+ YTS_INCOMING_FILE_ERROR_ACCEPT_FAILED,
+ YTS_INCOMING_FILE_ERROR_ALREADY_ACCEPTED,
+ YTS_INCOMING_FILE_ERROR_LOCAL,
+ YTS_INCOMING_FILE_ERROR_REMOTE
+};
+
+bool
+yts_incoming_file_accept (YtsIncomingFile *self,
+ GFile *file,
+ GError **error);
+
+bool
+yts_incoming_file_reject (YtsIncomingFile *self,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* YTS_INCOMING_FILE_H */
+
diff --git a/ytstenut/yts-outgoing-file-internal.h b/ytstenut/yts-outgoing-file-internal.h
index 3025cc8..f350f7c 100644
--- a/ytstenut/yts-outgoing-file-internal.h
+++ b/ytstenut/yts-outgoing-file-internal.h
@@ -51,6 +51,9 @@ yts_outgoing_file_new (TpAccount *tp_account,
char const *recipient_service_id,
char const *description);
+GFile *const
+yts_outgoing_file_get_file (YtsOutgoingFile *self);
+
G_END_DECLS
#endif /* YTS_OUTGOING_FILE_INTERNAL_H */
diff --git a/ytstenut/yts-outgoing-file.c b/ytstenut/yts-outgoing-file.c
index 0bdb9d6..67a3d38 100644
--- a/ytstenut/yts-outgoing-file.c
+++ b/ytstenut/yts-outgoing-file.c
@@ -20,6 +20,7 @@
*/
#include <stdbool.h>
+#include <stdint.h>
#include <telepathy-glib/telepathy-glib.h>
#include "yts-file-transfer.h"
@@ -42,13 +43,14 @@ G_DEFINE_TYPE_WITH_CODE (YtsOutgoingFile,
_file_transfer_interface_init))
/**
- * SECTION:yts-outgoing-file
+ * SECTION: yts-outgoing-file
+ * @title: YtsOutgoingFile
* @short_description: File upload implementation.
*
* #YtsOutgoingFile represents an ongoing file upload operation to another
* Ytstenut service.
*
- * TODO add cancellation in finalize(), and cancel API. Take care not to touch
+ * TODO add cancellation in dispose(), and cancel API. Take care not to touch
* self any more after cancellation.
*/
@@ -60,13 +62,13 @@ enum {
/* YtsFileTransfer */
PROP_FILE_TRANSFER_PROGRESS,
+ PROP_FILE_TRANSFER_FILE,
/* YtsOutgoingFile */
PROP_TP_ACCOUNT,
- PROP_FILE,
+ PROP_DESCRIPTION,
PROP_RECIPIENT_CONTACT_ID,
- PROP_RECIPIENT_SERVICE_ID,
- PROP_DESCRIPTION
+ PROP_RECIPIENT_SERVICE_ID
};
typedef struct {
@@ -77,8 +79,9 @@ typedef struct {
char *recipient_service_id;
char *description;
float progress;
- /* Data*/
- goffset file_size;
+ /* Data */
+ TpFileTransferChannel *tp_channel;
+ uint64_t size;
} YtsOutgoingFilePrivate;
static gboolean
@@ -185,15 +188,32 @@ set_and_emit_error (YtsOutgoingFile *self,
}
static void
+_channel_close (GObject *source,
+ GAsyncResult *result,
+ void *data)
+{
+ /* Object might be in dispose() already, do not touch self/priv any more. */
+ GError *error_in = NULL;
+
+ tp_channel_close_finish (TP_CHANNEL (source), result, &error_in);
+ if (error_in) {
+ g_critical ("Failed to close the file-transfer channel");
+ g_clear_error (&error_in);
+ }
+}
+
+static void
_channel_provide_file (GObject *object,
GAsyncResult *result,
gpointer data)
{
- YtsOutgoingFile *self = YTS_OUTGOING_FILE (data);
- TpFileTransferChannel *channel = TP_FILE_TRANSFER_CHANNEL (object);
- GError *error_in = NULL;
+ YtsOutgoingFile *self = YTS_OUTGOING_FILE (data);
+ YtsOutgoingFilePrivate *priv = GET_PRIVATE (self);
+ GError *error_in = NULL;
- tp_file_transfer_channel_provide_file_finish (channel, result, &error_in);
+ tp_file_transfer_channel_provide_file_finish (priv->tp_channel,
+ result,
+ &error_in);
if (error_in) {
GError *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR,
YTS_OUTGOING_FILE_ERROR_TRANSFER_FAILED,
@@ -212,12 +232,16 @@ _channel_notify_state (TpFileTransferChannel *channel,
YtsOutgoingFile *self)
{
YtsOutgoingFilePrivate *priv = GET_PRIVATE (self);
- TpFileTransferState state = tp_file_transfer_channel_get_state (channel, NULL);
+ TpFileTransferState state;
+ TpFileTransferStateChangeReason reason;
+ bool close_channel = false;
+
+ state = tp_file_transfer_channel_get_state (priv->tp_channel, &reason);
if (state == TP_FILE_TRANSFER_STATE_ACCEPTED
&& tp_channel_get_requested (TP_CHANNEL (channel))) {
- tp_file_transfer_channel_provide_file_async (channel,
+ tp_file_transfer_channel_provide_file_async (priv->tp_channel,
priv->file,
_channel_provide_file,
self);
@@ -226,9 +250,40 @@ _channel_notify_state (TpFileTransferChannel *channel,
priv->progress = 1.1;
g_object_notify (G_OBJECT (self), "progress");
+ close_channel = true;
+
+ } else if (reason == TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_STOPPED) {
- /* PONDERING hope that's correct. */
- g_object_unref (channel);
+ g_signal_emit_by_name (self, "cancelled");
+ priv->progress = -0.1;
+ g_object_notify (G_OBJECT (self), "progress");
+ close_channel = true;
+
+ } else if (reason == TP_FILE_TRANSFER_STATE_CHANGE_REASON_LOCAL_ERROR) {
+
+ GError *error = g_error_new (YTS_OUTGOING_FILE_ERROR,
+ YTS_OUTGOING_FILE_ERROR_LOCAL,
+ "Transmission failed because of a local error");
+ set_and_emit_error (self, error);
+ g_error_free (error);
+ close_channel = true;
+
+ } else if (reason == TP_FILE_TRANSFER_STATE_CHANGE_REASON_REMOTE_ERROR) {
+
+ GError *error = g_error_new (YTS_OUTGOING_FILE_ERROR,
+ YTS_OUTGOING_FILE_ERROR_REMOTE,
+ "Transmission failed because of a remote error");
+ set_and_emit_error (self, error);
+ g_error_free (error);
+ close_channel = true;
+ }
+
+ if (close_channel && priv->tp_channel) {
+ tp_channel_close_async (TP_CHANNEL (priv->tp_channel),
+ _channel_close,
+ self);
+ g_object_unref (priv->tp_channel);
+ priv->tp_channel = NULL;
}
}
@@ -241,7 +296,7 @@ _channel_notify_transferred_bytes (TpFileTransferChannel *channel,
float transferred_bytes;
transferred_bytes = tp_file_transfer_channel_get_transferred_bytes (channel);
- priv->progress = transferred_bytes / priv->file_size;
+ priv->progress = transferred_bytes / priv->size;
g_object_notify (G_OBJECT (self), "progress");
}
@@ -251,15 +306,16 @@ _account_channel_request_create (GObject *source,
gpointer data)
{
YtsOutgoingFile *self = YTS_OUTGOING_FILE (data);
+ YtsOutgoingFilePrivate *priv = GET_PRIVATE (self);
TpAccountChannelRequest *channel_request = TP_ACCOUNT_CHANNEL_REQUEST (source);
- TpChannel *channel;
GError *error_in = NULL;
- channel = tp_account_channel_request_create_and_handle_channel_finish (
+ priv->tp_channel = TP_FILE_TRANSFER_CHANNEL (
+ tp_account_channel_request_create_and_handle_channel_finish (
channel_request,
result,
NULL,
- &error_in);
+ &error_in));
if (error_in) {
GError *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR,
YTS_OUTGOING_FILE_ERROR_CHANNEL_FAILED,
@@ -272,9 +328,9 @@ _account_channel_request_create (GObject *source,
return;
}
- g_signal_connect (channel, "notify::state",
+ g_signal_connect (priv->tp_channel, "notify::state",
G_CALLBACK (_channel_notify_state), self);
- g_signal_connect (channel, "notify::transferred-bytes",
+ g_signal_connect (priv->tp_channel, "notify::transferred-bytes",
G_CALLBACK (_channel_notify_transferred_bytes), self);
}
@@ -316,7 +372,7 @@ _initable_init (GInitable *initable,
name = g_file_info_get_name (info);
mimetype = g_file_info_get_content_type (info);
g_file_info_get_modification_time (info, &mtime);
- priv->file_size = g_file_info_get_size (info);
+ priv->size = g_file_info_get_size (info);
/* Now we have everything prepared to continue, let's create the
* Ytstenut channel handler with service name specified. */
@@ -329,7 +385,7 @@ _initable_init (GInitable *initable,
TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DESCRIPTION, G_TYPE_STRING, priv->description,
TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME, G_TYPE_STRING, name,
TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_INITIAL_OFFSET, G_TYPE_UINT64, (guint64) 0,
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE, G_TYPE_UINT64, (guint64) priv->file_size,
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE, G_TYPE_UINT64, (guint64) priv->size,
/* here is the remote service */
TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME, G_TYPE_STRING, priv->recipient_service_id,
NULL);
@@ -363,14 +419,17 @@ _get_property (GObject *object,
/* YtsFileTransfer */
+ case PROP_FILE_TRANSFER_FILE:
+ g_value_set_object (value, priv->file);
+ break;
case PROP_FILE_TRANSFER_PROGRESS:
g_value_set_float (value, priv->progress);
break;
/* YtsOutgoingFile */
- case PROP_FILE:
- g_value_set_object (value, priv->file);
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, priv->description);
break;
case PROP_RECIPIENT_CONTACT_ID:
g_value_set_string (value, priv->recipient_contact_id);
@@ -378,9 +437,6 @@ _get_property (GObject *object,
case PROP_RECIPIENT_SERVICE_ID:
g_value_set_string (value, priv->recipient_service_id);
break;
- case PROP_DESCRIPTION:
- g_value_set_string (value, priv->description);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -395,37 +451,56 @@ _set_property (GObject *object,
YtsOutgoingFilePrivate *priv = GET_PRIVATE (object);
switch (property_id) {
+
+ /* YtsFileTransfer */
+
+ case PROP_FILE_TRANSFER_FILE:
+ /* Construct-only */
+ priv->file = g_value_dup_object (value);
+ break;
+
+ /* YtsOutgoingFile */
+
case PROP_TP_ACCOUNT:
/* Construct-only */
priv->tp_account = g_value_dup_object (value);
break;
- case PROP_FILE:
+ case PROP_DESCRIPTION:
/* Construct-only */
- priv->file = g_value_dup_object (value);
- break;
+ priv->description = g_value_dup_string (value);
+ break;
case PROP_RECIPIENT_CONTACT_ID:
/* Construct-only */
priv->recipient_contact_id = g_value_dup_string (value);
- break;
+ break;
case PROP_RECIPIENT_SERVICE_ID:
/* Construct-only */
priv->recipient_service_id = g_value_dup_string (value);
break;
- case PROP_DESCRIPTION:
- /* Construct-only */
- priv->description = g_value_dup_string (value);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
-_finalize (GObject *object)
+_dispose (GObject *object)
{
YtsOutgoingFilePrivate *priv = GET_PRIVATE (object);
- g_debug ("%s() %s", __FILE__, __FUNCTION__);
+ if (priv->tp_channel) {
+ tp_channel_close_async (TP_CHANNEL (priv->tp_channel),
+ _channel_close,
+ object);
+ priv->tp_channel = NULL;
+ }
+
+ G_OBJECT_CLASS (yts_outgoing_file_parent_class)->finalize (object);
+}
+
+static void
+_finalize (GObject *object)
+{
+ YtsOutgoingFilePrivate *priv = GET_PRIVATE (object);
if (priv->tp_account) {
g_object_unref (priv->tp_account);
@@ -465,41 +540,45 @@ yts_outgoing_file_class_init (YtsOutgoingFileClass *klass)
object_class->get_property = _get_property;
object_class->set_property = _set_property;
+ object_class->dispose = _dispose;
object_class->finalize = _finalize;
/* YtsFileTransfer properties */
g_object_class_override_property (object_class,
+ PROP_FILE_TRANSFER_FILE,
+ "file");
+ g_object_class_override_property (object_class,
PROP_FILE_TRANSFER_PROGRESS,
"progress");
/* YtsOutgoingFile properties */
/**
- * YtsOutgoingFile:tp-account:
+ * YtsOutgoingFile:description:
*
- * Internal use only.
+ * Describes the purpose of the file transfer. May be %NULL.
+ *
+ * Since: 0.4
*/
- pspec = g_param_spec_object ("tp-account", "", "",
- TP_TYPE_ACCOUNT,
- G_PARAM_WRITABLE |
+ pspec = g_param_spec_string ("description", "", "",
+ NULL,
+ G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_TP_ACCOUNT, pspec);
+ g_object_class_install_property (object_class, PROP_DESCRIPTION, pspec);
/**
- * YtsOutgoingFile:file:
- *
- * The #GFile that is going to be transmitted.
+ * YtsOutgoingFile:tp-account:
*
- * Since: 0.4
+ * Internal use only.
*/
- pspec = g_param_spec_object ("file", "", "",
- G_TYPE_FILE,
- G_PARAM_READWRITE |
+ pspec = g_param_spec_object ("tp-account", "", "",
+ TP_TYPE_ACCOUNT,
+ G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_FILE, pspec);
+ g_object_class_install_property (object_class, PROP_TP_ACCOUNT, pspec);
/**
* YtsOutgoingFile:recipient-contact-id:
@@ -532,24 +611,6 @@ yts_outgoing_file_class_init (YtsOutgoingFileClass *klass)
g_object_class_install_property (object_class,
PROP_RECIPIENT_SERVICE_ID,
pspec);
-
- /**
- * YtsOutgoingFile:description:
- *
- * Describes the purpose of the file transfer. Optional, may be %NULL.
- *
- * Since: 0.4
- */
- pspec = g_param_spec_string ("description", "", "",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class,
- PROP_DESCRIPTION,
- pspec);
-
- /* Signals */
}
static void
@@ -573,3 +634,23 @@ yts_outgoing_file_new (TpAccount *tp_account,
NULL);
}
+char const *
+yts_outgoing_file_get_description (YtsOutgoingFile *self)
+{
+ YtsOutgoingFilePrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (YTS_IS_OUTGOING_FILE (self), NULL);
+
+ return priv->description;
+}
+
+GFile *const
+yts_outgoing_file_get_file (YtsOutgoingFile *self)
+{
+ YtsOutgoingFilePrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (YTS_IS_OUTGOING_FILE (self), NULL);
+
+ return priv->file;
+}
+
diff --git a/ytstenut/yts-outgoing-file.h b/ytstenut/yts-outgoing-file.h
index 702a1b6..a81390d 100644
--- a/ytstenut/yts-outgoing-file.h
+++ b/ytstenut/yts-outgoing-file.h
@@ -48,9 +48,15 @@ enum {
YTS_OUTGOING_FILE_ERROR_NO_RECIPIENT_CONTACT,
YTS_OUTGOING_FILE_ERROR_NO_RECIPIENT_SERVICE,
YTS_OUTGOING_FILE_ERROR_CHANNEL_FAILED,
- YTS_OUTGOING_FILE_ERROR_TRANSFER_FAILED
+ YTS_OUTGOING_FILE_ERROR_TRANSFER_FAILED,
+ YTS_OUTGOING_FILE_ERROR_LOCAL,
+ YTS_OUTGOING_FILE_ERROR_REMOTE,
+ YTS_OUTGOING_FILE_ERROR_CHANNEL_CLOSE_FAILED
};
+char const *
+yts_outgoing_file_get_description (YtsOutgoingFile *self);
+
G_END_DECLS
#endif /* YTS_OUTGOING_FILE_H */
diff --git a/ytstenut/yts-roster-internal.h b/ytstenut/yts-roster-internal.h
index e4e7721..a8c6f29 100644
--- a/ytstenut/yts-roster-internal.h
+++ b/ytstenut/yts-roster-internal.h
@@ -58,6 +58,11 @@ yts_roster_add_service (YtsRoster *roster,
GHashTable *names,
GHashTable *statuses);
+YtsService *const
+yts_roster_find_service_by_id (YtsRoster *self,
+ char const *contact_id,
+ char const *service_id);
+
void
yts_roster_remove_service_by_id (YtsRoster *roster,
char const *contact_id,
diff --git a/ytstenut/yts-roster.c b/ytstenut/yts-roster.c
index 0fc957d..510733f 100644
--- a/ytstenut/yts-roster.c
+++ b/ytstenut/yts-roster.c
@@ -23,6 +23,7 @@
#include <telepathy-ytstenut-glib/telepathy-ytstenut-glib.h>
#include "yts-contact-impl.h"
+#include "yts-contact-internal.h"
#include "yts-marshal.h"
#include "yts-metadata.h"
#include "yts-outgoing-file.h"
@@ -253,6 +254,22 @@ yts_roster_init (YtsRoster *self)
g_object_unref);
}
+YtsService *const
+yts_roster_find_service_by_id (YtsRoster *self,
+ char const *contact_id,
+ char const *service_id)
+{
+ YtsContact *contact;
+ YtsService *service = NULL;
+
+ contact = yts_roster_find_contact_by_id (self, contact_id);
+ if (contact) {
+ service = yts_contact_find_service_by_id (contact, service_id);
+ }
+
+ return service;
+}
+
/*
* yts_roster_remove_service_by_id:
* @self: object on which to invoke this method.
diff --git a/ytstenut/ytstenut.h b/ytstenut/ytstenut.h
index 3bc311b..9b9913f 100644
--- a/ytstenut/ytstenut.h
+++ b/ytstenut/ytstenut.h
@@ -25,6 +25,7 @@
#include <ytstenut/yts-capability.h>
#include <ytstenut/yts-client.h>
#include <ytstenut/yts-contact.h>
+#include <ytstenut/yts-incoming-file.h>
#include <ytstenut/yts-outgoing-file.h>
#include <ytstenut/yts-file-transfer.h>
#include <ytstenut/yts-roster.h>
diff --git a/ytstenut/ytstenut.sym b/ytstenut/ytstenut.sym
index fea0261..4ced70f 100644
--- a/ytstenut/ytstenut.sym
+++ b/ytstenut/ytstenut.sym
@@ -20,6 +20,10 @@ yts_contact_get_name
yts_contact_get_type
yts_file_transfer_get_progress
yts_file_transfer_get_type
+yts_incoming_file_accept
+yts_incoming_file_get_type
+yts_incoming_file_reject
+yts_outgoing_file_get_description
yts_outgoing_file_get_type
yts_message_get_type
yts_message_new