1 /*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2006-2007 Nokia Corporation
6 * Copyright (C) 2004-2008 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 <stdlib.h>
31 #include <string.h>
32
33 #include <bluetooth/bluetooth.h>
34 #include <bluetooth/hci.h>
35 #include <bluetooth/hci_lib.h>
36 #include <bluetooth/sdp.h>
37 #include <bluetooth/sdp_lib.h>
38
39 #include <gdbus.h>
40
41 #include "hcid.h"
42 #include "sdpd.h"
43 #include "sdp-xml.h"
44 #include "manager.h"
45 #include "adapter.h"
46 #include "dbus-hci.h"
47 #include "dbus-common.h"
48 #include "error.h"
49 #include "dbus-service.h"
50 #include "dbus-security.h"
51 #include "dbus-database.h"
52
53 static GSList *records = NULL;
54
55 struct record_data {
56 uint32_t handle;
57 char *sender;
58 guint listener_id;
59 };
60
find_record(uint32_t handle,const char * sender)61 static struct record_data *find_record(uint32_t handle, const char *sender)
62 {
63 GSList *list;
64
65 for (list = records; list; list = list->next) {
66 struct record_data *data = list->data;
67 if (handle == data->handle && !strcmp(sender, data->sender))
68 return data;
69 }
70
71 return NULL;
72 }
73
exit_callback(void * user_data)74 static void exit_callback(void *user_data)
75 {
76 struct record_data *user_record = user_data;
77
78 debug("remove record");
79
80 records = g_slist_remove(records, user_record);
81
82 remove_record_from_server(user_record->handle);
83
84 g_free(user_record->sender);
85 g_free(user_record);
86 }
87
invalid_arguments(DBusMessage * msg)88 static inline DBusMessage *invalid_arguments(DBusMessage *msg)
89 {
90 return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
91 "Invalid arguments in method call");
92 }
93
not_available(DBusMessage * msg)94 static inline DBusMessage *not_available(DBusMessage *msg)
95 {
96 return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable",
97 "Not Available");
98 }
99
failed(DBusMessage * msg)100 static inline DBusMessage *failed(DBusMessage *msg)
101 {
102 return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "Failed");
103 }
104
add_service_record(DBusConnection * conn,DBusMessage * msg,void * data)105 static DBusMessage *add_service_record(DBusConnection *conn,
106 DBusMessage *msg, void *data)
107 {
108 DBusMessageIter iter, array;
109 const char *sender;
110 struct record_data *user_record;
111 sdp_record_t *sdp_record;
112 const uint8_t *record;
113 int scanned, len = -1;
114
115 dbus_message_iter_init(msg, &iter);
116 dbus_message_iter_recurse(&iter, &array);
117
118 dbus_message_iter_get_fixed_array(&array, &record, &len);
119 if (len <= 0)
120 return invalid_arguments(msg);
121
122 sdp_record = sdp_extract_pdu_safe(record, len, &scanned);
123 if (!sdp_record) {
124 error("Parsing of service record failed");
125 return failed(msg);
126 }
127
128 if (scanned != len) {
129 error("Size mismatch of service record");
130 sdp_record_free(sdp_record);
131 return failed(msg);
132 }
133
134 if (add_record_to_server(BDADDR_ANY, sdp_record) < 0) {
135 error("Failed to register service record");
136 sdp_record_free(sdp_record);
137 return failed(msg);
138 }
139
140 user_record = g_new0(struct record_data, 1);
141
142 user_record->handle = sdp_record->handle;
143
144 sender = dbus_message_get_sender(msg);
145
146 user_record->sender = g_strdup(sender);
147
148 records = g_slist_append(records, user_record);
149
150 user_record->listener_id = g_dbus_add_disconnect_watch(conn, sender,
151 exit_callback,
152 user_record,
153 NULL);
154
155 debug("listener_id %d", user_record->listener_id);
156
157 return g_dbus_create_reply(msg, DBUS_TYPE_UINT32, &user_record->handle,
158 DBUS_TYPE_INVALID);
159 }
160
add_xml_record(DBusConnection * conn,const char * sender,bdaddr_t * src,const char * record,dbus_uint32_t * handle)161 int add_xml_record(DBusConnection *conn, const char *sender, bdaddr_t *src,
162 const char *record, dbus_uint32_t *handle)
163 {
164 struct record_data *user_record;
165 sdp_record_t *sdp_record;
166
167 sdp_record = sdp_xml_parse_record(record, strlen(record));
168 if (!sdp_record) {
169 error("Parsing of XML service record failed");
170 return -EIO;
171 }
172
173 if (add_record_to_server(src, sdp_record) < 0) {
174 error("Failed to register service record");
175 sdp_record_free(sdp_record);
176 return -EIO;
177 }
178
179 user_record = g_new0(struct record_data, 1);
180
181 user_record->handle = sdp_record->handle;
182
183 user_record->sender = g_strdup(sender);
184
185 records = g_slist_append(records, user_record);
186
187 user_record->listener_id = g_dbus_add_disconnect_watch(conn, sender,
188 exit_callback, user_record, NULL);
189
190 debug("listener_id %d", user_record->listener_id);
191
192 *handle = user_record->handle;
193
194 return 0;
195 }
196
add_service_record_from_xml(DBusConnection * conn,DBusMessage * msg,void * data)197 static DBusMessage *add_service_record_from_xml(DBusConnection *conn,
198 DBusMessage *msg, void *data)
199 {
200 const char *sender, *record;
201 dbus_uint32_t handle;
202 int err;
203
204 if (dbus_message_get_args(msg, NULL,
205 DBUS_TYPE_STRING, &record, DBUS_TYPE_INVALID) == FALSE)
206 return NULL;
207
208 sender = dbus_message_get_sender(msg);
209
210 err = add_xml_record(conn, sender, BDADDR_ANY, record, &handle);
211 if (err < 0)
212 return failed(msg);
213
214 return g_dbus_create_reply(msg, DBUS_TYPE_UINT32, &handle,
215 DBUS_TYPE_INVALID);
216 }
217
update_record(DBusConnection * conn,DBusMessage * msg,bdaddr_t * src,dbus_uint32_t handle,sdp_record_t * sdp_record)218 static DBusMessage *update_record(DBusConnection *conn, DBusMessage *msg,
219 bdaddr_t *src, dbus_uint32_t handle, sdp_record_t *sdp_record)
220 {
221 int err;
222
223 if (remove_record_from_server(handle) < 0) {
224 sdp_record_free(sdp_record);
225 return g_dbus_create_error(msg,
226 ERROR_INTERFACE ".NotAvailable",
227 "Not Available");
228 }
229
230 sdp_record->handle = handle;
231 err = add_record_to_server(src, sdp_record);
232 if (err < 0) {
233 sdp_record_free(sdp_record);
234 error("Failed to update the service record");
235 return g_dbus_create_error(msg,
236 ERROR_INTERFACE ".Failed",
237 strerror(EIO));
238 }
239
240 return dbus_message_new_method_return(msg);
241 }
242
update_service_record(DBusConnection * conn,DBusMessage * msg,void * data)243 static DBusMessage *update_service_record(DBusConnection *conn,
244 DBusMessage *msg, void *data)
245 {
246 struct record_data *user_record;
247 DBusMessageIter iter, array;
248 sdp_record_t *sdp_record;
249 dbus_uint32_t handle;
250 const uint8_t *bin_record;
251 int scanned, size = -1;
252
253 dbus_message_iter_init(msg, &iter);
254 dbus_message_iter_get_basic(&iter, &handle);
255 dbus_message_iter_next(&iter);
256 dbus_message_iter_recurse(&iter, &array);
257
258 dbus_message_iter_get_fixed_array(&array, &bin_record, &size);
259 if (size <= 0)
260 return invalid_arguments(msg);
261
262 user_record = find_record(handle, dbus_message_get_sender(msg));
263 if (!user_record)
264 return not_available(msg);
265
266 sdp_record = sdp_extract_pdu_safe(bin_record, size, &scanned);
267 if (!sdp_record) {
268 error("Parsing of service record failed");
269 return invalid_arguments(msg);
270 }
271
272 if (scanned != size) {
273 error("Size mismatch of service record");
274 sdp_record_free(sdp_record);
275 return invalid_arguments(msg);
276 }
277
278 return update_record(conn, msg, BDADDR_ANY, handle, sdp_record);
279 }
280
update_xml_record(DBusConnection * conn,DBusMessage * msg,bdaddr_t * src)281 DBusMessage *update_xml_record(DBusConnection *conn,
282 DBusMessage *msg, bdaddr_t *src)
283 {
284 struct record_data *user_record;
285 sdp_record_t *sdp_record;
286 const char *record;
287 dbus_uint32_t handle;
288 int len;
289
290 if (dbus_message_get_args(msg, NULL,
291 DBUS_TYPE_UINT32, &handle,
292 DBUS_TYPE_STRING, &record,
293 DBUS_TYPE_INVALID) == FALSE)
294 return NULL;
295
296 len = (record ? strlen(record) : 0);
297 if (len == 0)
298 return invalid_arguments(msg);
299
300 user_record = find_record(handle, dbus_message_get_sender(msg));
301 if (!user_record)
302 return g_dbus_create_error(msg,
303 ERROR_INTERFACE ".NotAvailable",
304 "Not Available");
305
306 sdp_record = sdp_xml_parse_record(record, len);
307 if (!sdp_record) {
308 error("Parsing of XML service record failed");
309 sdp_record_free(sdp_record);
310 return g_dbus_create_error(msg,
311 ERROR_INTERFACE ".Failed",
312 strerror(EIO));
313 }
314
315 return update_record(conn, msg, src, handle, sdp_record);
316 }
317
update_service_record_from_xml(DBusConnection * conn,DBusMessage * msg,void * data)318 static DBusMessage *update_service_record_from_xml(DBusConnection *conn,
319 DBusMessage *msg, void *data)
320 {
321 return update_xml_record(conn, msg, BDADDR_ANY);
322 }
323
remove_record(DBusConnection * conn,const char * sender,dbus_uint32_t handle)324 int remove_record(DBusConnection *conn, const char *sender,
325 dbus_uint32_t handle)
326 {
327 struct record_data *user_record;
328
329 debug("remove record 0x%x", handle);
330
331 user_record = find_record(handle, sender);
332 if (!user_record)
333 return -1;
334
335 debug("listner_id %d", user_record->listener_id);
336
337 g_dbus_remove_watch(conn, user_record->listener_id);
338
339 exit_callback(user_record);
340
341 return 0;
342 }
343
remove_service_record(DBusConnection * conn,DBusMessage * msg,void * data)344 static DBusMessage *remove_service_record(DBusConnection *conn,
345 DBusMessage *msg, void *data)
346 {
347 dbus_uint32_t handle;
348 const char *sender;
349
350 if (dbus_message_get_args(msg, NULL,
351 DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID) == FALSE)
352 return NULL;
353
354 sender = dbus_message_get_sender(msg);
355
356 if (remove_record(conn, sender, handle) < 0)
357 return not_available(msg);
358
359 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
360 }
361
362 static GDBusMethodTable database_methods[] = {
363 { "AddServiceRecord", "ay", "u", add_service_record },
364 { "AddServiceRecordFromXML", "s", "u", add_service_record_from_xml },
365 { "UpdateServiceRecord", "uay", "", update_service_record },
366 { "UpdateServiceRecordFromXML", "us", "", update_service_record_from_xml },
367 { "RemoveServiceRecord", "u", "", remove_service_record },
368 { }
369 };
370
database_init(DBusConnection * conn,const char * path)371 dbus_bool_t database_init(DBusConnection *conn, const char *path)
372 {
373 return g_dbus_register_interface(conn, path, DATABASE_INTERFACE,
374 database_methods, NULL, NULL, NULL, NULL);
375 }
376
database_cleanup(DBusConnection * conn,const char * path)377 void database_cleanup(DBusConnection *conn, const char *path)
378 {
379 g_dbus_unregister_interface(conn, path, DATABASE_INTERFACE);
380 }
381