Base Code merged to SPIN 2.4
[platform/upstream/connman.git] / gsupplicant / dbus.c
1 /*
2  *
3  *  WPA supplicant library with GLib integration
4  *
5  *  Copyright (C) 2012-2013  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 <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <dbus/dbus.h>
30 #include <glib.h>
31
32 #include "dbus.h"
33
34 #define TIMEOUT 30000
35
36 static DBusConnection *connection;
37
38 static GSList *method_calls;
39
40 struct method_call_data {
41         gpointer caller;
42         DBusPendingCall *pending_call;
43         supplicant_dbus_result_function function;
44         void *user_data;
45 };
46
47 static void method_call_free(void *pointer)
48 {
49         struct method_call_data *method_call = pointer;
50         method_calls = g_slist_remove(method_calls, method_call);
51         g_free(method_call);
52 }
53
54 static int find_method_call_by_caller(gconstpointer a, gconstpointer b)
55 {
56         const struct method_call_data *method_call = a;
57         gconstpointer caller = b;
58
59         return method_call->caller != caller;
60 }
61
62 static GSList *property_calls;
63
64 struct property_call_data {
65         gpointer caller;
66         DBusPendingCall *pending_call;
67         supplicant_dbus_property_function function;
68         void *user_data;
69 };
70
71 static void property_call_free(void *pointer)
72 {
73         struct property_call_data *property_call = pointer;
74         property_calls = g_slist_remove(property_calls, property_call);
75         g_free(property_call);
76 }
77
78 static int find_property_call_by_caller(gconstpointer a, gconstpointer b)
79 {
80         const struct property_call_data *property_call = a;
81         gconstpointer caller = b;
82
83         return property_call->caller != caller;
84 }
85
86 void supplicant_dbus_setup(DBusConnection *conn)
87 {
88         connection = conn;
89         method_calls = NULL;
90         property_calls = NULL;
91 }
92
93 void supplicant_dbus_array_foreach(DBusMessageIter *iter,
94                                 supplicant_dbus_array_function function,
95                                                         void *user_data)
96 {
97         DBusMessageIter entry;
98
99         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
100                 return;
101
102         dbus_message_iter_recurse(iter, &entry);
103
104         while (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_INVALID) {
105                 if (function)
106                         function(&entry, user_data);
107
108                 dbus_message_iter_next(&entry);
109         }
110 }
111
112 void supplicant_dbus_property_foreach(DBusMessageIter *iter,
113                                 supplicant_dbus_property_function function,
114                                                         void *user_data)
115 {
116         DBusMessageIter dict;
117
118         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
119                 return;
120
121         dbus_message_iter_recurse(iter, &dict);
122
123         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
124                 DBusMessageIter entry, value;
125                 const char *key;
126
127                 dbus_message_iter_recurse(&dict, &entry);
128
129                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
130                         return;
131
132                 dbus_message_iter_get_basic(&entry, &key);
133                 dbus_message_iter_next(&entry);
134
135                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
136                         return;
137
138                 dbus_message_iter_recurse(&entry, &value);
139
140                 if (key) {
141                         if (strcmp(key, "Properties") == 0)
142                                 supplicant_dbus_property_foreach(&value,
143                                                         function, user_data);
144                         else if (function)
145                                 function(key, &value, user_data);
146                 }
147
148                 dbus_message_iter_next(&dict);
149         }
150 }
151
152 void supplicant_dbus_property_call_cancel_all(gpointer caller)
153 {
154         while (property_calls) {
155                 struct property_call_data *property_call;
156                 GSList *elem = g_slist_find_custom(property_calls, caller,
157                                                 find_property_call_by_caller);
158                 if (!elem)
159                         break;
160
161                 property_call = elem->data;
162                 property_calls = g_slist_delete_link(property_calls, elem);
163
164                 dbus_pending_call_cancel(property_call->pending_call);
165
166                 dbus_pending_call_unref(property_call->pending_call);
167         }
168 }
169
170 static void property_get_all_reply(DBusPendingCall *call, void *user_data)
171 {
172         struct property_call_data *property_call = user_data;
173         DBusMessage *reply;
174         DBusMessageIter iter;
175
176         reply = dbus_pending_call_steal_reply(call);
177
178         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
179                 goto done;
180
181         if (!dbus_message_iter_init(reply, &iter))
182                 goto done;
183
184         supplicant_dbus_property_foreach(&iter, property_call->function,
185                                                 property_call->user_data);
186
187         if (property_call->function)
188                 property_call->function(NULL, NULL, property_call->user_data);
189
190 done:
191         dbus_message_unref(reply);
192
193         dbus_pending_call_unref(call);
194 }
195
196 int supplicant_dbus_property_get_all(const char *path, const char *interface,
197                                 supplicant_dbus_property_function function,
198                                 void *user_data, gpointer caller)
199 {
200         struct property_call_data *property_call = NULL;
201         DBusMessage *message;
202         DBusPendingCall *call;
203
204         if (!connection)
205                 return -EINVAL;
206
207         if (!path || !interface)
208                 return -EINVAL;
209
210         property_call = g_try_new0(struct property_call_data, 1);
211         if (!property_call)
212                 return -ENOMEM;
213
214         message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
215                                         DBUS_INTERFACE_PROPERTIES, "GetAll");
216         if (!message) {
217                 g_free(property_call);
218                 return -ENOMEM;
219         }
220
221         dbus_message_set_auto_start(message, FALSE);
222
223         dbus_message_append_args(message, DBUS_TYPE_STRING, &interface, NULL);
224
225         if (!dbus_connection_send_with_reply(connection, message,
226                                                 &call, TIMEOUT)) {
227                 dbus_message_unref(message);
228                 g_free(property_call);
229                 return -EIO;
230         }
231
232         if (!call) {
233                 dbus_message_unref(message);
234                 g_free(property_call);
235                 return -EIO;
236         }
237
238         property_call->caller = caller;
239         property_call->pending_call = call;
240         property_call->function = function;
241         property_call->user_data = user_data;
242
243         property_calls = g_slist_prepend(property_calls, property_call);
244
245         dbus_pending_call_set_notify(call, property_get_all_reply,
246                                 property_call, property_call_free);
247
248         dbus_message_unref(message);
249
250         return 0;
251 }
252
253 static void property_get_reply(DBusPendingCall *call, void *user_data)
254 {
255         struct property_call_data *property_call = user_data;
256         DBusMessage *reply;
257         DBusMessageIter iter;
258
259         reply = dbus_pending_call_steal_reply(call);
260
261         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
262                 goto done;
263
264         if (!dbus_message_iter_init(reply, &iter))
265                 goto done;
266
267         if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) {
268                 DBusMessageIter variant;
269
270                 dbus_message_iter_recurse(&iter, &variant);
271
272                 if (property_call->function)
273                         property_call->function(NULL, &variant,
274                                                 property_call->user_data);
275         }
276 done:
277         dbus_message_unref(reply);
278
279         dbus_pending_call_unref(call);
280 }
281
282 int supplicant_dbus_property_get(const char *path, const char *interface,
283                                 const char *method,
284                                 supplicant_dbus_property_function function,
285                                 void *user_data, gpointer caller)
286 {
287         struct property_call_data *property_call = NULL;
288         DBusMessage *message;
289         DBusPendingCall *call;
290
291         if (!connection)
292                 return -EINVAL;
293
294         if (!path || !interface || !method)
295                 return -EINVAL;
296
297         property_call = g_try_new0(struct property_call_data, 1);
298         if (!property_call)
299                 return -ENOMEM;
300
301         message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
302                                         DBUS_INTERFACE_PROPERTIES, "Get");
303
304         if (!message) {
305                 g_free(property_call);
306                 return -ENOMEM;
307         }
308
309         dbus_message_set_auto_start(message, FALSE);
310
311         dbus_message_append_args(message, DBUS_TYPE_STRING, &interface,
312                                         DBUS_TYPE_STRING, &method, NULL);
313
314         if (!dbus_connection_send_with_reply(connection, message,
315                                                 &call, TIMEOUT)) {
316                 dbus_message_unref(message);
317                 g_free(property_call);
318                 return -EIO;
319         }
320
321         if (!call) {
322                 dbus_message_unref(message);
323                 g_free(property_call);
324                 return -EIO;
325         }
326
327         property_call->caller = caller;
328         property_call->pending_call = call;
329         property_call->function = function;
330         property_call->user_data = user_data;
331
332         property_calls = g_slist_prepend(property_calls, property_call);
333
334         dbus_pending_call_set_notify(call, property_get_reply,
335                                         property_call, property_call_free);
336
337         dbus_message_unref(message);
338
339         return 0;
340 }
341
342 static void property_set_reply(DBusPendingCall *call, void *user_data)
343 {
344         struct property_call_data *property_call = user_data;
345         DBusMessage *reply;
346         DBusMessageIter iter;
347         const char *error;
348
349         reply = dbus_pending_call_steal_reply(call);
350
351         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
352                 error = dbus_message_get_error_name(reply);
353         else
354                 error = NULL;
355
356         dbus_message_iter_init(reply, &iter);
357
358         if (property_call->function)
359                 property_call->function(error, &iter, property_call->user_data);
360
361         dbus_message_unref(reply);
362
363         dbus_pending_call_unref(call);
364 }
365
366 int supplicant_dbus_property_set(const char *path, const char *interface,
367                                 const char *key, const char *signature,
368                                 supplicant_dbus_setup_function setup,
369                                 supplicant_dbus_result_function function,
370                                 void *user_data, gpointer caller)
371 {
372         struct property_call_data *property_call = NULL;
373         DBusMessage *message;
374         DBusMessageIter iter, value;
375         DBusPendingCall *call;
376
377         if (!connection)
378                 return -EINVAL;
379
380         if (!path || !interface)
381                 return -EINVAL;
382
383         if (!key || !signature || !setup)
384                 return -EINVAL;
385
386         property_call = g_try_new0(struct property_call_data, 1);
387         if (!property_call)
388                 return -ENOMEM;
389
390         message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
391                                         DBUS_INTERFACE_PROPERTIES, "Set");
392         if (!message) {
393                 g_free(property_call);
394                 return -ENOMEM;
395         }
396
397         dbus_message_set_auto_start(message, FALSE);
398
399         dbus_message_iter_init_append(message, &iter);
400         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface);
401         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
402
403         dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
404                                                         signature, &value);
405         setup(&value, user_data);
406         dbus_message_iter_close_container(&iter, &value);
407
408         if (!dbus_connection_send_with_reply(connection, message,
409                                                 &call, TIMEOUT)) {
410                 dbus_message_unref(message);
411                 g_free(property_call);
412                 return -EIO;
413         }
414
415         if (!call) {
416                 dbus_message_unref(message);
417                 g_free(property_call);
418                 return -EIO;
419         }
420
421         property_call->caller = caller;
422         property_call->pending_call = call;
423         property_call->function = function;
424         property_call->user_data = user_data;
425
426         property_calls = g_slist_prepend(property_calls, property_call);
427
428         dbus_pending_call_set_notify(call, property_set_reply,
429                                         property_call, property_call_free);
430
431         dbus_message_unref(message);
432
433         return 0;
434 }
435
436 void supplicant_dbus_method_call_cancel_all(gpointer caller)
437 {
438         while (method_calls) {
439                 struct method_call_data *method_call;
440                 GSList *elem = g_slist_find_custom(method_calls, caller,
441                                                 find_method_call_by_caller);
442                 if (!elem)
443                         break;
444
445                 method_call = elem->data;
446                 method_calls = g_slist_delete_link(method_calls, elem);
447
448                 dbus_pending_call_cancel(method_call->pending_call);
449
450                 if (method_call->function)
451                         method_call->function("net.connman.Error.OperationAborted",
452                                         NULL, method_call->user_data);
453
454                 dbus_pending_call_unref(method_call->pending_call);
455         }
456 }
457
458 static void method_call_reply(DBusPendingCall *call, void *user_data)
459 {
460         struct method_call_data *method_call = user_data;
461         DBusMessage *reply;
462         DBusMessageIter iter;
463         const char *error;
464
465         reply = dbus_pending_call_steal_reply(call);
466
467         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
468                 error = dbus_message_get_error_name(reply);
469         else
470                 error = NULL;
471
472         dbus_message_iter_init(reply, &iter);
473
474         if (method_call && method_call->function)
475                 method_call->function(error, &iter, method_call->user_data);
476
477         dbus_message_unref(reply);
478
479         dbus_pending_call_unref(call);
480 }
481
482 int supplicant_dbus_method_call(const char *path,
483                                 const char *interface, const char *method,
484                                 supplicant_dbus_setup_function setup,
485                                 supplicant_dbus_result_function function,
486                                 void *user_data,
487                                 gpointer caller)
488 {
489         struct method_call_data *method_call = NULL;
490         DBusMessage *message;
491         DBusMessageIter iter;
492         DBusPendingCall *call;
493
494         if (!connection)
495                 return -EINVAL;
496
497         if (!path || !interface || !method)
498                 return -EINVAL;
499
500         method_call = g_try_new0(struct method_call_data, 1);
501         if (!method_call)
502                 return -ENOMEM;
503
504         message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
505                                                         interface, method);
506         if (!message) {
507                 g_free(method_call);
508                 return -ENOMEM;
509         }
510
511         dbus_message_set_auto_start(message, FALSE);
512
513         dbus_message_iter_init_append(message, &iter);
514         if (setup)
515                 setup(&iter, user_data);
516
517         if (!dbus_connection_send_with_reply(connection, message,
518                                                 &call, TIMEOUT)) {
519                 dbus_message_unref(message);
520                 g_free(method_call);
521                 return -EIO;
522         }
523
524         if (!call) {
525                 dbus_message_unref(message);
526                 g_free(method_call);
527                 return -EIO;
528         }
529
530         method_call->caller = caller;
531         method_call->pending_call = call;
532         method_call->function = function;
533         method_call->user_data = user_data;
534         method_calls = g_slist_prepend(method_calls, method_call);
535
536         dbus_pending_call_set_notify(call, method_call_reply, method_call,
537                                 method_call_free);
538
539         dbus_message_unref(message);
540
541         return 0;
542 }
543
544 void supplicant_dbus_property_append_basic(DBusMessageIter *iter,
545                                         const char *key, int type, void *val)
546 {
547         DBusMessageIter value;
548         const char *signature;
549
550         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
551
552         switch (type) {
553         case DBUS_TYPE_BOOLEAN:
554                 signature = DBUS_TYPE_BOOLEAN_AS_STRING;
555                 break;
556         case DBUS_TYPE_STRING:
557                 signature = DBUS_TYPE_STRING_AS_STRING;
558                 break;
559         case DBUS_TYPE_BYTE:
560                 signature = DBUS_TYPE_BYTE_AS_STRING;
561                 break;
562         case DBUS_TYPE_UINT16:
563                 signature = DBUS_TYPE_UINT16_AS_STRING;
564                 break;
565         case DBUS_TYPE_INT16:
566                 signature = DBUS_TYPE_INT16_AS_STRING;
567                 break;
568         case DBUS_TYPE_UINT32:
569                 signature = DBUS_TYPE_UINT32_AS_STRING;
570                 break;
571         case DBUS_TYPE_INT32:
572                 signature = DBUS_TYPE_INT32_AS_STRING;
573                 break;
574         case DBUS_TYPE_OBJECT_PATH:
575                 signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
576                 break;
577         default:
578                 signature = DBUS_TYPE_VARIANT_AS_STRING;
579                 break;
580         }
581
582         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
583                                                         signature, &value);
584         dbus_message_iter_append_basic(&value, type, val);
585         dbus_message_iter_close_container(iter, &value);
586 }
587
588 void supplicant_dbus_property_append_fixed_array(DBusMessageIter *iter,
589                                 const char *key, int type, void *val, int len)
590 {
591         DBusMessageIter value, array;
592         const char *variant_sig, *array_sig;
593
594         switch (type) {
595         case DBUS_TYPE_BYTE:
596                 variant_sig = DBUS_TYPE_ARRAY_AS_STRING
597                                         DBUS_TYPE_BYTE_AS_STRING;
598                 array_sig = DBUS_TYPE_BYTE_AS_STRING;
599                 break;
600         default:
601                 return;
602         }
603
604         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
605
606         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
607                                                         variant_sig, &value);
608
609         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
610                                                         array_sig, &array);
611         dbus_message_iter_append_fixed_array(&array, type, val, len);
612         dbus_message_iter_close_container(&value, &array);
613
614         dbus_message_iter_close_container(iter, &value);
615 }
616
617 void supplicant_dbus_property_append_array(DBusMessageIter *iter,
618                                 const char *key, int type,
619                                 supplicant_dbus_array_function function,
620                                 void *user_data)
621 {
622         DBusMessageIter value, array;
623         const char *variant_sig, *array_sig;
624
625         switch (type) {
626         case DBUS_TYPE_STRING:
627         case DBUS_TYPE_BYTE:
628                 variant_sig = DBUS_TYPE_ARRAY_AS_STRING
629                                 DBUS_TYPE_ARRAY_AS_STRING
630                                 DBUS_TYPE_BYTE_AS_STRING;
631                 array_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
632                 break;
633         default:
634                 return;
635         }
636
637         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
638
639         dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
640                                                         variant_sig, &value);
641
642         dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
643                                                         array_sig, &array);
644         if (function)
645                 function(&array, user_data);
646
647         dbus_message_iter_close_container(&value, &array);
648
649         dbus_message_iter_close_container(iter, &value);
650 }