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
38struct 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
45struct timeout_data {
46    pa_dbus_wrap_connection *connection;
47    DBusTimeout *timeout;
48};
49
50static 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 */
59static 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
78static 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 */
99static 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 */
123static 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 */
143static 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 */
161static 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 */
173static 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
186static 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 */
191static 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 */
215static 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 */
227static 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
244static 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
254pa_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
287pa_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
312void 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
328DBusConnection* 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
335int 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
355fail:
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
368void 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
380pa_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
403void 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
417void 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
424void 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
435const 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
449void 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
470void 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
481void 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
495static 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
513void 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. */
536static 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
554void 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
575void 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
590void 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
609void 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
621void 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
638void 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
652void 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
671void 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
703void 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
714void 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
727pa_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_se(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
773fail:
774    if (proplist)
775        pa_proplist_free(proplist);
776
777    return NULL;
778}
779