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_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
773 fail:
774 if (proplist)
775 pa_proplist_free(proplist);
776
777 return NULL;
778 }
779