upgrade obexd to 0.47
[profile/ivi/obexd.git] / client / transfer.c
1 /*
2  *
3  *  OBEX Client
4  *
5  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
6  *  Copyright (C) 2011-2012  BMW Car IT GmbH. All rights reserved.
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <inttypes.h>
37
38 #include <glib.h>
39 #include <gdbus.h>
40 #include <gobex.h>
41
42 #include "dbus.h"
43 #include "log.h"
44 #include "transfer.h"
45
46 #define TRANSFER_INTERFACE "org.bluez.obex.Transfer"
47 #define ERROR_INTERFACE "org.bluez.obex.Error"
48
49 #define OBC_TRANSFER_ERROR obc_transfer_error_quark()
50
51 static guint64 counter = 0;
52
53 struct transfer_callback {
54         transfer_callback_t func;
55         void *data;
56 };
57
58 struct obc_transfer_params {
59         void *data;
60         size_t size;
61 };
62
63 struct obc_transfer {
64         GObex *obex;
65         guint8 op;
66         struct obc_transfer_params *params;
67         struct transfer_callback *callback;
68         DBusConnection *conn;
69         DBusMessage *msg;
70         char *owner;            /* Transfer initiator */
71         char *path;             /* Transfer path */
72         gchar *filename;        /* Transfer file location */
73         char *name;             /* Transfer object name */
74         char *type;             /* Transfer object type */
75         int fd;
76         guint xfer;
77         gint64 size;
78         gint64 transferred;
79         gint64 progress;
80         guint progress_id;
81 };
82
83 static GQuark obc_transfer_error_quark(void)
84 {
85         return g_quark_from_static_string("obc-transfer-error-quark");
86 }
87
88 static void obc_transfer_append_dbus_properties(struct obc_transfer *transfer,
89                                                         DBusMessageIter *dict)
90 {
91         obex_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &transfer->name);
92         obex_dbus_dict_append(dict, "Size", DBUS_TYPE_UINT64, &transfer->size);
93
94         if (transfer->filename != NULL)
95                 obex_dbus_dict_append(dict, "Filename", DBUS_TYPE_STRING,
96                                                         &transfer->filename);
97
98         if (transfer->obex != NULL)
99                 obex_dbus_dict_append(dict, "Progress", DBUS_TYPE_UINT64,
100                                                 &transfer->progress);
101 }
102
103 static DBusMessage *obc_transfer_get_properties(DBusConnection *connection,
104                                         DBusMessage *message, void *user_data)
105 {
106         struct obc_transfer *transfer = user_data;
107         DBusMessage *reply;
108         DBusMessageIter iter, dict;
109
110         reply = dbus_message_new_method_return(message);
111         if (!reply)
112                 return NULL;
113
114         dbus_message_iter_init_append(reply, &iter);
115         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
116                                                 OBC_PROPERTIES_ARRAY_SIGNATURE,
117                                                 &dict);
118
119         obc_transfer_append_dbus_properties(transfer, &dict);
120
121         dbus_message_iter_close_container(&iter, &dict);
122
123         return reply;
124 }
125
126 static void obc_transfer_append_dbus_data(struct obc_transfer *transfer,
127                                                         DBusMessageIter *iter)
128 {
129         const char *path = transfer->path;
130         DBusMessageIter entry, dict;
131
132         dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry);
133         dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path);
134         dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
135                                                 OBC_PROPERTIES_ARRAY_SIGNATURE,
136                                                 &dict);
137
138         obc_transfer_append_dbus_properties(transfer, &dict);
139
140         dbus_message_iter_close_container(&entry, &dict);
141         dbus_message_iter_close_container(iter, &entry);
142 }
143
144 DBusMessage *obc_transfer_create_dbus_reply(struct obc_transfer *transfer,
145                                                         DBusMessage *message)
146 {
147         DBusMessage *reply;
148         DBusMessageIter iter;
149
150         reply = dbus_message_new_method_return(message);
151         if (reply == NULL)
152                 return NULL;
153
154         dbus_message_iter_init_append(reply, &iter);
155         obc_transfer_append_dbus_data(transfer, &iter);
156
157         return reply;
158 }
159
160 static void abort_complete(GObex *obex, GError *err, gpointer user_data)
161 {
162         struct obc_transfer *transfer = user_data;
163         struct transfer_callback *callback = transfer->callback;
164         DBusMessage *reply;
165
166         transfer->xfer = 0;
167
168         reply = dbus_message_new_method_return(transfer->msg);
169         if (reply)
170                 g_dbus_send_message(transfer->conn, reply);
171
172         dbus_message_unref(transfer->msg);
173         transfer->msg = NULL;
174
175         if (callback == NULL)
176                 return;
177
178         if (err) {
179                 callback->func(transfer, err, callback->data);
180         } else {
181                 GError *abort_err;
182
183                 abort_err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s",
184                                                 "Transfer cancelled by user");
185                 callback->func(transfer, abort_err, callback->data);
186                 g_error_free(abort_err);
187         }
188 }
189
190 static DBusMessage *obc_transfer_cancel(DBusConnection *connection,
191                                         DBusMessage *message, void *user_data)
192 {
193         struct obc_transfer *transfer = user_data;
194         const gchar *sender;
195
196         sender = dbus_message_get_sender(message);
197         if (g_strcmp0(transfer->owner, sender) != 0)
198                 return g_dbus_create_error(message,
199                                 ERROR_INTERFACE ".NotAuthorized",
200                                 "Not Authorized");
201
202         if (transfer->msg != NULL)
203                 return g_dbus_create_error(message,
204                                 ERROR_INTERFACE ".InProgress",
205                                 "Cancellation already in progress");
206
207         if (transfer->xfer == 0) {
208                 struct transfer_callback *callback = transfer->callback;
209
210                 if (callback != NULL) {
211                         GError *err;
212
213                         err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s",
214                                                 "Transfer cancelled by user");
215                         callback->func(transfer, err, callback->data);
216                         g_error_free(err);
217                 }
218
219                 return dbus_message_new_method_return(message);
220         }
221
222         if (transfer->progress_id != 0) {
223                 g_source_remove(transfer->progress_id);
224                 transfer->progress_id = 0;
225         }
226
227         if (!g_obex_cancel_transfer(transfer->xfer, abort_complete, transfer))
228                 return g_dbus_create_error(message,
229                                 ERROR_INTERFACE ".Failed",
230                                 "Failed");
231
232         transfer->msg = dbus_message_ref(message);
233
234         return NULL;
235 }
236
237 static const GDBusMethodTable obc_transfer_methods[] = {
238         { GDBUS_METHOD("GetProperties",
239                                 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
240                                 obc_transfer_get_properties) },
241         { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL,
242                                 obc_transfer_cancel) },
243         { }
244 };
245
246 static const GDBusSignalTable obc_transfer_signals[] = {
247         { GDBUS_SIGNAL("PropertyChanged",
248                 GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
249         { GDBUS_SIGNAL("Complete", NULL) },
250         { GDBUS_SIGNAL("Error",
251                 GDBUS_ARGS({ "code", "s" }, { "message", "s" })) },
252         { }
253 };
254
255 static void obc_transfer_free(struct obc_transfer *transfer)
256 {
257         DBG("%p", transfer);
258
259         if (transfer->xfer)
260                 g_obex_cancel_transfer(transfer->xfer, NULL, NULL);
261
262         if (transfer->progress_id != 0) {
263                 g_source_remove(transfer->progress_id);
264                 transfer->progress_id = 0;
265         }
266
267         if (transfer->op == G_OBEX_OP_GET &&
268                                         transfer->transferred != transfer->size)
269                 remove(transfer->filename);
270
271         if (transfer->fd > 0)
272                 close(transfer->fd);
273
274         if (transfer->params != NULL) {
275                 g_free(transfer->params->data);
276                 g_free(transfer->params);
277         }
278
279         if (transfer->conn)
280                 dbus_connection_unref(transfer->conn);
281
282         if (transfer->msg)
283                 dbus_message_unref(transfer->msg);
284
285         if (transfer->obex)
286                 g_obex_unref(transfer->obex);
287
288         g_free(transfer->callback);
289         g_free(transfer->owner);
290         g_free(transfer->filename);
291         g_free(transfer->name);
292         g_free(transfer->type);
293         g_free(transfer->path);
294         g_free(transfer);
295 }
296
297 static struct obc_transfer *obc_transfer_create(guint8 op,
298                                                 const char *filename,
299                                                 const char *name,
300                                                 const char *type)
301 {
302         struct obc_transfer *transfer;
303
304         transfer = g_new0(struct obc_transfer, 1);
305         transfer->op = op;
306         transfer->filename = g_strdup(filename);
307         transfer->name = g_strdup(name);
308         transfer->type = g_strdup(type);
309
310         return transfer;
311 }
312
313 gboolean obc_transfer_register(struct obc_transfer *transfer,
314                                                 DBusConnection *conn,
315                                                 const char *path,
316                                                 const char *owner,
317                                                 GError **err)
318 {
319         transfer->owner = g_strdup(owner);
320
321         transfer->path = g_strdup_printf("%s/transfer%ju", path, counter++);
322
323         transfer->conn = dbus_connection_ref(conn);
324         if (transfer->conn == NULL) {
325                 g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
326                                                 "Unable to connect to D-Bus");
327                 return FALSE;
328         }
329
330         if (g_dbus_register_interface(transfer->conn, transfer->path,
331                                 TRANSFER_INTERFACE,
332                                 obc_transfer_methods, obc_transfer_signals,
333                                 NULL, transfer, NULL) == FALSE) {
334                 g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
335                                                 "Unable to register to D-Bus");
336                 return FALSE;
337         }
338
339         DBG("%p registered %s", transfer, transfer->path);
340
341         return TRUE;
342 }
343
344 static gboolean transfer_open(struct obc_transfer *transfer, int flags,
345                                                 mode_t mode, GError **err)
346 {
347         int fd;
348         char *filename;
349
350         if (transfer->filename != NULL && strcmp(transfer->filename, "") != 0) {
351                 fd = open(transfer->filename, flags, mode);
352                 if (fd < 0) {
353                         error("open(): %s(%d)", strerror(errno), errno);
354                         g_set_error(err, OBC_TRANSFER_ERROR, -errno,
355                                                         "Unable to open file");
356                         return FALSE;
357                 }
358                 goto done;
359         }
360
361         fd = g_file_open_tmp("obex-clientXXXXXX", &filename, err);
362         if (fd < 0) {
363                 error("g_file_open_tmp(): %s", (*err)->message);
364                 return FALSE;
365         }
366
367         if (transfer->filename == NULL) {
368                 remove(filename); /* remove always only if NULL was given */
369                 g_free(filename);
370         } else {
371                 g_free(transfer->filename);
372                 transfer->filename = filename;
373         }
374
375 done:
376         transfer->fd = fd;
377         return TRUE;
378 }
379
380 struct obc_transfer *obc_transfer_get(const char *type, const char *name,
381                                         const char *filename, GError **err)
382 {
383         struct obc_transfer *transfer;
384         int perr;
385
386         transfer = obc_transfer_create(G_OBEX_OP_GET, filename, name, type);
387
388         perr = transfer_open(transfer, O_WRONLY | O_CREAT | O_TRUNC, 0600, err);
389         if (perr < 0) {
390                 obc_transfer_free(transfer);
391                 return NULL;
392         }
393
394         return transfer;
395 }
396
397 struct obc_transfer *obc_transfer_put(const char *type, const char *name,
398                                         const char *filename,
399                                         const void *contents, size_t size,
400                                         GError **err)
401 {
402         struct obc_transfer *transfer;
403         struct stat st;
404         int perr;
405
406         if (filename == NULL || strcmp(filename, "") == 0) {
407                 g_set_error(err, OBC_TRANSFER_ERROR, -EINVAL,
408                                                 "Invalid filename given");
409                 return NULL;
410         }
411
412         transfer = obc_transfer_create(G_OBEX_OP_PUT, filename, name, type);
413
414         if (contents != NULL) {
415                 ssize_t w;
416
417                 if (!transfer_open(transfer, O_RDWR, 0, err))
418                         goto fail;
419
420                 w = write(transfer->fd, contents, size);
421                 if (w < 0) {
422                         perr = errno;
423                         error("write(): %s(%d)", strerror(perr), perr);
424                         g_set_error(err, OBC_TRANSFER_ERROR, -perr,
425                                                 "Writing to file failed");
426                         goto fail;
427                 } else if ((size_t) w != size) {
428                         error("Unable to write all contents to file");
429                         g_set_error(err, OBC_TRANSFER_ERROR, -EFAULT,
430                                         "Writing all contents to file failed");
431                         goto fail;
432                 }
433         } else {
434                 if (!transfer_open(transfer, O_RDONLY, 0, err))
435                         goto fail;
436         }
437
438         if (fstat(transfer->fd, &st) < 0) {
439                 perr = errno;
440                 error("fstat(): %s(%d)", strerror(perr), perr);
441                 g_set_error(err, OBC_TRANSFER_ERROR, -perr,
442                                                 "Unable to get file status");
443                 goto fail;
444         }
445
446         transfer->size = st.st_size;
447
448         return transfer;
449
450 fail:
451         obc_transfer_free(transfer);
452         return NULL;
453 }
454
455 void obc_transfer_unregister(struct obc_transfer *transfer)
456 {
457         if (transfer->path) {
458                 g_dbus_unregister_interface(transfer->conn,
459                         transfer->path, TRANSFER_INTERFACE);
460         }
461
462         DBG("%p unregistered %s", transfer, transfer->path);
463
464         obc_transfer_free(transfer);
465 }
466
467 static gboolean get_xfer_progress(const void *buf, gsize len,
468                                                         gpointer user_data)
469 {
470         struct obc_transfer *transfer = user_data;
471
472         if (transfer->fd > 0) {
473                 gint w;
474
475                 w = write(transfer->fd, buf, len);
476                 if (w < 0)
477                         return FALSE;
478
479                 transfer->transferred += w;
480         }
481
482         return TRUE;
483 }
484
485 static void xfer_complete(GObex *obex, GError *err, gpointer user_data)
486 {
487         struct obc_transfer *transfer = user_data;
488         struct transfer_callback *callback = transfer->callback;
489
490         transfer->xfer = 0;
491
492         if (transfer->progress_id != 0) {
493                 g_source_remove(transfer->progress_id);
494                 transfer->progress_id = 0;
495         }
496
497         if (err == NULL) {
498                 transfer->size = transfer->transferred;
499
500                 if (transfer->path != NULL)
501                         g_dbus_emit_signal(transfer->conn, transfer->path,
502                                                 TRANSFER_INTERFACE, "Complete",
503                                                 DBUS_TYPE_INVALID);
504         } else {
505                 const char *code = ERROR_INTERFACE ".Failed";
506
507                 if (transfer->op == G_OBEX_OP_GET && transfer->filename != NULL)
508                         remove(transfer->filename);
509
510                 if (transfer->path != NULL)
511                         g_dbus_emit_signal(transfer->conn, transfer->path,
512                                                 TRANSFER_INTERFACE, "Error",
513                                                 DBUS_TYPE_STRING,
514                                                 &code,
515                                                 DBUS_TYPE_STRING,
516                                                 &err->message,
517                                                 DBUS_TYPE_INVALID);
518         }
519
520         if (callback)
521                 callback->func(transfer, err, callback->data);
522 }
523
524 static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp,
525                                                         gpointer user_data)
526 {
527         struct obc_transfer *transfer = user_data;
528         GObexPacket *req;
529         GObexHeader *hdr;
530         const guint8 *buf;
531         gsize len;
532         guint8 rspcode;
533         gboolean final;
534
535         if (err != NULL) {
536                 xfer_complete(obex, err, transfer);
537                 return;
538         }
539
540         rspcode = g_obex_packet_get_operation(rsp, &final);
541         if (rspcode != G_OBEX_RSP_SUCCESS && rspcode != G_OBEX_RSP_CONTINUE) {
542                 err = g_error_new(OBC_TRANSFER_ERROR, rspcode,
543                                         "Transfer failed (0x%02x)", rspcode);
544                 xfer_complete(obex, err, transfer);
545                 g_error_free(err);
546                 return;
547         }
548
549         hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_APPARAM);
550         if (hdr) {
551                 g_obex_header_get_bytes(hdr, &buf, &len);
552                 if (len != 0) {
553                         if (transfer->params == NULL)
554                                 transfer->params =
555                                         g_new0(struct obc_transfer_params, 1);
556                         else
557                                 g_free(transfer->params->data);
558
559                         transfer->params->data = g_memdup(buf, len);
560                         transfer->params->size = len;
561                 }
562         }
563
564         hdr = g_obex_packet_get_body(rsp);
565         if (hdr) {
566                 g_obex_header_get_bytes(hdr, &buf, &len);
567                 if (len != 0)
568                         get_xfer_progress(buf, len, transfer);
569         }
570
571         if (rspcode == G_OBEX_RSP_SUCCESS) {
572                 xfer_complete(obex, err, transfer);
573                 return;
574         }
575
576         if (!g_obex_srm_active(obex)) {
577                 req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
578
579                 transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress,
580                                                 xfer_complete, transfer,
581                                                 &err);
582         }
583 }
584
585 static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data)
586 {
587         struct obc_transfer *transfer = user_data;
588         gssize size;
589
590         size = read(transfer->fd, buf, len);
591         if (size <= 0)
592                 return size;
593
594         transfer->transferred += size;
595
596         return size;
597 }
598
599 gboolean obc_transfer_set_callback(struct obc_transfer *transfer,
600                                         transfer_callback_t func,
601                                         void *user_data)
602 {
603         struct transfer_callback *callback;
604
605         if (transfer->callback != NULL)
606                 return FALSE;
607
608         callback = g_new0(struct transfer_callback, 1);
609         callback->func = func;
610         callback->data = user_data;
611
612         transfer->callback = callback;
613
614         return TRUE;
615 }
616
617 static gboolean report_progress(gpointer data)
618 {
619         struct obc_transfer *transfer = data;
620
621         if (transfer->transferred == transfer->progress)
622                 return TRUE;
623
624         transfer->progress = transfer->transferred;
625
626         if (transfer->transferred == transfer->size) {
627                 transfer->progress_id = 0;
628                 return FALSE;
629         }
630
631         obex_dbus_signal_property_changed(transfer->conn,
632                                                 transfer->path,
633                                                 TRANSFER_INTERFACE, "Progress",
634                                                 DBUS_TYPE_INT64,
635                                                 &transfer->progress);
636
637         return TRUE;
638 }
639
640 static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err)
641 {
642         GObexPacket *req;
643
644         if (transfer->xfer > 0) {
645                 g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY,
646                                                 "Transfer already started");
647                 return FALSE;
648         }
649
650         req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
651
652         if (transfer->name != NULL)
653                 g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
654                                                         transfer->name);
655
656         if (transfer->type != NULL)
657                 g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
658                                                 strlen(transfer->type) + 1);
659
660         if (transfer->params != NULL)
661                 g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM,
662                                                 transfer->params->data,
663                                                 transfer->params->size);
664
665         transfer->xfer = g_obex_send_req(transfer->obex, req, -1,
666                                                 get_xfer_progress_first,
667                                                 transfer, err);
668         if (transfer->xfer == 0)
669                 return FALSE;
670
671         if (transfer->path == NULL)
672                 return TRUE;
673
674         transfer->progress_id = g_timeout_add_seconds(1, report_progress,
675                                                                 transfer);
676
677         return TRUE;
678 }
679
680 static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err)
681 {
682         GObexPacket *req;
683
684         if (transfer->xfer > 0) {
685                 g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY,
686                                                 "Transfer already started");
687                 return FALSE;
688         }
689
690         req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
691
692         if (transfer->name != NULL)
693                 g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
694                                                         transfer->name);
695
696         if (transfer->type != NULL)
697                 g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
698                                                 strlen(transfer->type) + 1);
699
700         if (transfer->size < UINT32_MAX)
701                 g_obex_packet_add_uint32(req, G_OBEX_HDR_LENGTH, transfer->size);
702
703         if (transfer->params != NULL)
704                 g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM,
705                                                 transfer->params->data,
706                                                 transfer->params->size);
707
708         transfer->xfer = g_obex_put_req_pkt(transfer->obex, req,
709                                         put_xfer_progress, xfer_complete,
710                                         transfer, err);
711         if (transfer->xfer == 0)
712                 return FALSE;
713
714         if (transfer->path == NULL)
715                 return TRUE;
716
717         transfer->progress_id = g_timeout_add_seconds(1, report_progress,
718                                                                 transfer);
719
720         return TRUE;
721 }
722
723 gboolean obc_transfer_start(struct obc_transfer *transfer, void *obex,
724                                                                 GError **err)
725 {
726         transfer->obex = g_obex_ref(obex);
727
728         switch (transfer->op) {
729         case G_OBEX_OP_GET:
730                 return transfer_start_get(transfer, err);
731         case G_OBEX_OP_PUT:
732                 return transfer_start_put(transfer, err);
733         }
734
735         g_set_error(err, OBC_TRANSFER_ERROR, -ENOTSUP, "Not supported");
736         return FALSE;
737 }
738
739 guint8 obc_transfer_get_operation(struct obc_transfer *transfer)
740 {
741         return transfer->op;
742 }
743
744 void obc_transfer_set_params(struct obc_transfer *transfer,
745                                                 const void *data, size_t size)
746 {
747         if (transfer->params != NULL) {
748                 g_free(transfer->params->data);
749                 g_free(transfer->params);
750         }
751
752         if (data == NULL)
753                 return;
754
755         transfer->params = g_new0(struct obc_transfer_params, 1);
756         transfer->params->data = g_memdup(data, size);
757         transfer->params->size = size;
758 }
759
760 const void *obc_transfer_get_params(struct obc_transfer *transfer, size_t *size)
761 {
762         if (transfer->params == NULL)
763                 return NULL;
764
765         if (size != NULL)
766                 *size = transfer->params->size;
767
768         return transfer->params->data;
769 }
770
771 int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents,
772                                                                 size_t *size)
773 {
774         struct stat st;
775         ssize_t ret;
776
777         if (contents == NULL)
778                 return -EINVAL;
779
780         if (fstat(transfer->fd, &st) < 0) {
781                 error("fstat(): %s(%d)", strerror(errno), errno);
782                 return -errno;
783         }
784
785         if (lseek(transfer->fd, 0, SEEK_SET) < 0) {
786                 error("lseek(): %s(%d)", strerror(errno), errno);
787                 return -errno;
788         }
789
790         *contents = g_malloc(st.st_size + 1);
791
792         ret = read(transfer->fd, *contents, st.st_size);
793         if (ret < 0) {
794                 error("read(): %s(%d)", strerror(errno), errno);
795                 g_free(*contents);
796                 return -errno;
797         }
798
799         (*contents)[ret] = '\0';
800
801         if (size)
802                 *size = ret;
803
804         return 0;
805 }
806
807 const char *obc_transfer_get_path(struct obc_transfer *transfer)
808 {
809         return transfer->path;
810 }
811
812 gint64 obc_transfer_get_size(struct obc_transfer *transfer)
813 {
814         return transfer->size;
815 }