5 * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
41 #define TRANSFER_INTERFACE "org.openobex.Transfer"
42 #define TRANSFER_BASEPATH "/org/openobex"
44 #define DEFAULT_BUFFER_SIZE 4096
46 static guint64 counter = 0;
48 struct transfer_callback {
49 transfer_callback_t func;
54 struct obc_session *session;
55 struct obc_transfer_params *params;
56 struct transfer_callback *callback;
58 char *path; /* Transfer path */
59 gchar *filename; /* Transfer file location */
60 char *name; /* Transfer object name */
61 char *type; /* Transfer object type */
72 static void append_entry(DBusMessageIter *dict,
73 const char *key, int type, void *val)
75 DBusMessageIter entry, value;
76 const char *signature;
78 dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
81 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
84 case DBUS_TYPE_STRING:
85 signature = DBUS_TYPE_STRING_AS_STRING;
88 signature = DBUS_TYPE_BYTE_AS_STRING;
90 case DBUS_TYPE_UINT64:
91 signature = DBUS_TYPE_UINT64_AS_STRING;
94 signature = DBUS_TYPE_VARIANT_AS_STRING;
98 dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
100 dbus_message_iter_append_basic(&value, type, val);
101 dbus_message_iter_close_container(&entry, &value);
103 dbus_message_iter_close_container(dict, &entry);
106 static DBusMessage *obc_transfer_get_properties(DBusConnection *connection,
107 DBusMessage *message, void *user_data)
109 struct obc_transfer *transfer = user_data;
111 DBusMessageIter iter, dict;
113 reply = dbus_message_new_method_return(message);
117 dbus_message_iter_init_append(reply, &iter);
119 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
120 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
121 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
122 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
124 append_entry(&dict, "Name", DBUS_TYPE_STRING, &transfer->name);
125 append_entry(&dict, "Size", DBUS_TYPE_UINT64, &transfer->size);
126 append_entry(&dict, "Filename", DBUS_TYPE_STRING, &transfer->filename);
128 dbus_message_iter_close_container(&iter, &dict);
133 static void obc_transfer_abort(struct obc_transfer *transfer)
135 struct transfer_callback *callback = transfer->callback;
137 if (transfer->xfer == 0)
140 g_obex_cancel_transfer(transfer->xfer);
146 err = g_error_new(OBEX_IO_ERROR, -ECANCELED, "%s",
147 strerror(ECANCELED));
148 callback->func(transfer, transfer->transferred, err,
154 static DBusMessage *obc_transfer_cancel(DBusConnection *connection,
155 DBusMessage *message, void *user_data)
157 struct obc_transfer *transfer = user_data;
158 struct obc_session *session = transfer->session;
159 const gchar *sender, *agent;
162 sender = dbus_message_get_sender(message);
163 agent = obc_session_get_agent(session);
164 if (g_str_equal(sender, agent) == FALSE)
165 return g_dbus_create_error(message,
166 "org.openobex.Error.NotAuthorized",
169 reply = dbus_message_new_method_return(message);
173 obc_transfer_abort(transfer);
178 static GDBusMethodTable obc_transfer_methods[] = {
179 { "GetProperties", "", "a{sv}", obc_transfer_get_properties },
180 { "Cancel", "", "", obc_transfer_cancel },
184 static void obc_transfer_free(struct obc_transfer *transfer)
186 struct obc_session *session = transfer->session;
191 g_obex_cancel_transfer(transfer->xfer);
193 if (transfer->fd > 0)
196 obc_session_remove_transfer(session, transfer);
198 obc_session_unref(session);
200 if (transfer->params != NULL) {
201 g_free(transfer->params->data);
202 g_free(transfer->params);
206 dbus_connection_unref(transfer->conn);
208 g_free(transfer->callback);
209 g_free(transfer->filename);
210 g_free(transfer->name);
211 g_free(transfer->type);
212 g_free(transfer->path);
213 g_free(transfer->buffer);
217 struct obc_transfer *obc_transfer_register(DBusConnection *conn,
218 const char *filename,
221 struct obc_transfer_params *params,
224 struct obc_session *session = user_data;
225 struct obc_transfer *transfer;
227 transfer = g_new0(struct obc_transfer, 1);
228 transfer->session = obc_session_ref(session);
229 transfer->filename = g_strdup(filename);
230 transfer->name = g_strdup(name);
231 transfer->type = g_strdup(type);
232 transfer->params = params;
234 /* for OBEX specific mime types we don't need to register a transfer */
236 (strncmp(type, "x-obex/", 7) == 0 ||
237 strncmp(type, "x-bt/", 5) == 0))
240 transfer->path = g_strdup_printf("%s/transfer%ju",
241 TRANSFER_BASEPATH, counter++);
242 transfer->conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
243 if (transfer->conn == NULL) {
244 obc_transfer_free(transfer);
248 if (g_dbus_register_interface(transfer->conn, transfer->path,
250 obc_transfer_methods, NULL, NULL,
251 transfer, NULL) == FALSE) {
252 obc_transfer_free(transfer);
257 DBG("%p registered %s", transfer, transfer->path);
259 obc_session_add_transfer(session, transfer);
264 void obc_transfer_unregister(struct obc_transfer *transfer)
266 if (transfer->path) {
267 g_dbus_unregister_interface(transfer->conn,
268 transfer->path, TRANSFER_INTERFACE);
271 DBG("%p unregistered %s", transfer, transfer->path);
273 obc_transfer_free(transfer);
276 static void obc_transfer_read(struct obc_transfer *transfer,
277 const void *buf, gsize len)
281 /* copy all buffered data */
282 bsize = transfer->buffer_len - transfer->filled;
285 transfer->buffer_len += len - bsize;
286 transfer->buffer = g_realloc(transfer->buffer,
287 transfer->buffer_len);
290 memcpy(transfer->buffer + transfer->filled, buf, len);
292 transfer->filled += len;
293 transfer->transferred += len;
296 static void get_buf_xfer_complete(GObex *obex, GError *err, gpointer user_data)
298 struct obc_transfer *transfer = user_data;
299 struct transfer_callback *callback = transfer->callback;
305 transfer->err = err->code;
309 if (transfer->filled > 0 &&
310 transfer->buffer[transfer->filled - 1] == '\0')
313 bsize = transfer->buffer_len - transfer->filled;
315 transfer->buffer_len += 1;
316 transfer->buffer = g_realloc(transfer->buffer,
317 transfer->buffer_len);
320 transfer->buffer[transfer->filled] = '\0';
321 transfer->size = strlen(transfer->buffer);
325 callback->func(transfer, transfer->size, err, callback->data);
328 static void get_buf_xfer_progress(GObex *obex, GError *err, GObexPacket *rsp,
331 struct obc_transfer *transfer = user_data;
332 struct transfer_callback *callback = transfer->callback;
341 get_buf_xfer_complete(obex, err, transfer);
345 rspcode = g_obex_packet_get_operation(rsp, &final);
346 if (rspcode != G_OBEX_RSP_SUCCESS && rspcode != G_OBEX_RSP_CONTINUE) {
347 err = g_error_new(OBEX_IO_ERROR, rspcode,
348 "Transfer failed (0x%02x)", rspcode);
349 get_buf_xfer_complete(obex, err, transfer);
354 hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_APPARAM);
356 g_obex_header_get_bytes(hdr, &buf, &len);
358 if (transfer->params == NULL)
360 g_new0(struct obc_transfer_params, 1);
362 g_free(transfer->params->data);
364 transfer->params->data = g_memdup(buf, len);
365 transfer->params->size = len;
369 hdr = g_obex_packet_get_body(rsp);
371 g_obex_header_get_bytes(hdr, &buf, &len);
373 obc_transfer_read(transfer, buf, len);
376 if (rspcode == G_OBEX_RSP_SUCCESS) {
377 get_buf_xfer_complete(obex, err, transfer);
381 req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
383 transfer->xfer = g_obex_send_req(obex, req, -1, get_buf_xfer_progress,
387 callback->func(transfer, transfer->transferred, err,
391 static void xfer_complete(GObex *obex, GError *err, gpointer user_data)
393 struct obc_transfer *transfer = user_data;
394 struct transfer_callback *callback = transfer->callback;
399 transfer->err = err->code;
403 transfer->size = transfer->transferred;
407 callback->func(transfer, transfer->size, err, callback->data);
410 static gboolean get_xfer_progress(const void *buf, gsize len,
413 struct obc_transfer *transfer = user_data;
414 struct transfer_callback *callback = transfer->callback;
416 obc_transfer_read(transfer, buf, len);
418 if (transfer->fd > 0) {
421 w = write(transfer->fd, transfer->buffer, transfer->filled);
423 transfer->err = -errno;
427 transfer->filled -= w;
431 callback->func(transfer, transfer->transferred, NULL,
437 static gssize put_buf_xfer_progress(void *buf, gsize len, gpointer user_data)
439 struct obc_transfer *transfer = user_data;
440 struct transfer_callback *callback = transfer->callback;
443 if (transfer->transferred == transfer->size)
446 size = transfer->size - transfer->transferred;
447 size = len > size ? len : size;
451 memcpy(buf, transfer->buffer + transfer->transferred, size);
453 transfer->transferred += size;
456 callback->func(transfer, transfer->transferred, NULL,
462 static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data)
464 struct obc_transfer *transfer = user_data;
465 struct transfer_callback *callback = transfer->callback;
468 size = read(transfer->fd, buf, len);
470 transfer->err = -errno;
475 callback->func(transfer, transfer->transferred, NULL,
478 transfer->transferred += size;
483 static void obc_transfer_set_callback(struct obc_transfer *transfer,
484 transfer_callback_t func,
487 struct transfer_callback *callback;
489 g_free(transfer->callback);
491 callback = g_new0(struct transfer_callback, 1);
492 callback->func = func;
493 callback->data = user_data;
495 transfer->callback = callback;
498 int obc_transfer_get(struct obc_transfer *transfer, transfer_callback_t func,
501 struct obc_session *session = transfer->session;
505 GObexDataConsumer data_cb;
506 GObexFunc complete_cb;
507 GObexResponseFunc rsp_cb = NULL;
509 if (transfer->xfer != 0)
512 if (transfer->type != NULL &&
513 (strncmp(transfer->type, "x-obex/", 7) == 0 ||
514 strncmp(transfer->type, "x-bt/", 5) == 0)) {
515 rsp_cb = get_buf_xfer_progress;
517 int fd = open(transfer->name ? : transfer->filename,
518 O_WRONLY | O_CREAT, 0600);
521 error("open(): %s(%d)", strerror(errno), errno);
525 data_cb = get_xfer_progress;
526 complete_cb = xfer_complete;
529 obex = obc_session_get_obex(session);
531 req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
533 if (transfer->filename != NULL)
534 g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
537 if (transfer->type != NULL)
538 g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
539 strlen(transfer->type) + 1);
541 if (transfer->params != NULL)
542 g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM,
543 transfer->params->data,
544 transfer->params->size);
547 transfer->xfer = g_obex_send_req(obex, req, -1, rsp_cb,
550 transfer->xfer = g_obex_get_req_pkt(obex, req, data_cb,
551 complete_cb, transfer,
554 if (transfer->xfer == 0)
558 obc_transfer_set_callback(transfer, func, user_data);
563 int obc_transfer_put(struct obc_transfer *transfer, transfer_callback_t func,
566 struct obc_session *session = transfer->session;
570 GObexDataProducer data_cb;
572 if (transfer->xfer != 0)
575 if (transfer->buffer) {
576 data_cb = put_buf_xfer_progress;
580 data_cb = put_xfer_progress;
583 obex = obc_session_get_obex(session);
584 req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
586 if (transfer->name != NULL)
587 g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
590 if (transfer->type != NULL)
591 g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
592 strlen(transfer->type) + 1);
594 if (transfer->size < UINT32_MAX)
595 g_obex_packet_add_uint32(req, G_OBEX_HDR_LENGTH, transfer->size);
597 if (transfer->params != NULL)
598 g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM,
599 transfer->params->data,
600 transfer->params->size);
602 transfer->xfer = g_obex_put_req_pkt(obex, req, data_cb, xfer_complete,
604 if (transfer->xfer == 0)
608 obc_transfer_set_callback(transfer, func, user_data);
613 int obc_transfer_get_params(struct obc_transfer *transfer,
614 struct obc_transfer_params *params)
616 if (transfer->xfer == 0)
619 params->data = transfer->params->data;
620 params->size = transfer->params->size;
625 void obc_transfer_clear_buffer(struct obc_transfer *transfer)
627 transfer->filled = 0;
630 const char *obc_transfer_get_buffer(struct obc_transfer *transfer, int *size)
633 *size = transfer->filled;
635 return transfer->buffer;
638 void obc_transfer_set_buffer(struct obc_transfer *transfer, char *buffer)
640 transfer->size = strlen(buffer);
641 transfer->buffer = buffer;
644 void obc_transfer_set_name(struct obc_transfer *transfer, const char *name)
646 g_free(transfer->name);
647 transfer->name = g_strdup(name);
650 const char *obc_transfer_get_path(struct obc_transfer *transfer)
652 return transfer->path;
655 gint64 obc_transfer_get_size(struct obc_transfer *transfer)
657 return transfer->size;
660 int obc_transfer_set_file(struct obc_transfer *transfer)
665 fd = open(transfer->filename, O_RDONLY);
667 error("open(): %s(%d)", strerror(errno), errno);
671 if (fstat(fd, &st) < 0) {
672 error("fstat(): %s(%d)", strerror(errno), errno);
678 transfer->size = st.st_size;