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