summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Staudinger <robsta@linux.intel.com>2012-02-02 23:15:30 +0100
committerRob Staudinger <robsta@linux.intel.com>2012-02-02 23:15:30 +0100
commitd3b84c2c71e33539e909a77f90ddf1fad0bae3f6 (patch)
tree72d496cfde3d9949acc10795ed2621a812ea3095
parent2916018392df240d98fe00dc9e1c08b2547aeb5f (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.am1
-rw-r--r--docs/reference/ytstenut/ytstenut-docs.xml.in2
-rw-r--r--examples/Makefile.am5
-rw-r--r--examples/file-transfer.c314
-rw-r--r--ytstenut/Makefile.am7
-rw-r--r--ytstenut/marshal.list3
-rw-r--r--ytstenut/yts-client-status.c3
-rw-r--r--ytstenut/yts-client.c43
-rw-r--r--ytstenut/yts-contact-impl.c29
-rw-r--r--ytstenut/yts-contact-impl.h9
-rw-r--r--ytstenut/yts-contact.c21
-rw-r--r--ytstenut/yts-file-transfer.c120
-rw-r--r--ytstenut/yts-file-transfer.h57
-rw-r--r--ytstenut/yts-outgoing-file-internal.h57
-rw-r--r--ytstenut/yts-outgoing-file.c575
-rw-r--r--ytstenut/yts-outgoing-file.h57
-rw-r--r--ytstenut/yts-roster-impl.c31
-rw-r--r--ytstenut/yts-roster-impl.h10
-rw-r--r--ytstenut/yts-roster.c31
-rw-r--r--ytstenut/yts-service-emitter.c32
-rw-r--r--ytstenut/yts-service-emitter.h8
-rw-r--r--ytstenut/yts-service.c28
-rw-r--r--ytstenut/yts-service.h8
-rw-r--r--ytstenut/ytstenut.h2
-rw-r--r--ytstenut/ytstenut.sym4
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