device: Do not double free the device records list
[framework/connectivity/neard.git] / src / device.c
1 /*
2  *
3  *  neard - Near Field Communication manager
4  *
5  *  Copyright (C) 2011  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <string.h>
30
31 #include <glib.h>
32
33 #include <gdbus.h>
34
35 #include "near.h"
36
37 struct near_device {
38         char *path;
39
40         uint32_t adapter_idx;
41         uint32_t target_idx;
42
43         uint8_t nfcid[NFC_MAX_NFCID1_LEN];
44         uint8_t nfcid_len;
45
46         size_t data_length;
47         uint8_t *data;
48
49         uint32_t n_records;
50         GList *records;
51
52         DBusMessage *push_msg; /* Push pending message */
53 };
54
55 static DBusConnection *connection = NULL;
56
57 static GHashTable *device_hash;
58
59 static GSList *driver_list = NULL;
60
61 static void free_device(gpointer data)
62 {
63         struct near_device *device = data;
64
65         DBG("device %p", device);
66
67         near_ndef_records_free(device->records);
68
69         g_free(device->path);
70         g_free(device->data);
71         g_free(device);
72 }
73
74 struct near_device *near_device_get_device(uint32_t adapter_idx,
75                                                 uint32_t target_idx)
76 {
77         struct near_device *device;
78         char *path;
79
80         DBG("");
81
82         path = g_strdup_printf("%s/nfc%d/device%d", NFC_PATH,
83                                         adapter_idx, target_idx);
84         if (path == NULL)
85                 return NULL;
86
87         device = g_hash_table_lookup(device_hash, path);
88         g_free(path);
89
90         /* TODO refcount */
91         return device;
92 }
93
94 void __near_device_remove(struct near_device *device)
95 {
96         char *path = device->path;
97
98         DBG("path %s", device->path);
99
100         if (g_hash_table_lookup(device_hash, device->path) == NULL)
101                 return;
102
103         g_dbus_unregister_interface(connection, device->path,
104                                                 NFC_DEVICE_INTERFACE);
105
106         g_hash_table_remove(device_hash, path);
107 }
108
109 const char *__near_device_get_path(struct near_device *device)
110 {
111         return device->path;
112 }
113
114 uint32_t __neard_device_get_idx(struct near_device *device)
115 {
116         return device->target_idx;
117 }
118
119 static void append_records(DBusMessageIter *iter, void *user_data)
120 {
121         struct near_device *device = user_data;
122         GList *list;
123
124         DBG("");
125
126         for (list = device->records; list; list = list->next) {
127                 struct near_ndef_record *record = list->data;
128                 char *path;
129
130                 path = __near_ndef_record_get_path(record);
131                 if (path == NULL)
132                         continue;
133
134                 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
135                                                         &path);
136         }
137 }
138
139 static DBusMessage *get_properties(DBusConnection *conn,
140                                         DBusMessage *msg, void *data)
141 {
142         struct near_device *device = data;
143         DBusMessage *reply;
144         DBusMessageIter array, dict;
145
146         DBG("conn %p", conn);
147
148         reply = dbus_message_new_method_return(msg);
149         if (reply == NULL)
150                 return NULL;
151
152         dbus_message_iter_init_append(reply, &array);
153
154         near_dbus_dict_open(&array, &dict);
155
156         near_dbus_dict_append_array(&dict, "Records",
157                                 DBUS_TYPE_OBJECT_PATH, append_records, device);
158
159         near_dbus_dict_close(&array, &dict);
160
161         return reply;
162 }
163
164 static DBusMessage *set_property(DBusConnection *conn,
165                                         DBusMessage *msg, void *data)
166 {
167         DBG("conn %p", conn);
168
169         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
170 }
171
172 static void push_cb(uint32_t adapter_idx, uint32_t target_idx, int status)
173 {
174         struct near_device *device;
175         DBusConnection *conn;
176         DBusMessage *reply;
177
178         DBG("Push status %d", status);
179
180         conn = near_dbus_get_connection();
181         device = near_device_get_device(adapter_idx, target_idx);
182
183         if (conn == NULL || device == NULL)
184                 return;
185
186         if (status != 0) {
187                 reply = __near_error_failed(device->push_msg, EINVAL);
188                 if (reply != NULL)
189                         g_dbus_send_message(conn, reply);
190         } else {
191                 g_dbus_send_reply(conn, device->push_msg, DBUS_TYPE_INVALID);
192         }
193
194         dbus_message_unref(device->push_msg);
195         device->push_msg = NULL;
196 }
197
198 static char *sn_from_message(DBusMessage *msg)
199 {
200         DBusMessageIter iter;
201         DBusMessageIter arr_iter;
202
203         DBG("");
204
205         dbus_message_iter_init(msg, &iter);
206         dbus_message_iter_recurse(&iter, &arr_iter);
207
208         while (dbus_message_iter_get_arg_type(&arr_iter) !=
209                                                 DBUS_TYPE_INVALID) {
210                 const char *key, *value;
211                 DBusMessageIter ent_iter;
212                 DBusMessageIter var_iter;
213
214                 dbus_message_iter_recurse(&arr_iter, &ent_iter);
215                 dbus_message_iter_get_basic(&ent_iter, &key);
216
217                 if (g_strcmp0(key, "Type") != 0) {
218                         dbus_message_iter_next(&arr_iter);
219                         continue;
220                 }
221
222                 dbus_message_iter_next(&ent_iter);
223                 dbus_message_iter_recurse(&ent_iter, &var_iter);
224
225                 switch (dbus_message_iter_get_arg_type(&var_iter)) {
226                 case DBUS_TYPE_STRING:
227                         dbus_message_iter_get_basic(&var_iter, &value);
228
229                         if (g_strcmp0(value, "Text") == 0)
230                                 return NEAR_DEVICE_SN_SNEP;
231                         else if (g_strcmp0(value, "URI") == 0)
232                                 return NEAR_DEVICE_SN_SNEP;
233                         else if (g_strcmp0(value, "SmartPoster") == 0)
234                                 return NEAR_DEVICE_SN_SNEP;
235                         else if (g_strcmp0(value, "Handover") == 0)
236                                 return NEAR_DEVICE_SN_HANDOVER;
237                         else
238                                 return NULL;
239
240                         break;
241                 }
242
243                 dbus_message_iter_next(&arr_iter);
244         }
245
246         return NULL;
247 }
248
249 static DBusMessage *push_ndef(DBusConnection *conn,
250                                 DBusMessage *msg, void *data)
251 {
252         struct near_device *device = data;
253         struct near_ndef_message *ndef;
254         char *service_name;
255         int err;
256
257         DBG("conn %p", conn);
258
259         if (device->push_msg)
260                 return __near_error_in_progress(msg);
261
262         device->push_msg = dbus_message_ref(msg);
263
264         service_name = sn_from_message(msg);
265         if (service_name == NULL) {
266                 err = -EINVAL;
267                 goto error;
268         }
269
270         ndef = __ndef_build_from_message(msg);
271         if (ndef == NULL) {
272                 err = -EINVAL;
273                 goto error;
274         }
275
276         err = __near_device_push(device, ndef, service_name, push_cb);
277         if (err < 0)
278                 goto error;
279
280         g_free(ndef->data);
281         g_free(ndef);
282
283         return NULL;
284
285 error:
286         dbus_message_unref(device->push_msg);
287         device->push_msg = NULL;
288
289         return __near_error_failed(msg, -err);
290 }
291
292 static const GDBusMethodTable device_methods[] = {
293         { GDBUS_METHOD("GetProperties",
294                                 NULL, GDBUS_ARGS({"properties", "a{sv}"}),
295                                 get_properties) },
296         { GDBUS_METHOD("SetProperty",
297                                 GDBUS_ARGS({"name", "s"}, {"value", "v"}),
298                                 NULL, set_property) },
299         { GDBUS_ASYNC_METHOD("Push", GDBUS_ARGS({"attributes", "a{sv}"}),
300                                                         NULL, push_ndef) },
301         { },
302 };
303
304 static const GDBusSignalTable device_signals[] = {
305         { GDBUS_SIGNAL("PropertyChanged",
306                                 GDBUS_ARGS({"name", "s"}, {"value", "v"})) },
307         { }
308 };
309
310 int near_device_add_data(uint32_t adapter_idx, uint32_t target_idx,
311                         uint8_t *data, size_t data_length)
312 {
313         struct near_device *device;
314
315         device = near_device_get_device(adapter_idx, target_idx);
316         if (device == NULL)
317                 return -ENODEV;
318
319         device->data_length = data_length;
320         device->data = g_try_malloc0(data_length);
321         if (device->data == NULL)
322                 return -ENOMEM;
323
324         if (data != NULL)
325                 memcpy(device->data, data, data_length);
326
327         return 0;
328 }
329
330 int near_device_add_records(struct near_device *device, GList *records,
331                                 near_device_io_cb cb, int status)
332 {
333         GList *list;
334         struct near_ndef_record *record;
335         char *path;
336
337         DBG("records %p", records);
338
339         near_ndef_records_free(device->records);
340         device->records = NULL;
341
342         for (list = records; list; list = list->next) {
343                 record = list->data;
344
345                 path = g_strdup_printf("%s/nfc%d/device%d/record%d",
346                                         NFC_PATH, device->adapter_idx,
347                                         device->target_idx, device->n_records);
348
349                 if (path == NULL)
350                         continue;
351
352                 __near_ndef_record_register(record, path);
353
354                 device->n_records++;
355                 device->records = g_list_append(device->records, record);
356         }
357
358         __near_agent_ndef_parse_records(device->records);
359
360         near_dbus_property_changed_array(device->path,
361                                         NFC_DEVICE_INTERFACE, "Records",
362                                         DBUS_TYPE_OBJECT_PATH, append_records,
363                                         device);
364
365         if (cb != NULL)
366                 cb(device->adapter_idx, device->target_idx, status);
367
368         g_list_free(records);
369
370         return 0;
371 }
372
373 struct near_device *__near_device_add(uint32_t adapter_idx, uint32_t target_idx,
374                                         uint8_t *nfcid, uint8_t nfcid_len)
375 {
376         struct near_device *device;
377         char *path;
378
379         device = near_device_get_device(adapter_idx, target_idx);
380         if (device != NULL)
381                 return NULL;
382
383         device = g_try_malloc0(sizeof(struct near_device));
384         if (device == NULL)
385                 return NULL;
386
387         device->path = g_strdup_printf("%s/nfc%d/device%d", NFC_PATH,
388                                         adapter_idx, target_idx);
389         if (device->path == NULL) {
390                 g_free(device);
391                 return NULL;
392         }
393         device->adapter_idx = adapter_idx;
394         device->target_idx = target_idx;
395         device->n_records = 0;
396
397         if (nfcid_len <= NFC_MAX_NFCID1_LEN && nfcid_len > 0) {
398                 device->nfcid_len = nfcid_len;
399                 memcpy(device->nfcid, nfcid, nfcid_len);
400         }
401
402         path = g_strdup(device->path);
403         if (path == NULL) {
404                 g_free(device);
405                 return NULL;
406         }
407
408         g_hash_table_insert(device_hash, path, device);
409
410         DBG("connection %p", connection);
411
412         g_dbus_register_interface(connection, device->path,
413                                         NFC_DEVICE_INTERFACE,
414                                         device_methods, device_signals,
415                                                         NULL, device, NULL);
416
417         return device;
418 }
419
420 int __near_device_listen(struct near_device *device, near_device_io_cb cb)
421 {
422         GSList *list;
423
424         DBG("");
425
426         for (list = driver_list; list; list = list->next) {
427                 struct near_device_driver *driver = list->data;
428
429                 return driver->listen(device->adapter_idx, cb);
430         }
431
432         return 0;
433 }
434
435 int __near_device_push(struct near_device *device,
436                         struct near_ndef_message *ndef, char *service_name,
437                         near_device_io_cb cb)
438 {
439         GSList *list;
440
441         DBG("");
442
443         if (__near_adapter_get_dep_state(device->adapter_idx) == FALSE) {
444                 near_error("DEP link is not established");
445                 return -ENOLINK;
446         }
447
448         for (list = driver_list; list; list = list->next) {
449                 struct near_device_driver *driver = list->data;
450
451                 return driver->push(device->adapter_idx, device->target_idx,
452                                         ndef, service_name, cb);
453         }
454
455         return 0;
456 }
457
458 static gint cmp_prio(gconstpointer a, gconstpointer b)
459 {
460         const struct near_tag_driver *driver1 = a;
461         const struct near_tag_driver *driver2 = b;
462
463         return driver2->priority - driver1->priority;
464 }
465
466 int near_device_driver_register(struct near_device_driver *driver)
467 {
468         DBG("");
469
470         if (driver->listen == NULL)
471                 return -EINVAL;
472
473         driver_list = g_slist_insert_sorted(driver_list, driver, cmp_prio);
474
475         __near_adapter_listen(driver);
476
477         return 0;
478 }
479
480 void near_device_driver_unregister(struct near_device_driver *driver)
481 {
482         DBG("");
483
484         driver_list = g_slist_remove(driver_list, driver);
485 }
486
487 int __near_device_init(void)
488 {
489         DBG("");
490
491         connection = near_dbus_get_connection();
492
493         device_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
494                                                 g_free, free_device);
495
496         return 0;
497 }
498
499 void __near_device_cleanup(void)
500 {
501         DBG("");
502
503         g_hash_table_destroy(device_hash);
504         device_hash = NULL;
505 }