diff options
author | Rob Staudinger <robsta@linux.intel.com> | 2012-02-02 23:15:30 +0100 |
---|---|---|
committer | Rob Staudinger <robsta@linux.intel.com> | 2012-02-02 23:15:30 +0100 |
commit | d3b84c2c71e33539e909a77f90ddf1fad0bae3f6 (patch) | |
tree | 72d496cfde3d9949acc10795ed2621a812ea3095 | |
parent | 2916018392df240d98fe00dc9e1c08b2547aeb5f (diff) |
OutgoingFile: Add API for transmitting files
Add new YtsOutgoingFile class implementing also new YtsFileTransfer
interface. This class offers a very simple API to send a GFile to
a remote service and track progress and errors.
Also create example "file-transfer" to demonstrate and exercise the
new API.
Tags: feature, api
-rw-r--r-- | docs/reference/ytstenut/Makefile.am | 1 | ||||
-rw-r--r-- | docs/reference/ytstenut/ytstenut-docs.xml.in | 2 | ||||
-rw-r--r-- | examples/Makefile.am | 5 | ||||
-rw-r--r-- | examples/file-transfer.c | 314 | ||||
-rw-r--r-- | ytstenut/Makefile.am | 7 | ||||
-rw-r--r-- | ytstenut/marshal.list | 3 | ||||
-rw-r--r-- | ytstenut/yts-client-status.c | 3 | ||||
-rw-r--r-- | ytstenut/yts-client.c | 43 | ||||
-rw-r--r-- | ytstenut/yts-contact-impl.c | 29 | ||||
-rw-r--r-- | ytstenut/yts-contact-impl.h | 9 | ||||
-rw-r--r-- | ytstenut/yts-contact.c | 21 | ||||
-rw-r--r-- | ytstenut/yts-file-transfer.c | 120 | ||||
-rw-r--r-- | ytstenut/yts-file-transfer.h | 57 | ||||
-rw-r--r-- | ytstenut/yts-outgoing-file-internal.h | 57 | ||||
-rw-r--r-- | ytstenut/yts-outgoing-file.c | 575 | ||||
-rw-r--r-- | ytstenut/yts-outgoing-file.h | 57 | ||||
-rw-r--r-- | ytstenut/yts-roster-impl.c | 31 | ||||
-rw-r--r-- | ytstenut/yts-roster-impl.h | 10 | ||||
-rw-r--r-- | ytstenut/yts-roster.c | 31 | ||||
-rw-r--r-- | ytstenut/yts-service-emitter.c | 32 | ||||
-rw-r--r-- | ytstenut/yts-service-emitter.h | 8 | ||||
-rw-r--r-- | ytstenut/yts-service.c | 28 | ||||
-rw-r--r-- | ytstenut/yts-service.h | 8 | ||||
-rw-r--r-- | ytstenut/ytstenut.h | 2 | ||||
-rw-r--r-- | ytstenut/ytstenut.sym | 4 |
25 files changed, 1449 insertions, 8 deletions
diff --git a/docs/reference/ytstenut/Makefile.am b/docs/reference/ytstenut/Makefile.am index 7768e92..303f53e 100644 --- a/docs/reference/ytstenut/Makefile.am +++ b/docs/reference/ytstenut/Makefile.am @@ -65,6 +65,7 @@ IGNORE_HFILES = \ yts-message.h \ yts-metadata.h \ yts-metadata-internal.h \ + yts-outgoing-file-internal.h \ yts-profile-adapter.h \ yts-profile.h \ yts-profile-impl.h \ diff --git a/docs/reference/ytstenut/ytstenut-docs.xml.in b/docs/reference/ytstenut/ytstenut-docs.xml.in index 0de6dfc..f80b06c 100644 --- a/docs/reference/ytstenut/ytstenut-docs.xml.in +++ b/docs/reference/ytstenut/ytstenut-docs.xml.in @@ -53,10 +53,12 @@ <xi:include href="xml/yts-error-message.xml"/> <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-invocation-message.xml"/> <xi:include href="xml/yts-message.xml"/> <xi:include href="xml/yts-metadata-service.xml"/> <xi:include href="xml/yts-metadata.xml"/> + <xi:include href="xml/yts-outgoing-file.xml"/> <xi:include href="xml/yts-proxy-service.xml"/> <xi:include href="xml/yts-proxy.xml"/> <xi:include href="xml/yts-response-message.xml"/> diff --git a/examples/Makefile.am b/examples/Makefile.am index d3ed64f..e9e18d8 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -15,6 +15,7 @@ LDADD = $(YTS_LIBS) noinst_PROGRAMS = \ dictionary-message \ echo \ + file-transfer \ status \ $(NULL) @@ -26,6 +27,10 @@ echo_SOURCES = \ echo.c \ $(NULL) +file_transfer_SOURCES = \ + file-transfer.c \ + $(NULL) + status_SOURCES = \ status.c \ $(NULL) diff --git a/examples/file-transfer.c b/examples/file-transfer.c new file mode 100644 index 0000000..8758f3a --- /dev/null +++ b/examples/file-transfer.c @@ -0,0 +1,314 @@ +/* + * 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> + */ + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <glib.h> + +#include <ytstenut/ytstenut.h> + +#define CLIENT_UID "org.freedesktop.ytstenut.FileTransferClient" +#define CLIENT_JID "ytstenut2@test.collabora.co.uk0" + +#define SERVER_UID "org.freedesktop.ytstenut.FileTransferServer" +#define SERVER_JID "ytstenut1@test.collabora.co.uk0" + +/* + * Client + */ + +static void +_client_authenticated (YtsClient *client, + void *data) +{ + g_debug ("%s()", __FUNCTION__); +} + +static void +_client_ready (YtsClient *client, + void *data) +{ + g_debug ("%s()", __FUNCTION__); +} + +static void +_client_disconnected (YtsClient *client, + void *data) +{ + g_debug ("%s()", __FUNCTION__); +} + +static void +_client_text_message (YtsClient *client, + char const *text, + void *data) +{ + g_debug ("%s()", __FUNCTION__); + g_debug ("Message is \"%s\"", text); +} + +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) +{ + static bool _is_sent = false; + char const *service_id; + + service_id = yts_service_get_id (service); + + if (!_is_sent && + 0 == g_strcmp0 (service_id, SERVER_UID)) { + + GError *error = NULL; + GFile *file = g_file_new_for_path (path); + YtsOutgoingFile *outgoing = yts_service_send_file (service, + file, + "Hello, like file?", + &error); + if (error) { + g_critical ("%s", error->message); + g_clear_error (&error); + return; + } + + g_signal_connect (outgoing, "error", + G_CALLBACK (_outgoing_error), NULL); + g_signal_connect (outgoing, "notify::progress", + G_CALLBACK (_outgoing_notify_progress), NULL); + + g_object_unref (file); + _is_sent = true; + } +} + +static int +run_client (bool p2p, + char const *path) +{ + YtsClient *client; + YtsRoster *roster; + GMainLoop *mainloop; + + if (p2p) + client = yts_client_new_p2p (CLIENT_UID); + else + client = yts_client_new_c2s (CLIENT_JID, CLIENT_UID); + + g_signal_connect (client, "authenticated", + G_CALLBACK (_client_authenticated), NULL); + g_signal_connect (client, "ready", + G_CALLBACK (_client_ready), NULL); + g_signal_connect (client, "disconnected", + G_CALLBACK (_client_disconnected), NULL); + g_signal_connect (client, "text-message", + G_CALLBACK (_client_text_message), NULL); + + roster = yts_client_get_roster (client); + g_signal_connect (roster, "service-added", + G_CALLBACK (_client_roster_service_added), (void *) path); + + yts_client_connect (client); + + mainloop = g_main_loop_new (NULL, false); + g_main_loop_run (mainloop); + g_main_loop_unref (mainloop); + + return EXIT_SUCCESS; +} + +/* + * Server + */ + +typedef struct { + YtsService *service; +} ServerData; + +static void +_server_authenticated (YtsClient *client, + void *data) +{ + g_debug ("%s()", __FUNCTION__); +} + +static void +_server_ready (YtsClient *client, + void *data) +{ + g_debug ("%s()", __FUNCTION__); +} + +static void +_server_disconnected (YtsClient *client, + void *data) +{ + g_debug ("%s()", __FUNCTION__); +} + +static void +_server_text_message (YtsClient *client, + char const *text, + ServerData *self) +{ + g_debug ("%s() know client: %s", __FUNCTION__, + self->service ? "yes" : "no"); + + if (self->service) { + g_debug ("%s() echoing \"%s\"", __FUNCTION__, text); + yts_service_send_text (YTS_SERVICE (self->service), text); + } +} + +static void +_server_roster_service_added (YtsRoster *roster, + YtsService *service, + ServerData *self) +{ + char const *uid; + + uid = yts_service_get_id (service); + + g_debug ("%s() %s", __FUNCTION__, uid); + + /* 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; + } +} + +static int +run_server (bool p2p) +{ + YtsClient *client; + YtsRoster *roster; + GMainLoop *mainloop; + ServerData self = { NULL, }; + + if (p2p) + client = yts_client_new_p2p (SERVER_UID); + else + client = yts_client_new_c2s (SERVER_JID, SERVER_UID); + + g_signal_connect (client, "authenticated", + G_CALLBACK (_server_authenticated), NULL); + g_signal_connect (client, "ready", + 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); + + roster = yts_client_get_roster (client); + g_signal_connect (roster, "service-added", + G_CALLBACK (_server_roster_service_added), &self); + + yts_client_connect (client); + + mainloop = g_main_loop_new (NULL, false); + g_main_loop_run (mainloop); + g_main_loop_unref (mainloop); + + return EXIT_SUCCESS; +} + +int +main (int argc, + char **argv) +{ + bool client = false; + bool server = true; + bool p2p = false; + char const *path = NULL; + GOptionEntry entries[] = { + { "client", 'c', 0, G_OPTION_ARG_NONE, &client, "Run as client", NULL }, + { "server", 's', 0, G_OPTION_ARG_NONE, &server, "Run as server (default)", NULL }, + { "p2p", 'p', 0, G_OPTION_ARG_NONE, &p2p, "Run in p2p mode", NULL }, + { "file", 'f', 0, G_OPTION_ARG_STRING, &path, "Path to read or save file", NULL }, + { NULL, } + }; + + GError *error = NULL; + GOptionContext *context; + int ret; + + g_type_init (); + + context = g_option_context_new ("- Ytstenut file-transfer example"); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_parse (context, &argc, &argv, &error); + if (error) { + g_warning ("%s : %s", G_STRLOC, error->message); + g_clear_error (&error); + } + + if (client) { + 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; + } else { + g_warning ("%s : Not running as server or client, quitting", G_STRLOC); + ret = -1; + } + + return ret; +} diff --git a/ytstenut/Makefile.am b/ytstenut/Makefile.am index baa658c..7792059 100644 --- a/ytstenut/Makefile.am +++ b/ytstenut/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir) \ - -DG_LOG_DOMAIN=\"$(PACKAGE)\0unspecified\" \ + -DG_LOG_DOMAIN=\"$(PACKAGE)\\0unspecified\" \ -DG_DISABLE_DEPRECATED \ $(NULL) @@ -19,6 +19,8 @@ libhdr_la_SOURCES = \ $(srcdir)/yts-capability.h \ $(srcdir)/yts-client.h \ $(srcdir)/yts-contact.h \ + $(srcdir)/yts-file-transfer.h \ + $(srcdir)/yts-outgoing-file.h \ $(srcdir)/yts-roster.h \ $(srcdir)/yts-service.h \ $(srcdir)/yts-version.h \ @@ -61,6 +63,8 @@ libsrc_la_SOURCES = \ $(srcdir)/yts-factory.c \ $(srcdir)/yts-invocation-message.c \ $(srcdir)/yts-service-emitter.c \ + $(srcdir)/yts-file-transfer.c \ + $(srcdir)/yts-outgoing-file.c \ $(srcdir)/yts-proxy.c \ $(srcdir)/yts-proxy-factory.c \ $(srcdir)/yts-proxy-service.c \ @@ -101,6 +105,7 @@ libprv_la_SOURCES = \ $(srcdir)/yts-error.h \ $(srcdir)/yts-factory.h \ $(srcdir)/yts-metadata-internal.h \ + $(srcdir)/yts-outgoing-file-internal.h \ $(srcdir)/yts-proxy-factory.h \ $(srcdir)/yts-proxy-internal.h \ $(srcdir)/yts-proxy-service-impl.h \ diff --git a/ytstenut/marshal.list b/ytstenut/marshal.list index a5828b9..4be0386 100644 --- a/ytstenut/marshal.list +++ b/ytstenut/marshal.list @@ -14,3 +14,6 @@ VOID:STRING,BOOLEAN VOID:STRING,BOXED VOID:STRING,STRING VOID:STRING,STRING,BOXED +OBJECT:OBJECT,STRING,POINTER +OBJECT:OBJECT,OBJECT,STRING,POINTER +OBJECT:OBJECT,OBJECT,OBJECT,STRING,POINTER diff --git a/ytstenut/yts-client-status.c b/ytstenut/yts-client-status.c index d19f06d..a23e125 100644 --- a/ytstenut/yts-client-status.c +++ b/ytstenut/yts-client-status.c @@ -106,7 +106,8 @@ yts_client_status_class_init (YtsClientStatusClass *klass) pspec = g_param_spec_string ("service-id", "", "", NULL, G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SERVICE_ID, pspec); } diff --git a/ytstenut/yts-client.c b/ytstenut/yts-client.c index 4fa59f6..2faee06 100644 --- a/ytstenut/yts-client.c +++ b/ytstenut/yts-client.c @@ -45,6 +45,7 @@ #include "yts-invocation-message.h" #include "yts-marshal.h" #include "yts-metadata-internal.h" +#include "yts-outgoing-file-internal.h" #include "yts-response-message.h" #include "yts-roster-impl.h" #include "yts-service.h" @@ -1282,6 +1283,42 @@ _roster_send_message (YtsRoster *roster, yts_client_send_message (self, contact, service_id, message); } +static YtsOutgoingFile * +_roster_send_file (YtsRoster *roster, + YtsContact *contact, + YtsService *service, + GFile *file, + char const *description, + GError **error_out, + YtsClient *self) +{ + YtsClientPrivate *priv = GET_PRIVATE (self); + YtsOutgoingFile *outgoing; + char const *recipient_contact_id; + char const *recipient_service_id; + GError *error = NULL; + + g_return_val_if_fail (YTS_IS_CLIENT (self), NULL); + + recipient_contact_id = yts_contact_get_id (contact); + recipient_service_id = yts_service_get_id (service); + outgoing = yts_outgoing_file_new (priv->tp_account, + file, + recipient_contact_id, + recipient_service_id, + description); + + g_initable_init (G_INITABLE (outgoing), NULL, &error); + if (error) { + g_object_unref (outgoing); + g_propagate_error (error_out, error); + g_clear_error (&error); + return NULL; + } + + return outgoing; +} + static void _roster_contact_removed (YtsRoster *roster, YtsContact *contact, @@ -1373,14 +1410,20 @@ yts_client_constructed (GObject *object) priv->roster = yts_roster_impl_new (); g_signal_connect (priv->roster, "send-message", G_CALLBACK (_roster_send_message), object); + g_signal_connect (priv->roster, "send-file", + G_CALLBACK (_roster_send_file), object); g_signal_connect (priv->roster, "contact-removed", G_CALLBACK (_roster_contact_removed), object); priv->unwanted = yts_roster_impl_new (); +#if 0 /* TODO */ g_signal_connect (priv->unwanted, "send-message", G_CALLBACK (_roster_send_message), object); + g_signal_connect (priv->roster, "send-file", + G_CALLBACK (_roster_send_file), object); g_signal_connect (priv->unwanted, "contact-removed", G_CALLBACK (_roster_contact_removed), object); +#endif if (!priv->service_id || !*priv->service_id) { g_critical ("Service-ID must be set at construction time."); diff --git a/ytstenut/yts-contact-impl.c b/ytstenut/yts-contact-impl.c index 644433d..3854f3b 100644 --- a/ytstenut/yts-contact-impl.c +++ b/ytstenut/yts-contact-impl.c @@ -29,6 +29,7 @@ G_DEFINE_TYPE (YtsContactImpl, yts_contact_impl, YTS_TYPE_CONTACT) enum { SIG_SEND_MESSAGE, + SIG_SEND_FILE, N_SIGNALS }; @@ -54,6 +55,17 @@ yts_contact_impl_class_init (YtsContactImplClass *klass) G_TYPE_NONE, 2, YTS_TYPE_SERVICE, YTS_TYPE_METADATA); + + _signals[SIG_SEND_FILE] = g_signal_new ("send-file", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + yts_marshal_OBJECT__OBJECT_OBJECT_STRING_POINTER, + YTS_TYPE_OUTGOING_FILE, 4, + YTS_TYPE_SERVICE, + G_TYPE_FILE, + G_TYPE_STRING, + G_TYPE_POINTER); } static void @@ -82,3 +94,20 @@ yts_contact_impl_send_message (YtsContactImpl *self, service, message); } +YtsOutgoingFile * +yts_contact_impl_send_file (YtsContactImpl *self, + YtsService *service, + GFile *file, + char const *description, + GError **error_out) +{ + YtsOutgoingFile *transfer; + + transfer = NULL; + g_signal_emit (self, _signals[SIG_SEND_FILE], 0, + service, file, description, error_out, + &transfer); + + return transfer; +} + diff --git a/ytstenut/yts-contact-impl.h b/ytstenut/yts-contact-impl.h index 9dcc6d8..ce9a980 100644 --- a/ytstenut/yts-contact-impl.h +++ b/ytstenut/yts-contact-impl.h @@ -22,9 +22,11 @@ #define YTS_CONTACT_IMPL_H #include <glib-object.h> +#include <gio/gio.h> #include <telepathy-glib/contact.h> #include <ytstenut/yts-contact-internal.h> #include <ytstenut/yts-metadata.h> +#include <ytstenut/yts-outgoing-file.h> G_BEGIN_DECLS @@ -64,6 +66,13 @@ yts_contact_impl_send_message (YtsContactImpl *self, YtsService *service, YtsMetadata *message); +YtsOutgoingFile * +yts_contact_impl_send_file (YtsContactImpl *self, + YtsService *service, + GFile *file, + char const *description, + GError **error_out); + G_END_DECLS #endif /* YTS_CONTACT_IMPL_H */ diff --git a/ytstenut/yts-contact.c b/ytstenut/yts-contact.c index c11ef52..045749e 100644 --- a/ytstenut/yts-contact.c +++ b/ytstenut/yts-contact.c @@ -247,6 +247,22 @@ _service_send_message (YtsService *service, yts_contact_impl_send_message (YTS_CONTACT_IMPL (self), service, message); } +static YtsOutgoingFile * +_service_send_file (YtsService *service, + GFile *file, + char const *description, + GError **error_out, + YtsContact *self) +{ + /* This is a bit of a hack, we require the non-abstract subclass to + * implement this interface. */ + return yts_contact_impl_send_file (YTS_CONTACT_IMPL (self), + service, + file, + description, + error_out); +} + static void _service_added (YtsContact *self, YtsService *service, @@ -264,6 +280,8 @@ _service_added (YtsContact *self, g_signal_connect (service, "send-message", G_CALLBACK (_service_send_message), self); + g_signal_connect (service, "send-file", + G_CALLBACK (_service_send_file), self); } static void @@ -282,6 +300,9 @@ _service_removed (YtsContact *self, g_signal_handlers_disconnect_by_func (service, _service_send_message, self); + g_signal_handlers_disconnect_by_func (service, + _service_send_file, + self); } static void diff --git a/ytstenut/yts-file-transfer.c b/ytstenut/yts-file-transfer.c new file mode 100644 index 0000000..7498d83 --- /dev/null +++ b/ytstenut/yts-file-transfer.c @@ -0,0 +1,120 @@ +/* + * Copyright © 2011 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> + */ + +#include <stdbool.h> + +#include "yts-file-transfer.h" +#include "yts-marshal.h" + +#include "config.h" + +G_DEFINE_INTERFACE (YtsFileTransfer, yts_file_transfer, G_TYPE_OBJECT) + +/** + * SECTION:yts-file-transfer + * @short_description: Common interface for file transfers between Ytstenut + * services. + * + * #YtsFileTransfer represents an ongoing file transfer operation between + * Ytstenut services, and offers progress- and status-reporting facilities. + */ + +enum { + SIG_ERROR = 0, + + 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) +{ + static bool _initialized = false; + + if (!_initialized) { + + GParamSpec *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. + */ + 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. + * @error: the #GError causing the transmission to fail. + * + * This signal is emitted when the transmission failed, transporting + * error details. + * + * Since: 0.4 + */ + _signals[SIG_ERROR] = g_signal_new ("error", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + yts_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + G_TYPE_ERROR); + } +} + +/** + * yts_file_transfer_get_progress: + * @self: object on which to invoke this method. + * + * Get progress of file transfer operation, see #YtsFileTransfer.progress for + * details about the range of values. + * + * Returns: file transfer progress. + */ +float +yts_file_transfer_get_progress (YtsFileTransfer *self) +{ + float progress; + + g_return_val_if_fail (YTS_IS_FILE_TRANSFER (self), -1.0); + + g_object_get (self, "progress", &progress, NULL); + + return progress; +} + diff --git a/ytstenut/yts-file-transfer.h b/ytstenut/yts-file-transfer.h new file mode 100644 index 0000000..6aa482b --- /dev/null +++ b/ytstenut/yts-file-transfer.h @@ -0,0 +1,57 @@ +/* + * Copyright © 2011 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_FILE_TRANSFER_H +#define YTS_FILE_TRANSFER_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define YTS_TYPE_FILE_TRANSFER yts_file_transfer_get_type () + +#define YTS_FILE_TRANSFER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), YTS_TYPE_FILE_TRANSFER, YtsFileTransfer)) + +#define YTS_IS_FILE_TRANSFER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YTS_TYPE_FILE_TRANSFER)) + +#define YTS_FILE_TRANSFER_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((obj), YTS_TYPE_FILE_TRANSFER, YtsFileTransferInterface)) + +typedef struct YtsFileTransfer YtsFileTransfer; + +typedef struct { + + /*< private >*/ + GTypeInterface parent; + +} YtsFileTransferInterface; + +GType +yts_file_transfer_get_type (void) G_GNUC_CONST; + +float +yts_file_transfer_get_progress (YtsFileTransfer *self); + +G_END_DECLS + +#endif /* YTS_FILE_TRANSFER_H */ + diff --git a/ytstenut/yts-outgoing-file-internal.h b/ytstenut/yts-outgoing-file-internal.h new file mode 100644 index 0000000..3025cc8 --- /dev/null +++ b/ytstenut/yts-outgoing-file-internal.h @@ -0,0 +1,57 @@ +/* + * 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_OUTGOING_FILE_INTERNAL_H +#define YTS_OUTGOING_FILE_INTERNAL_H + +#include <gio/gio.h> +#include <telepathy-glib/account.h> +#include <ytstenut/yts-outgoing-file.h> + +G_BEGIN_DECLS + +#define YTS_OUTGOING_FILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), YTS_TYPE_OUTGOING_FILE, YtsOutgoingFileClass)) + +#define YTS_IS_OUTGOING_FILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), YTS_TYPE_OUTGOING_FILE)) + +#define YTS_OUTGOING_FILE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), YTS_TYPE_OUTGOING_FILE, YtsOutgoingFileClass)) + +struct YtsOutgoingFile { + GObject parent; +}; + +typedef struct { + GObjectClass parent; +} YtsOutgoingFileClass; + +YtsOutgoingFile * +yts_outgoing_file_new (TpAccount *tp_account, + GFile *file, + char const *recipient_contact_id, + char const *recipient_service_id, + char const *description); + +G_END_DECLS + +#endif /* YTS_OUTGOING_FILE_INTERNAL_H */ + diff --git a/ytstenut/yts-outgoing-file.c b/ytstenut/yts-outgoing-file.c new file mode 100644 index 0000000..0bdb9d6 --- /dev/null +++ b/ytstenut/yts-outgoing-file.c @@ -0,0 +1,575 @@ +/* + * 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 <telepathy-glib/telepathy-glib.h> + +#include "yts-file-transfer.h" +#include "yts-outgoing-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 (YtsOutgoingFile, + yts_outgoing_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-outgoing-file + * @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 + * self any more after cancellation. + */ + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), YTS_TYPE_OUTGOING_FILE, YtsOutgoingFilePrivate)) + +enum { + PROP_0, + + /* YtsFileTransfer */ + PROP_FILE_TRANSFER_PROGRESS, + + /* YtsOutgoingFile */ + PROP_TP_ACCOUNT, + PROP_FILE, + PROP_RECIPIENT_CONTACT_ID, + PROP_RECIPIENT_SERVICE_ID, + PROP_DESCRIPTION +}; + +typedef struct { + /* Properties */ + TpAccount *tp_account; + GFile *file; + char *recipient_contact_id; + char *recipient_service_id; + char *description; + float progress; + /* Data*/ + goffset file_size; +} YtsOutgoingFilePrivate; + +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. */ +} + +/* + * YtsOutgoingFile + */ + +static bool +validate (YtsOutgoingFile *self, + GError **error_out) +{ + YtsOutgoingFilePrivate *priv = GET_PRIVATE (self); + + if (!TP_IS_ACCOUNT (priv->tp_account)) { + if (error_out) { + *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR, + YTS_OUTGOING_FILE_ERROR_NO_ACCOUNT, + "No account to send file from"); + } + return false; + } + + if (TP_CONNECTION_STATUS_CONNECTED != + tp_account_get_connection_status (priv->tp_account, NULL)) { + if (error_out) { + char const *reason = tp_account_get_detailed_error (priv->tp_account, + NULL); + *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR, + YTS_OUTGOING_FILE_ERROR_NO_CONNECTION, + "Account not online (%s)", reason); + } + return false; + } + + if (!G_IS_FILE (priv->file)) { + if (error_out) { + *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR, + YTS_OUTGOING_FILE_ERROR_NO_FILE, + "No file specified for file transfer"); + } + return false; + } + + if (NULL == priv->recipient_contact_id) { + if (error_out) { + *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR, + YTS_OUTGOING_FILE_ERROR_NO_RECIPIENT_CONTACT, + "No recipient contact specified for file transfer"); + } + return false; + } + + if (NULL == priv->recipient_service_id) { + if (error_out) { + *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR, + YTS_OUTGOING_FILE_ERROR_NO_RECIPIENT_SERVICE, + "No recipient service specified for file transfer"); + } + return false; + } + + return true; +} + +static void +set_and_emit_error (YtsOutgoingFile *self, + GError *error) +{ + YtsOutgoingFilePrivate *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_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; + + tp_file_transfer_channel_provide_file_finish (channel, result, &error_in); + if (error_in) { + GError *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR, + YTS_OUTGOING_FILE_ERROR_TRANSFER_FAILED, + "Failed to transfer file " + "(%s)", + error_in->message); + set_and_emit_error (self, error_out); + g_error_free (error_out); + g_clear_error (&error_in); + } +} + +static void +_channel_notify_state (TpFileTransferChannel *channel, + GParamSpec *pspec, + YtsOutgoingFile *self) +{ + YtsOutgoingFilePrivate *priv = GET_PRIVATE (self); + TpFileTransferState state = tp_file_transfer_channel_get_state (channel, NULL); + + if (state == TP_FILE_TRANSFER_STATE_ACCEPTED + && tp_channel_get_requested (TP_CHANNEL (channel))) { + + tp_file_transfer_channel_provide_file_async (channel, + priv->file, + _channel_provide_file, + self); + + } else if (state == TP_FILE_TRANSFER_STATE_COMPLETED) { + + priv->progress = 1.1; + g_object_notify (G_OBJECT (self), "progress"); + + /* PONDERING hope that's correct. */ + g_object_unref (channel); + } +} + +static void +_channel_notify_transferred_bytes (TpFileTransferChannel *channel, + GParamSpec *pspec, + YtsOutgoingFile *self) +{ + YtsOutgoingFilePrivate *priv = GET_PRIVATE (self); + float transferred_bytes; + + transferred_bytes = tp_file_transfer_channel_get_transferred_bytes (channel); + priv->progress = transferred_bytes / priv->file_size; + g_object_notify (G_OBJECT (self), "progress"); +} + +static void +_account_channel_request_create (GObject *source, + GAsyncResult *result, + gpointer data) +{ + YtsOutgoingFile *self = YTS_OUTGOING_FILE (data); + TpAccountChannelRequest *channel_request = TP_ACCOUNT_CHANNEL_REQUEST (source); + TpChannel *channel; + GError *error_in = NULL; + + channel = tp_account_channel_request_create_and_handle_channel_finish ( + channel_request, + result, + NULL, + &error_in); + if (error_in) { + GError *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR, + YTS_OUTGOING_FILE_ERROR_CHANNEL_FAILED, + "Failed to create the file transfer channel " + "(%s)", + error_in->message); + set_and_emit_error (self, error_out); + g_error_free (error_out); + g_clear_error (&error_in); + return; + } + + g_signal_connect (channel, "notify::state", + G_CALLBACK (_channel_notify_state), self); + g_signal_connect (channel, "notify::transferred-bytes", + G_CALLBACK (_channel_notify_transferred_bytes), self); +} + +static gboolean +_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error_out) +{ + YtsOutgoingFilePrivate *priv = GET_PRIVATE (initable); + GFileInfo *info; + char const *name; + char const *mimetype; + GTimeVal mtime; + GHashTable *request; + TpAccountChannelRequest *channel_request; + GError *error = NULL; + + if (!validate (YTS_OUTGOING_FILE (initable), error_out)) { + return false; + } + + info = g_file_query_info (priv->file, + "*", + G_FILE_QUERY_INFO_NONE, + NULL, + &error); + if (NULL == info) { + if (error_out) { + char *uri = g_file_get_uri (priv->file); + *error_out = g_error_new (YTS_OUTGOING_FILE_ERROR, + YTS_OUTGOING_FILE_ERROR_READ_FAILED, + "Failed to read file %s", + uri); + g_free (uri); + } + return false; + } + + 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); + + /* Now we have everything prepared to continue, let's create the + * Ytstenut channel handler with service name specified. */ + request = 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_TARGET_ID, G_TYPE_STRING, priv->recipient_contact_id, + TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE, G_TYPE_STRING, mimetype, + TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE, G_TYPE_INT64, (gint64) mtime.tv_sec, + 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, + /* here is the remote service */ + TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME, G_TYPE_STRING, priv->recipient_service_id, + NULL); + + channel_request = tp_account_channel_request_new ( + priv->tp_account, + request, + TP_USER_ACTION_TIME_CURRENT_TIME); + + tp_account_channel_request_create_and_handle_channel_async ( + channel_request, + NULL, + _account_channel_request_create, + initable); + + g_hash_table_unref (request); + g_object_unref (info); + + return true; +} + +static void +_get_property (GObject *object, + unsigned property_id, + GValue *value, + GParamSpec *pspec) +{ + YtsOutgoingFilePrivate *priv = GET_PRIVATE (object); + + switch (property_id) { + + /* YtsFileTransfer */ + + case PROP_FILE_TRANSFER_PROGRESS: + g_value_set_float (value, priv->progress); + break; + + /* YtsOutgoingFile */ + + case PROP_FILE: + g_value_set_object (value, priv->file); + break; + case PROP_RECIPIENT_CONTACT_ID: + g_value_set_string (value, priv->recipient_contact_id); + break; + 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); + } +} + +static void +_set_property (GObject *object, + unsigned property_id, + const GValue *value, + GParamSpec *pspec) +{ + YtsOutgoingFilePrivate *priv = GET_PRIVATE (object); + + switch (property_id) { + case PROP_TP_ACCOUNT: + /* Construct-only */ + priv->tp_account = g_value_dup_object (value); + break; + case PROP_FILE: + /* Construct-only */ + priv->file = g_value_dup_object (value); + break; + case PROP_RECIPIENT_CONTACT_ID: + /* Construct-only */ + priv->recipient_contact_id = g_value_dup_string (value); + 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) +{ + YtsOutgoingFilePrivate *priv = GET_PRIVATE (object); + + g_debug ("%s() %s", __FILE__, __FUNCTION__); + + if (priv->tp_account) { + g_object_unref (priv->tp_account); + priv->tp_account = NULL; + } + + if (priv->file) { + g_object_unref (priv->file); + priv->file = NULL; + } + + if (priv->recipient_contact_id) { + g_free (priv->recipient_contact_id); + priv->recipient_contact_id = NULL; + } + + if (priv->recipient_service_id) { + g_free (priv->recipient_service_id); + priv->recipient_service_id = NULL; + } + + if (priv->description) { + g_free (priv->description); + priv->description = NULL; + } + + G_OBJECT_CLASS (yts_outgoing_file_parent_class)->finalize (object); +} + +static void +yts_outgoing_file_class_init (YtsOutgoingFileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (YtsOutgoingFilePrivate)); + + object_class->get_property = _get_property; + object_class->set_property = _set_property; + object_class->finalize = _finalize; + + /* YtsFileTransfer properties */ + + g_object_class_override_property (object_class, + PROP_FILE_TRANSFER_PROGRESS, + "progress"); + + /* YtsOutgoingFile properties */ + + /** + * YtsOutgoingFile:tp-account: + * + * Internal use only. + */ + 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_TP_ACCOUNT, pspec); + + /** + * YtsOutgoingFile:file: + * + * The #GFile that is going to be transmitted. + * + * 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_class_install_property (object_class, PROP_FILE, pspec); + + /** + * YtsOutgoingFile:recipient-contact-id: + * + * Contact ID of the file recipient. + * + * Since: 0.4 + */ + pspec = g_param_spec_string ("recipient-contact-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, + PROP_RECIPIENT_CONTACT_ID, + pspec); + + /** + * YtsOutgoingFile:recipient-service-id: + * + * Service ID of the file recipient. + * + * Since: 0.4 + */ + pspec = g_param_spec_string ("recipient-service-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + 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 +yts_outgoing_file_init (YtsOutgoingFile *self) +{ +} + +YtsOutgoingFile * +yts_outgoing_file_new (TpAccount *tp_account, + GFile *file, + char const *recipient_contact_id, + char const *recipient_service_id, + char const *description) +{ + return g_object_new (YTS_TYPE_OUTGOING_FILE, + "tp-account", tp_account, + "file", file, + "recipient-contact-id", recipient_contact_id, + "recipient-service-id", recipient_service_id, + "description", description, + NULL); +} + diff --git a/ytstenut/yts-outgoing-file.h b/ytstenut/yts-outgoing-file.h new file mode 100644 index 0000000..702a1b6 --- /dev/null +++ b/ytstenut/yts-outgoing-file.h @@ -0,0 +1,57 @@ +/* + * 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_OUTGOING_FILE_H +#define YTS_OUTGOING_FILE_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define YTS_TYPE_OUTGOING_FILE yts_outgoing_file_get_type() + +#define YTS_OUTGOING_FILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), YTS_TYPE_OUTGOING_FILE, YtsOutgoingFile)) + +#define YTS_IS_OUTGOING_FILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YTS_TYPE_OUTGOING_FILE)) + +typedef struct YtsOutgoingFile YtsOutgoingFile; + +GType +yts_outgoing_file_get_type (void) G_GNUC_CONST; + +#define YTS_OUTGOING_FILE_ERROR g_quark_from_static_string (__FILE__) + +enum { + YTS_OUTGOING_FILE_ERROR_NO_ACCOUNT, + YTS_OUTGOING_FILE_ERROR_NO_CONNECTION, + YTS_OUTGOING_FILE_ERROR_NO_FILE, + YTS_OUTGOING_FILE_ERROR_READ_FAILED, + 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 +}; + +G_END_DECLS + +#endif /* YTS_OUTGOING_FILE_H */ + diff --git a/ytstenut/yts-roster-impl.c b/ytstenut/yts-roster-impl.c index 3fa18cc..1182274 100644 --- a/ytstenut/yts-roster-impl.c +++ b/ytstenut/yts-roster-impl.c @@ -29,6 +29,7 @@ G_DEFINE_TYPE (YtsRosterImpl, yts_roster_impl, YTS_TYPE_ROSTER) enum { SIG_SEND_MESSAGE, + SIG_SEND_FILE, N_SIGNALS }; @@ -55,6 +56,18 @@ yts_roster_impl_class_init (YtsRosterImplClass *klass) YTS_TYPE_CONTACT, YTS_TYPE_SERVICE, YTS_TYPE_METADATA); + + _signals[SIG_SEND_FILE] = g_signal_new ("send-file", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + yts_marshal_OBJECT__OBJECT_OBJECT_OBJECT_STRING_POINTER, + YTS_TYPE_OUTGOING_FILE, 5, + YTS_TYPE_CONTACT, + YTS_TYPE_SERVICE, + G_TYPE_FILE, + G_TYPE_STRING, + G_TYPE_POINTER); } static void @@ -83,3 +96,21 @@ yts_roster_impl_send_message (YtsRosterImpl *self, contact, service, message); } +YtsOutgoingFile * +yts_roster_impl_send_file (YtsRosterImpl *self, + YtsContact *contact, + YtsService *service, + GFile *file, + char const *description, + GError **error_out) +{ + YtsOutgoingFile *transfer; + + transfer = NULL; + g_signal_emit (self, _signals[SIG_SEND_FILE], 0, + contact, service, file, description, error_out, + &transfer); + + return transfer; +} + diff --git a/ytstenut/yts-roster-impl.h b/ytstenut/yts-roster-impl.h index 5c782ae..11bd3a5 100644 --- a/ytstenut/yts-roster-impl.h +++ b/ytstenut/yts-roster-impl.h @@ -22,8 +22,10 @@ #define YTS_ROSTER_IMPL_H #include <glib-object.h> +#include <gio/gio.h> #include <ytstenut/yts-roster-internal.h> #include <ytstenut/yts-metadata.h> +#include <ytstenut/yts-outgoing-file.h> G_BEGIN_DECLS @@ -64,6 +66,14 @@ yts_roster_impl_send_message (YtsRosterImpl *self, YtsService *service, YtsMetadata *message); +YtsOutgoingFile * +yts_roster_impl_send_file (YtsRosterImpl *self, + YtsContact *contact, + YtsService *service, + GFile *file, + char const *description, + GError **error_out); + G_END_DECLS #endif /* YTS_ROSTER_IMPL_H */ diff --git a/ytstenut/yts-roster.c b/ytstenut/yts-roster.c index a38dbfb..7d8f9a8 100644 --- a/ytstenut/yts-roster.c +++ b/ytstenut/yts-roster.c @@ -19,11 +19,13 @@ * Rob Staudinger <robsta@linux.intel.com> */ +#include <gio/gio.h> #include <telepathy-ytstenut-glib/telepathy-ytstenut-glib.h> #include "yts-contact-impl.h" #include "yts-marshal.h" #include "yts-metadata.h" +#include "yts-outgoing-file.h" #include "yts-roster-impl.h" #include "yts-roster-internal.h" #include "yts-service-factory.h" @@ -83,6 +85,24 @@ _contact_send_message (YtsContact *contact, message); } +static YtsOutgoingFile * +_contact_send_file (YtsContact *contact, + YtsService *service, + GFile *file, + char const *description, + GError **error_out, + YtsRoster *self) +{ + /* This is a bit of a hack, we require the non-abstract subclass to + * implement this interface. */ + return yts_roster_impl_send_file (YTS_ROSTER_IMPL (self), + contact, + service, + file, + description, + error_out); +} + static void _get_property (GObject *object, unsigned property_id, @@ -126,6 +146,9 @@ _dispose (GObject *object) g_signal_handlers_disconnect_by_func (contact, _contact_send_message, object); + g_signal_handlers_disconnect_by_func (contact, + _contact_send_file, + object); } g_hash_table_destroy (priv->contacts); @@ -262,6 +285,9 @@ yts_roster_remove_service_by_id (YtsRoster *self, g_signal_handlers_disconnect_by_func (contact, _contact_send_message, self); + g_signal_handlers_disconnect_by_func (contact, + _contact_send_file, + self); g_object_ref (contact); g_hash_table_remove (priv->contacts, contact_id); g_signal_emit (self, _signals[SIG_CONTACT_REMOVED], 0, contact); @@ -354,6 +380,9 @@ yts_roster_clear (YtsRoster *self) g_signal_handlers_disconnect_by_func (contact, _contact_send_message, self); + g_signal_handlers_disconnect_by_func (contact, + _contact_send_file, + self); g_object_ref (contact); @@ -430,6 +459,8 @@ _connection_get_contacts (TpConnection *connection, g_signal_connect (contact, "send-message", G_CALLBACK (_contact_send_message), self); + g_signal_connect (contact, "send-file", + G_CALLBACK (_contact_send_file), self); yts_contact_add_service (contact, service); g_object_unref (service); diff --git a/ytstenut/yts-service-emitter.c b/ytstenut/yts-service-emitter.c index 75ec660..b7595f3 100644 --- a/ytstenut/yts-service-emitter.c +++ b/ytstenut/yts-service-emitter.c @@ -29,6 +29,7 @@ G_DEFINE_INTERFACE (YtsServiceEmitter, yts_service_emitter, YTS_TYPE_SERVICE) enum { SIG_SEND_MESSAGE, + SIG_SEND_FILE, N_SIGNALS }; @@ -42,12 +43,6 @@ yts_service_emitter_default_init (YtsServiceEmitterInterface *interface) if (!_initialized) { - /** - * YtsServiceImpl::send-message: - * - * Internal signal, should not be considered by language bindings at this - * time. Maybe in the future when we allow for custom service subclasses. - */ _signals[SIG_SEND_MESSAGE] = g_signal_new ("send-message", G_TYPE_FROM_INTERFACE (interface), G_SIGNAL_RUN_LAST, @@ -56,6 +51,15 @@ yts_service_emitter_default_init (YtsServiceEmitterInterface *interface) G_TYPE_NONE, 1, YTS_TYPE_METADATA); + _signals[SIG_SEND_FILE] = g_signal_new ("send-file", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + yts_marshal_OBJECT__OBJECT_STRING_POINTER, + YTS_TYPE_OUTGOING_FILE, 3, + G_TYPE_FILE, + G_TYPE_STRING, + G_TYPE_POINTER); _initialized = true; } } @@ -67,3 +71,19 @@ yts_service_emitter_send_message (YtsServiceEmitter *self, g_signal_emit (self, _signals[SIG_SEND_MESSAGE], 0, message); } +YtsOutgoingFile * +yts_service_emitter_send_file (YtsServiceEmitter *self, + GFile *file, + char const *description, + GError **error_out) +{ + YtsOutgoingFile *transfer; + + transfer = NULL; + g_signal_emit (self, _signals[SIG_SEND_FILE], 0, + file, description, error_out, + &transfer); + + return transfer; +} + diff --git a/ytstenut/yts-service-emitter.h b/ytstenut/yts-service-emitter.h index cb6e33d..962890b 100644 --- a/ytstenut/yts-service-emitter.h +++ b/ytstenut/yts-service-emitter.h @@ -22,7 +22,9 @@ #define YTS_SERVICE_EMITTER_H #include <glib-object.h> +#include <gio/gio.h> #include <ytstenut/yts-metadata.h> +#include <ytstenut/yts-outgoing-file.h> G_BEGIN_DECLS @@ -53,6 +55,12 @@ void yts_service_emitter_send_message (YtsServiceEmitter *self, YtsMetadata *message); +YtsOutgoingFile * +yts_service_emitter_send_file (YtsServiceEmitter *self, + GFile *file, + char const *description, + GError **error_out); + G_END_DECLS #endif /* YTS_SERVICE_EMITTER_H */ diff --git a/ytstenut/yts-service.c b/ytstenut/yts-service.c index d724120..0e52387 100644 --- a/ytstenut/yts-service.c +++ b/ytstenut/yts-service.c @@ -510,3 +510,31 @@ yts_service_send_dictionary (YtsService *self, g_object_unref (message); } +/** + * yts_service_send_file: + * @self: object on which to invoke this method. + * @file: file to send. + * @description: an optional text that is meant to be presented receiving user. + * @error_out: error out pointer. If set the error code can be any of + * YTS_OUTGOING_FILE_ERROR_. + * + * Send @file to remote service @self. + * + * Returns (transfer full): an #YtsOutgoingFile instance if the transfer + * could be initated, or %NULL on error, in which case @error will be set if + * non-null. + * + * Since: 0.4 + */ +YtsOutgoingFile * +yts_service_send_file (YtsService *self, + GFile *file, + char const *description, + GError **error_out) +{ + return yts_service_emitter_send_file (YTS_SERVICE_EMITTER (self), + file, + description, + error_out); +} + diff --git a/ytstenut/yts-service.h b/ytstenut/yts-service.h index 9ef58d7..1fcc230 100644 --- a/ytstenut/yts-service.h +++ b/ytstenut/yts-service.h @@ -23,6 +23,8 @@ #define YTS_SERVICE_H #include <glib-object.h> +#include <gio/gio.h> +#include <ytstenut/yts-outgoing-file.h> G_BEGIN_DECLS @@ -67,6 +69,12 @@ yts_service_send_dictionary (YtsService *self, char const *const *dictionary, int length); +YtsOutgoingFile * +yts_service_send_file (YtsService *self, + GFile *file, + char const *description, + GError **error_out); + G_END_DECLS #endif /* YTS_SERVICE_H */ diff --git a/ytstenut/ytstenut.h b/ytstenut/ytstenut.h index bd610db..3bc311b 100644 --- a/ytstenut/ytstenut.h +++ b/ytstenut/ytstenut.h @@ -25,6 +25,8 @@ #include <ytstenut/yts-capability.h> #include <ytstenut/yts-client.h> #include <ytstenut/yts-contact.h> +#include <ytstenut/yts-outgoing-file.h> +#include <ytstenut/yts-file-transfer.h> #include <ytstenut/yts-roster.h> #include <ytstenut/yts-service.h> #include <ytstenut/yts-version.h> diff --git a/ytstenut/ytstenut.sym b/ytstenut/ytstenut.sym index b577e41..6d9d707 100644 --- a/ytstenut/ytstenut.sym +++ b/ytstenut/ytstenut.sym @@ -22,6 +22,9 @@ yts_contact_get_id yts_contact_get_name yts_contact_get_type yts_contact_send_file +yts_file_transfer_get_progress +yts_file_transfer_get_type +yts_outgoing_file_get_type yts_message_get_type yts_message_new yts_metadata_add_attribute @@ -47,6 +50,7 @@ yts_service_get_names yts_service_get_service_type yts_service_get_statuses yts_service_get_type +yts_service_send_file yts_service_send_text yts_service_send_list yts_service_send_dictionary |