upgrade obexd to 0.47
[profile/ivi/obexd.git] / client / session.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
37 #include <glib.h>
38 #include <gdbus.h>
39 #include <gobex.h>
40
41 #include "dbus.h"
42 #include "log.h"
43 #include "transfer.h"
44 #include "session.h"
45 #include "driver.h"
46 #include "transport.h"
47
48 #define SESSION_INTERFACE "org.bluez.obex.Session"
49 #define ERROR_INTERFACE "org.bluez.obex.Error"
50 #define SESSION_BASEPATH "/org/bluez/obex"
51
52 #define OBEX_IO_ERROR obex_io_error_quark()
53 #define OBEX_IO_ERROR_FIRST (0xff + 1)
54
55 enum {
56         OBEX_IO_DISCONNECTED = OBEX_IO_ERROR_FIRST,
57         OBEX_IO_BUSY,
58 };
59
60 static guint64 counter = 0;
61
62 struct callback_data {
63         struct obc_session *session;
64         session_callback_t func;
65         void *data;
66 };
67
68 struct pending_request {
69         guint id;
70         guint req_id;
71         struct obc_session *session;
72         struct obc_transfer *transfer;
73         session_callback_t func;
74         void *data;
75 };
76
77 struct setpath_data {
78         char **remaining;
79         int index;
80         session_callback_t func;
81         void *user_data;
82 };
83
84 struct obc_session {
85         guint id;
86         gint refcount;
87         char *source;
88         char *destination;
89         uint8_t channel;
90         struct obc_transport *transport;
91         struct obc_driver *driver;
92         gchar *path;            /* Session path */
93         DBusConnection *conn;
94         GObex *obex;
95         struct pending_request *p;
96         gchar *owner;           /* Session owner */
97         guint watch;
98         GQueue *queue;
99         guint queue_complete_id;
100 };
101
102 static GSList *sessions = NULL;
103
104 static void session_process_queue(struct obc_session *session);
105 static void session_terminate_transfer(struct obc_session *session,
106                                         struct obc_transfer *transfer,
107                                         GError *gerr);
108 static void transfer_complete(struct obc_transfer *transfer,
109                                         GError *err, void *user_data);
110
111 static GQuark obex_io_error_quark(void)
112 {
113         return g_quark_from_static_string("obex-io-error-quark");
114 }
115
116 struct obc_session *obc_session_ref(struct obc_session *session)
117 {
118         g_atomic_int_inc(&session->refcount);
119
120         DBG("%p: ref=%d", session, session->refcount);
121
122         return session;
123 }
124
125 static void session_unregistered(struct obc_session *session)
126 {
127         char *path;
128
129         if (session->driver && session->driver->remove)
130                 session->driver->remove(session);
131
132         path = session->path;
133         session->path = NULL;
134
135         g_dbus_unregister_interface(session->conn, path, SESSION_INTERFACE);
136
137         DBG("Session(%p) unregistered %s", session, path);
138
139         g_free(path);
140 }
141
142 static struct pending_request *pending_request_new(struct obc_session *session,
143                                                 struct obc_transfer *transfer,
144                                                 session_callback_t func,
145                                                 void *data)
146 {
147         struct pending_request *p;
148         static guint id = 0;
149
150         p = g_new0(struct pending_request, 1);
151         p->id = ++id;
152         p->session = obc_session_ref(session);
153         p->transfer = transfer;
154         p->func = func;
155         p->data = data;
156
157         return p;
158 }
159
160 static void pending_request_free(struct pending_request *p)
161 {
162         if (p->transfer)
163                 obc_transfer_unregister(p->transfer);
164
165         if (p->session)
166                 obc_session_unref(p->session);
167
168         g_free(p);
169 }
170
171 static void session_free(struct obc_session *session)
172 {
173         DBG("%p", session);
174
175         if (session->queue_complete_id != 0)
176                 g_source_remove(session->queue_complete_id);
177
178         if (session->queue) {
179                 g_queue_foreach(session->queue, (GFunc) pending_request_free,
180                                                                         NULL);
181                 g_queue_free(session->queue);
182         }
183
184         if (session->watch)
185                 g_dbus_remove_watch(session->conn, session->watch);
186
187         if (session->obex != NULL)
188                 g_obex_unref(session->obex);
189
190         if (session->id > 0 && session->transport != NULL)
191                 session->transport->disconnect(session->id);
192
193         if (session->path)
194                 session_unregistered(session);
195
196         if (session->conn)
197                 dbus_connection_unref(session->conn);
198
199         if (session->p)
200                 pending_request_free(session->p);
201
202         sessions = g_slist_remove(sessions, session);
203
204         g_free(session->path);
205         g_free(session->owner);
206         g_free(session->source);
207         g_free(session->destination);
208         g_free(session);
209 }
210
211 void obc_session_unref(struct obc_session *session)
212 {
213         gboolean ret;
214
215         ret = g_atomic_int_dec_and_test(&session->refcount);
216
217         DBG("%p: ref=%d", session, session->refcount);
218
219         if (ret == FALSE)
220                 return;
221
222         session_free(session);
223 }
224
225 static void connect_cb(GObex *obex, GError *err, GObexPacket *rsp,
226                                                         gpointer user_data)
227 {
228         struct callback_data *callback = user_data;
229         GError *gerr = NULL;
230         uint8_t rsp_code;
231
232         if (err != NULL) {
233                 error("connect_cb: %s", err->message);
234                 gerr = g_error_copy(err);
235                 goto done;
236         }
237
238         rsp_code = g_obex_packet_get_operation(rsp, NULL);
239         if (rsp_code != G_OBEX_RSP_SUCCESS)
240                 gerr = g_error_new(OBEX_IO_ERROR, -EIO,
241                                 "OBEX Connect failed with 0x%02x", rsp_code);
242
243 done:
244         callback->func(callback->session, NULL, gerr, callback->data);
245         if (gerr != NULL)
246                 g_error_free(gerr);
247         obc_session_unref(callback->session);
248         g_free(callback);
249 }
250
251 static void transport_func(GIOChannel *io, GError *err, gpointer user_data)
252 {
253         struct callback_data *callback = user_data;
254         struct obc_session *session = callback->session;
255         struct obc_driver *driver = session->driver;
256         struct obc_transport *transport = session->transport;
257         GObex *obex;
258         GObexTransportType type;
259         int tx_mtu = -1;
260         int rx_mtu = -1;
261
262         DBG("");
263
264         if (err != NULL) {
265                 error("%s", err->message);
266                 goto done;
267         }
268
269         g_io_channel_set_close_on_unref(io, FALSE);
270
271         if (transport->getpacketopt &&
272                         transport->getpacketopt(io, &tx_mtu, &rx_mtu) == 0)
273                 type = G_OBEX_TRANSPORT_PACKET;
274         else
275                 type = G_OBEX_TRANSPORT_STREAM;
276
277         obex = g_obex_new(io, type, tx_mtu, rx_mtu);
278         if (obex == NULL)
279                 goto done;
280
281         g_io_channel_set_close_on_unref(io, TRUE);
282
283         if (driver->target != NULL)
284                 g_obex_connect(obex, connect_cb, callback, &err,
285                         G_OBEX_HDR_TARGET, driver->target, driver->target_len,
286                         G_OBEX_HDR_INVALID);
287         else
288                 g_obex_connect(obex, connect_cb, callback, &err,
289                                                         G_OBEX_HDR_INVALID);
290
291         if (err != NULL) {
292                 error("%s", err->message);
293                 g_obex_unref(obex);
294                 goto done;
295         }
296
297         session->obex = obex;
298         sessions = g_slist_prepend(sessions, session);
299
300         return;
301 done:
302         callback->func(callback->session, NULL, err, callback->data);
303         obc_session_unref(callback->session);
304         g_free(callback);
305 }
306
307 static void owner_disconnected(DBusConnection *connection, void *user_data)
308 {
309         struct obc_session *session = user_data;
310
311         DBG("");
312
313         obc_session_shutdown(session);
314 }
315
316 int obc_session_set_owner(struct obc_session *session, const char *name,
317                         GDBusWatchFunction func)
318 {
319         if (session == NULL)
320                 return -EINVAL;
321
322         if (session->watch)
323                 g_dbus_remove_watch(session->conn, session->watch);
324
325         session->watch = g_dbus_add_disconnect_watch(session->conn, name, func,
326                                                         session, NULL);
327         if (session->watch == 0)
328                 return -EINVAL;
329
330         session->owner = g_strdup(name);
331
332         return 0;
333 }
334
335
336 static struct obc_session *session_find(const char *source,
337                                                 const char *destination,
338                                                 const char *service,
339                                                 uint8_t channel,
340                                                 const char *owner)
341 {
342         GSList *l;
343
344         for (l = sessions; l; l = l->next) {
345                 struct obc_session *session = l->data;
346
347                 if (g_strcmp0(session->destination, destination))
348                         continue;
349
350                 if (g_strcmp0(service, session->driver->service))
351                         continue;
352
353                 if (source && g_strcmp0(session->source, source))
354                         continue;
355
356                 if (channel && session->channel != channel)
357                         continue;
358
359                 if (g_strcmp0(owner, session->owner))
360                         continue;
361
362                 return session;
363         }
364
365         return NULL;
366 }
367
368 static gboolean connection_complete(gpointer data)
369 {
370         struct callback_data *cb = data;
371
372         cb->func(cb->session, NULL, NULL, cb->data);
373
374         obc_session_unref(cb->session);
375
376         g_free(cb);
377
378         return FALSE;
379 }
380
381 static int session_connect(struct obc_session *session,
382                                 session_callback_t function, void *user_data)
383 {
384         struct callback_data *callback;
385         struct obc_transport *transport = session->transport;
386         struct obc_driver *driver = session->driver;
387
388         callback = g_try_malloc0(sizeof(*callback));
389         if (callback == NULL)
390                 return -ENOMEM;
391
392         callback->func = function;
393         callback->data = user_data;
394         callback->session = obc_session_ref(session);
395
396         /* Connection completed */
397         if (session->obex) {
398                 g_idle_add(connection_complete, callback);
399                 return 0;
400         }
401
402         /* Ongoing connection */
403         if (session->id > 0) {
404                 obc_session_unref(callback->session);
405                 g_free(callback);
406                 return 0;
407         }
408
409         session->id = transport->connect(session->source, session->destination,
410                                         driver->uuid, session->channel,
411                                         transport_func, callback);
412         if (session->id == 0) {
413                 obc_session_unref(callback->session);
414                 g_free(callback);
415                 return -EINVAL;
416         }
417
418         return 0;
419 }
420
421 struct obc_session *obc_session_create(const char *source,
422                                                 const char *destination,
423                                                 const char *service,
424                                                 uint8_t channel,
425                                                 const char *owner,
426                                                 session_callback_t function,
427                                                 void *user_data)
428 {
429         DBusConnection *conn;
430         struct obc_session *session;
431         struct obc_transport *transport;
432         struct obc_driver *driver;
433
434         if (destination == NULL)
435                 return NULL;
436
437         session = session_find(source, destination, service, channel, owner);
438         if (session != NULL)
439                 goto proceed;
440
441         /* FIXME: Do proper transport lookup when the API supports it */
442         transport = obc_transport_find("Bluetooth");
443         if (transport == NULL)
444                 return NULL;
445
446         driver = obc_driver_find(service);
447         if (driver == NULL)
448                 return NULL;
449
450         conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
451         if (conn == NULL)
452                 return NULL;
453
454         session = g_try_malloc0(sizeof(*session));
455         if (session == NULL)
456                 return NULL;
457
458         session->refcount = 1;
459         session->transport = transport;
460         session->driver = driver;
461         session->conn = conn;
462         session->source = g_strdup(source);
463         session->destination = g_strdup(destination);
464         session->channel = channel;
465         session->queue = g_queue_new();
466
467         if (owner)
468                 obc_session_set_owner(session, owner, owner_disconnected);
469
470 proceed:
471         if (session_connect(session, function, user_data) < 0) {
472                 obc_session_unref(session);
473                 return NULL;
474         }
475
476         DBG("session %p transport %s driver %s", session,
477                         session->transport->name, session->driver->service);
478
479         return session;
480 }
481
482 void obc_session_shutdown(struct obc_session *session)
483 {
484         struct pending_request *p;
485         GError *err;
486
487         DBG("%p", session);
488
489         obc_session_ref(session);
490
491         /* Unregister any pending transfer */
492         err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
493                                                 "Session closed by user");
494
495         if (session->p != NULL && session->p->id != 0) {
496                 p = session->p;
497                 session->p = NULL;
498
499                 if (p->func)
500                         p->func(session, p->transfer, err, p->data);
501
502                 pending_request_free(p);
503         }
504
505         while ((p = g_queue_pop_head(session->queue))) {
506                 if (p->func)
507                         p->func(session, p->transfer, err, p->data);
508
509                 pending_request_free(p);
510         }
511
512         g_error_free(err);
513
514         /* Unregister interfaces */
515         if (session->path)
516                 session_unregistered(session);
517
518         /* Disconnect transport */
519         if (session->id > 0 && session->transport != NULL) {
520                 session->transport->disconnect(session->id);
521                 session->id = 0;
522         }
523
524         obc_session_unref(session);
525 }
526
527 static DBusMessage *session_get_properties(DBusConnection *connection,
528                                 DBusMessage *message, void *user_data)
529 {
530         struct obc_session *session = user_data;
531         DBusMessage *reply;
532         DBusMessageIter iter, dict;
533
534         reply = dbus_message_new_method_return(message);
535         if (!reply)
536                 return NULL;
537
538         dbus_message_iter_init_append(reply, &iter);
539
540         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
541                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
542                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
543                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
544
545         if (session->source != NULL)
546                 obex_dbus_dict_append(&dict, "Source", DBUS_TYPE_STRING,
547                                                         &session->source);
548
549         obex_dbus_dict_append(&dict, "Destination", DBUS_TYPE_STRING,
550                                                         &session->destination);
551
552         obex_dbus_dict_append(&dict, "Channel", DBUS_TYPE_BYTE,
553                                                         &session->channel);
554
555         dbus_message_iter_close_container(&iter, &dict);
556
557         return reply;
558 }
559
560 static void capabilities_complete_callback(struct obc_session *session,
561                                                 struct obc_transfer *transfer,
562                                                 GError *err, void *user_data)
563 {
564         DBusMessage *message = user_data;
565         char *contents;
566         size_t size;
567         int perr;
568
569         if (err != NULL) {
570                 DBusMessage *error = g_dbus_create_error(message,
571                                         ERROR_INTERFACE ".Failed",
572                                         "%s", err->message);
573                 g_dbus_send_message(session->conn, error);
574                 goto done;
575         }
576
577         perr = obc_transfer_get_contents(transfer, &contents, &size);
578         if (perr < 0) {
579                 DBusMessage *error = g_dbus_create_error(message,
580                                                 ERROR_INTERFACE ".Failed",
581                                                 "Error reading contents: %s",
582                                                 strerror(-perr));
583                 g_dbus_send_message(session->conn, error);
584                 goto done;
585         }
586
587         g_dbus_send_reply(session->conn, message,
588                                                 DBUS_TYPE_STRING, &contents,
589                                                 DBUS_TYPE_INVALID);
590         g_free(contents);
591
592 done:
593         dbus_message_unref(message);
594 }
595
596 static DBusMessage *get_capabilities(DBusConnection *connection,
597                                         DBusMessage *message, void *user_data)
598 {
599         struct obc_session *session = user_data;
600         struct obc_transfer *pull;
601         DBusMessage *reply;
602         GError *gerr = NULL;
603
604         pull = obc_transfer_get("x-obex/capability", NULL, NULL, &gerr);
605         if (pull == NULL)
606                 goto fail;
607
608         if (!obc_session_queue(session, pull, capabilities_complete_callback,
609                                                                 message, &gerr))
610                 goto fail;
611
612         dbus_message_ref(message);
613
614         return NULL;
615
616 fail:
617         reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
618                                                                 gerr->message);
619         g_error_free(gerr);
620         return reply;
621
622 }
623
624 static const GDBusMethodTable session_methods[] = {
625         { GDBUS_METHOD("GetProperties",
626                                 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
627                                 session_get_properties) },
628         { GDBUS_ASYNC_METHOD("GetCapabilities",
629                                 NULL, GDBUS_ARGS({ "capabilities", "s" }),
630                                 get_capabilities) },
631         { }
632 };
633
634 static gboolean session_queue_complete(gpointer data)
635 {
636         struct obc_session *session = data;
637
638         session_process_queue(session);
639
640         session->queue_complete_id = 0;
641
642         return FALSE;
643 }
644
645 guint obc_session_queue(struct obc_session *session,
646                                 struct obc_transfer *transfer,
647                                 session_callback_t func, void *user_data,
648                                 GError **err)
649 {
650         struct pending_request *p;
651
652         if (session->obex == NULL) {
653                 obc_transfer_unregister(transfer);
654                 g_set_error(err, OBEX_IO_ERROR, -ENOTCONN,
655                                                 "Session not connected");
656                 return 0;
657         }
658
659         if (!obc_transfer_register(transfer, session->conn, session->path,
660                                                         session->owner, err)) {
661                 obc_transfer_unregister(transfer);
662                 return 0;
663         }
664
665         obc_transfer_set_callback(transfer, transfer_complete, session);
666
667         p = pending_request_new(session, transfer, func, user_data);
668         g_queue_push_tail(session->queue, p);
669
670         if (session->queue_complete_id == 0)
671                 session->queue_complete_id = g_idle_add(
672                                         session_queue_complete, session);
673
674         return p->id;
675 }
676
677 static void session_process_queue(struct obc_session *session)
678 {
679         struct pending_request *p;
680
681         if (session->p != NULL)
682                 return;
683
684         if (session->queue == NULL || g_queue_is_empty(session->queue))
685                 return;
686
687         obc_session_ref(session);
688
689         while ((p = g_queue_pop_head(session->queue))) {
690                 GError *gerr = NULL;
691
692                 DBG("Transfer(%p) started", p->transfer);
693
694                 if (obc_transfer_start(p->transfer, session->obex, &gerr)) {
695                         session->p = p;
696                         break;
697                 }
698
699                 if (p->func)
700                         p->func(session, p->transfer, gerr, p->data);
701
702                 g_clear_error(&gerr);
703
704                 pending_request_free(p);
705         }
706
707         obc_session_unref(session);
708 }
709
710 static gint pending_transfer_cmptransfer(gconstpointer a, gconstpointer b)
711 {
712         const struct pending_request *p = a;
713         const struct obc_transfer *transfer = b;
714
715         if (p->transfer == transfer)
716                 return 0;
717
718         return -1;
719 }
720
721 static void session_terminate_transfer(struct obc_session *session,
722                                         struct obc_transfer *transfer,
723                                         GError *gerr)
724 {
725         struct pending_request *p = session->p;
726
727         if (p == NULL || p->transfer != transfer) {
728                 GList *match;
729
730                 match = g_list_find_custom(session->queue->head, transfer,
731                                                 pending_transfer_cmptransfer);
732                 if (match == NULL)
733                         return;
734
735                 p = match->data;
736                 g_queue_delete_link(session->queue, match);
737         } else
738                 session->p = NULL;
739
740         obc_session_ref(session);
741
742         if (p->func)
743                 p->func(session, p->transfer, gerr, p->data);
744
745         pending_request_free(p);
746
747         if (session->p == NULL)
748                 session_process_queue(session);
749
750         obc_session_unref(session);
751 }
752
753 static void session_notify_complete(struct obc_session *session,
754                                 struct obc_transfer *transfer)
755 {
756         DBG("Transfer(%p) complete", transfer);
757
758         session_terminate_transfer(session, transfer, NULL);
759 }
760
761 static void session_notify_error(struct obc_session *session,
762                                 struct obc_transfer *transfer,
763                                 GError *err)
764 {
765         error("Transfer(%p) Error: %s", transfer, err->message);
766
767         session_terminate_transfer(session, transfer, err);
768 }
769
770 static void transfer_complete(struct obc_transfer *transfer,
771                                         GError *err, void *user_data)
772 {
773         struct obc_session *session = user_data;
774
775         if (err != 0)
776                 goto fail;
777
778         session_notify_complete(session, transfer);
779
780         return;
781
782 fail:
783         session_notify_error(session, transfer, err);
784 }
785
786 const char *obc_session_register(struct obc_session *session,
787                                                 GDBusDestroyFunction destroy)
788 {
789         if (session->path)
790                 return session->path;
791
792         session->path = g_strdup_printf("%s/session%ju",
793                                                 SESSION_BASEPATH, counter++);
794
795         if (g_dbus_register_interface(session->conn, session->path,
796                                         SESSION_INTERFACE, session_methods,
797                                         NULL, NULL, session, destroy) == FALSE)
798                 goto fail;
799
800         if (session->driver->probe && session->driver->probe(session) < 0) {
801                 g_dbus_unregister_interface(session->conn, session->path,
802                                                         SESSION_INTERFACE);
803                 goto fail;
804         }
805
806         DBG("Session(%p) registered %s", session, session->path);
807
808         return session->path;
809
810 fail:
811         g_free(session->path);
812         session->path = NULL;
813         return NULL;
814 }
815
816 const char *obc_session_get_owner(struct obc_session *session)
817 {
818         if (session == NULL)
819                 return NULL;
820
821         return session->owner;
822 }
823
824 const char *obc_session_get_path(struct obc_session *session)
825 {
826         return session->path;
827 }
828
829 const char *obc_session_get_target(struct obc_session *session)
830 {
831         return session->driver->target;
832 }
833
834 static void setpath_complete(struct obc_session *session,
835                                                 struct obc_transfer *transfer,
836                                                 GError *err, void *user_data)
837 {
838         struct pending_request *p = user_data;
839         struct setpath_data *data = p->data;
840
841         if (data->func)
842                 data->func(session, NULL, err, data->user_data);
843
844         g_strfreev(data->remaining);
845         g_free(data);
846
847         if (session->p == p)
848                 session->p = NULL;
849
850         pending_request_free(p);
851
852         session_process_queue(session);
853 }
854
855 static void setpath_cb(GObex *obex, GError *err, GObexPacket *rsp,
856                                                         gpointer user_data)
857 {
858         struct pending_request *p = user_data;
859         struct setpath_data *data = p->data;
860         char *next;
861         guint8 code;
862
863         p->req_id = 0;
864
865         if (err != NULL) {
866                 setpath_complete(p->session, NULL, err, user_data);
867                 return;
868         }
869
870         code = g_obex_packet_get_operation(rsp, NULL);
871         if (code != G_OBEX_RSP_SUCCESS) {
872                 GError *gerr = NULL;
873                 g_set_error(&gerr, OBEX_IO_ERROR, code, "%s",
874                                                         g_obex_strerror(code));
875                 setpath_complete(p->session, NULL, err, user_data);
876                 g_clear_error(&gerr);
877                 return;
878         }
879
880         next = data->remaining[data->index];
881         if (next == NULL) {
882                 setpath_complete(p->session, NULL, NULL, user_data);
883                 return;
884         }
885
886         data->index++;
887
888         p->req_id = g_obex_setpath(obex, next, setpath_cb, p, &err);
889         if (err != NULL) {
890                 setpath_complete(p->session, NULL, err, data);
891                 g_error_free(err);
892         }
893 }
894
895 guint obc_session_setpath(struct obc_session *session, const char *path,
896                                 session_callback_t func, void *user_data,
897                                 GError **err)
898 {
899         struct setpath_data *data;
900         struct pending_request *p;
901         const char *first = "";
902
903         if (session->obex == NULL) {
904                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
905                                                 "Session disconnected");
906                 return 0;
907         }
908
909         if (session->p != NULL) {
910                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY,
911                                                         "Session busy");
912                 return 0;
913         }
914
915         data = g_new0(struct setpath_data, 1);
916         data->func = func;
917         data->user_data = user_data;
918         data->remaining = g_strsplit(path, "/", 3);
919
920         p = pending_request_new(session, NULL, setpath_complete, data);
921
922         /* Relative path */
923         if (path[0] != '/') {
924                 first = data->remaining[data->index];
925                 data->index++;
926         }
927
928         p->req_id = g_obex_setpath(session->obex, first, setpath_cb, p, err);
929         if (*err != NULL)
930                 goto fail;
931
932         session->p = p;
933
934         return p->id;
935
936 fail:
937         g_strfreev(data->remaining);
938         g_free(data);
939         pending_request_free(p);
940         return 0;
941 }
942
943 static void async_cb(GObex *obex, GError *err, GObexPacket *rsp,
944                                                         gpointer user_data)
945 {
946         struct pending_request *p = user_data;
947         struct obc_session *session = p->session;
948         GError *gerr = NULL;
949         uint8_t code;
950
951         p->req_id = 0;
952
953         if (err != NULL) {
954                 if (p->func)
955                         p->func(p->session, NULL, err, p->data);
956                 goto done;
957         }
958
959         code = g_obex_packet_get_operation(rsp, NULL);
960         if (code != G_OBEX_RSP_SUCCESS)
961                 g_set_error(&gerr, OBEX_IO_ERROR, code, "%s",
962                                                         g_obex_strerror(code));
963
964         if (p->func)
965                 p->func(p->session, NULL, gerr, p->data);
966
967         if (gerr != NULL)
968                 g_clear_error(&gerr);
969
970 done:
971         pending_request_free(p);
972         session->p = NULL;
973
974         session_process_queue(session);
975 }
976
977 guint obc_session_mkdir(struct obc_session *session, const char *folder,
978                                 session_callback_t func, void *user_data,
979                                 GError **err)
980 {
981         struct pending_request *p;
982
983         if (session->obex == NULL) {
984                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
985                                                 "Session disconnected");
986                 return 0;
987         }
988
989         if (session->p != NULL) {
990                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy");
991                 return 0;
992         }
993
994
995         p = pending_request_new(session, NULL, func, user_data);
996
997         p->req_id = g_obex_mkdir(session->obex, folder, async_cb, p, err);
998         if (*err != NULL) {
999                 pending_request_free(p);
1000                 return 0;
1001         }
1002
1003         session->p = p;
1004         return p->id;
1005 }
1006
1007 guint obc_session_copy(struct obc_session *session, const char *srcname,
1008                                 const char *destname, session_callback_t func,
1009                                 void *user_data, GError **err)
1010 {
1011         struct pending_request *p;
1012
1013         if (session->obex == NULL) {
1014                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1015                                                 "Session disconnected");
1016                 return 0;
1017         }
1018
1019         if (session->p != NULL) {
1020                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy");
1021                 return 0;
1022         }
1023
1024         p = pending_request_new(session, NULL, func, user_data);
1025
1026         p->req_id = g_obex_copy(session->obex, srcname, destname, async_cb, p,
1027                                                                         err);
1028         if (*err != NULL) {
1029                 pending_request_free(p);
1030                 return 0;
1031         }
1032
1033         session->p = p;
1034         return p->id;
1035 }
1036
1037 guint obc_session_move(struct obc_session *session, const char *srcname,
1038                                 const char *destname, session_callback_t func,
1039                                 void *user_data, GError **err)
1040 {
1041         struct pending_request *p;
1042
1043         if (session->obex == NULL) {
1044                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1045                                                 "Session disconnected");
1046                 return 0;
1047         }
1048
1049         if (session->p != NULL) {
1050                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy");
1051                 return 0;
1052         }
1053
1054         p = pending_request_new(session, NULL, func, user_data);
1055
1056         p->req_id = g_obex_move(session->obex, srcname, destname, async_cb, p,
1057                                                                         err);
1058         if (*err != NULL) {
1059                 pending_request_free(p);
1060                 return 0;
1061         }
1062
1063         session->p = p;
1064         return p->id;
1065 }
1066
1067 guint obc_session_delete(struct obc_session *session, const char *file,
1068                                 session_callback_t func, void *user_data,
1069                                 GError **err)
1070 {
1071         struct pending_request *p;
1072
1073         if (session->obex == NULL) {
1074                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1075                                                 "Session disconnected");
1076                 return 0;
1077         }
1078
1079         if (session->p != NULL) {
1080                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy");
1081                 return 0;
1082         }
1083
1084         p = pending_request_new(session, NULL, func, user_data);
1085
1086         p->req_id = g_obex_delete(session->obex, file, async_cb, p, err);
1087         if (*err != NULL) {
1088                 pending_request_free(p);
1089                 return 0;
1090         }
1091
1092         session->p = p;
1093         return p->id;
1094 }
1095
1096 void obc_session_cancel(struct obc_session *session, guint id,
1097                                                         gboolean remove)
1098 {
1099         struct pending_request *p = session->p;
1100
1101         if (p == NULL || p->id != id)
1102                 return;
1103
1104         if (p->req_id == 0)
1105                 return;
1106
1107         g_obex_cancel_req(session->obex, p->req_id, remove);
1108         if (!remove)
1109                 return;
1110
1111         pending_request_free(p);
1112         session->p = NULL;
1113
1114         session_process_queue(session);
1115 }