upload tizen1.0 source
[profile/ivi/obexd.git] / client / transfer.c
1 /*
2  *
3  *  OBEX Client
4  *
5  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
6  *
7  *
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.
12  *
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.
17  *
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
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <sys/stat.h>
33
34 #include <glib.h>
35 #include <gdbus.h>
36
37 #include "log.h"
38 #include "transfer.h"
39 #include "session.h"
40
41 #define TRANSFER_INTERFACE  "org.openobex.Transfer"
42 #define TRANSFER_BASEPATH   "/org/openobex"
43
44 #define DEFAULT_BUFFER_SIZE 4096
45
46 static guint64 counter = 0;
47
48 struct transfer_callback {
49         transfer_callback_t func;
50         void *data;
51 };
52
53 struct obc_transfer {
54         struct obc_session *session;
55         struct obc_transfer_params *params;
56         struct transfer_callback *callback;
57         DBusConnection *conn;
58         char *path;             /* Transfer path */
59         gchar *filename;        /* Transfer file location */
60         char *name;             /* Transfer object name */
61         char *type;             /* Transfer object type */
62         int fd;
63         guint xfer;
64         char *buffer;
65         size_t buffer_len;
66         int filled;
67         gint64 size;
68         gint64 transferred;
69         int err;
70 };
71
72 static void append_entry(DBusMessageIter *dict,
73                                 const char *key, int type, void *val)
74 {
75         DBusMessageIter entry, value;
76         const char *signature;
77
78         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
79                                                                 NULL, &entry);
80
81         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
82
83         switch (type) {
84         case DBUS_TYPE_STRING:
85                 signature = DBUS_TYPE_STRING_AS_STRING;
86                 break;
87         case DBUS_TYPE_BYTE:
88                 signature = DBUS_TYPE_BYTE_AS_STRING;
89                 break;
90         case DBUS_TYPE_UINT64:
91                 signature = DBUS_TYPE_UINT64_AS_STRING;
92                 break;
93         default:
94                 signature = DBUS_TYPE_VARIANT_AS_STRING;
95                 break;
96         }
97
98         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
99                                                         signature, &value);
100         dbus_message_iter_append_basic(&value, type, val);
101         dbus_message_iter_close_container(&entry, &value);
102
103         dbus_message_iter_close_container(dict, &entry);
104 }
105
106 static DBusMessage *obc_transfer_get_properties(DBusConnection *connection,
107                                         DBusMessage *message, void *user_data)
108 {
109         struct obc_transfer *transfer = user_data;
110         DBusMessage *reply;
111         DBusMessageIter iter, dict;
112
113         reply = dbus_message_new_method_return(message);
114         if (!reply)
115                 return NULL;
116
117         dbus_message_iter_init_append(reply, &iter);
118
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);
123
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);
127
128         dbus_message_iter_close_container(&iter, &dict);
129
130         return reply;
131 }
132
133 static void obc_transfer_abort(struct obc_transfer *transfer)
134 {
135         struct transfer_callback *callback = transfer->callback;
136
137         if (transfer->xfer == 0)
138                 return;
139
140         g_obex_cancel_transfer(transfer->xfer);
141         transfer->xfer = 0;
142
143         if (callback) {
144                 GError *err;
145
146                 err = g_error_new(OBEX_IO_ERROR, -ECANCELED, "%s",
147                                                         strerror(ECANCELED));
148                 callback->func(transfer, transfer->transferred, err,
149                                                         callback->data);
150                 g_error_free(err);
151         }
152 }
153
154 static DBusMessage *obc_transfer_cancel(DBusConnection *connection,
155                                         DBusMessage *message, void *user_data)
156 {
157         struct obc_transfer *transfer = user_data;
158         struct obc_session *session = transfer->session;
159         const gchar *sender, *agent;
160         DBusMessage *reply;
161
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",
167                                 "Not Authorized");
168
169         reply = dbus_message_new_method_return(message);
170         if (!reply)
171                 return NULL;
172
173         obc_transfer_abort(transfer);
174
175         return reply;
176 }
177
178 static GDBusMethodTable obc_transfer_methods[] = {
179         { "GetProperties", "", "a{sv}", obc_transfer_get_properties },
180         { "Cancel", "", "", obc_transfer_cancel },
181         { }
182 };
183
184 static void obc_transfer_free(struct obc_transfer *transfer)
185 {
186         struct obc_session *session = transfer->session;
187
188         DBG("%p", transfer);
189
190         if (transfer->xfer)
191                 g_obex_cancel_transfer(transfer->xfer);
192
193         if (transfer->fd > 0)
194                 close(transfer->fd);
195
196         obc_session_remove_transfer(session, transfer);
197
198         obc_session_unref(session);
199
200         if (transfer->params != NULL) {
201                 g_free(transfer->params->data);
202                 g_free(transfer->params);
203         }
204
205         if (transfer->conn)
206                 dbus_connection_unref(transfer->conn);
207
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);
214         g_free(transfer);
215 }
216
217 struct obc_transfer *obc_transfer_register(DBusConnection *conn,
218                                                 const char *filename,
219                                                 const char *name,
220                                                 const char *type,
221                                                 struct obc_transfer_params *params,
222                                                 void *user_data)
223 {
224         struct obc_session *session = user_data;
225         struct obc_transfer *transfer;
226
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;
233
234         /* for OBEX specific mime types we don't need to register a transfer */
235         if (type != NULL &&
236                         (strncmp(type, "x-obex/", 7) == 0 ||
237                         strncmp(type, "x-bt/", 5) == 0))
238                 goto done;
239
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);
245                 return NULL;
246         }
247
248         if (g_dbus_register_interface(transfer->conn, transfer->path,
249                                 TRANSFER_INTERFACE,
250                                 obc_transfer_methods, NULL, NULL,
251                                 transfer, NULL) == FALSE) {
252                 obc_transfer_free(transfer);
253                 return NULL;
254         }
255
256 done:
257         DBG("%p registered %s", transfer, transfer->path);
258
259         obc_session_add_transfer(session, transfer);
260
261         return transfer;
262 }
263
264 void obc_transfer_unregister(struct obc_transfer *transfer)
265 {
266         if (transfer->path) {
267                 g_dbus_unregister_interface(transfer->conn,
268                         transfer->path, TRANSFER_INTERFACE);
269         }
270
271         DBG("%p unregistered %s", transfer, transfer->path);
272
273         obc_transfer_free(transfer);
274 }
275
276 static void obc_transfer_read(struct obc_transfer *transfer,
277                                                 const void *buf, gsize len)
278 {
279         gsize bsize;
280
281         /* copy all buffered data */
282         bsize = transfer->buffer_len - transfer->filled;
283
284         if (bsize < len) {
285                 transfer->buffer_len += len - bsize;
286                 transfer->buffer = g_realloc(transfer->buffer,
287                                                         transfer->buffer_len);
288         }
289
290         memcpy(transfer->buffer + transfer->filled, buf, len);
291
292         transfer->filled += len;
293         transfer->transferred += len;
294 }
295
296 static void get_buf_xfer_complete(GObex *obex, GError *err, gpointer user_data)
297 {
298         struct obc_transfer *transfer = user_data;
299         struct transfer_callback *callback = transfer->callback;
300         gsize bsize;
301
302         transfer->xfer = 0;
303
304         if (err) {
305                 transfer->err = err->code;
306                 goto done;
307         }
308
309         if (transfer->filled > 0 &&
310                         transfer->buffer[transfer->filled - 1] == '\0')
311                 goto done;
312
313         bsize = transfer->buffer_len - transfer->filled;
314         if (bsize < 1) {
315                 transfer->buffer_len += 1;
316                 transfer->buffer = g_realloc(transfer->buffer,
317                                                 transfer->buffer_len);
318         }
319
320         transfer->buffer[transfer->filled] = '\0';
321         transfer->size = strlen(transfer->buffer);
322
323 done:
324         if (callback)
325                 callback->func(transfer, transfer->size, err, callback->data);
326 }
327
328 static void get_buf_xfer_progress(GObex *obex, GError *err, GObexPacket *rsp,
329                                                         gpointer user_data)
330 {
331         struct obc_transfer *transfer = user_data;
332         struct transfer_callback *callback = transfer->callback;
333         GObexPacket *req;
334         GObexHeader *hdr;
335         const guint8 *buf;
336         gsize len;
337         guint8 rspcode;
338         gboolean final;
339
340         if (err != NULL) {
341                 get_buf_xfer_complete(obex, err, transfer);
342                 return;
343         }
344
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);
350                 g_error_free(err);
351                 return;
352         }
353
354         hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_APPARAM);
355         if (hdr) {
356                 g_obex_header_get_bytes(hdr, &buf, &len);
357                 if (len != 0) {
358                         if (transfer->params == NULL)
359                                 transfer->params =
360                                         g_new0(struct obc_transfer_params, 1);
361                         else
362                                 g_free(transfer->params->data);
363
364                         transfer->params->data = g_memdup(buf, len);
365                         transfer->params->size = len;
366                 }
367         }
368
369         hdr = g_obex_packet_get_body(rsp);
370         if (hdr) {
371                 g_obex_header_get_bytes(hdr, &buf, &len);
372                 if (len != 0)
373                         obc_transfer_read(transfer, buf, len);
374         }
375
376         if (rspcode == G_OBEX_RSP_SUCCESS) {
377                 get_buf_xfer_complete(obex, err, transfer);
378                 return;
379         }
380
381         req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
382
383         transfer->xfer = g_obex_send_req(obex, req, -1, get_buf_xfer_progress,
384                                                         transfer, &err);
385
386         if (callback)
387                 callback->func(transfer, transfer->transferred, err,
388                                                         callback->data);
389 }
390
391 static void xfer_complete(GObex *obex, GError *err, gpointer user_data)
392 {
393         struct obc_transfer *transfer = user_data;
394         struct transfer_callback *callback = transfer->callback;
395
396         transfer->xfer = 0;
397
398         if (err) {
399                 transfer->err = err->code;
400                 goto done;
401         }
402
403         transfer->size = transfer->transferred;
404
405 done:
406         if (callback)
407                 callback->func(transfer, transfer->size, err, callback->data);
408 }
409
410 static gboolean get_xfer_progress(const void *buf, gsize len,
411                                                         gpointer user_data)
412 {
413         struct obc_transfer *transfer = user_data;
414         struct transfer_callback *callback = transfer->callback;
415
416         obc_transfer_read(transfer, buf, len);
417
418         if (transfer->fd > 0) {
419                 gint w;
420
421                 w = write(transfer->fd, transfer->buffer, transfer->filled);
422                 if (w < 0) {
423                         transfer->err = -errno;
424                         return FALSE;
425                 }
426
427                 transfer->filled -= w;
428         }
429
430         if (callback)
431                 callback->func(transfer, transfer->transferred, NULL,
432                                                         callback->data);
433
434         return TRUE;
435 }
436
437 static gssize put_buf_xfer_progress(void *buf, gsize len, gpointer user_data)
438 {
439         struct obc_transfer *transfer = user_data;
440         struct transfer_callback *callback = transfer->callback;
441         gsize size;
442
443         if (transfer->transferred == transfer->size)
444                 return 0;
445
446         size = transfer->size - transfer->transferred;
447         size = len > size ? len : size;
448         if (size == 0)
449                 return 0;
450
451         memcpy(buf, transfer->buffer + transfer->transferred, size);
452
453         transfer->transferred += size;
454
455         if (callback)
456                 callback->func(transfer, transfer->transferred, NULL,
457                                                         callback->data);
458
459         return size;
460 }
461
462 static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data)
463 {
464         struct obc_transfer *transfer = user_data;
465         struct transfer_callback *callback = transfer->callback;
466         gssize size;
467
468         size = read(transfer->fd, buf, len);
469         if (size <= 0) {
470                 transfer->err = -errno;
471                 return size;
472         }
473
474         if (callback)
475                 callback->func(transfer, transfer->transferred, NULL,
476                                                         callback->data);
477
478         transfer->transferred += size;
479
480         return size;
481 }
482
483 static void obc_transfer_set_callback(struct obc_transfer *transfer,
484                                         transfer_callback_t func,
485                                         void *user_data)
486 {
487         struct transfer_callback *callback;
488
489         g_free(transfer->callback);
490
491         callback = g_new0(struct transfer_callback, 1);
492         callback->func = func;
493         callback->data = user_data;
494
495         transfer->callback = callback;
496 }
497
498 int obc_transfer_get(struct obc_transfer *transfer, transfer_callback_t func,
499                         void *user_data)
500 {
501         struct obc_session *session = transfer->session;
502         GError *err = NULL;
503         GObex *obex;
504         GObexPacket *req;
505         GObexDataConsumer data_cb;
506         GObexFunc complete_cb;
507         GObexResponseFunc rsp_cb = NULL;
508
509         if (transfer->xfer != 0)
510                 return -EALREADY;
511
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;
516         } else {
517                 int fd = open(transfer->name ? : transfer->filename,
518                                 O_WRONLY | O_CREAT, 0600);
519
520                 if (fd < 0) {
521                         error("open(): %s(%d)", strerror(errno), errno);
522                         return -errno;
523                 }
524                 transfer->fd = fd;
525                 data_cb = get_xfer_progress;
526                 complete_cb = xfer_complete;
527         }
528
529         obex = obc_session_get_obex(session);
530
531         req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
532
533         if (transfer->filename != NULL)
534                 g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
535                                                         transfer->filename);
536
537         if (transfer->type != NULL)
538                 g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
539                                                 strlen(transfer->type) + 1);
540
541         if (transfer->params != NULL)
542                 g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM,
543                                                 transfer->params->data,
544                                                 transfer->params->size);
545
546         if (rsp_cb)
547                 transfer->xfer = g_obex_send_req(obex, req, -1, rsp_cb,
548                                                         transfer, &err);
549         else
550                 transfer->xfer = g_obex_get_req_pkt(obex, req, data_cb,
551                                                         complete_cb, transfer,
552                                                         &err);
553
554         if (transfer->xfer == 0)
555                 return -ENOTCONN;
556
557         if (func)
558                 obc_transfer_set_callback(transfer, func, user_data);
559
560         return 0;
561 }
562
563 int obc_transfer_put(struct obc_transfer *transfer, transfer_callback_t func,
564                         void *user_data)
565 {
566         struct obc_session *session = transfer->session;
567         GError *err = NULL;
568         GObex *obex;
569         GObexPacket *req;
570         GObexDataProducer data_cb;
571
572         if (transfer->xfer != 0)
573                 return -EALREADY;
574
575         if (transfer->buffer) {
576                 data_cb = put_buf_xfer_progress;
577                 goto done;
578         }
579
580         data_cb = put_xfer_progress;
581
582 done:
583         obex = obc_session_get_obex(session);
584         req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE, G_OBEX_HDR_INVALID);
585
586         if (transfer->name != NULL)
587                 g_obex_packet_add_unicode(req, G_OBEX_HDR_NAME,
588                                                         transfer->name);
589
590         if (transfer->type != NULL)
591                 g_obex_packet_add_bytes(req, G_OBEX_HDR_TYPE, transfer->type,
592                                                 strlen(transfer->type) + 1);
593
594         if (transfer->size < UINT32_MAX)
595                 g_obex_packet_add_uint32(req, G_OBEX_HDR_LENGTH, transfer->size);
596
597         if (transfer->params != NULL)
598                 g_obex_packet_add_bytes(req, G_OBEX_HDR_APPARAM,
599                                                 transfer->params->data,
600                                                 transfer->params->size);
601
602         transfer->xfer = g_obex_put_req_pkt(obex, req, data_cb, xfer_complete,
603                                                         transfer, &err);
604         if (transfer->xfer == 0)
605                 return -ENOTCONN;
606
607         if (func)
608                 obc_transfer_set_callback(transfer, func, user_data);
609
610         return 0;
611 }
612
613 int obc_transfer_get_params(struct obc_transfer *transfer,
614                                         struct obc_transfer_params *params)
615 {
616         if (transfer->xfer == 0)
617                 return -ENOTCONN;
618
619         params->data = transfer->params->data;
620         params->size = transfer->params->size;
621
622         return 0;
623 }
624
625 void obc_transfer_clear_buffer(struct obc_transfer *transfer)
626 {
627         transfer->filled = 0;
628 }
629
630 const char *obc_transfer_get_buffer(struct obc_transfer *transfer, int *size)
631 {
632         if (size)
633                 *size = transfer->filled;
634
635         return transfer->buffer;
636 }
637
638 void obc_transfer_set_buffer(struct obc_transfer *transfer, char *buffer)
639 {
640         transfer->size = strlen(buffer);
641         transfer->buffer = buffer;
642 }
643
644 void obc_transfer_set_name(struct obc_transfer *transfer, const char *name)
645 {
646         g_free(transfer->name);
647         transfer->name = g_strdup(name);
648 }
649
650 const char *obc_transfer_get_path(struct obc_transfer *transfer)
651 {
652         return transfer->path;
653 }
654
655 gint64 obc_transfer_get_size(struct obc_transfer *transfer)
656 {
657         return transfer->size;
658 }
659
660 int obc_transfer_set_file(struct obc_transfer *transfer)
661 {
662         int fd;
663         struct stat st;
664
665         fd = open(transfer->filename, O_RDONLY);
666         if (fd < 0) {
667                 error("open(): %s(%d)", strerror(errno), errno);
668                 return -errno;
669         }
670
671         if (fstat(fd, &st) < 0) {
672                 error("fstat(): %s(%d)", strerror(errno), errno);
673                 close(fd);
674                 return -errno;
675         }
676
677         transfer->fd = fd;
678         transfer->size = st.st_size;
679
680         return 0;
681 }