Check os_snprintf() result more consistently - success case
[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 (ret < 0 || (size_t) ret >= blen)
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)",
392                    msg_interface, method, path);
393
394         /* if message is introspection method call */
395         if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
396                         WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
397             !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
398                         WPAS_DBUS_INTERFACE_MAX)) {
399 #ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
400                 reply = wpa_dbus_introspect(message, obj_dsc);
401 #else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
402                 reply = dbus_message_new_error(
403                         message, DBUS_ERROR_UNKNOWN_METHOD,
404                         "wpa_supplicant was compiled without "
405                         "introspection support.");
406 #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
407         } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
408                              WPAS_DBUS_INTERFACE_MAX)) {
409                 /* if message is properties method call */
410                 reply = properties_handler(message, obj_dsc);
411         } else {
412                 reply = msg_method_handler(message, obj_dsc);
413         }
414
415         /* If handler succeed returning NULL, reply empty message */
416         if (!reply)
417                 reply = dbus_message_new_method_return(message);
418         if (reply) {
419                 if (!dbus_message_get_no_reply(message))
420                         dbus_connection_send(connection, reply, NULL);
421                 dbus_message_unref(reply);
422         }
423
424         wpa_dbus_flush_all_changed_properties(connection);
425
426         return DBUS_HANDLER_RESULT_HANDLED;
427 }
428
429
430 /**
431  * free_dbus_object_desc - Frees object description data structure
432  * @connection: DBus connection
433  * @obj_dsc: Object description to free
434  *
435  * Frees each of properties, methods and signals description lists and
436  * the object description structure itself.
437  */
438 void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc)
439 {
440         if (!obj_dsc)
441                 return;
442
443         /* free handler's argument */
444         if (obj_dsc->user_data_free_func)
445                 obj_dsc->user_data_free_func(obj_dsc->user_data);
446
447         os_free(obj_dsc->path);
448         os_free(obj_dsc->prop_changed_flags);
449         os_free(obj_dsc);
450 }
451
452
453 static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc)
454 {
455         free_dbus_object_desc(obj_dsc);
456 }
457
458 /**
459  * wpa_dbus_ctrl_iface_init - Initialize dbus control interface
460  * @application_data: Pointer to application specific data structure
461  * @dbus_path: DBus path to interface object
462  * @dbus_service: DBus service name to register with
463  * @messageHandler: a pointer to function which will handle dbus messages
464  * coming on interface
465  * Returns: 0 on success, -1 on failure
466  *
467  * Initialize the dbus control interface and start receiving commands from
468  * external programs over the bus.
469  */
470 int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
471                              char *dbus_path, char *dbus_service,
472                              struct wpa_dbus_object_desc *obj_desc)
473 {
474         DBusError error;
475         int ret = -1;
476         DBusObjectPathVTable wpa_vtable = {
477                 &free_dbus_object_desc_cb, &message_handler,
478                 NULL, NULL, NULL, NULL
479         };
480
481         obj_desc->connection = iface->con;
482         obj_desc->path = os_strdup(dbus_path);
483
484         /* Register the message handler for the global dbus interface */
485         if (!dbus_connection_register_object_path(iface->con,
486                                                   dbus_path, &wpa_vtable,
487                                                   obj_desc)) {
488                 wpa_printf(MSG_ERROR, "dbus: Could not set up message "
489                            "handler");
490                 return -1;
491         }
492
493         /* Register our service with the message bus */
494         dbus_error_init(&error);
495         switch (dbus_bus_request_name(iface->con, dbus_service,
496                                       0, &error)) {
497         case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
498                 ret = 0;
499                 break;
500         case DBUS_REQUEST_NAME_REPLY_EXISTS:
501         case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
502         case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
503                 wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
504                            "already registered");
505                 break;
506         default:
507                 wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
508                            "%s %s", error.name, error.message);
509                 break;
510         }
511         dbus_error_free(&error);
512
513         if (ret != 0)
514                 return -1;
515
516         wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service);
517
518         return 0;
519 }
520
521
522 /**
523  * wpa_dbus_register_object_per_iface - Register a new object with dbus
524  * @ctrl_iface: pointer to dbus private data
525  * @path: DBus path to object
526  * @ifname: interface name
527  * @obj_desc: description of object's methods, signals and properties
528  * Returns: 0 on success, -1 on error
529  *
530  * Registers a new interface with dbus and assigns it a dbus object path.
531  */
532 int wpa_dbus_register_object_per_iface(
533         struct wpas_dbus_priv *ctrl_iface,
534         const char *path, const char *ifname,
535         struct wpa_dbus_object_desc *obj_desc)
536 {
537         DBusConnection *con;
538         DBusError error;
539
540         DBusObjectPathVTable vtable = {
541                 &free_dbus_object_desc_cb, &message_handler,
542                 NULL, NULL, NULL, NULL
543         };
544
545         /* Do nothing if the control interface is not turned on */
546         if (ctrl_iface == NULL)
547                 return 0;
548
549         con = ctrl_iface->con;
550         obj_desc->connection = con;
551         obj_desc->path = os_strdup(path);
552
553         dbus_error_init(&error);
554         /* Register the message handler for the interface functions */
555         if (!dbus_connection_try_register_object_path(con, path, &vtable,
556                                                       obj_desc, &error)) {
557                 if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) {
558                         wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
559                 } else {
560                         wpa_printf(MSG_ERROR, "dbus: Could not set up message "
561                                    "handler for interface %s object %s",
562                                    ifname, path);
563                         wpa_printf(MSG_ERROR, "dbus error: %s", error.name);
564                         wpa_printf(MSG_ERROR, "dbus: %s", error.message);
565                 }
566                 dbus_error_free(&error);
567                 return -1;
568         }
569
570         dbus_error_free(&error);
571         return 0;
572 }
573
574
575 static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx);
576
577
578 /**
579  * wpa_dbus_unregister_object_per_iface - Unregisters DBus object
580  * @ctrl_iface: Pointer to dbus private data
581  * @path: DBus path to object which will be unregistered
582  * Returns: Zero on success and -1 on failure
583  *
584  * Unregisters DBus object given by its path
585  */
586 int wpa_dbus_unregister_object_per_iface(
587         struct wpas_dbus_priv *ctrl_iface, const char *path)
588 {
589         DBusConnection *con = ctrl_iface->con;
590         struct wpa_dbus_object_desc *obj_desc = NULL;
591
592         dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
593         if (!obj_desc) {
594                 wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's "
595                            "private data: %s", __func__, path);
596                 return 0;
597         }
598
599         eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
600
601         if (!dbus_connection_unregister_object_path(con, path))
602                 return -1;
603
604         return 0;
605 }
606
607
608 static dbus_bool_t put_changed_properties(
609         const struct wpa_dbus_object_desc *obj_dsc, const char *interface,
610         DBusMessageIter *dict_iter, int clear_changed)
611 {
612         DBusMessageIter entry_iter;
613         const struct wpa_dbus_property_desc *dsc;
614         int i;
615         DBusError error;
616
617         for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
618              dsc++, i++) {
619                 if (obj_dsc->prop_changed_flags == NULL ||
620                     !obj_dsc->prop_changed_flags[i])
621                         continue;
622                 if (os_strcmp(dsc->dbus_interface, interface) != 0)
623                         continue;
624                 if (clear_changed)
625                         obj_dsc->prop_changed_flags[i] = 0;
626
627                 if (!dbus_message_iter_open_container(dict_iter,
628                                                       DBUS_TYPE_DICT_ENTRY,
629                                                       NULL, &entry_iter))
630                         return FALSE;
631
632                 if (!dbus_message_iter_append_basic(&entry_iter,
633                                                     DBUS_TYPE_STRING,
634                                                     &dsc->dbus_property))
635                         return FALSE;
636
637                 dbus_error_init(&error);
638                 if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
639                         if (dbus_error_is_set (&error)) {
640                                 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
641                                            "new value of property %s: (%s) %s",
642                                            __func__, dsc->dbus_property,
643                                            error.name, error.message);
644                         } else {
645                                 wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
646                                            "new value of property %s",
647                                            __func__, dsc->dbus_property);
648                         }
649                         dbus_error_free(&error);
650                         return FALSE;
651                 }
652
653                 if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
654                         return FALSE;
655         }
656
657         return TRUE;
658 }
659
660
661 static void do_send_prop_changed_signal(
662         DBusConnection *con, const char *path, const char *interface,
663         const struct wpa_dbus_object_desc *obj_dsc)
664 {
665         DBusMessage *msg;
666         DBusMessageIter signal_iter, dict_iter;
667
668         msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES,
669                                       "PropertiesChanged");
670         if (msg == NULL)
671                 return;
672
673         dbus_message_iter_init_append(msg, &signal_iter);
674
675         if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
676                                             &interface))
677                 goto err;
678
679         /* Changed properties dict */
680         if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
681                                               "{sv}", &dict_iter))
682                 goto err;
683
684         if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0))
685                 goto err;
686
687         if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
688                 goto err;
689
690         /* Invalidated properties array (empty) */
691         if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
692                                               "s", &dict_iter))
693                 goto err;
694
695         if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
696                 goto err;
697
698         dbus_connection_send(con, msg, NULL);
699
700 out:
701         dbus_message_unref(msg);
702         return;
703
704 err:
705         wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
706                    __func__);
707         goto out;
708 }
709
710
711 static void do_send_deprecated_prop_changed_signal(
712         DBusConnection *con, const char *path, const char *interface,
713         const struct wpa_dbus_object_desc *obj_dsc)
714 {
715         DBusMessage *msg;
716         DBusMessageIter signal_iter, dict_iter;
717
718         msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
719         if (msg == NULL)
720                 return;
721
722         dbus_message_iter_init_append(msg, &signal_iter);
723
724         if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
725                                               "{sv}", &dict_iter))
726                 goto err;
727
728         if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1))
729                 goto err;
730
731         if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
732                 goto err;
733
734         dbus_connection_send(con, msg, NULL);
735
736 out:
737         dbus_message_unref(msg);
738         return;
739
740 err:
741         wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
742                    __func__);
743         goto out;
744 }
745
746
747 static void send_prop_changed_signal(
748         DBusConnection *con, const char *path, const char *interface,
749         const struct wpa_dbus_object_desc *obj_dsc)
750 {
751         /*
752          * First, send property change notification on the standardized
753          * org.freedesktop.DBus.Properties interface. This call will not
754          * clear the property change bits, so that they are preserved for
755          * the call that follows.
756          */
757         do_send_prop_changed_signal(con, path, interface, obj_dsc);
758
759         /*
760          * Now send PropertiesChanged on our own interface for backwards
761          * compatibility. This is deprecated and will be removed in a future
762          * release.
763          */
764         do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc);
765
766         /* Property change bits have now been cleared. */
767 }
768
769
770 static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
771 {
772         DBusConnection *con = eloop_ctx;
773         struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
774
775         wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties "
776                    "of object %s", __func__, obj_desc->path);
777         wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
778 }
779
780
781 static void recursive_flush_changed_properties(DBusConnection *con,
782                                                const char *path)
783 {
784         char **objects = NULL;
785         char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
786         int i;
787
788         wpa_dbus_flush_object_changed_properties(con, path);
789
790         if (!dbus_connection_list_registered(con, path, &objects))
791                 goto out;
792
793         for (i = 0; objects[i]; i++) {
794                 os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
795                             "%s/%s", path, objects[i]);
796                 recursive_flush_changed_properties(con, subobj_path);
797         }
798
799 out:
800         dbus_free_string_array(objects);
801 }
802
803
804 /**
805  * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
806  * @con: DBus connection
807  *
808  * Traverses through all registered objects and sends PropertiesChanged for
809  * each properties.
810  */
811 void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
812 {
813         recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
814 }
815
816
817 /**
818  * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
819  * @con: DBus connection
820  * @path: path to a DBus object for which PropertiesChanged will be sent.
821  *
822  * Iterates over all properties registered with object and for each interface
823  * containing properties marked as changed, sends a PropertiesChanged signal
824  * containing names and new values of properties that have changed.
825  *
826  * You need to call this function after wpa_dbus_mark_property_changed()
827  * if you want to send PropertiesChanged signal immediately (i.e., without
828  * waiting timeout to expire). PropertiesChanged signal for an object is sent
829  * automatically short time after first marking property as changed. All
830  * PropertiesChanged signals are sent automatically after responding on DBus
831  * message, so if you marked a property changed as a result of DBus call
832  * (e.g., param setter), you usually do not need to call this function.
833  */
834 void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
835                                               const char *path)
836 {
837         struct wpa_dbus_object_desc *obj_desc = NULL;
838         const struct wpa_dbus_property_desc *dsc;
839         int i;
840
841         dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
842         if (!obj_desc)
843                 return;
844         eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
845
846         for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
847              dsc++, i++) {
848                 if (obj_desc->prop_changed_flags == NULL ||
849                     !obj_desc->prop_changed_flags[i])
850                         continue;
851                 send_prop_changed_signal(con, path, dsc->dbus_interface,
852                                          obj_desc);
853         }
854 }
855
856
857 #define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
858
859
860 /**
861  * wpa_dbus_mark_property_changed - Mark a property as changed and
862  * @iface: dbus priv struct
863  * @path: path to DBus object which property has changed
864  * @interface: interface containing changed property
865  * @property: property name which has changed
866  *
867  * Iterates over all properties registered with an object and marks the one
868  * given in parameters as changed. All parameters registered for an object
869  * within a single interface will be aggregated together and sent in one
870  * PropertiesChanged signal when function
871  * wpa_dbus_flush_object_changed_properties() is called.
872  */
873 void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
874                                     const char *path, const char *interface,
875                                     const char *property)
876 {
877         struct wpa_dbus_object_desc *obj_desc = NULL;
878         const struct wpa_dbus_property_desc *dsc;
879         int i = 0;
880
881         if (iface == NULL)
882                 return;
883
884         dbus_connection_get_object_path_data(iface->con, path,
885                                              (void **) &obj_desc);
886         if (!obj_desc) {
887                 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
888                            "could not obtain object's private data: %s", path);
889                 return;
890         }
891
892         for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
893                 if (os_strcmp(property, dsc->dbus_property) == 0 &&
894                     os_strcmp(interface, dsc->dbus_interface) == 0) {
895                         if (obj_desc->prop_changed_flags)
896                                 obj_desc->prop_changed_flags[i] = 1;
897                         break;
898                 }
899
900         if (!dsc || !dsc->dbus_property) {
901                 wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
902                            "no property %s in object %s", property, path);
903                 return;
904         }
905
906         if (!eloop_is_timeout_registered(flush_object_timeout_handler,
907                                          iface->con, obj_desc->path)) {
908                 eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
909                                        flush_object_timeout_handler,
910                                        iface->con, obj_desc);
911         }
912 }
913
914
915 /**
916  * wpa_dbus_get_object_properties - Put object's properties into dictionary
917  * @iface: dbus priv struct
918  * @path: path to DBus object which properties will be obtained
919  * @interface: interface name which properties will be obtained
920  * @iter: DBus message iter at which to append property dictionary.
921  *
922  * Iterates over all properties registered with object and execute getters
923  * of those, which are readable and which interface matches interface
924  * specified as argument. Obtained properties values are stored in
925  * dict_iter dictionary.
926  */
927 dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
928                                            const char *path,
929                                            const char *interface,
930                                            DBusMessageIter *iter)
931 {
932         struct wpa_dbus_object_desc *obj_desc = NULL;
933         DBusMessageIter dict_iter;
934         DBusError error;
935
936         dbus_connection_get_object_path_data(iface->con, path,
937                                              (void **) &obj_desc);
938         if (!obj_desc) {
939                 wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's "
940                            "private data: %s", __func__, path);
941                 return FALSE;
942         }
943
944         if (!wpa_dbus_dict_open_write(iter, &dict_iter)) {
945                 wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict",
946                            __func__);
947                 return FALSE;
948         }
949
950         dbus_error_init(&error);
951         if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
952                                        interface, obj_desc->user_data,
953                                        &error)) {
954                 wpa_printf(MSG_ERROR, "dbus: %s: failed to get object"
955                            " properties: (%s) %s", __func__,
956                            dbus_error_is_set(&error) ? error.name : "none",
957                            dbus_error_is_set(&error) ? error.message : "none");
958                 dbus_error_free(&error);
959                 return FALSE;
960         }
961
962         return wpa_dbus_dict_close_write(iter, &dict_iter);
963 }
964
965 /**
966  * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
967  * @path: The dbus object path
968  * @p2p_persistent_group: indicates whether to parse the path as a P2P
969  *                        persistent group object
970  * @network: (out) the configured network this object path refers to, if any
971  * @bssid: (out) the scanned bssid this object path refers to, if any
972  * Returns: The object path of the network interface this path refers to
973  *
974  * For a given object path, decomposes the object path into object id, network,
975  * and BSSID parts, if those parts exist.
976  */
977 char *wpas_dbus_new_decompose_object_path(const char *path,
978                                            int p2p_persistent_group,
979                                            char **network,
980                                            char **bssid)
981 {
982         const unsigned int dev_path_prefix_len =
983                 os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
984         char *obj_path_only;
985         char *next_sep;
986
987         /* Be a bit paranoid about path */
988         if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
989                                 dev_path_prefix_len))
990                 return NULL;
991
992         /* Ensure there's something at the end of the path */
993         if ((path + dev_path_prefix_len)[0] == '\0')
994                 return NULL;
995
996         obj_path_only = os_strdup(path);
997         if (obj_path_only == NULL)
998                 return NULL;
999
1000         next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
1001         if (next_sep != NULL) {
1002                 const char *net_part = os_strstr(
1003                         next_sep, p2p_persistent_group ?
1004                         WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" :
1005                         WPAS_DBUS_NEW_NETWORKS_PART "/");
1006                 const char *bssid_part = os_strstr(
1007                         next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
1008
1009                 if (network && net_part) {
1010                         /* Deal with a request for a configured network */
1011                         const char *net_name = net_part +
1012                                 os_strlen(p2p_persistent_group ?
1013                                           WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART
1014                                           "/" :
1015                                           WPAS_DBUS_NEW_NETWORKS_PART "/");
1016                         *network = NULL;
1017                         if (os_strlen(net_name))
1018                                 *network = os_strdup(net_name);
1019                 } else if (bssid && bssid_part) {
1020                         /* Deal with a request for a scanned BSSID */
1021                         const char *bssid_name = bssid_part +
1022                                 os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
1023                         if (os_strlen(bssid_name))
1024                                 *bssid = os_strdup(bssid_name);
1025                         else
1026                                 *bssid = NULL;
1027                 }
1028
1029                 /* Cut off interface object path before "/" */
1030                 *next_sep = '\0';
1031         }
1032
1033         return obj_path_only;
1034 }
1035
1036
1037 /**
1038  * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a
1039  *   dbus error structure
1040  * @message: The original request message for which the error is a reply
1041  * @error: The error containing a name and a descriptive error cause
1042  * @fallback_name: A generic error name if @error was not set
1043  * @fallback_string: A generic error string if @error was not set
1044  * Returns: A new D-Bus error message
1045  *
1046  * Given a DBusMessage structure, creates a new D-Bus error message using
1047  * the error name and string contained in that structure.
1048  */
1049 DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message,
1050                                              DBusError *error,
1051                                              const char *fallback_name,
1052                                              const char *fallback_string)
1053 {
1054         if (error && error->name && error->message) {
1055                 return dbus_message_new_error(message, error->name,
1056                                               error->message);
1057         }
1058         if (fallback_name && fallback_string) {
1059                 return dbus_message_new_error(message, fallback_name,
1060                                               fallback_string);
1061         }
1062         return NULL;
1063 }