tizen beta release
[profile/ivi/obexd.git] / plugins / bluetooth.c
1 /*
2  *
3  *  OBEX Server
4  *
5  *  Copyright (C) 2007-2010  Nokia Corporation
6  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
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 <errno.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <inttypes.h>
33
34 #include <glib.h>
35 #include <gdbus.h>
36
37 #include "obexd.h"
38 #include "plugin.h"
39 #include "server.h"
40 #include "obex.h"
41 #include "transport.h"
42 #include "service.h"
43 #include "log.h"
44 #include "btio.h"
45 #include "glib-helper.h"
46
47 #define BT_RX_MTU 32767
48 #define BT_TX_MTU 32767
49
50 #define TIMEOUT 60*1000 /* Timeout for user response (miliseconds) */
51
52 struct pending_request {
53         DBusPendingCall *call;
54         struct bluetooth_service *service;
55         char *adapter_path;
56         char address[18];
57         unsigned int watch;
58         GIOChannel *io;
59 };
60
61 struct bluetooth_service {
62         struct obex_server *server;
63         struct obex_service_driver *driver;
64         uint32_t handle;
65 };
66
67 struct adapter_any {
68         char *path;             /* Adapter ANY path */
69         GSList *services;       /* List of services to register records */
70 };
71
72 static DBusConnection *connection = NULL;
73 static struct adapter_any *any = NULL;
74
75 static void add_record_reply(DBusPendingCall *call, void *user_data)
76 {
77         struct bluetooth_service *service = user_data;
78         DBusMessage *reply = dbus_pending_call_steal_reply(call);
79         DBusError derr;
80         uint32_t handle;
81
82         dbus_error_init(&derr);
83         if (dbus_set_error_from_message(&derr, reply)) {
84                 error("bluetooth: Replied with an error: %s, %s",
85                                 derr.name, derr.message);
86                 dbus_error_free(&derr);
87                 handle = 0;
88         } else {
89                 dbus_message_get_args(reply, NULL,
90                                 DBUS_TYPE_UINT32, &handle,
91                                 DBUS_TYPE_INVALID);
92
93                 service->handle = handle;
94
95                 DBG("Registered: %s, handle: 0x%x",
96                                 service->driver->name, service->handle);
97         }
98
99         dbus_message_unref(reply);
100 }
101
102 static int add_record(const char *path, const char *xml,
103                         struct bluetooth_service *service)
104 {
105         DBusMessage *msg;
106         DBusPendingCall *call;
107         int ret = 0;
108
109         msg = dbus_message_new_method_call("org.bluez", path,
110                                         "org.bluez.Service", "AddRecord");
111
112         dbus_message_append_args(msg, DBUS_TYPE_STRING, &xml,
113                         DBUS_TYPE_INVALID);
114
115         if (dbus_connection_send_with_reply(connection,
116                                 msg, &call, -1) == FALSE) {
117                 ret = -1;
118                 goto failed;
119         }
120
121         dbus_pending_call_set_notify(call, add_record_reply, service, NULL);
122         dbus_pending_call_unref(call);
123
124 failed:
125         dbus_message_unref(msg);
126         return ret;
127 }
128
129 static struct bluetooth_service *find_service(
130                                         struct obex_service_driver *driver)
131 {
132         GSList *l;
133
134         for (l = any->services; l; l = l->next) {
135                 struct bluetooth_service *service = l->data;
136
137                 if (service->driver == driver)
138                         return service;
139         }
140
141         return NULL;
142 }
143
144 static void register_record(struct obex_server *server)
145 {
146         const GSList *l;
147
148         if (connection == NULL)
149                 return;
150
151         for (l = server->drivers; l; l = l->next) {
152                 struct obex_service_driver *driver = l->data;
153                 struct bluetooth_service *service;
154                 char *xml;
155
156                 service = find_service(driver);
157                 if (service == NULL) {
158                         service = g_new0(struct bluetooth_service, 1);
159                         service->driver = driver;
160                         service->server = server;
161                         any->services = g_slist_append(any->services, service);
162                 }
163
164                 /* Service already has a record registered */
165                 if (service->handle != 0)
166                         continue;
167
168                 /* Adapter ANY is not available yet: Add record later */
169                 if (any->path == NULL)
170                         continue;
171
172                 xml = g_markup_printf_escaped(driver->record, driver->channel,
173                                                 driver->name);
174                 add_record(any->path, xml, service);
175                 g_free(xml);
176         }
177 }
178
179 static void find_adapter_any_reply(DBusPendingCall *call, void *user_data)
180 {
181         DBusMessage *reply = dbus_pending_call_steal_reply(call);
182         const char *path;
183         GSList *l;
184         DBusError derr;
185
186         dbus_error_init(&derr);
187         if (dbus_set_error_from_message(&derr, reply)) {
188                 error("bluetooth: Replied with an error: %s, %s",
189                                 derr.name, derr.message);
190                 dbus_error_free(&derr);
191                 goto done;
192         }
193
194         dbus_message_get_args(reply, NULL,
195                         DBUS_TYPE_OBJECT_PATH, &path,
196                         DBUS_TYPE_INVALID);
197         any->path = g_strdup(path);
198
199         for (l = any->services; l; l = l->next) {
200                 struct bluetooth_service *service = l->data;
201                 char *xml;
202
203                 xml = g_markup_printf_escaped(service->driver->record,
204                                                 service->driver->channel,
205                                                 service->driver->name);
206                 add_record(any->path, xml, service);
207                 g_free(xml);
208         }
209
210 done:
211         dbus_message_unref(reply);
212 }
213
214 static DBusPendingCall *find_adapter(const char *pattern,
215                                 DBusPendingCallNotifyFunction function,
216                                 void *user_data)
217 {
218         DBusMessage *msg;
219         DBusPendingCall *call;
220
221         DBG("FindAdapter(%s)", pattern);
222
223         msg = dbus_message_new_method_call("org.bluez", "/",
224                                         "org.bluez.Manager", "FindAdapter");
225         if (!msg)
226                 return NULL;
227
228         dbus_message_append_args(msg, DBUS_TYPE_STRING, &pattern,
229                         DBUS_TYPE_INVALID);
230
231         if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
232                 dbus_message_unref(msg);
233                 return NULL;
234         }
235
236         dbus_pending_call_set_notify(call, function, user_data, NULL);
237
238         dbus_message_unref(msg);
239
240         return call;
241 }
242
243 static void name_acquired(DBusConnection *conn, void *user_data)
244 {
245         DBusPendingCall *call;
246
247         call = find_adapter("any", find_adapter_any_reply, NULL);
248         if (call)
249                 dbus_pending_call_unref(call);
250 }
251
252 static void name_released(DBusConnection *conn, void *user_data)
253 {
254         GSList *l;
255
256         /* reset handles so the services got register next time */
257         for (l = any->services; l; l = l->next) {
258                 struct bluetooth_service *service = l->data;
259
260                 service->handle = 0;
261         }
262
263         g_free(any->path);
264         any->path = NULL;
265
266 }
267
268 static void service_cancel(struct pending_request *pending)
269 {
270         DBusMessage *msg;
271
272         msg = dbus_message_new_method_call("org.bluez",
273                                         pending->adapter_path,
274                                         "org.bluez.Service",
275                                         "CancelAuthorization");
276
277         g_dbus_send_message(connection, msg);
278 }
279
280 static void pending_request_free(struct pending_request *pending)
281 {
282         if (pending->call)
283                 dbus_pending_call_unref(pending->call);
284         g_io_channel_unref(pending->io);
285         g_free(pending->adapter_path);
286         g_free(pending);
287 }
288
289 static void connect_event(GIOChannel *io, GError *err, void *user_data)
290 {
291         struct bluetooth_service *service = user_data;
292         struct obex_server *server = service->server;
293
294         if (err)
295                 goto drop;
296
297         if (obex_server_new_connection(server, io, BT_TX_MTU, BT_RX_MTU) < 0)
298                 g_io_channel_shutdown(io, TRUE, NULL);
299
300         return;
301
302 drop:
303         error("%s", err->message);
304         g_io_channel_shutdown(io, TRUE, NULL);
305         return;
306 }
307
308 static void service_reply(DBusPendingCall *call, void *user_data)
309 {
310         struct pending_request *pending = user_data;
311         GIOChannel *io = pending->io;
312         struct bluetooth_service *service = pending->service;
313         DBusMessage *reply = dbus_pending_call_steal_reply(call);
314         DBusError derr;
315         GError *err = NULL;
316
317         dbus_error_init(&derr);
318         if (dbus_set_error_from_message(&derr, reply)) {
319                 error("bluetooth: RequestAuthorization error: %s, %s",
320                                 derr.name, derr.message);
321
322                 if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY))
323                         service_cancel(pending);
324
325                 dbus_error_free(&derr);
326                 g_io_channel_shutdown(io, TRUE, NULL);
327                 goto done;
328         }
329
330         DBG("RequestAuthorization succeeded");
331
332         if (!bt_io_accept(io, connect_event, service, NULL, &err)) {
333                 error("%s", err->message);
334                 g_error_free(err);
335                 g_io_channel_shutdown(io, TRUE, NULL);
336         }
337
338 done:
339         g_source_remove(pending->watch);
340         pending_request_free(pending);
341         dbus_message_unref(reply);
342 }
343
344 static gboolean service_error(GIOChannel *io, GIOCondition cond,
345                         void *user_data)
346 {
347         struct pending_request *pending = user_data;
348
349         service_cancel(pending);
350
351         dbus_pending_call_cancel(pending->call);
352
353         pending_request_free(pending);
354
355         return FALSE;
356 }
357
358 static void find_adapter_reply(DBusPendingCall *call, void *user_data)
359 {
360         struct pending_request *pending = user_data;
361         DBusMessage *reply = dbus_pending_call_steal_reply(call);
362         DBusMessage *msg;
363         DBusPendingCall *pcall;
364         const char *path, *paddr = pending->address;
365         DBusError derr;
366
367         dbus_error_init(&derr);
368         if (dbus_set_error_from_message(&derr, reply)) {
369                 error("Replied with an error: %s, %s",
370                                 derr.name, derr.message);
371                 dbus_error_free(&derr);
372                 goto failed;
373         }
374
375         dbus_message_get_args(reply, NULL,
376                         DBUS_TYPE_OBJECT_PATH, &path,
377                         DBUS_TYPE_INVALID);
378
379         DBG("FindAdapter -> %s", path);
380         pending->adapter_path = g_strdup(path);
381         dbus_message_unref(reply);
382
383         msg = dbus_message_new_method_call("org.bluez", path,
384                         "org.bluez.Service", "RequestAuthorization");
385
386         dbus_message_append_args(msg, DBUS_TYPE_STRING, &paddr,
387                         DBUS_TYPE_UINT32, &pending->service->handle,
388                         DBUS_TYPE_INVALID);
389
390         if (!dbus_connection_send_with_reply(connection,
391                                         msg, &pcall, TIMEOUT)) {
392                 dbus_message_unref(msg);
393                 goto failed;
394         }
395
396         dbus_message_unref(msg);
397
398         DBG("RequestAuthorization(%s, %x)", paddr,
399                         pending->service->handle);
400
401         if (!dbus_pending_call_set_notify(pcall, service_reply, pending,
402                                                                 NULL)) {
403                 dbus_pending_call_unref(pcall);
404                 goto failed;
405         }
406
407         dbus_pending_call_unref(pending->call);
408         pending->call = pcall;
409
410         /* Catches errors before authorization response comes */
411         pending->watch = g_io_add_watch(pending->io,
412                                         G_IO_HUP | G_IO_ERR | G_IO_NVAL,
413                                         service_error, pending);
414
415         return;
416
417 failed:
418         g_io_channel_shutdown(pending->io, TRUE, NULL);
419         pending_request_free(pending);
420 }
421
422 static int request_service_authorization(struct bluetooth_service *service,
423                                         GIOChannel *io, const char *address)
424 {
425         struct pending_request *pending;
426         char source[18];
427         GError *err = NULL;
428
429         if (connection == NULL || any->path == NULL)
430                 return -1;
431
432         bt_io_get(io, BT_IO_RFCOMM, &err,
433                         BT_IO_OPT_SOURCE, source,
434                         BT_IO_OPT_INVALID);
435         if (err) {
436                 error("%s", err->message);
437                 g_error_free(err);
438                 return -EINVAL;
439         }
440
441         pending = g_new0(struct pending_request, 1);
442         pending->call = find_adapter(source, find_adapter_reply, pending);
443         if (!pending->call) {
444                 g_free(pending);
445                 return -ENOMEM;
446         }
447
448         pending->service = service;
449         pending->io = g_io_channel_ref(io);
450         memcpy(pending->address, address, sizeof(pending->address));
451
452         return 0;
453 }
454
455 static void confirm_event(GIOChannel *io, void *user_data)
456 {
457         struct bluetooth_service *service;
458         GError *err = NULL;
459         char address[18];
460         uint8_t channel;
461         struct obex_service_driver *driver = user_data;
462
463         bt_io_get(io, BT_IO_RFCOMM, &err,
464                         BT_IO_OPT_DEST, address,
465                         BT_IO_OPT_CHANNEL, &channel,
466                         BT_IO_OPT_INVALID);
467         if (err) {
468                 error("%s", err->message);
469                 g_error_free(err);
470                 goto drop;
471         }
472
473         info("bluetooth: New connection from: %s, channel %u", address,
474                         channel);
475
476         service = find_service(driver);
477         if (service == NULL) {
478                 error("bluetooth: Unable to find service");
479                 goto drop;
480         }
481
482         if (driver->secure) {
483                 if (request_service_authorization(service, io, address) < 0)
484                         goto drop;
485
486                 return;
487         }
488
489         if (!bt_io_accept(io, connect_event, service, NULL, &err)) {
490                 error("%s", err->message);
491                 g_error_free(err);
492                 goto drop;
493         }
494
495         return;
496
497 drop:
498         g_io_channel_shutdown(io, TRUE, NULL);
499 }
500
501 static GIOChannel *start(struct obex_server *server,
502                                 struct obex_service_driver *service)
503 {
504         BtIOSecLevel sec_level;
505         GIOChannel *io;
506         GError *err = NULL;
507
508         if (service->secure == TRUE)
509                 sec_level = BT_IO_SEC_MEDIUM;
510         else
511                 sec_level = BT_IO_SEC_LOW;
512
513
514         io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_event,
515                                 service, NULL, &err,
516                                 BT_IO_OPT_CHANNEL, service->channel,
517                                 BT_IO_OPT_SEC_LEVEL, sec_level,
518                                 BT_IO_OPT_INVALID);
519         if (io == NULL) {
520                 error("bluetooth: unable to listen in channel %d: %s",
521                                 service->channel, err->message);
522                 g_error_free(err);
523         } else
524                 DBG("listening on channel %d", service->channel);
525
526         return io;
527 }
528
529 static void *bluetooth_start(struct obex_server *server, int *err)
530 {
531         GSList *ios = NULL;
532         const GSList *l;
533
534         for (l = server->drivers; l; l = l->next) {
535                 struct obex_service_driver *service = l->data;
536                 GIOChannel *io;
537
538                 io = start(server, service);
539                 if (io == NULL)
540                         continue;
541
542                 ios = g_slist_prepend(ios, io);
543         }
544
545         register_record(server);
546
547         return ios;
548 }
549
550 static void stop(gpointer data)
551 {
552         GIOChannel *io = data;
553
554         g_io_channel_shutdown(io, TRUE, NULL);
555         g_io_channel_unref(io);
556 }
557
558 static void bluetooth_stop(void *data)
559 {
560         GSList *ios = data;
561
562         g_slist_free_full(ios, stop);
563 }
564
565 static int bluetooth_getpeername(GIOChannel *io, char **name)
566 {
567         GError *gerr = NULL;
568         char address[18];
569
570         bt_io_get(io, BT_IO_RFCOMM, &gerr,
571                         BT_IO_OPT_DEST, address,
572                         BT_IO_OPT_INVALID);
573         if (gerr)
574                 return -EINVAL;
575
576         *name = g_strdup(address);
577
578         return 0;
579 }
580
581 static struct obex_transport_driver driver = {
582         .name = "bluetooth",
583         .start = bluetooth_start,
584         .getpeername = bluetooth_getpeername,
585         .stop = bluetooth_stop
586 };
587
588 static unsigned int listener_id = 0;
589
590 static int bluetooth_init(void)
591 {
592         any = g_new0(struct adapter_any, 1);
593
594         connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
595         if (connection == NULL)
596                 return -EPERM;
597
598         listener_id = g_dbus_add_service_watch(connection, "org.bluez",
599                                 name_acquired, name_released, NULL, NULL);
600
601         return obex_transport_driver_register(&driver);
602 }
603
604 static void bluetooth_exit(void)
605 {
606         g_dbus_remove_watch(connection, listener_id);
607
608         if (any) {
609                 g_slist_free_full(any->services, g_free);
610                 g_free(any->path);
611                 g_free(any);
612         }
613
614         if (connection)
615                 dbus_connection_unref(connection);
616
617         obex_transport_driver_unregister(&driver);
618 }
619
620 OBEX_PLUGIN_DEFINE(bluetooth, bluetooth_init, bluetooth_exit)