Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / obexd / 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
39 #include "gdbus/gdbus.h"
40 #include "gobex/gobex.h"
41
42 #include "obexd/src/log.h"
43 #include "dbus.h"
44 #include "transfer.h"
45 #include "session.h"
46 #ifdef __TIZEN_PATCH__
47 #include "manager.h"
48 #endif
49 #include "driver.h"
50 #include "transport.h"
51
52 #define SESSION_INTERFACE "org.bluez.obex.Session1"
53 #define ERROR_INTERFACE "org.bluez.obex.Error"
54 #define SESSION_BASEPATH "/org/bluez/obex/client"
55
56 #define OBEX_IO_ERROR obex_io_error_quark()
57 #define OBEX_IO_ERROR_FIRST (0xff + 1)
58
59 enum {
60         OBEX_IO_DISCONNECTED = OBEX_IO_ERROR_FIRST,
61         OBEX_IO_BUSY,
62 };
63
64 static guint64 counter = 0;
65
66 struct callback_data {
67         struct obc_session *session;
68         guint id;
69         session_callback_t func;
70         void *data;
71 };
72
73 struct pending_request;
74 typedef int (*session_process_t) (struct pending_request *p, GError **err);
75 typedef void (*destroy_t) (void *data);
76
77 struct pending_request {
78         guint id;
79         guint req_id;
80         struct obc_session *session;
81         session_process_t process;
82         struct obc_transfer *transfer;
83         session_callback_t func;
84         void *data;
85         destroy_t destroy;
86 };
87
88 struct setpath_data {
89         char **remaining;
90         int index;
91         session_callback_t func;
92         void *user_data;
93 };
94
95 struct file_data {
96         char *srcname;
97         char *destname;
98         session_callback_t func;
99         void *user_data;
100 };
101
102 struct obc_session {
103         guint id;
104         int refcount;
105         char *source;
106         char *destination;
107         uint8_t channel;
108         struct obc_transport *transport;
109         struct obc_driver *driver;
110         char *path;             /* Session path */
111         DBusConnection *conn;
112         GObex *obex;
113         struct pending_request *p;
114         char *owner;            /* Session owner */
115         guint watch;
116         GQueue *queue;
117         guint process_id;
118         char *folder;
119         struct callback_data *callback;
120 };
121
122 static GSList *sessions = NULL;
123
124 static void session_process_queue(struct obc_session *session);
125 static void session_terminate_transfer(struct obc_session *session,
126                                         struct obc_transfer *transfer,
127                                         GError *gerr);
128 static void transfer_complete(struct obc_transfer *transfer,
129                                         GError *err, void *user_data);
130
131 static GQuark obex_io_error_quark(void)
132 {
133         return g_quark_from_static_string("obex-io-error-quark");
134 }
135
136 struct obc_session *obc_session_ref(struct obc_session *session)
137 {
138         int refs = __sync_add_and_fetch(&session->refcount, 1);
139
140         DBG("%p: ref=%d", session, refs);
141
142         return session;
143 }
144
145 static void session_unregistered(struct obc_session *session)
146 {
147         char *path;
148
149         if (session->driver && session->driver->remove)
150                 session->driver->remove(session);
151
152         path = session->path;
153         session->path = NULL;
154
155         g_dbus_unregister_interface(session->conn, path, SESSION_INTERFACE);
156
157         DBG("Session(%p) unregistered %s", session, path);
158
159         g_free(path);
160 }
161
162 static struct pending_request *pending_request_new(struct obc_session *session,
163                                                 session_process_t process,
164                                                 struct obc_transfer *transfer,
165                                                 session_callback_t func,
166                                                 void *data,
167                                                 destroy_t destroy)
168 {
169         struct pending_request *p;
170         static guint id = 0;
171
172         p = g_new0(struct pending_request, 1);
173         p->id = ++id;
174         p->session = obc_session_ref(session);
175         p->process = process;
176         p->destroy = destroy;
177         p->transfer = transfer;
178         p->func = func;
179         p->data = data;
180
181         return p;
182 }
183
184 static void pending_request_free(struct pending_request *p)
185 {
186         if (p->req_id > 0)
187                 g_obex_cancel_req(p->session->obex, p->req_id, TRUE);
188
189         if (p->destroy)
190                 p->destroy(p->data);
191
192         if (p->transfer)
193                 obc_transfer_unregister(p->transfer);
194
195         if (p->session)
196                 obc_session_unref(p->session);
197
198         g_free(p);
199 }
200
201 static void setpath_data_free(void *process_data)
202 {
203         struct setpath_data *data = process_data;
204
205         g_strfreev(data->remaining);
206         g_free(data);
207 }
208
209 static void file_data_free(void *process_data)
210 {
211         struct file_data *data = process_data;
212
213         g_free(data->srcname);
214         g_free(data->destname);
215         g_free(data);
216 }
217
218 static void session_free(struct obc_session *session)
219 {
220         DBG("%p", session);
221
222         if (session->process_id != 0)
223                 g_source_remove(session->process_id);
224
225         if (session->queue) {
226                 g_queue_foreach(session->queue, (GFunc) pending_request_free,
227                                                                         NULL);
228                 g_queue_free(session->queue);
229         }
230
231         if (session->watch)
232                 g_dbus_remove_watch(session->conn, session->watch);
233
234         if (session->obex) {
235                 g_obex_set_disconnect_function(session->obex, NULL, NULL);
236                 g_obex_unref(session->obex);
237         }
238
239         if (session->id > 0 && session->transport != NULL)
240                 session->transport->disconnect(session->id);
241
242         if (session->path)
243                 session_unregistered(session);
244
245         if (session->conn)
246                 dbus_connection_unref(session->conn);
247
248         if (session->p)
249                 pending_request_free(session->p);
250
251         g_free(session->path);
252         g_free(session->owner);
253         g_free(session->source);
254         g_free(session->destination);
255         g_free(session->folder);
256         g_free(session);
257 }
258
259 static void disconnect_complete(GObex *obex, GError *err, GObexPacket *rsp,
260                                                         void *user_data)
261 {
262         struct obc_session *session = user_data;
263
264         DBG("");
265
266         if (err)
267                 error("%s", err->message);
268
269         /* Disconnect transport */
270         if (session->id > 0 && session->transport != NULL) {
271                 session->transport->disconnect(session->id);
272                 session->id = 0;
273         }
274
275         session_free(session);
276 }
277
278 void obc_session_unref(struct obc_session *session)
279 {
280         int refs;
281
282         refs = __sync_sub_and_fetch(&session->refcount, 1);
283
284         DBG("%p: ref=%d", session, refs);
285
286         if (refs > 0)
287                 return;
288
289         sessions = g_slist_remove(sessions, session);
290
291         if (!session->obex)
292                 goto disconnect;
293
294         /* Wait OBEX Disconnect to complete if command succeed otherwise
295          * proceed with transport disconnection since there is nothing else to
296          * be done */
297         if (g_obex_disconnect(session->obex, disconnect_complete, session,
298                                                                         NULL))
299                 return;
300
301 disconnect:
302         /* Disconnect transport */
303         if (session->id > 0 && session->transport != NULL) {
304                 session->transport->disconnect(session->id);
305                 session->id = 0;
306         }
307
308         session_free(session);
309 }
310
311 static void callback_destroy(struct callback_data *callback, GError *err)
312 {
313         struct obc_session *session = callback->session;
314
315         if (callback->id > 0)
316                 g_obex_cancel_req(session->obex, callback->id, TRUE);
317
318         callback->func(session, NULL, err, callback->data);
319         g_free(callback);
320         session->callback = NULL;
321         obc_session_unref(session);
322 }
323
324 static void connect_cb(GObex *obex, GError *err, GObexPacket *rsp,
325                                                         gpointer user_data)
326 {
327         struct callback_data *callback = user_data;
328         GError *gerr = NULL;
329         uint8_t rsp_code;
330
331         callback->id = 0;
332
333         if (err != NULL) {
334                 error("connect_cb: %s", err->message);
335                 gerr = g_error_copy(err);
336                 goto done;
337         }
338
339         rsp_code = g_obex_packet_get_operation(rsp, NULL);
340         if (rsp_code != G_OBEX_RSP_SUCCESS)
341                 gerr = g_error_new(OBEX_IO_ERROR, -EIO,
342                                 "OBEX Connect failed with 0x%02x", rsp_code);
343
344 done:
345         callback_destroy(callback, gerr);
346         if (gerr != NULL)
347                 g_error_free(gerr);
348 }
349
350 static void session_disconnected(GObex *obex, GError *err, gpointer user_data)
351 {
352         struct obc_session *session = user_data;
353
354         if (err)
355                 error("%s", err->message);
356 #ifdef __TIZEN_PATCH__
357         release_session(session);
358 #else
359         obc_session_shutdown(session);
360 #endif
361 }
362
363 static void transport_func(GIOChannel *io, GError *err, gpointer user_data)
364 {
365         struct callback_data *callback = user_data;
366         struct obc_session *session = callback->session;
367         struct obc_driver *driver = session->driver;
368         struct obc_transport *transport = session->transport;
369         GObex *obex;
370         GObexApparam *apparam;
371         GObexTransportType type;
372         int tx_mtu = -1;
373         int rx_mtu = -1;
374
375         DBG("");
376
377         if (err != NULL) {
378                 error("%s", err->message);
379                 goto done;
380         }
381
382         g_io_channel_set_close_on_unref(io, FALSE);
383
384         if (transport->getpacketopt &&
385                         transport->getpacketopt(io, &tx_mtu, &rx_mtu) == 0)
386                 type = G_OBEX_TRANSPORT_PACKET;
387         else
388                 type = G_OBEX_TRANSPORT_STREAM;
389
390         obex = g_obex_new(io, type, tx_mtu, rx_mtu);
391         if (obex == NULL)
392                 goto done;
393
394         g_io_channel_set_close_on_unref(io, TRUE);
395
396         apparam = NULL;
397
398         if (driver->supported_features)
399                 apparam = driver->supported_features(session);
400
401         if (apparam) {
402                 uint8_t buf[1024];
403                 ssize_t len;
404
405                 len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
406                 if (driver->target)
407                         callback->id = g_obex_connect(obex, connect_cb,
408                                         callback, &err,
409                                         G_OBEX_HDR_TARGET,
410                                         driver->target, driver->target_len,
411                                         G_OBEX_HDR_APPARAM,
412                                         buf, len,
413                                         G_OBEX_HDR_INVALID);
414                 else
415                         callback->id = g_obex_connect(obex, connect_cb,
416                                         callback, &err,
417                                         G_OBEX_HDR_APPARAM, buf, len,
418                                         G_OBEX_HDR_INVALID);
419                 g_obex_apparam_free(apparam);
420         } else if (driver->target)
421                 callback->id = g_obex_connect(obex, connect_cb, callback, &err,
422                         G_OBEX_HDR_TARGET, driver->target, driver->target_len,
423                         G_OBEX_HDR_INVALID);
424         else
425                 callback->id = g_obex_connect(obex, connect_cb, callback,
426                                                 &err, G_OBEX_HDR_INVALID);
427
428         if (err != NULL) {
429                 error("%s", err->message);
430                 g_obex_unref(obex);
431                 goto done;
432         }
433
434         session->obex = obex;
435         sessions = g_slist_prepend(sessions, session);
436
437         g_obex_set_disconnect_function(obex, session_disconnected, session);
438
439         return;
440 done:
441         callback_destroy(callback, err);
442 }
443
444 static void owner_disconnected(DBusConnection *connection, void *user_data)
445 {
446         struct obc_session *session = user_data;
447         GError *err;
448
449         DBG("");
450
451         /*
452          * If connection still connecting notify the callback to destroy the
453          * session.
454          */
455         if (session->callback) {
456                 err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
457                                                 "Session closed by user");
458                 callback_destroy(session->callback, err);
459                 g_error_free(err);
460                 return;
461         }
462
463         obc_session_shutdown(session);
464 }
465
466 int obc_session_set_owner(struct obc_session *session, const char *name,
467                         GDBusWatchFunction func)
468 {
469         if (session == NULL)
470                 return -EINVAL;
471
472         if (session->watch)
473                 g_dbus_remove_watch(session->conn, session->watch);
474
475         session->watch = g_dbus_add_disconnect_watch(session->conn, name, func,
476                                                         session, NULL);
477         if (session->watch == 0)
478                 return -EINVAL;
479
480         session->owner = g_strdup(name);
481
482         return 0;
483 }
484
485
486 static struct obc_session *session_find(const char *source,
487                                                 const char *destination,
488                                                 const char *service,
489                                                 uint8_t channel,
490                                                 const char *owner)
491 {
492         GSList *l;
493
494         for (l = sessions; l; l = l->next) {
495                 struct obc_session *session = l->data;
496
497                 if (g_strcmp0(session->destination, destination))
498                         continue;
499
500                 if (g_strcmp0(service, session->driver->service))
501                         continue;
502
503                 if (source && g_strcmp0(session->source, source))
504                         continue;
505
506                 if (channel && session->channel != channel)
507                         continue;
508
509                 if (g_strcmp0(owner, session->owner))
510                         continue;
511
512                 return session;
513         }
514
515         return NULL;
516 }
517
518 static gboolean connection_complete(gpointer data)
519 {
520         struct callback_data *cb = data;
521
522         cb->func(cb->session, NULL, NULL, cb->data);
523
524         obc_session_unref(cb->session);
525
526         g_free(cb);
527
528         return FALSE;
529 }
530
531 static int session_connect(struct obc_session *session,
532                                 session_callback_t function, void *user_data)
533 {
534         struct callback_data *callback;
535         struct obc_transport *transport = session->transport;
536         struct obc_driver *driver = session->driver;
537
538         callback = g_try_malloc0(sizeof(*callback));
539         if (callback == NULL)
540                 return -ENOMEM;
541
542         callback->func = function;
543         callback->data = user_data;
544         callback->session = obc_session_ref(session);
545
546         /* Connection completed */
547         if (session->obex) {
548                 g_idle_add(connection_complete, callback);
549                 return 0;
550         }
551
552         /* Ongoing connection */
553         if (session->id > 0) {
554                 obc_session_unref(callback->session);
555                 g_free(callback);
556                 return 0;
557         }
558
559         session->id = transport->connect(session->source, session->destination,
560                                         driver->uuid, session->channel,
561                                         transport_func, callback);
562         if (session->id == 0) {
563                 obc_session_unref(callback->session);
564                 g_free(callback);
565                 return -EINVAL;
566         }
567
568         session->callback = callback;
569
570         return 0;
571 }
572
573 struct obc_session *obc_session_create(const char *source,
574                                                 const char *destination,
575                                                 const char *service,
576                                                 uint8_t channel,
577                                                 const char *owner,
578                                                 session_callback_t function,
579                                                 void *user_data)
580 {
581         DBusConnection *conn;
582         struct obc_session *session;
583         struct obc_transport *transport;
584         struct obc_driver *driver;
585
586         if (destination == NULL)
587                 return NULL;
588
589         session = session_find(source, destination, service, channel, owner);
590         if (session != NULL)
591                 goto proceed;
592
593         /* FIXME: Do proper transport lookup when the API supports it */
594         transport = obc_transport_find("Bluetooth");
595         if (transport == NULL)
596                 return NULL;
597
598         driver = obc_driver_find(service);
599         if (driver == NULL)
600                 return NULL;
601
602         conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
603         if (conn == NULL)
604                 return NULL;
605
606         session = g_try_malloc0(sizeof(*session));
607         if (session == NULL)
608                 return NULL;
609
610         session->refcount = 1;
611         session->transport = transport;
612         session->driver = driver;
613         session->conn = conn;
614         session->source = g_strdup(source);
615         session->destination = g_strdup(destination);
616         session->channel = channel;
617         session->queue = g_queue_new();
618         session->folder = g_strdup("/");
619
620         if (owner)
621                 obc_session_set_owner(session, owner, owner_disconnected);
622
623 proceed:
624         if (session_connect(session, function, user_data) < 0) {
625                 obc_session_unref(session);
626                 return NULL;
627         }
628
629         DBG("session %p transport %s driver %s", session,
630                         session->transport->name, session->driver->service);
631
632         return session;
633 }
634
635 void obc_session_shutdown(struct obc_session *session)
636 {
637         struct pending_request *p;
638         GError *err;
639
640         DBG("%p", session);
641
642         obc_session_ref(session);
643
644         /* Unregister any pending transfer */
645         err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
646                                                 "Session closed by user");
647
648         if (session->p != NULL && session->p->id != 0) {
649                 p = session->p;
650                 session->p = NULL;
651
652                 if (p->func)
653                         p->func(session, p->transfer, err, p->data);
654
655                 pending_request_free(p);
656         }
657
658         while ((p = g_queue_pop_head(session->queue))) {
659                 if (p->func)
660                         p->func(session, p->transfer, err, p->data);
661
662                 pending_request_free(p);
663         }
664
665         g_error_free(err);
666
667         /* Unregister interfaces */
668         if (session->path)
669                 session_unregistered(session);
670
671         obc_session_unref(session);
672 }
673
674 static void capabilities_complete_callback(struct obc_session *session,
675                                                 struct obc_transfer *transfer,
676                                                 GError *err, void *user_data)
677 {
678         DBusMessage *message = user_data;
679         char *contents;
680         size_t size;
681         int perr;
682
683         if (err != NULL) {
684                 DBusMessage *error = g_dbus_create_error(message,
685                                         ERROR_INTERFACE ".Failed",
686                                         "%s", err->message);
687                 g_dbus_send_message(session->conn, error);
688                 goto done;
689         }
690
691         perr = obc_transfer_get_contents(transfer, &contents, &size);
692         if (perr < 0) {
693                 DBusMessage *error = g_dbus_create_error(message,
694                                                 ERROR_INTERFACE ".Failed",
695                                                 "Error reading contents: %s",
696                                                 strerror(-perr));
697                 g_dbus_send_message(session->conn, error);
698                 goto done;
699         }
700
701         g_dbus_send_reply(session->conn, message,
702                                                 DBUS_TYPE_STRING, &contents,
703                                                 DBUS_TYPE_INVALID);
704         g_free(contents);
705
706 done:
707         dbus_message_unref(message);
708 }
709
710 static DBusMessage *get_capabilities(DBusConnection *connection,
711                                         DBusMessage *message, void *user_data)
712 {
713         struct obc_session *session = user_data;
714         struct obc_transfer *pull;
715         DBusMessage *reply;
716         GError *gerr = NULL;
717
718         pull = obc_transfer_get("x-obex/capability", NULL, NULL, &gerr);
719         if (pull == NULL)
720                 goto fail;
721
722         if (!obc_session_queue(session, pull, capabilities_complete_callback,
723                                                                 message, &gerr))
724                 goto fail;
725
726         dbus_message_ref(message);
727
728         return NULL;
729
730 fail:
731         reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
732                                                                 gerr->message);
733         g_error_free(gerr);
734         return reply;
735
736 }
737
738 static gboolean get_source(const GDBusPropertyTable *property,
739                                         DBusMessageIter *iter, void *data)
740 {
741         struct obc_session *session = data;
742
743         if (session->source == NULL)
744                 return FALSE;
745
746         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
747                                                         &session->source);
748
749         return TRUE;
750 }
751
752 static gboolean source_exists(const GDBusPropertyTable *property, void *data)
753 {
754         struct obc_session *session = data;
755
756         return session->source != NULL;
757 }
758
759 static gboolean get_destination(const GDBusPropertyTable *property,
760                                         DBusMessageIter *iter, void *data)
761 {
762         struct obc_session *session = data;
763
764         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
765                                                         &session->destination);
766
767         return TRUE;
768 }
769
770 static gboolean get_channel(const GDBusPropertyTable *property,
771                                         DBusMessageIter *iter, void *data)
772 {
773         struct obc_session *session = data;
774
775         dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
776                                                         &session->channel);
777
778         return TRUE;
779 }
780
781 static const GDBusMethodTable session_methods[] = {
782         { GDBUS_ASYNC_METHOD("GetCapabilities",
783                                 NULL, GDBUS_ARGS({ "capabilities", "s" }),
784                                 get_capabilities) },
785         { }
786 };
787
788 static gboolean get_target(const GDBusPropertyTable *property,
789                                         DBusMessageIter *iter, void *data)
790 {
791         struct obc_session *session = data;
792
793         if (session->driver->uuid == NULL)
794                 return FALSE;
795
796         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
797                                                 &session->driver->uuid);
798
799         return TRUE;
800 }
801
802 static gboolean target_exists(const GDBusPropertyTable *property, void *data)
803 {
804         struct obc_session *session = data;
805
806         return session->driver->uuid != NULL;
807 }
808
809 static const GDBusPropertyTable session_properties[] = {
810         { "Source", "s", get_source, NULL, source_exists },
811         { "Destination", "s", get_destination },
812         { "Channel", "y", get_channel },
813         { "Target", "s", get_target, NULL, target_exists },
814         { }
815 };
816
817 static gboolean session_process(gpointer data)
818 {
819         struct obc_session *session = data;
820
821         session->process_id = 0;
822
823         session_process_queue(session);
824
825         return FALSE;
826 }
827
828 static void session_queue(struct pending_request *p)
829 {
830         g_queue_push_tail(p->session->queue, p);
831
832         if (p->session->process_id == 0)
833                 p->session->process_id = g_idle_add(session_process,
834                                                                 p->session);
835 }
836
837 static int session_process_transfer(struct pending_request *p, GError **err)
838 {
839         if (!obc_transfer_start(p->transfer, p->session->obex, err))
840                 return -1;
841
842         DBG("Tranfer(%p) started", p->transfer);
843         p->session->p = p;
844         return 0;
845 }
846
847 guint obc_session_queue(struct obc_session *session,
848                                 struct obc_transfer *transfer,
849                                 session_callback_t func, void *user_data,
850                                 GError **err)
851 {
852         struct pending_request *p;
853
854         if (session->obex == NULL) {
855                 obc_transfer_unregister(transfer);
856                 g_set_error(err, OBEX_IO_ERROR, -ENOTCONN,
857                                                 "Session not connected");
858                 return 0;
859         }
860
861         if (!obc_transfer_register(transfer, session->conn, session->path,
862                                                         session->owner, err)) {
863                 obc_transfer_unregister(transfer);
864                 return 0;
865         }
866
867         obc_transfer_set_callback(transfer, transfer_complete, session);
868
869         p = pending_request_new(session, session_process_transfer, transfer,
870                                                         func, user_data, NULL);
871         session_queue(p);
872         return p->id;
873 }
874
875 static void session_process_queue(struct obc_session *session)
876 {
877         struct pending_request *p;
878
879         if (session->p != NULL)
880                 return;
881
882         if (session->queue == NULL || g_queue_is_empty(session->queue))
883                 return;
884
885         obc_session_ref(session);
886
887         while ((p = g_queue_pop_head(session->queue))) {
888                 GError *gerr = NULL;
889
890                 if (p->process(p, &gerr) == 0)
891                         break;
892
893                 if (p->func)
894                         p->func(session, p->transfer, gerr, p->data);
895
896                 g_clear_error(&gerr);
897
898                 pending_request_free(p);
899         }
900
901         obc_session_unref(session);
902 }
903
904 static int pending_transfer_cmptransfer(gconstpointer a, gconstpointer b)
905 {
906         const struct pending_request *p = a;
907         const struct obc_transfer *transfer = b;
908
909         if (p->transfer == transfer)
910                 return 0;
911
912         return -1;
913 }
914
915 static void session_terminate_transfer(struct obc_session *session,
916                                         struct obc_transfer *transfer,
917                                         GError *gerr)
918 {
919         struct pending_request *p = session->p;
920
921         if (p == NULL || p->transfer != transfer) {
922                 GList *match;
923
924                 match = g_list_find_custom(session->queue->head, transfer,
925                                                 pending_transfer_cmptransfer);
926                 if (match == NULL)
927                         return;
928
929                 p = match->data;
930                 g_queue_delete_link(session->queue, match);
931         } else
932                 session->p = NULL;
933
934         obc_session_ref(session);
935
936         if (p->func)
937                 p->func(session, p->transfer, gerr, p->data);
938
939         pending_request_free(p);
940
941         if (session->p == NULL)
942                 session_process_queue(session);
943
944         obc_session_unref(session);
945 }
946
947 static void session_notify_complete(struct obc_session *session,
948                                 struct obc_transfer *transfer)
949 {
950         DBG("Transfer(%p) complete", transfer);
951
952         session_terminate_transfer(session, transfer, NULL);
953 }
954
955 static void session_notify_error(struct obc_session *session,
956                                 struct obc_transfer *transfer,
957                                 GError *err)
958 {
959         error("Transfer(%p) Error: %s", transfer, err->message);
960
961         session_terminate_transfer(session, transfer, err);
962 }
963
964 static void transfer_complete(struct obc_transfer *transfer,
965                                         GError *err, void *user_data)
966 {
967         struct obc_session *session = user_data;
968
969         if (err != 0)
970                 goto fail;
971
972         session_notify_complete(session, transfer);
973
974         return;
975
976 fail:
977         session_notify_error(session, transfer, err);
978 }
979
980 const char *obc_session_register(struct obc_session *session,
981                                                 GDBusDestroyFunction destroy)
982 {
983         if (session->path)
984                 return session->path;
985
986         session->path = g_strdup_printf("%s/session%ju",
987                                                 SESSION_BASEPATH, counter++);
988
989         if (g_dbus_register_interface(session->conn, session->path,
990                                         SESSION_INTERFACE, session_methods,
991                                         NULL, session_properties, session,
992                                         destroy) == FALSE)
993                 goto fail;
994
995         if (session->driver->probe && session->driver->probe(session) < 0) {
996                 g_dbus_unregister_interface(session->conn, session->path,
997                                                         SESSION_INTERFACE);
998                 goto fail;
999         }
1000
1001         DBG("Session(%p) registered %s", session, session->path);
1002
1003         return session->path;
1004
1005 fail:
1006         g_free(session->path);
1007         session->path = NULL;
1008         return NULL;
1009 }
1010
1011 const void *obc_session_get_attribute(struct obc_session *session,
1012                                                         int attribute_id)
1013 {
1014         if (session == NULL || session->id == 0)
1015                 return NULL;
1016
1017         return session->transport->getattribute(session->id, attribute_id);
1018 }
1019
1020 const char *obc_session_get_owner(struct obc_session *session)
1021 {
1022         if (session == NULL)
1023                 return NULL;
1024
1025         return session->owner;
1026 }
1027
1028 const char *obc_session_get_destination(struct obc_session *session)
1029 {
1030         return session->destination;
1031 }
1032
1033 const char *obc_session_get_path(struct obc_session *session)
1034 {
1035         return session->path;
1036 }
1037
1038 const char *obc_session_get_target(struct obc_session *session)
1039 {
1040         return session->driver->target;
1041 }
1042
1043 const char *obc_session_get_folder(struct obc_session *session)
1044 {
1045         return session->folder;
1046 }
1047
1048 static void setpath_complete(struct obc_session *session,
1049                                                 struct obc_transfer *transfer,
1050                                                 GError *err, void *user_data)
1051 {
1052         struct pending_request *p = user_data;
1053
1054         if (p->func)
1055                 p->func(session, NULL, err, p->data);
1056
1057         if (session->p == p)
1058                 session->p = NULL;
1059
1060         pending_request_free(p);
1061
1062         session_process_queue(session);
1063 }
1064
1065 static void setpath_op_complete(struct obc_session *session,
1066                                                 struct obc_transfer *transfer,
1067                                                 GError *err, void *user_data)
1068 {
1069         struct setpath_data *data = user_data;
1070
1071         if (data->func)
1072                 data->func(session, NULL, err, data->user_data);
1073 }
1074
1075 static void setpath_set_folder(struct obc_session *session, const char *cur)
1076 {
1077         char *folder = NULL;
1078         const char *delim;
1079
1080         delim = strrchr(session->folder, '/');
1081         if (strlen(cur) == 0 || delim == NULL ||
1082                         (strcmp(cur, "..") == 0 && delim == session->folder)) {
1083                 folder = g_strdup("/");
1084         } else {
1085                 if (strcmp(cur, "..") == 0) {
1086                         folder = g_strndup(session->folder,
1087                                                 delim - session->folder);
1088                 } else {
1089                         if (g_str_has_suffix(session->folder, "/"))
1090                                 folder = g_strconcat(session->folder,
1091                                                                 cur, NULL);
1092                         else
1093                                 folder = g_strconcat(session->folder, "/",
1094                                                                 cur, NULL);
1095                 }
1096         }
1097         g_free(session->folder);
1098         session->folder = folder;
1099 }
1100
1101 static void setpath_cb(GObex *obex, GError *err, GObexPacket *rsp,
1102                                                         gpointer user_data)
1103 {
1104         struct pending_request *p = user_data;
1105         struct setpath_data *data = p->data;
1106         char *next;
1107         char *current;
1108         guint8 code;
1109
1110         p->req_id = 0;
1111
1112         if (err != NULL) {
1113                 setpath_complete(p->session, NULL, err, user_data);
1114                 return;
1115         }
1116
1117         code = g_obex_packet_get_operation(rsp, NULL);
1118         if (code != G_OBEX_RSP_SUCCESS) {
1119                 GError *gerr = NULL;
1120                 g_set_error(&gerr, OBEX_IO_ERROR, code, "%s",
1121                                                         g_obex_strerror(code));
1122                 setpath_complete(p->session, NULL, gerr, user_data);
1123                 g_clear_error(&gerr);
1124                 return;
1125         }
1126
1127         current = data->remaining[data->index - 1];
1128         setpath_set_folder(p->session, current);
1129
1130         /* Ignore empty folder names to avoid resetting the current path */
1131         while ((next = data->remaining[data->index]) && strlen(next) == 0)
1132                 data->index++;
1133
1134         if (next == NULL) {
1135                 setpath_complete(p->session, NULL, NULL, user_data);
1136                 return;
1137         }
1138
1139         data->index++;
1140
1141         p->req_id = g_obex_setpath(obex, next, setpath_cb, p, &err);
1142         if (err != NULL) {
1143                 setpath_complete(p->session, NULL, err, user_data);
1144                 g_error_free(err);
1145         }
1146 }
1147
1148 static int session_process_setpath(struct pending_request *p, GError **err)
1149 {
1150         struct setpath_data *req = p->data;
1151         const char *first = "";
1152
1153         /* Relative path */
1154         if (req->remaining[0][0] != '/')
1155                 first = req->remaining[req->index];
1156         req->index++;
1157
1158         p->req_id = g_obex_setpath(p->session->obex, first, setpath_cb, p, err);
1159         if (*err != NULL)
1160                 return (*err)->code;
1161
1162         p->session->p = p;
1163
1164         return 0;
1165 }
1166
1167 guint obc_session_setpath(struct obc_session *session, const char *path,
1168                                 session_callback_t func, void *user_data,
1169                                 GError **err)
1170 {
1171         struct setpath_data *data;
1172         struct pending_request *p;
1173
1174         if (session->obex == NULL) {
1175                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1176                                                 "Session disconnected");
1177                 return 0;
1178         }
1179
1180         data = g_new0(struct setpath_data, 1);
1181         data->func = func;
1182         data->user_data = user_data;
1183         data->remaining = g_strsplit(strlen(path) ? path : "/", "/", 0);
1184
1185         if (!data->remaining || !data->remaining[0]) {
1186                 error("obc_session_setpath: invalid path %s", path);
1187                 g_set_error(err, OBEX_IO_ERROR, -EINVAL, "Invalid argument");
1188 #ifdef __TIZEN_PATCH__
1189                 setpath_data_free(data);
1190 #endif
1191                 return 0;
1192         }
1193
1194         p = pending_request_new(session, session_process_setpath, NULL,
1195                                 setpath_op_complete, data, setpath_data_free);
1196         session_queue(p);
1197         return p->id;
1198 }
1199
1200 static void async_cb(GObex *obex, GError *err, GObexPacket *rsp,
1201                                                         gpointer user_data)
1202 {
1203         struct pending_request *p = user_data;
1204         struct obc_session *session = p->session;
1205         GError *gerr = NULL;
1206         uint8_t code;
1207
1208         p->req_id = 0;
1209
1210         if (err != NULL) {
1211                 if (p->func)
1212                         p->func(p->session, NULL, err, p->data);
1213                 goto done;
1214         }
1215
1216         code = g_obex_packet_get_operation(rsp, NULL);
1217         if (code != G_OBEX_RSP_SUCCESS)
1218                 g_set_error(&gerr, OBEX_IO_ERROR, code, "%s",
1219                                                         g_obex_strerror(code));
1220
1221         if (p->func)
1222                 p->func(p->session, NULL, gerr, p->data);
1223
1224         if (gerr != NULL)
1225                 g_clear_error(&gerr);
1226
1227 done:
1228         pending_request_free(p);
1229         session->p = NULL;
1230
1231         session_process_queue(session);
1232 }
1233
1234 static void file_op_complete(struct obc_session *session,
1235                                                 struct obc_transfer *transfer,
1236                                                 GError *err, void *user_data)
1237 {
1238         struct file_data *data = user_data;
1239
1240         if (data->func)
1241                 data->func(session, NULL, err, data->user_data);
1242 }
1243
1244 static int session_process_mkdir(struct pending_request *p, GError **err)
1245 {
1246         struct file_data *req = p->data;
1247
1248         p->req_id = g_obex_mkdir(p->session->obex, req->srcname, async_cb, p,
1249                                                                         err);
1250         if (*err != NULL)
1251                 return (*err)->code;
1252
1253         p->session->p = p;
1254
1255         return 0;
1256 }
1257
1258 guint obc_session_mkdir(struct obc_session *session, const char *folder,
1259                                 session_callback_t func, void *user_data,
1260                                 GError **err)
1261 {
1262         struct file_data *data;
1263         struct pending_request *p;
1264
1265         if (session->obex == NULL) {
1266                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1267                                                 "Session disconnected");
1268                 return 0;
1269         }
1270
1271         data = g_new0(struct file_data, 1);
1272         data->srcname = g_strdup(folder);
1273         data->func = func;
1274         data->user_data = user_data;
1275
1276         p = pending_request_new(session, session_process_mkdir, NULL,
1277                                         file_op_complete, data, file_data_free);
1278         session_queue(p);
1279         return p->id;
1280 }
1281
1282 static int session_process_copy(struct pending_request *p, GError **err)
1283 {
1284         struct file_data *req = p->data;
1285
1286         p->req_id = g_obex_copy(p->session->obex, req->srcname, req->destname,
1287                                                         async_cb, p, err);
1288         if (*err != NULL)
1289                 return (*err)->code;
1290
1291         p->session->p = p;
1292
1293         return 0;
1294 }
1295
1296 guint obc_session_copy(struct obc_session *session, const char *srcname,
1297                                 const char *destname, session_callback_t func,
1298                                 void *user_data, GError **err)
1299 {
1300         struct file_data *data;
1301         struct pending_request *p;
1302
1303         if (session->obex == NULL) {
1304                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1305                                                 "Session disconnected");
1306                 return 0;
1307         }
1308
1309         data = g_new0(struct file_data, 1);
1310         data->srcname = g_strdup(srcname);
1311         data->destname = g_strdup(destname);
1312         data->func = func;
1313         data->user_data = user_data;
1314
1315         p = pending_request_new(session, session_process_copy, NULL,
1316                                 file_op_complete, data, file_data_free);
1317         session_queue(p);
1318         return p->id;
1319 }
1320
1321 static int session_process_move(struct pending_request *p, GError **err)
1322 {
1323         struct file_data *req = p->data;
1324
1325         p->req_id = g_obex_move(p->session->obex, req->srcname, req->destname,
1326                                                         async_cb, p, err);
1327         if (*err != NULL)
1328                 return (*err)->code;
1329
1330         p->session->p = p;
1331
1332         return 0;
1333 }
1334
1335 guint obc_session_move(struct obc_session *session, const char *srcname,
1336                                 const char *destname, session_callback_t func,
1337                                 void *user_data, GError **err)
1338 {
1339         struct file_data *data;
1340         struct pending_request *p;
1341
1342         if (session->obex == NULL) {
1343                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1344                                                 "Session disconnected");
1345                 return 0;
1346         }
1347
1348         data = g_new0(struct file_data, 1);
1349         data->srcname = g_strdup(srcname);
1350         data->destname = g_strdup(destname);
1351         data->func = func;
1352         data->user_data = user_data;
1353
1354         p = pending_request_new(session, session_process_move, NULL,
1355                                 file_op_complete, data, file_data_free);
1356         session_queue(p);
1357         return p->id;
1358 }
1359
1360 static int session_process_delete(struct pending_request *p, GError **err)
1361 {
1362         struct file_data *req = p->data;
1363
1364         p->req_id = g_obex_delete(p->session->obex, req->srcname, async_cb, p,
1365                                                                         err);
1366         if (*err != NULL)
1367                 return (*err)->code;
1368
1369         p->session->p = p;
1370
1371         return 0;
1372 }
1373
1374 guint obc_session_delete(struct obc_session *session, const char *file,
1375                                 session_callback_t func, void *user_data,
1376                                 GError **err)
1377 {
1378         struct file_data *data;
1379         struct pending_request *p;
1380
1381         if (session->obex == NULL) {
1382                 g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED,
1383                                                 "Session disconnected");
1384                 return 0;
1385         }
1386
1387         data = g_new0(struct file_data, 1);
1388         data->srcname = g_strdup(file);
1389         data->func = func;
1390         data->user_data = user_data;
1391
1392         p = pending_request_new(session, session_process_delete, NULL,
1393                                 file_op_complete, data, file_data_free);
1394         session_queue(p);
1395         return p->id;
1396 }
1397
1398 void obc_session_cancel(struct obc_session *session, guint id,
1399                                                         gboolean remove)
1400 {
1401         struct pending_request *p = session->p;
1402
1403         if (p == NULL || p->id != id)
1404                 return;
1405
1406         if (p->req_id == 0)
1407                 return;
1408
1409         g_obex_cancel_req(session->obex, p->req_id, remove);
1410         p->req_id = 0;
1411
1412         if (!remove)
1413                 return;
1414
1415         pending_request_free(p);
1416         session->p = NULL;
1417
1418         session_process_queue(session);
1419 }