5 * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
6 * Copyright (C) 2011-2012 BMW Car IT GmbH. All rights reserved.
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.
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.
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
46 #define TRANSFER_INTERFACE "org.bluez.obex.Transfer"
47 #define ERROR_INTERFACE "org.bluez.obex.Error"
49 #define OBC_TRANSFER_ERROR obc_transfer_error_quark()
51 static guint64 counter = 0;
53 struct transfer_callback {
54 transfer_callback_t func;
58 struct obc_transfer_params {
66 struct obc_transfer_params *params;
67 struct transfer_callback *callback;
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 */
83 static GQuark obc_transfer_error_quark(void)
85 return g_quark_from_static_string("obc-transfer-error-quark");
88 static void obc_transfer_append_dbus_properties(struct obc_transfer *transfer,
89 DBusMessageIter *dict)
91 obex_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &transfer->name);
92 obex_dbus_dict_append(dict, "Size", DBUS_TYPE_UINT64, &transfer->size);
94 if (transfer->filename != NULL)
95 obex_dbus_dict_append(dict, "Filename", DBUS_TYPE_STRING,
98 if (transfer->obex != NULL)
99 obex_dbus_dict_append(dict, "Progress", DBUS_TYPE_UINT64,
100 &transfer->progress);
103 static DBusMessage *obc_transfer_get_properties(DBusConnection *connection,
104 DBusMessage *message, void *user_data)
106 struct obc_transfer *transfer = user_data;
108 DBusMessageIter iter, dict;
110 reply = dbus_message_new_method_return(message);
114 dbus_message_iter_init_append(reply, &iter);
115 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
116 OBC_PROPERTIES_ARRAY_SIGNATURE,
119 obc_transfer_append_dbus_properties(transfer, &dict);
121 dbus_message_iter_close_container(&iter, &dict);
126 static void obc_transfer_append_dbus_data(struct obc_transfer *transfer,
127 DBusMessageIter *iter)
129 const char *path = transfer->path;
130 DBusMessageIter entry, dict;
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,
138 obc_transfer_append_dbus_properties(transfer, &dict);
140 dbus_message_iter_close_container(&entry, &dict);
141 dbus_message_iter_close_container(iter, &entry);
144 DBusMessage *obc_transfer_create_dbus_reply(struct obc_transfer *transfer,
145 DBusMessage *message)
148 DBusMessageIter iter;
150 reply = dbus_message_new_method_return(message);
154 dbus_message_iter_init_append(reply, &iter);
155 obc_transfer_append_dbus_data(transfer, &iter);
160 static void abort_complete(GObex *obex, GError *err, gpointer user_data)
162 struct obc_transfer *transfer = user_data;
163 struct transfer_callback *callback = transfer->callback;
168 reply = dbus_message_new_method_return(transfer->msg);
170 g_dbus_send_message(transfer->conn, reply);
172 dbus_message_unref(transfer->msg);
173 transfer->msg = NULL;
175 if (callback == NULL)
179 callback->func(transfer, err, callback->data);
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);
190 static DBusMessage *obc_transfer_cancel(DBusConnection *connection,
191 DBusMessage *message, void *user_data)
193 struct obc_transfer *transfer = user_data;
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",
202 if (transfer->msg != NULL)
203 return g_dbus_create_error(message,
204 ERROR_INTERFACE ".InProgress",
205 "Cancellation already in progress");
207 if (transfer->xfer == 0) {
208 struct transfer_callback *callback = transfer->callback;
210 if (callback != NULL) {
213 err = g_error_new(OBC_TRANSFER_ERROR, -ECANCELED, "%s",
214 "Transfer cancelled by user");
215 callback->func(transfer, err, callback->data);
219 return dbus_message_new_method_return(message);
222 if (transfer->progress_id != 0) {
223 g_source_remove(transfer->progress_id);
224 transfer->progress_id = 0;
227 if (!g_obex_cancel_transfer(transfer->xfer, abort_complete, transfer))
228 return g_dbus_create_error(message,
229 ERROR_INTERFACE ".Failed",
232 transfer->msg = dbus_message_ref(message);
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) },
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" })) },
255 static void obc_transfer_free(struct obc_transfer *transfer)
260 g_obex_cancel_transfer(transfer->xfer, NULL, NULL);
262 if (transfer->progress_id != 0) {
263 g_source_remove(transfer->progress_id);
264 transfer->progress_id = 0;
267 if (transfer->op == G_OBEX_OP_GET &&
268 transfer->transferred != transfer->size)
269 remove(transfer->filename);
271 if (transfer->fd > 0)
274 if (transfer->params != NULL) {
275 g_free(transfer->params->data);
276 g_free(transfer->params);
280 dbus_connection_unref(transfer->conn);
283 dbus_message_unref(transfer->msg);
286 g_obex_unref(transfer->obex);
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);
297 static struct obc_transfer *obc_transfer_create(guint8 op,
298 const char *filename,
302 struct obc_transfer *transfer;
304 transfer = g_new0(struct obc_transfer, 1);
306 transfer->filename = g_strdup(filename);
307 transfer->name = g_strdup(name);
308 transfer->type = g_strdup(type);
313 gboolean obc_transfer_register(struct obc_transfer *transfer,
314 DBusConnection *conn,
319 transfer->owner = g_strdup(owner);
321 transfer->path = g_strdup_printf("%s/transfer%ju", path, counter++);
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");
330 if (g_dbus_register_interface(transfer->conn, transfer->path,
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");
339 DBG("%p registered %s", transfer, transfer->path);
344 static gboolean transfer_open(struct obc_transfer *transfer, int flags,
345 mode_t mode, GError **err)
350 if (transfer->filename != NULL && strcmp(transfer->filename, "") != 0) {
351 fd = open(transfer->filename, flags, mode);
353 error("open(): %s(%d)", strerror(errno), errno);
354 g_set_error(err, OBC_TRANSFER_ERROR, -errno,
355 "Unable to open file");
361 fd = g_file_open_tmp("obex-clientXXXXXX", &filename, err);
363 error("g_file_open_tmp(): %s", (*err)->message);
367 if (transfer->filename == NULL) {
368 remove(filename); /* remove always only if NULL was given */
371 g_free(transfer->filename);
372 transfer->filename = filename;
380 struct obc_transfer *obc_transfer_get(const char *type, const char *name,
381 const char *filename, GError **err)
383 struct obc_transfer *transfer;
386 transfer = obc_transfer_create(G_OBEX_OP_GET, filename, name, type);
388 perr = transfer_open(transfer, O_WRONLY | O_CREAT | O_TRUNC, 0600, err);
390 obc_transfer_free(transfer);
397 struct obc_transfer *obc_transfer_put(const char *type, const char *name,
398 const char *filename,
399 const void *contents, size_t size,
402 struct obc_transfer *transfer;
406 if (filename == NULL || strcmp(filename, "") == 0) {
407 g_set_error(err, OBC_TRANSFER_ERROR, -EINVAL,
408 "Invalid filename given");
412 transfer = obc_transfer_create(G_OBEX_OP_PUT, filename, name, type);
414 if (contents != NULL) {
417 if (!transfer_open(transfer, O_RDWR, 0, err))
420 w = write(transfer->fd, contents, size);
423 error("write(): %s(%d)", strerror(perr), perr);
424 g_set_error(err, OBC_TRANSFER_ERROR, -perr,
425 "Writing to file failed");
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");
434 if (!transfer_open(transfer, O_RDONLY, 0, err))
438 if (fstat(transfer->fd, &st) < 0) {
440 error("fstat(): %s(%d)", strerror(perr), perr);
441 g_set_error(err, OBC_TRANSFER_ERROR, -perr,
442 "Unable to get file status");
446 transfer->size = st.st_size;
451 obc_transfer_free(transfer);
455 void obc_transfer_unregister(struct obc_transfer *transfer)
457 if (transfer->path) {
458 g_dbus_unregister_interface(transfer->conn,
459 transfer->path, TRANSFER_INTERFACE);
462 DBG("%p unregistered %s", transfer, transfer->path);
464 obc_transfer_free(transfer);
467 static gboolean get_xfer_progress(const void *buf, gsize len,
470 struct obc_transfer *transfer = user_data;
472 if (transfer->fd > 0) {
475 w = write(transfer->fd, buf, len);
479 transfer->transferred += w;
485 static void xfer_complete(GObex *obex, GError *err, gpointer user_data)
487 struct obc_transfer *transfer = user_data;
488 struct transfer_callback *callback = transfer->callback;
492 if (transfer->progress_id != 0) {
493 g_source_remove(transfer->progress_id);
494 transfer->progress_id = 0;
498 transfer->size = transfer->transferred;
500 if (transfer->path != NULL)
501 g_dbus_emit_signal(transfer->conn, transfer->path,
502 TRANSFER_INTERFACE, "Complete",
505 const char *code = ERROR_INTERFACE ".Failed";
507 if (transfer->op == G_OBEX_OP_GET && transfer->filename != NULL)
508 remove(transfer->filename);
510 if (transfer->path != NULL)
511 g_dbus_emit_signal(transfer->conn, transfer->path,
512 TRANSFER_INTERFACE, "Error",
521 callback->func(transfer, err, callback->data);
524 static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp,
527 struct obc_transfer *transfer = user_data;
536 xfer_complete(obex, err, transfer);
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);
549 hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_APPARAM);
551 g_obex_header_get_bytes(hdr, &buf, &len);
553 if (transfer->params == NULL)
555 g_new0(struct obc_transfer_params, 1);
557 g_free(transfer->params->data);
559 transfer->params->data = g_memdup(buf, len);
560 transfer->params->size = len;
564 hdr = g_obex_packet_get_body(rsp);
566 g_obex_header_get_bytes(hdr, &buf, &len);
568 get_xfer_progress(buf, len, transfer);
571 if (rspcode == G_OBEX_RSP_SUCCESS) {
572 xfer_complete(obex, err, transfer);
576 if (!g_obex_srm_active(obex)) {
577 req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
579 transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress,
580 xfer_complete, transfer,
585 static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data)
587 struct obc_transfer *transfer = user_data;
590 size = read(transfer->fd, buf, len);
594 transfer->transferred += size;
599 gboolean obc_transfer_set_callback(struct obc_transfer *transfer,
600 transfer_callback_t func,
603 struct transfer_callback *callback;
605 if (transfer->callback != NULL)
608 callback = g_new0(struct transfer_callback, 1);
609 callback->func = func;
610 callback->data = user_data;
612 transfer->callback = callback;
617 static gboolean report_progress(gpointer data)
619 struct obc_transfer *transfer = data;
621 if (transfer->transferred == transfer->progress)
624 transfer->progress = transfer->transferred;
626 if (transfer->transferred == transfer->size) {
627 transfer->progress_id = 0;
631 obex_dbus_signal_property_changed(transfer->conn,
633 TRANSFER_INTERFACE, "Progress",
635 &transfer->progress);
640 static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err)
644 if (transfer->xfer > 0) {
645 g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY,
646 "Transfer already started");
650 req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
652 if (transfer->name != NULL)
653 g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
656 if (transfer->type != NULL)
657 g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
658 strlen(transfer->type) + 1);
660 if (transfer->params != NULL)
661 g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM,
662 transfer->params->data,
663 transfer->params->size);
665 transfer->xfer = g_obex_send_req(transfer->obex, req, -1,
666 get_xfer_progress_first,
668 if (transfer->xfer == 0)
671 if (transfer->path == NULL)
674 transfer->progress_id = g_timeout_add_seconds(1, report_progress,
680 static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err)
684 if (transfer->xfer > 0) {
685 g_set_error(err, OBC_TRANSFER_ERROR, -EALREADY,
686 "Transfer already started");
690 req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
692 if (transfer->name != NULL)
693 g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
696 if (transfer->type != NULL)
697 g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
698 strlen(transfer->type) + 1);
700 if (transfer->size < UINT32_MAX)
701 g_obex_packet_add_uint32(req, G_OBEX_HDR_LENGTH, transfer->size);
703 if (transfer->params != NULL)
704 g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM,
705 transfer->params->data,
706 transfer->params->size);
708 transfer->xfer = g_obex_put_req_pkt(transfer->obex, req,
709 put_xfer_progress, xfer_complete,
711 if (transfer->xfer == 0)
714 if (transfer->path == NULL)
717 transfer->progress_id = g_timeout_add_seconds(1, report_progress,
723 gboolean obc_transfer_start(struct obc_transfer *transfer, void *obex,
726 transfer->obex = g_obex_ref(obex);
728 switch (transfer->op) {
730 return transfer_start_get(transfer, err);
732 return transfer_start_put(transfer, err);
735 g_set_error(err, OBC_TRANSFER_ERROR, -ENOTSUP, "Not supported");
739 guint8 obc_transfer_get_operation(struct obc_transfer *transfer)
744 void obc_transfer_set_params(struct obc_transfer *transfer,
745 const void *data, size_t size)
747 if (transfer->params != NULL) {
748 g_free(transfer->params->data);
749 g_free(transfer->params);
755 transfer->params = g_new0(struct obc_transfer_params, 1);
756 transfer->params->data = g_memdup(data, size);
757 transfer->params->size = size;
760 const void *obc_transfer_get_params(struct obc_transfer *transfer, size_t *size)
762 if (transfer->params == NULL)
766 *size = transfer->params->size;
768 return transfer->params->data;
771 int obc_transfer_get_contents(struct obc_transfer *transfer, char **contents,
777 if (contents == NULL)
780 if (fstat(transfer->fd, &st) < 0) {
781 error("fstat(): %s(%d)", strerror(errno), errno);
785 if (lseek(transfer->fd, 0, SEEK_SET) < 0) {
786 error("lseek(): %s(%d)", strerror(errno), errno);
790 *contents = g_malloc(st.st_size + 1);
792 ret = read(transfer->fd, *contents, st.st_size);
794 error("read(): %s(%d)", strerror(errno), errno);
799 (*contents)[ret] = '\0';
807 const char *obc_transfer_get_path(struct obc_transfer *transfer)
809 return transfer->path;
812 gint64 obc_transfer_get_size(struct obc_transfer *transfer)
814 return transfer->size;