]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / wpa / wpa_supplicant / dbus / dbus_new_helpers.c
1 /*
2  * WPA Supplicant / dbus-based control interface
3  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
4  * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
5  *
6  * This software may be distributed under the terms of the BSD license.
7  * See README for more details.
8  */
9
10 #include "utils/includes.h"
11
12 #include "utils/common.h"
13 #include "utils/eloop.h"
14 #include "dbus_common.h"
15 #include "dbus_common_i.h"
16 #include "dbus_new.h"
17 #include "dbus_new_helpers.h"
18 #include "dbus_dict_helpers.h"
19
20
21 static dbus_bool_t fill_dict_with_properties(
22         DBusMessageIter *dict_iter,
23         const struct wpa_dbus_property_desc *props,
24         const char *interface, void *user_data, DBusError *error)
25 {
26         DBusMessageIter entry_iter;
27         const struct wpa_dbus_property_desc *dsc;
28
29         for (dsc = props; dsc && dsc->dbus_property; dsc++) {
30                 /* Only return properties for the requested D-Bus interface */
31                 if (os_strncmp(dsc->dbus_interface, interface,
32                                WPAS_DBUS_INTERFACE_MAX) != 0)
33                         continue;
34
35                 /* Skip write-only properties */
36                 if (dsc->getter == NULL)
37                         continue;
38
39                 if (!dbus_message_iter_open_container(dict_iter,
40                                                       DBUS_TYPE_DICT_ENTRY,
41                                                       NULL, &entry_iter)) {
42                         dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
43                                              "no memory");
44                         return FALSE;
45                 }
46                 if (!dbus_message_iter_append_basic(&entry_iter,
47                                                     DBUS_TYPE_STRING,
48                                                     &dsc->dbus_property)) {
49                         dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
50                                              "no memory");
51                         return FALSE;
52                 }
53
54                 /* An error getting a property fails the request entirely */
55                 if (!dsc->getter(&entry_iter, error, user_data))
56                         return FALSE;
57
58                 dbus_message_iter_close_container(dict_iter, &entry_iter);
59         }
60
61         return TRUE;
62 }
63
64
65 /**
66  * get_all_properties - Responds for GetAll properties calls on object
67  * @message: Message with GetAll call
68  * @interface: interface name which properties will be returned
69  * @property_dsc: list of object's properties
70  * Returns: Message with dict of variants as argument with properties values
71  *
72  * Iterates over all properties registered with object and execute getters
73  * of those, which are readable and which interface matches interface
74  * specified as argument. Returned message contains one dict argument
75  * with properties names as keys and theirs values as values.
76  */
77 static DBusMessage * get_all_properties(DBusMessage *message, char *interface,
78                                         struct wpa_dbus_object_desc *obj_dsc)
79 {
80         DBusMessage *reply;
81         DBusMessageIter iter, dict_iter;
82         DBusError error;
83
84         reply = dbus_message_new_method_return(message);
85         if (reply == NULL) {
86                 wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply",
87                            __func__);
88                 return NULL;
89         }
90
91         dbus_message_iter_init_append(reply, &iter);
92         if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
93                 wpa_printf(MSG_ERROR, "%s: out of memory creating reply",
94                            __func__);
95                 dbus_message_unref(reply);
96                 reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
97                                                "out of memory");
98                 return reply;
99         }
100
101         dbus_error_init(&error);
102         if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
103                                        interface, obj_dsc->user_data, &error))
104         {
105                 dbus_message_unref(reply);
106                 reply = wpas_dbus_reply_new_from_error(message, &error,
107                                                        DBUS_ERROR_INVALID_ARGS,
108                                                        "No readable properties"
109                                                        " in this interface");
110                 dbus_error_free(&error);
111                 return reply;
112         }
113
114         wpa_dbus_dict_close_write(&iter, &dict_iter);
115         return reply;
116 }
117
118
119 static int is_signature_correct(DBusMessage *message,
120                                 const struct wpa_dbus_method_desc *method_dsc)
121 {
122         /* According to DBus documentation max length of signature is 255 */
123 #define MAX_SIG_LEN 256
124         char registered_sig[MAX_SIG_LEN], *pos;
125         const char *sig = dbus_message_get_signature(message);
126         int ret;
127         const struct wpa_dbus_argument *arg;
128
129         pos = registered_sig;
130         *pos = '\0';
131
132         for (arg = method_dsc->args; arg && arg->name; arg++) {
133                 if (arg->dir == ARG_IN) {
134                         size_t blen = registered_sig + MAX_SIG_LEN - pos;
135                         ret = os_snprintf(pos, blen, "%s", arg->type);
136                         if (ret < 0 || (size_t) ret >= blen)
137                                 return 0;
138                         pos += ret;
139                 }
140         }
141
142         return !os_strncmp(registered_sig, sig, MAX_SIG_LEN);
143 }
144
145
146 static DBusMessage * properties_get_all(DBusMessage *message, char *interface,
147                                         struct wpa_dbus_object_desc *obj_dsc)
148 {
149         if (os_strcmp(dbus_message_get_signature(message), "s") != 0)
150                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
151                                               NULL);
152
153         return get_all_properties(message, interface, obj_dsc);
154 }
155
156
157 static DBusMessage * properties_get(DBusMessage *message,
158                                     const struct wpa_dbus_property_desc *dsc,
159                                     void *user_data)
160 {
161         DBusMessage *reply;
162         DBusMessageIter iter;
163         DBusError error;
164
165         if (os_strcmp(dbus_message_get_signature(message), "ss")) {
166                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
167                                               NULL);
168         }
169
170         if (dsc->getter == NULL) {
171                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
172                                               "Property is write-only");
173         }
174
175         reply = dbus_message_new_method_return(message);
176         dbus_message_iter_init_append(reply, &iter);
177
178         dbus_error_init(&error);
179         if (dsc->getter(&iter, &error, user_data) == FALSE) {
180                 dbus_message_unref(reply);
181                 reply = wpas_dbus_reply_new_from_error(
182                         message, &error, DBUS_ERROR_FAILED,
183                         "Failed to read property");
184                 dbus_error_free(&error);
185         }
186
187         return reply;
188 }
189
190
191 static DBusMessage * properties_set(DBusMessage *message,
192                                     const struct wpa_dbus_property_desc *dsc,
193                                     void *user_data)
194 {
195         DBusMessage *reply;
196         DBusMessageIter iter;
197         DBusError error;
198
199         if (os_strcmp(dbus_message_get_signature(message), "ssv")) {
200                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
201                                               NULL);
202         }
203
204         if (dsc->setter == NULL) {
205                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
206                                               "Property is read-only");
207         }
208
209         dbus_message_iter_init(message, &iter);
210         /* Skip the interface name and the property name */
211         dbus_message_iter_next(&iter);
212         dbus_message_iter_next(&iter);
213
214         /* Iter will now point to the property's new value */
215         dbus_error_init(&error);
216         if (dsc->setter(&iter, &error, user_data) == TRUE) {
217                 /* Success */
218                 reply = dbus_message_new_method_return(message);
219         } else {
220                 reply = wpas_dbus_reply_new_from_error(
221                         message, &error, DBUS_ERROR_FAILED,
222                         "Failed to set property");
223                 dbus_error_free(&error);
224         }
225
226         return reply;
227 }
228
229
230 static DBusMessage *
231 properties_get_or_set(DBusMessage *message, DBusMessageIter *iter,
232                       char *interface,
233                       struct wpa_dbus_object_desc *obj_dsc)
234 {
235         const struct wpa_dbus_property_desc *property_dsc;
236         char *property;
237         const char *method;
238
239         method = dbus_message_get_member(message);
240         property_dsc = obj_dsc->properties;
241
242         /* Second argument: property name (DBUS_TYPE_STRING) */
243         if (!dbus_message_iter_next(iter) ||
244             dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
245                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
246                                               NULL);
247         }
248         dbus_message_iter_get_basic(iter, &property);
249
250         while (property_dsc && property_dsc->dbus_property) {
251                 /* compare property names and
252                  * interfaces */
253                 if (!os_strncmp(property_dsc->dbus_property, property,
254                                 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
255                     !os_strncmp(property_dsc->dbus_interface, interface,
256                                 WPAS_DBUS_INTERFACE_MAX))
257                         break;
258
259                 property_dsc++;
260         }
261         if (property_dsc == NULL || property_dsc->dbus_property == NULL) {
262                 wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s",
263                            interface, property,
264                            dbus_message_get_path(message));
265                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
266                                               "No such property");
267         }
268
269         if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
270                        WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0)
271                 return properties_get(message, property_dsc,
272                                       obj_dsc->user_data);
273
274         return properties_set(message, property_dsc, obj_dsc->user_data);
275 }
276
277
278 static DBusMessage * properties_handler(DBusMessage *message,
279                                         struct wpa_dbus_object_desc *obj_dsc)
280 {
281         DBusMessageIter iter;
282         char *interface;
283         const char *method;
284
285         method = dbus_message_get_member(message);
286         dbus_message_iter_init(message, &iter);
287
288         if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
289                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
290             !os_strncmp(WPA_DBUS_PROPERTIES_SET, method,
291                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
292             !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
293                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
294                 /* First argument: interface name (DBUS_TYPE_STRING) */
295                 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
296                 {
297                         return dbus_message_new_error(message,
298                                                       DBUS_ERROR_INVALID_ARGS,
299                                                       NULL);
300                 }
301
302                 dbus_message_iter_get_basic(&iter, &interface);
303
304                 if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
305                                 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
306                         /* GetAll */
307                         return properties_get_all(message, interface, obj_dsc);
308                 }
309                 /* Get or Set */
310                 return properties_get_or_set(message, &iter, interface,
311                                              obj_dsc);
312         }
313         return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD,
314                                       NULL);
315 }
316
317
318 static DBusMessage * msg_method_handler(DBusMessage *message,
319                                         struct wpa_dbus_object_desc *obj_dsc)
320 {
321         const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
322         const char *method;
323         const char *msg_interface;
324
325         method = dbus_message_get_member(message);
326         msg_interface = dbus_message_get_interface(message);
327
328         /* try match call to any registered method */
329         while (method_dsc && method_dsc->dbus_method) {
330                 /* compare method names and interfaces */
331                 if (!os_strncmp(method_dsc->dbus_method, method,
332                                 WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
333                     !os_strncmp(method_dsc->dbus_interface, msg_interface,
334                                 WPAS_DBUS_INTERFACE_MAX))
335                         break;
336
337                 method_dsc++;
338         }
339         if (method_dsc == NULL || method_dsc->dbus_method == NULL) {
340                 wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s",
341                            msg_interface, method,
342                            dbus_message_get_path(message));
343                 return dbus_message_new_error(message,
344                                               DBUS_ERROR_UNKNOWN_METHOD, NULL);
345         }
346
347         if (!is_signature_correct(message, method_dsc)) {
348                 return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
349                                               NULL);
350         }
351
352         return method_dsc->method_handler(message,
353                                           obj_dsc->user_data);
354 }
355
356
357 /**
358  * message_handler - Handles incoming DBus messages
359  * @connection: DBus connection on which message was received
360  * @message: Received message
361  * @user_data: pointer to description of object to which message was sent
362  * Returns: Returns information whether message was handled or not
363  *
364  * Reads message interface and method name, then checks if they matches one
365  * of the special cases i.e. introspection call or properties get/getall/set
366  * methods and handles it. Else it iterates over registered methods list
367  * and tries to match method's name and interface to those read from message
368  * If appropriate method was found its handler function is called and
369  * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message
370  * will be sent.
371  */
372 static DBusHandlerResult message_handler(DBusConnection *connection,
373                                          DBusMessage *message, void *user_data)
374 {
375         struct wpa_dbus_object_desc *obj_dsc = user_data;
376         const char *method;
377         const char *path;
378         const char *msg_interface;
379         DBusMessage *reply;
380
381         /* get method, interface and path the message is addressed to */
382         method = dbus_message_get_member(message);
383         path = dbus_message_get_path(message);
384         msg_interface = dbus_message_get_interface(message);
385         if (!method || !path || !msg_interface)
386                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
387
388         wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)",
389                    msg_interface, method, path);
390
391         /* if message is introspection method call */
392         if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
393                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
394             !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
395                         WPAS_DBUS_INTERFACE_MAX)) {
396 #ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
397                 reply = wpa_dbus_introspect(message, obj_dsc);
398 #else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
399                 reply = dbus_message_new_error(
400                         message, DBUS_ERROR_UNKNOWN_METHOD,
401                         "wpa_supplicant was compiled without "
402                         "introspection support.");
403 #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
404         } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
405                              WPAS_DBUS_INTERFACE_MAX)) {
406                 /* if message is properties method call */
407                 reply = properties_handler(message, obj_dsc);
408         } else {
409                 reply = msg_method_handler(message, obj_dsc);
410         }
411
412         /* If handler succeed returning NULL, reply empty message */
413         if (!reply)
414                 reply = dbus_message_new_method_return(message);
415         if (reply) {
416                 if (!dbus_message_get_no_reply(message))
417                         dbus_connection_send(connection, reply, NULL);
418                 dbus_message_unref(reply);
419         }
420
421         wpa_dbus_flush_all_changed_properties(connection);
422
423         return DBUS_HANDLER_RESULT_HANDLED;
424 }
425
426
427 /**
428  * free_dbus_object_desc - Frees object description data structure
429  * @connection: DBus connection
430  * @obj_dsc: Object description to free
431  *
432  * Frees each of properties, methods and signals description lists and
433  * the object description structure itself.
434  */
435 void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc)
436 {
437         if (!obj_dsc)
438                 return;
439
440         /* free handler's argument */
441         if (obj_dsc->user_data_free_func)
442                 obj_dsc->user_data_free_func(obj_dsc->user_data);
443
444         os_free(obj_dsc->path);
445         os_free(obj_dsc->prop_changed_flags);
446         os_free(obj_dsc);
447 }
448
449
450 static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc)
451 {
452         free_dbus_object_desc(obj_dsc);
453 }
454
455 /**
456  * wpa_dbus_ctrl_iface_init - Initialize dbus control interface
457  * @application_data: Pointer to application specific data structure
458  * @dbus_path: DBus path to interface object
459  * @dbus_service: DBus service name to register with
460  * @messageHandler: a pointer to function which will handle dbus messages
461  * coming on interface
462  * Returns: 0 on success, -1 on failure
463  *
464  * Initialize the dbus control interface and start receiving commands from
465  * external programs over the bus.
466  */
467 int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
468                              char *dbus_path, char *dbus_service,
469                              struct wpa_dbus_object_desc *obj_desc)
470 {
471         DBusError error;
472         int ret = -1;
473         DBusObjectPathVTable wpa_vtable = {
474                 &free_dbus_object_desc_cb, &message_handler,
475                 NULL, NULL, NULL, NULL
476         };
477
478         obj_desc->connection = iface->con;
479         obj_desc->path = os_strdup(dbus_path);
480
481         /* Register the message handler for the global dbus interface */
482         if (!dbus_connection_register_object_path(iface->con,
483                                                   dbus_path, &wpa_vtable,
484                                                   obj_desc)) {
485                 wpa_printf(MSG_ERROR, "dbus: Could not set up message "
486                            "handler");
487                 return -1;
488         }
489
490         /* Register our service with the message bus */
491         dbus_error_init(&error);
492         switch (dbus_bus_request_name(iface->con, dbus_service,
493                                       0, &error)) {
494         case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
495                 ret = 0;
496                 break;
497         case DBUS_REQUEST_NAME_REPLY_EXISTS:
498         case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
499         case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
500                 wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
501                            "already registered");
502                 break;
503         default:
504                 wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
505                            "%s %s", error.name, error.message);
506                 break;
507         }
508         dbus_error_free(&error);
509
510         if (ret != 0)
511                 return -1;
512
513         wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service);
514
515         return 0;
516 }
517
518
519 /**
520  * wpa_dbus_register_object_per_iface - Register a new object with dbus
521  * @ctrl_iface: pointer to dbus private data
522  * @path: DBus path to object
523  * @ifname: interface name
524  * @obj_desc: description of object's methods, signals and properties
525  * Returns: 0 on success, -1 on error
526  *
527  * Registers a new interface with dbus and assigns it a dbus object path.
528  */
529 int wpa_dbus_register_object_per_iface(
530         struct wpas_dbus_priv *ctrl_iface,
531         const char *path, const char *ifname,
532         struct wpa_dbus_object_desc *obj_desc)
533 {
534         DBusConnection *con;
535         DBusError error;
536
537         DBusObjectPathVTable vtable = {
538                 &free_dbus_object_desc_cb, &message_handler,
539                 NULL, NULL, NULL, NULL
540         };
541
542         /* Do nothing if the control interface is not turned on */
543         if (ctrl_iface == NULL)
544                 return 0;
545
546         con = ctrl_iface->con;
547         obj_desc->connection = con;
548         obj_desc->path = os_strdup(path);
549
550         dbus_error_init(&error);
551         /* Register the message handler for the interface functions */
552         if (!dbus_connection_try_register_object_path(con, path, &vtable,
553                                                       obj_desc, &error)) {
554                 if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) {
555                         wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
556                 } else {
557                         wpa_printf(MSG_ERROR, "dbus: Could not set up message "
558                                    "handler for interface %s object %s",
559                                    ifname, path);
560                         wpa_printf(MSG_ERROR, "dbus error: %s", error.name);
561                         wpa_printf(MSG_ERROR, "dbus: %s", error.message);
562                 }
563                 dbus_error_free(&error);
564                 return -1;
565         }
566
567         dbus_error_free(&error);
568         return 0;
569 }
570
571
572 static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx);
573
574
575 /**
576  * wpa_dbus_unregister_object_per_iface - Unregisters DBus object
577  * @ctrl_iface: Pointer to dbus private data
578  * @path: DBus path to object which will be unregistered
579  * Returns: Zero on success and -1 on failure
580  *
581  * Unregisters DBus object given by its path
582  */
583 int wpa_dbus_unregister_object_per_iface(
584         struct wpas_dbus_priv *ctrl_iface, const char *path)
585 {
586         DBusConnection *con = ctrl_iface->con;
587         struct wpa_dbus_object_desc *obj_desc = NULL;
588
589         dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
590         if (!obj_desc) {
591                 wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's "
592                            "private data: %s", __func__, path);
593         } else {
594                 eloop_cancel_timeout(flush_object_timeout_handler, con,
595                                      obj_desc);
596         }
597
598         if (!dbus_connection_unregister_object_path(con, path))
599                 return -1;
600
601         return 0;
602 }
603
604
605 static dbus_bool_t put_changed_properties(
606         const struct wpa_dbus_object_desc *obj_dsc, const char *interface,
607         DBusMessageIter *dict_iter, int clear_changed)
608 {
609         DBusMessageIter entry_iter;
610         const struct wpa_dbus_property_desc *dsc;
611         int i;
612         DBusError error;
613
614         for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
615              dsc++, i++) {
616                 if (obj_dsc->prop_changed_flags == NULL ||
617                     !obj_dsc->prop_changed_flags[i])
618                         continue;
619                 if (os_strcmp(dsc->dbus_interface, interface) != 0)
620                         continue;
621                 if (clear_changed)
622                         obj_dsc->prop_changed_flags[i] = 0;
623
624                 if (!dbus_message_iter_open_container(dict_iter,
625                                                       DBUS_TYPE_DICT_ENTRY,
626                                                       NULL, &entry_iter))
627                         return FALSE;
628
629                 if (!dbus_message_iter_append_basic(&entry_iter,
630                                                     DBUS_TYPE_STRING,
631                                                     &dsc->dbus_property))
632                         return FALSE;
633
634                 dbus_error_init(&error);
635                 if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
636                         if (dbus_error_is_set (&error)) {
637                                 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
638                                            "new value of property %s: (%s) %s",
639                                            __func__, dsc->dbus_property,
640                                            error.name, error.message);
641                         } else {
642                                 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
643                                            "new value of property %s",
644                                            __func__, dsc->dbus_property);
645                         }
646                         dbus_error_free(&error);
647                         return FALSE;
648                 }
649
650                 if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
651                         return FALSE;
652         }
653
654         return TRUE;
655 }
656
657
658 static void do_send_prop_changed_signal(
659         DBusConnection *con, const char *path, const char *interface,
660         const struct wpa_dbus_object_desc *obj_dsc)
661 {
662         DBusMessage *msg;
663         DBusMessageIter signal_iter, dict_iter;
664
665         msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES,
666                                       "PropertiesChanged");
667         if (msg == NULL)
668                 return;
669
670         dbus_message_iter_init_append(msg, &signal_iter);
671
672         if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
673                                             &interface))
674                 goto err;
675
676         /* Changed properties dict */
677         if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
678                                               "{sv}", &dict_iter))
679                 goto err;
680
681         if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0))
682                 goto err;
683
684         if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
685                 goto err;
686
687         /* Invalidated properties array (empty) */
688         if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
689                                               "s", &dict_iter))
690                 goto err;
691
692         if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
693                 goto err;
694
695         dbus_connection_send(con, msg, NULL);
696
697 out:
698         dbus_message_unref(msg);
699         return;
700
701 err:
702         wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
703                    __func__);
704         goto out;
705 }
706
707
708 static void do_send_deprecated_prop_changed_signal(
709         DBusConnection *con, const char *path, const char *interface,
710         const struct wpa_dbus_object_desc *obj_dsc)
711 {
712         DBusMessage *msg;
713         DBusMessageIter signal_iter, dict_iter;
714
715         msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
716         if (msg == NULL)
717                 return;
718
719         dbus_message_iter_init_append(msg, &signal_iter);
720
721         if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
722                                               "{sv}", &dict_iter))
723                 goto err;
724
725         if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1))
726                 goto err;
727
728         if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
729                 goto err;
730
731         dbus_connection_send(con, msg, NULL);
732
733 out:
734         dbus_message_unref(msg);
735         return;
736
737 err:
738         wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
739                    __func__);
740         goto out;
741 }
742
743
744 static void send_prop_changed_signal(
745         DBusConnection *con, const char *path, const char *interface,
746         const struct wpa_dbus_object_desc *obj_dsc)
747 {
748         /*
749          * First, send property change notification on the standardized
750          * org.freedesktop.DBus.Properties interface. This call will not
751          * clear the property change bits, so that they are preserved for
752          * the call that follows.
753          */
754         do_send_prop_changed_signal(con, path, interface, obj_dsc);
755
756         /*
757          * Now send PropertiesChanged on our own interface for backwards
758          * compatibility. This is deprecated and will be removed in a future
759          * release.
760          */
761         do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc);
762
763         /* Property change bits have now been cleared. */
764 }
765
766
767 static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
768 {
769         DBusConnection *con = eloop_ctx;
770         struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
771
772         wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties "
773                    "of object %s", __func__, obj_desc->path);
774         wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
775 }
776
777
778 static void recursive_flush_changed_properties(DBusConnection *con,
779                                                const char *path)
780 {
781         char **objects = NULL;
782         char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
783         int i;
784
785         wpa_dbus_flush_object_changed_properties(con, path);
786
787         if (!dbus_connection_list_registered(con, path, &objects))
788                 goto out;
789
790         for (i = 0; objects[i]; i++) {
791                 os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
792                             "%s/%s", path, objects[i]);
793                 recursive_flush_changed_properties(con, subobj_path);
794         }
795
796 out:
797         dbus_free_string_array(objects);
798 }
799
800
801 /**
802  * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
803  * @con: DBus connection
804  *
805  * Traverses through all registered objects and sends PropertiesChanged for
806  * each properties.
807  */
808 void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
809 {
810         recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
811 }
812
813
814 /**
815  * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
816  * @con: DBus connection
817  * @path: path to a DBus object for which PropertiesChanged will be sent.
818  *
819  * Iterates over all properties registered with object and for each interface
820  * containing properties marked as changed, sends a PropertiesChanged signal
821  * containing names and new values of properties that have changed.
822  *
823  * You need to call this function after wpa_dbus_mark_property_changed()
824  * if you want to send PropertiesChanged signal immediately (i.e., without
825  * waiting timeout to expire). PropertiesChanged signal for an object is sent
826  * automatically short time after first marking property as changed. All
827  * PropertiesChanged signals are sent automatically after responding on DBus
828  * message, so if you marked a property changed as a result of DBus call
829  * (e.g., param setter), you usually do not need to call this function.
830  */
831 void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
832                                               const char *path)
833 {
834         struct wpa_dbus_object_desc *obj_desc = NULL;
835         const struct wpa_dbus_property_desc *dsc;
836         int i;
837
838         dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
839         if (!obj_desc)
840                 return;
841         eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
842
843         dsc = obj_desc->properties;
844         for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
845              dsc++, i++) {
846                 if (obj_desc->prop_changed_flags == NULL ||
847                     !obj_desc->prop_changed_flags[i])
848                         continue;
849                 send_prop_changed_signal(con, path, dsc->dbus_interface,
850                                          obj_desc);
851         }
852 }
853
854
855 #define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
856
857
858 /**
859  * wpa_dbus_mark_property_changed - Mark a property as changed and
860  * @iface: dbus priv struct
861  * @path: path to DBus object which property has changed
862  * @interface: interface containing changed property
863  * @property: property name which has changed
864  *
865  * Iterates over all properties registered with an object and marks the one
866  * given in parameters as changed. All parameters registered for an object
867  * within a single interface will be aggregated together and sent in one
868  * PropertiesChanged signal when function
869  * wpa_dbus_flush_object_changed_properties() is called.
870  */
871 void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
872                                     const char *path, const char *interface,
873                                     const char *property)
874 {
875         struct wpa_dbus_object_desc *obj_desc = NULL;
876         const struct wpa_dbus_property_desc *dsc;
877         int i = 0;
878
879         if (iface == NULL)
880                 return;
881
882         dbus_connection_get_object_path_data(iface->con, path,
883                                              (void **) &obj_desc);
884         if (!obj_desc) {
885                 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
886                            "could not obtain object's private data: %s", path);
887                 return;
888         }
889
890         for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
891                 if (os_strcmp(property, dsc->dbus_property) == 0 &&
892                     os_strcmp(interface, dsc->dbus_interface) == 0) {
893                         if (obj_desc->prop_changed_flags)
894                                 obj_desc->prop_changed_flags[i] = 1;
895                         break;
896                 }
897
898         if (!dsc || !dsc->dbus_property) {
899                 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
900                            "no property %s in object %s", property, path);
901                 return;
902         }
903
904         if (!eloop_is_timeout_registered(flush_object_timeout_handler,
905                                          iface->con, obj_desc->path)) {
906                 eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
907                                        flush_object_timeout_handler,
908                                        iface->con, obj_desc);
909         }
910 }
911
912
913 /**
914  * wpa_dbus_get_object_properties - Put object's properties into dictionary
915  * @iface: dbus priv struct
916  * @path: path to DBus object which properties will be obtained
917  * @interface: interface name which properties will be obtained
918  * @iter: DBus message iter at which to append property dictionary.
919  *
920  * Iterates over all properties registered with object and execute getters
921  * of those, which are readable and which interface matches interface
922  * specified as argument. Obtained properties values are stored in
923  * dict_iter dictionary.
924  */
925 dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
926                                            const char *path,
927                                            const char *interface,
928                                            DBusMessageIter *iter)
929 {
930         struct wpa_dbus_object_desc *obj_desc = NULL;
931         DBusMessageIter dict_iter;
932         DBusError error;
933
934         dbus_connection_get_object_path_data(iface->con, path,
935                                              (void **) &obj_desc);
936         if (!obj_desc) {
937                 wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's "
938                            "private data: %s", __func__, path);
939                 return FALSE;
940         }
941
942         if (!wpa_dbus_dict_open_write(iter, &dict_iter)) {
943                 wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict",
944                            __func__);
945                 return FALSE;
946         }
947
948         dbus_error_init(&error);
949         if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
950                                        interface, obj_desc->user_data,
951                                        &error)) {
952                 wpa_printf(MSG_ERROR, "dbus: %s: failed to get object"
953                            " properties: (%s) %s", __func__,
954                            dbus_error_is_set(&error) ? error.name : "none",
955                            dbus_error_is_set(&error) ? error.message : "none");
956                 dbus_error_free(&error);
957                 return FALSE;
958         }
959
960         return wpa_dbus_dict_close_write(iter, &dict_iter);
961 }
962
963 /**
964  * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
965  * @path: The dbus object path
966  * @p2p_persistent_group: indicates whether to parse the path as a P2P
967  *                        persistent group object
968  * @network: (out) the configured network this object path refers to, if any
969  * @bssid: (out) the scanned bssid this object path refers to, if any
970  * Returns: The object path of the network interface this path refers to
971  *
972  * For a given object path, decomposes the object path into object id, network,
973  * and BSSID parts, if those parts exist.
974  */
975 char *wpas_dbus_new_decompose_object_path(const char *path,
976                                            int p2p_persistent_group,
977                                            char **network,
978                                            char **bssid)
979 {
980         const unsigned int dev_path_prefix_len =
981                 os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
982         char *obj_path_only;
983         char *next_sep;
984
985         /* Be a bit paranoid about path */
986         if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
987                                 dev_path_prefix_len))
988                 return NULL;
989
990         /* Ensure there's something at the end of the path */
991         if ((path + dev_path_prefix_len)[0] == '\0')
992                 return NULL;
993
994         obj_path_only = os_strdup(path);
995         if (obj_path_only == NULL)
996                 return NULL;
997
998         next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
999         if (next_sep != NULL) {
1000                 const char *net_part = os_strstr(
1001                         next_sep, p2p_persistent_group ?
1002                         WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" :
1003                         WPAS_DBUS_NEW_NETWORKS_PART "/");
1004                 const char *bssid_part = os_strstr(
1005                         next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
1006
1007                 if (network && net_part) {
1008                         /* Deal with a request for a configured network */
1009                         const char *net_name = net_part +
1010                                 os_strlen(p2p_persistent_group ?
1011                                           WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART
1012                                           "/" :
1013                                           WPAS_DBUS_NEW_NETWORKS_PART "/");
1014                         *network = NULL;
1015                         if (os_strlen(net_name))
1016                                 *network = os_strdup(net_name);
1017                 } else if (bssid && bssid_part) {
1018                         /* Deal with a request for a scanned BSSID */
1019                         const char *bssid_name = bssid_part +
1020                                 os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
1021                         if (os_strlen(bssid_name))
1022                                 *bssid = os_strdup(bssid_name);
1023                         else
1024                                 *bssid = NULL;
1025                 }
1026
1027                 /* Cut off interface object path before "/" */
1028                 *next_sep = '\0';
1029         }
1030
1031         return obj_path_only;
1032 }
1033
1034
1035 /**
1036  * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a
1037  *   dbus error structure
1038  * @message: The original request message for which the error is a reply
1039  * @error: The error containing a name and a descriptive error cause
1040  * @fallback_name: A generic error name if @error was not set
1041  * @fallback_string: A generic error string if @error was not set
1042  * Returns: A new D-Bus error message
1043  *
1044  * Given a DBusMessage structure, creates a new D-Bus error message using
1045  * the error name and string contained in that structure.
1046  */
1047 DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message,
1048                                              DBusError *error,
1049                                              const char *fallback_name,
1050                                              const char *fallback_string)
1051 {
1052         if (error && error->name && error->message) {
1053                 return dbus_message_new_error(message, error->name,
1054                                               error->message);
1055         }
1056         if (fallback_name && fallback_string) {
1057                 return dbus_message_new_error(message, fallback_name,
1058                                               fallback_string);
1059         }
1060         return NULL;
1061 }