• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2006 Lennart Poettering
5   Copyright 2006 Shams E. King
6 
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11 
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16 
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <stdarg.h>
26 
27 #include <pulse/rtclock.h>
28 #include <pulse/timeval.h>
29 #include <pulse/utf8.h>
30 #include <pulse/xmalloc.h>
31 
32 #include <pulsecore/core-rtclock.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/log.h>
35 
36 #include "dbus-util.h"
37 
38 struct pa_dbus_wrap_connection {
39     pa_mainloop_api *mainloop;
40     DBusConnection *connection;
41     pa_defer_event* dispatch_event;
42     bool use_rtclock:1;
43 };
44 
45 struct timeout_data {
46     pa_dbus_wrap_connection *connection;
47     DBusTimeout *timeout;
48 };
49 
dispatch_cb(pa_mainloop_api * ea,pa_defer_event * ev,void * userdata)50 static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
51     DBusConnection *conn = userdata;
52 
53     if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE)
54         /* no more data to process, disable the deferred */
55         ea->defer_enable(ev, 0);
56 }
57 
58 /* DBusDispatchStatusFunction callback for the pa mainloop */
dispatch_status(DBusConnection * conn,DBusDispatchStatus status,void * userdata)59 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
60     pa_dbus_wrap_connection *c = userdata;
61 
62     pa_assert(c);
63 
64     switch(status) {
65 
66         case DBUS_DISPATCH_COMPLETE:
67             c->mainloop->defer_enable(c->dispatch_event, 0);
68             break;
69 
70         case DBUS_DISPATCH_DATA_REMAINS:
71         case DBUS_DISPATCH_NEED_MEMORY:
72         default:
73             c->mainloop->defer_enable(c->dispatch_event, 1);
74             break;
75     }
76 }
77 
get_watch_flags(DBusWatch * watch)78 static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
79     unsigned int flags;
80     pa_io_event_flags_t events = 0;
81 
82     pa_assert(watch);
83 
84     flags = dbus_watch_get_flags(watch);
85 
86     /* no watch flags for disabled watches */
87     if (!dbus_watch_get_enabled(watch))
88         return PA_IO_EVENT_NULL;
89 
90     if (flags & DBUS_WATCH_READABLE)
91         events |= PA_IO_EVENT_INPUT;
92     if (flags & DBUS_WATCH_WRITABLE)
93         events |= PA_IO_EVENT_OUTPUT;
94 
95     return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
96 }
97 
98 /* pa_io_event_cb_t IO event handler */
handle_io_event(pa_mainloop_api * ea,pa_io_event * e,int fd,pa_io_event_flags_t events,void * userdata)99 static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
100     unsigned int flags = 0;
101     DBusWatch *watch = userdata;
102 
103     pa_assert(fd == dbus_watch_get_unix_fd(watch));
104 
105     if (!dbus_watch_get_enabled(watch)) {
106         pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
107         return;
108     }
109 
110     if (events & PA_IO_EVENT_INPUT)
111         flags |= DBUS_WATCH_READABLE;
112     if (events & PA_IO_EVENT_OUTPUT)
113         flags |= DBUS_WATCH_WRITABLE;
114     if (events & PA_IO_EVENT_HANGUP)
115         flags |= DBUS_WATCH_HANGUP;
116     if (events & PA_IO_EVENT_ERROR)
117         flags |= DBUS_WATCH_ERROR;
118 
119     dbus_watch_handle(watch, flags);
120 }
121 
122 /* pa_time_event_cb_t timer event handler */
handle_time_event(pa_mainloop_api * ea,pa_time_event * e,const struct timeval * t,void * userdata)123 static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *t, void *userdata) {
124     struct timeval tv;
125     struct timeout_data *d = userdata;
126 
127     pa_assert(d);
128     pa_assert(d->connection);
129 
130     if (dbus_timeout_get_enabled(d->timeout)) {
131         /* Restart it for the next scheduled time. We do this before
132          * calling dbus_timeout_handle() to make sure that the time
133          * event is still around. */
134         ea->time_restart(e, pa_timeval_rtstore(&tv,
135                                                pa_timeval_load(t) + dbus_timeout_get_interval(d->timeout) * PA_USEC_PER_MSEC,
136                                                d->connection->use_rtclock));
137 
138         dbus_timeout_handle(d->timeout);
139     }
140 }
141 
142 /* DBusAddWatchFunction callback for pa mainloop */
add_watch(DBusWatch * watch,void * data)143 static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
144     pa_dbus_wrap_connection *c = data;
145     pa_io_event *ev;
146 
147     pa_assert(watch);
148     pa_assert(c);
149 
150     ev = c->mainloop->io_new(
151             c->mainloop,
152             dbus_watch_get_unix_fd(watch),
153             get_watch_flags(watch), handle_io_event, watch);
154 
155     dbus_watch_set_data(watch, ev, NULL);
156 
157     return TRUE;
158 }
159 
160 /* DBusRemoveWatchFunction callback for pa mainloop */
remove_watch(DBusWatch * watch,void * data)161 static void remove_watch(DBusWatch *watch, void *data) {
162     pa_dbus_wrap_connection *c = data;
163     pa_io_event *ev;
164 
165     pa_assert(watch);
166     pa_assert(c);
167 
168     if ((ev = dbus_watch_get_data(watch)))
169         c->mainloop->io_free(ev);
170 }
171 
172 /* DBusWatchToggledFunction callback for pa mainloop */
toggle_watch(DBusWatch * watch,void * data)173 static void toggle_watch(DBusWatch *watch, void *data) {
174     pa_dbus_wrap_connection *c = data;
175     pa_io_event *ev;
176 
177     pa_assert(watch);
178     pa_assert(c);
179 
180     pa_assert_se(ev = dbus_watch_get_data(watch));
181 
182     /* get_watch_flags() checks if the watch is enabled */
183     c->mainloop->io_enable(ev, get_watch_flags(watch));
184 }
185 
time_event_destroy_cb(pa_mainloop_api * a,pa_time_event * e,void * userdata)186 static void time_event_destroy_cb(pa_mainloop_api *a, pa_time_event *e, void *userdata) {
187     pa_xfree(userdata);
188 }
189 
190 /* DBusAddTimeoutFunction callback for pa mainloop */
add_timeout(DBusTimeout * timeout,void * data)191 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
192     pa_dbus_wrap_connection *c = data;
193     pa_time_event *ev;
194     struct timeval tv;
195     struct timeout_data *d;
196 
197     pa_assert(timeout);
198     pa_assert(c);
199 
200     if (!dbus_timeout_get_enabled(timeout))
201         return FALSE;
202 
203     d = pa_xnew(struct timeout_data, 1);
204     d->connection = c;
205     d->timeout = timeout;
206     ev = c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + dbus_timeout_get_interval(timeout) * PA_USEC_PER_MSEC, c->use_rtclock), handle_time_event, d);
207     c->mainloop->time_set_destroy(ev, time_event_destroy_cb);
208 
209     dbus_timeout_set_data(timeout, ev, NULL);
210 
211     return TRUE;
212 }
213 
214 /* DBusRemoveTimeoutFunction callback for pa mainloop */
remove_timeout(DBusTimeout * timeout,void * data)215 static void remove_timeout(DBusTimeout *timeout, void *data) {
216     pa_dbus_wrap_connection *c = data;
217     pa_time_event *ev;
218 
219     pa_assert(timeout);
220     pa_assert(c);
221 
222     if ((ev = dbus_timeout_get_data(timeout)))
223         c->mainloop->time_free(ev);
224 }
225 
226 /* DBusTimeoutToggledFunction callback for pa mainloop */
toggle_timeout(DBusTimeout * timeout,void * data)227 static void toggle_timeout(DBusTimeout *timeout, void *data) {
228     struct timeout_data *d = data;
229     pa_time_event *ev;
230     struct timeval tv;
231 
232     pa_assert(d);
233     pa_assert(d->connection);
234     pa_assert(timeout);
235 
236     pa_assert_se(ev = dbus_timeout_get_data(timeout));
237 
238     if (dbus_timeout_get_enabled(timeout))
239         d->connection->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, pa_rtclock_now() + dbus_timeout_get_interval(timeout) * PA_USEC_PER_MSEC, d->connection->use_rtclock));
240     else
241         d->connection->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, PA_USEC_INVALID, d->connection->use_rtclock));
242 }
243 
wakeup_main(void * userdata)244 static void wakeup_main(void *userdata) {
245     pa_dbus_wrap_connection *c = userdata;
246 
247     pa_assert(c);
248 
249     /* this will wakeup the mainloop and dispatch events, although
250      * it may not be the cleanest way of accomplishing it */
251     c->mainloop->defer_enable(c->dispatch_event, 1);
252 }
253 
pa_dbus_wrap_connection_new(pa_mainloop_api * m,bool use_rtclock,DBusBusType type,DBusError * error)254 pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, bool use_rtclock, DBusBusType type, DBusError *error) {
255     DBusConnection *conn;
256     pa_dbus_wrap_connection *pconn;
257     char *id;
258 
259     pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
260 
261     if (!(conn = dbus_bus_get_private(type, error)))
262         return NULL;
263 
264     pconn = pa_xnew(pa_dbus_wrap_connection, 1);
265     pconn->mainloop = m;
266     pconn->connection = conn;
267     pconn->use_rtclock = use_rtclock;
268 
269     dbus_connection_set_exit_on_disconnect(conn, FALSE);
270     dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
271     dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
272     dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
273     dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
274 
275     pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
276 
277     pa_log_debug("Successfully connected to D-Bus %s bus %s as %s",
278                  type == DBUS_BUS_SYSTEM ? "system" : (type == DBUS_BUS_SESSION ? "session" : "starter"),
279                  pa_strnull((id = dbus_connection_get_server_id(conn))),
280                  pa_strnull(dbus_bus_get_unique_name(conn)));
281 
282     dbus_free(id);
283 
284     return pconn;
285 }
286 
pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api * m,bool use_rtclock,DBusConnection * conn)287 pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
288         pa_mainloop_api *m,
289         bool use_rtclock,
290         DBusConnection *conn) {
291     pa_dbus_wrap_connection *pconn;
292 
293     pa_assert(m);
294     pa_assert(conn);
295 
296     pconn = pa_xnew(pa_dbus_wrap_connection, 1);
297     pconn->mainloop = m;
298     pconn->connection = dbus_connection_ref(conn);
299     pconn->use_rtclock = use_rtclock;
300 
301     dbus_connection_set_exit_on_disconnect(conn, FALSE);
302     dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
303     dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
304     dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
305     dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
306 
307     pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
308 
309     return pconn;
310 }
311 
pa_dbus_wrap_connection_free(pa_dbus_wrap_connection * c)312 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
313     pa_assert(c);
314 
315     if (dbus_connection_get_is_connected(c->connection)) {
316         dbus_connection_close(c->connection);
317         /* must process remaining messages, bit of a kludge to handle
318          * both unload and shutdown */
319         while (dbus_connection_read_write_dispatch(c->connection, -1))
320             ;
321     }
322 
323     c->mainloop->defer_free(c->dispatch_event);
324     dbus_connection_unref(c->connection);
325     pa_xfree(c);
326 }
327 
pa_dbus_wrap_connection_get(pa_dbus_wrap_connection * c)328 DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
329   pa_assert(c);
330   pa_assert(c->connection);
331 
332   return c->connection;
333 }
334 
pa_dbus_add_matches(DBusConnection * c,DBusError * error,...)335 int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
336     const char *t;
337     va_list ap;
338     unsigned k = 0;
339 
340     pa_assert(c);
341     pa_assert(error);
342 
343     va_start(ap, error);
344     while ((t = va_arg(ap, const char*))) {
345         dbus_bus_add_match(c, t, error);
346 
347         if (dbus_error_is_set(error))
348             goto fail;
349 
350         k++;
351     }
352     va_end(ap);
353     return 0;
354 
355 fail:
356 
357     va_end(ap);
358     va_start(ap, error);
359     for (; k > 0; k--) {
360         pa_assert_se(t = va_arg(ap, const char*));
361         dbus_bus_remove_match(c, t, NULL);
362     }
363     va_end(ap);
364 
365     return -1;
366 }
367 
pa_dbus_remove_matches(DBusConnection * c,...)368 void pa_dbus_remove_matches(DBusConnection *c, ...) {
369     const char *t;
370     va_list ap;
371 
372     pa_assert(c);
373 
374     va_start(ap, c);
375     while ((t = va_arg(ap, const char*)))
376         dbus_bus_remove_match(c, t, NULL);
377     va_end(ap);
378 }
379 
pa_dbus_pending_new(DBusConnection * c,DBusMessage * m,DBusPendingCall * pending,void * context_data,void * call_data)380 pa_dbus_pending *pa_dbus_pending_new(
381         DBusConnection *c,
382         DBusMessage *m,
383         DBusPendingCall *pending,
384         void *context_data,
385         void *call_data) {
386 
387     pa_dbus_pending *p;
388 
389     pa_assert(pending);
390 
391     p = pa_xnew(pa_dbus_pending, 1);
392     p->connection = c;
393     p->message = m;
394     p->pending = pending;
395     p->context_data = context_data;
396     p->call_data = call_data;
397 
398     PA_LLIST_INIT(pa_dbus_pending, p);
399 
400     return p;
401 }
402 
pa_dbus_pending_free(pa_dbus_pending * p)403 void pa_dbus_pending_free(pa_dbus_pending *p) {
404     pa_assert(p);
405 
406     if (p->pending) {
407         dbus_pending_call_cancel(p->pending);
408         dbus_pending_call_unref(p->pending);
409     }
410 
411     if (p->message)
412         dbus_message_unref(p->message);
413 
414     pa_xfree(p);
415 }
416 
pa_dbus_sync_pending_list(pa_dbus_pending ** p)417 void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
418     pa_assert(p);
419 
420     while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
421         ;
422 }
423 
pa_dbus_free_pending_list(pa_dbus_pending ** p)424 void pa_dbus_free_pending_list(pa_dbus_pending **p) {
425     pa_dbus_pending *i;
426 
427     pa_assert(p);
428 
429     while ((i = *p)) {
430         PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
431         pa_dbus_pending_free(i);
432     }
433 }
434 
pa_dbus_get_error_message(DBusMessage * m)435 const char *pa_dbus_get_error_message(DBusMessage *m) {
436     const char *message;
437 
438     pa_assert(m);
439     pa_assert(dbus_message_get_type(m) == DBUS_MESSAGE_TYPE_ERROR);
440 
441     if (dbus_message_get_signature(m)[0] != 's')
442         return "<no explanation>";
443 
444     pa_assert_se(dbus_message_get_args(m, NULL, DBUS_TYPE_STRING, &message, DBUS_TYPE_INVALID));
445 
446     return message;
447 }
448 
pa_dbus_send_error(DBusConnection * c,DBusMessage * in_reply_to,const char * name,const char * format,...)449 void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) {
450     va_list ap;
451     char *message;
452     DBusMessage *reply = NULL;
453 
454     pa_assert(c);
455     pa_assert(in_reply_to);
456     pa_assert(name);
457     pa_assert(format);
458 
459     va_start(ap, format);
460     message = pa_vsprintf_malloc(format, ap);
461     va_end(ap);
462     pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message)));
463     pa_assert_se(dbus_connection_send(c, reply, NULL));
464 
465     dbus_message_unref(reply);
466 
467     pa_xfree(message);
468 }
469 
pa_dbus_send_empty_reply(DBusConnection * c,DBusMessage * in_reply_to)470 void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) {
471     DBusMessage *reply = NULL;
472 
473     pa_assert(c);
474     pa_assert(in_reply_to);
475 
476     pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
477     pa_assert_se(dbus_connection_send(c, reply, NULL));
478     dbus_message_unref(reply);
479 }
480 
pa_dbus_send_basic_value_reply(DBusConnection * c,DBusMessage * in_reply_to,int type,void * data)481 void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
482     DBusMessage *reply = NULL;
483 
484     pa_assert(c);
485     pa_assert(in_reply_to);
486     pa_assert(dbus_type_is_basic(type));
487     pa_assert(data);
488 
489     pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
490     pa_assert_se(dbus_message_append_args(reply, type, data, DBUS_TYPE_INVALID));
491     pa_assert_se(dbus_connection_send(c, reply, NULL));
492     dbus_message_unref(reply);
493 }
494 
signature_from_basic_type(int type)495 static const char *signature_from_basic_type(int type) {
496     switch (type) {
497         case DBUS_TYPE_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING;
498         case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING;
499         case DBUS_TYPE_INT16: return DBUS_TYPE_INT16_AS_STRING;
500         case DBUS_TYPE_UINT16: return DBUS_TYPE_UINT16_AS_STRING;
501         case DBUS_TYPE_INT32: return DBUS_TYPE_INT32_AS_STRING;
502         case DBUS_TYPE_UINT32: return DBUS_TYPE_UINT32_AS_STRING;
503         case DBUS_TYPE_INT64: return DBUS_TYPE_INT64_AS_STRING;
504         case DBUS_TYPE_UINT64: return DBUS_TYPE_UINT64_AS_STRING;
505         case DBUS_TYPE_DOUBLE: return DBUS_TYPE_DOUBLE_AS_STRING;
506         case DBUS_TYPE_STRING: return DBUS_TYPE_STRING_AS_STRING;
507         case DBUS_TYPE_OBJECT_PATH: return DBUS_TYPE_OBJECT_PATH_AS_STRING;
508         case DBUS_TYPE_SIGNATURE: return DBUS_TYPE_SIGNATURE_AS_STRING;
509         default: pa_assert_not_reached();
510     }
511 }
512 
pa_dbus_send_basic_variant_reply(DBusConnection * c,DBusMessage * in_reply_to,int type,void * data)513 void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
514     DBusMessage *reply = NULL;
515     DBusMessageIter msg_iter;
516     DBusMessageIter variant_iter;
517 
518     pa_assert(c);
519     pa_assert(in_reply_to);
520     pa_assert(dbus_type_is_basic(type));
521     pa_assert(data);
522 
523     pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
524     dbus_message_iter_init_append(reply, &msg_iter);
525     pa_assert_se(dbus_message_iter_open_container(&msg_iter,
526                                                   DBUS_TYPE_VARIANT,
527                                                   signature_from_basic_type(type),
528                                                   &variant_iter));
529     pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
530     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter));
531     pa_assert_se(dbus_connection_send(c, reply, NULL));
532     dbus_message_unref(reply);
533 }
534 
535 /* Note: returns sizeof(char*) for strings, object paths and signatures. */
basic_type_size(int type)536 static unsigned basic_type_size(int type) {
537     switch (type) {
538         case DBUS_TYPE_BOOLEAN: return sizeof(dbus_bool_t);
539         case DBUS_TYPE_BYTE: return 1;
540         case DBUS_TYPE_INT16: return sizeof(dbus_int16_t);
541         case DBUS_TYPE_UINT16: return sizeof(dbus_uint16_t);
542         case DBUS_TYPE_INT32: return sizeof(dbus_int32_t);
543         case DBUS_TYPE_UINT32: return sizeof(dbus_uint32_t);
544         case DBUS_TYPE_INT64: return sizeof(dbus_int64_t);
545         case DBUS_TYPE_UINT64: return sizeof(dbus_uint64_t);
546         case DBUS_TYPE_DOUBLE: return sizeof(double);
547         case DBUS_TYPE_STRING:
548         case DBUS_TYPE_OBJECT_PATH:
549         case DBUS_TYPE_SIGNATURE: return sizeof(char*);
550         default: pa_assert_not_reached();
551     }
552 }
553 
pa_dbus_send_basic_array_variant_reply(DBusConnection * c,DBusMessage * in_reply_to,int item_type,void * array,unsigned n)554 void pa_dbus_send_basic_array_variant_reply(
555         DBusConnection *c,
556         DBusMessage *in_reply_to,
557         int item_type,
558         void *array,
559         unsigned n) {
560     DBusMessage *reply = NULL;
561     DBusMessageIter msg_iter;
562 
563     pa_assert(c);
564     pa_assert(in_reply_to);
565     pa_assert(dbus_type_is_basic(item_type));
566     pa_assert(array || n == 0);
567 
568     pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
569     dbus_message_iter_init_append(reply, &msg_iter);
570     pa_dbus_append_basic_array_variant(&msg_iter, item_type, array, n);
571     pa_assert_se(dbus_connection_send(c, reply, NULL));
572     dbus_message_unref(reply);
573 }
574 
pa_dbus_send_proplist_variant_reply(DBusConnection * c,DBusMessage * in_reply_to,pa_proplist * proplist)575 void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) {
576     DBusMessage *reply = NULL;
577     DBusMessageIter msg_iter;
578 
579     pa_assert(c);
580     pa_assert(in_reply_to);
581     pa_assert(proplist);
582 
583     pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
584     dbus_message_iter_init_append(reply, &msg_iter);
585     pa_dbus_append_proplist_variant(&msg_iter, proplist);
586     pa_assert_se(dbus_connection_send(c, reply, NULL));
587     dbus_message_unref(reply);
588 }
589 
pa_dbus_append_basic_array(DBusMessageIter * iter,int item_type,const void * array,unsigned n)590 void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
591     DBusMessageIter array_iter;
592     unsigned i;
593     unsigned item_size;
594 
595     pa_assert(iter);
596     pa_assert(dbus_type_is_basic(item_type));
597     pa_assert(array || n == 0);
598 
599     item_size = basic_type_size(item_type);
600 
601     pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, signature_from_basic_type(item_type), &array_iter));
602 
603     for (i = 0; i < n; ++i)
604         pa_assert_se(dbus_message_iter_append_basic(&array_iter, item_type, &((uint8_t*) array)[i * item_size]));
605 
606     pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
607 }
608 
pa_dbus_append_basic_variant(DBusMessageIter * iter,int type,void * data)609 void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data) {
610     DBusMessageIter variant_iter;
611 
612     pa_assert(iter);
613     pa_assert(dbus_type_is_basic(type));
614     pa_assert(data);
615 
616     pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
617     pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
618     pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
619 }
620 
pa_dbus_append_basic_array_variant(DBusMessageIter * iter,int item_type,const void * array,unsigned n)621 void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
622     DBusMessageIter variant_iter;
623     char *array_signature;
624 
625     pa_assert(iter);
626     pa_assert(dbus_type_is_basic(item_type));
627     pa_assert(array || n == 0);
628 
629     array_signature = pa_sprintf_malloc("a%c", *signature_from_basic_type(item_type));
630 
631     pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_signature, &variant_iter));
632     pa_dbus_append_basic_array(&variant_iter, item_type, array, n);
633     pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
634 
635     pa_xfree(array_signature);
636 }
637 
pa_dbus_append_basic_variant_dict_entry(DBusMessageIter * dict_iter,const char * key,int type,void * data)638 void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data) {
639     DBusMessageIter dict_entry_iter;
640 
641     pa_assert(dict_iter);
642     pa_assert(key);
643     pa_assert(dbus_type_is_basic(type));
644     pa_assert(data);
645 
646     pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
647     pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
648     pa_dbus_append_basic_variant(&dict_entry_iter, type, data);
649     pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
650 }
651 
pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter * dict_iter,const char * key,int item_type,const void * array,unsigned n)652 void pa_dbus_append_basic_array_variant_dict_entry(
653         DBusMessageIter *dict_iter,
654         const char *key,
655         int item_type,
656         const void *array,
657         unsigned n) {
658     DBusMessageIter dict_entry_iter;
659 
660     pa_assert(dict_iter);
661     pa_assert(key);
662     pa_assert(dbus_type_is_basic(item_type));
663     pa_assert(array || n == 0);
664 
665     pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
666     pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
667     pa_dbus_append_basic_array_variant(&dict_entry_iter, item_type, array, n);
668     pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
669 }
670 
pa_dbus_append_proplist(DBusMessageIter * iter,pa_proplist * proplist)671 void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) {
672     DBusMessageIter dict_iter;
673     DBusMessageIter dict_entry_iter;
674     DBusMessageIter array_iter;
675     void *state = NULL;
676     const char *key;
677 
678     pa_assert(iter);
679     pa_assert(proplist);
680 
681     pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter));
682 
683     while ((key = pa_proplist_iterate(proplist, &state))) {
684         const void *value = NULL;
685         size_t nbytes;
686 
687         pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0);
688 
689         pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
690 
691         pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
692 
693         pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter));
694         pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes));
695         pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter));
696 
697         pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
698     }
699 
700     pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter));
701 }
702 
pa_dbus_append_proplist_variant(DBusMessageIter * iter,pa_proplist * proplist)703 void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) {
704     DBusMessageIter variant_iter;
705 
706     pa_assert(iter);
707     pa_assert(proplist);
708 
709     pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter));
710     pa_dbus_append_proplist(&variant_iter, proplist);
711     pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
712 }
713 
pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter * dict_iter,const char * key,pa_proplist * proplist)714 void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) {
715     DBusMessageIter dict_entry_iter;
716 
717     pa_assert(dict_iter);
718     pa_assert(key);
719     pa_assert(proplist);
720 
721     pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
722     pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
723     pa_dbus_append_proplist_variant(&dict_entry_iter, proplist);
724     pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
725 }
726 
pa_dbus_get_proplist_arg(DBusConnection * c,DBusMessage * msg,DBusMessageIter * iter)727 pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) {
728     DBusMessageIter dict_iter;
729     DBusMessageIter dict_entry_iter;
730     char *signature;
731     pa_proplist *proplist = NULL;
732     const char *key = NULL;
733     const uint8_t *value = NULL;
734     int value_length = 0;
735 
736     pa_assert(c);
737     pa_assert(msg);
738     pa_assert(iter);
739 
740     pa_assert(signature = dbus_message_iter_get_signature(iter));
741     pa_assert_se(pa_streq(signature, "a{say}"));
742 
743     dbus_free(signature);
744 
745     proplist = pa_proplist_new();
746 
747     dbus_message_iter_recurse(iter, &dict_iter);
748 
749     while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
750         dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
751 
752         dbus_message_iter_get_basic(&dict_entry_iter, &key);
753         dbus_message_iter_next(&dict_entry_iter);
754 
755         if (strlen(key) <= 0 || !pa_ascii_valid(key)) {
756             pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key: '%s'.", key);
757             goto fail;
758         }
759 
760         dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length);
761 
762         pa_assert(value_length >= 0);
763 
764         pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0);
765 
766         dbus_message_iter_next(&dict_iter);
767     }
768 
769     dbus_message_iter_next(iter);
770 
771     return proplist;
772 
773 fail:
774     if (proplist)
775         pa_proplist_free(proplist);
776 
777     return NULL;
778 }
779