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