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