Emeric Vigier | 2f62582 | 2012-08-06 11:09:52 -0400 | [diff] [blame] | 1 | /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
| 2 | /* signals.c Bus signal connection implementation |
| 3 | * |
| 4 | * Copyright (C) 2003, 2005 Red Hat, Inc. |
| 5 | * |
| 6 | * Licensed under the Academic Free License version 2.1 |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by |
| 10 | * the Free Software Foundation; either version 2 of the License, or |
| 11 | * (at your option) any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU General Public License |
| 19 | * along with this program; if not, write to the Free Software |
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 21 | * |
| 22 | */ |
| 23 | |
| 24 | #include <config.h> |
| 25 | #include "signals.h" |
| 26 | #include "services.h" |
| 27 | #include "utils.h" |
| 28 | #include <dbus/dbus-marshal-validate.h> |
| 29 | |
| 30 | struct BusMatchRule |
| 31 | { |
| 32 | int refcount; /**< reference count */ |
| 33 | |
| 34 | DBusConnection *matches_go_to; /**< Owner of the rule */ |
| 35 | |
| 36 | unsigned int flags; /**< BusMatchFlags */ |
| 37 | |
| 38 | int message_type; |
| 39 | char *interface; |
| 40 | char *member; |
| 41 | char *sender; |
| 42 | char *destination; |
| 43 | char *path; |
| 44 | |
| 45 | unsigned int *arg_lens; |
| 46 | char **args; |
| 47 | int args_len; |
| 48 | }; |
| 49 | |
| 50 | #define BUS_MATCH_ARG_IS_PATH 0x8000000u |
| 51 | |
| 52 | BusMatchRule* |
| 53 | bus_match_rule_new (DBusConnection *matches_go_to) |
| 54 | { |
| 55 | BusMatchRule *rule; |
| 56 | |
| 57 | rule = dbus_new0 (BusMatchRule, 1); |
| 58 | if (rule == NULL) |
| 59 | return NULL; |
| 60 | |
| 61 | rule->refcount = 1; |
| 62 | rule->matches_go_to = matches_go_to; |
| 63 | |
| 64 | #ifndef DBUS_BUILD_TESTS |
| 65 | _dbus_assert (rule->matches_go_to != NULL); |
| 66 | #endif |
| 67 | |
| 68 | return rule; |
| 69 | } |
| 70 | |
| 71 | BusMatchRule * |
| 72 | bus_match_rule_ref (BusMatchRule *rule) |
| 73 | { |
| 74 | _dbus_assert (rule->refcount > 0); |
| 75 | |
| 76 | rule->refcount += 1; |
| 77 | |
| 78 | return rule; |
| 79 | } |
| 80 | |
| 81 | void |
| 82 | bus_match_rule_unref (BusMatchRule *rule) |
| 83 | { |
| 84 | _dbus_assert (rule->refcount > 0); |
| 85 | |
| 86 | rule->refcount -= 1; |
| 87 | if (rule->refcount == 0) |
| 88 | { |
| 89 | dbus_free (rule->interface); |
| 90 | dbus_free (rule->member); |
| 91 | dbus_free (rule->sender); |
| 92 | dbus_free (rule->destination); |
| 93 | dbus_free (rule->path); |
| 94 | dbus_free (rule->arg_lens); |
| 95 | |
| 96 | /* can't use dbus_free_string_array() since there |
| 97 | * are embedded NULL |
| 98 | */ |
| 99 | if (rule->args) |
| 100 | { |
| 101 | int i; |
| 102 | |
| 103 | i = 0; |
| 104 | while (i < rule->args_len) |
| 105 | { |
| 106 | if (rule->args[i]) |
| 107 | dbus_free (rule->args[i]); |
| 108 | ++i; |
| 109 | } |
| 110 | |
| 111 | dbus_free (rule->args); |
| 112 | } |
| 113 | |
| 114 | dbus_free (rule); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | #ifdef DBUS_ENABLE_VERBOSE_MODE |
| 119 | /* Note this function does not do escaping, so it's only |
| 120 | * good for debug spew at the moment |
| 121 | */ |
| 122 | static char* |
| 123 | match_rule_to_string (BusMatchRule *rule) |
| 124 | { |
| 125 | DBusString str; |
| 126 | char *ret; |
| 127 | |
| 128 | if (!_dbus_string_init (&str)) |
| 129 | { |
| 130 | char *s; |
| 131 | while ((s = _dbus_strdup ("nomem")) == NULL) |
| 132 | ; /* only OK for debug spew... */ |
| 133 | return s; |
| 134 | } |
| 135 | |
| 136 | if (rule->flags & BUS_MATCH_MESSAGE_TYPE) |
| 137 | { |
| 138 | /* FIXME make type readable */ |
| 139 | if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type)) |
| 140 | goto nomem; |
| 141 | } |
| 142 | |
| 143 | if (rule->flags & BUS_MATCH_INTERFACE) |
| 144 | { |
| 145 | if (_dbus_string_get_length (&str) > 0) |
| 146 | { |
| 147 | if (!_dbus_string_append (&str, ",")) |
| 148 | goto nomem; |
| 149 | } |
| 150 | |
| 151 | if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface)) |
| 152 | goto nomem; |
| 153 | } |
| 154 | |
| 155 | if (rule->flags & BUS_MATCH_MEMBER) |
| 156 | { |
| 157 | if (_dbus_string_get_length (&str) > 0) |
| 158 | { |
| 159 | if (!_dbus_string_append (&str, ",")) |
| 160 | goto nomem; |
| 161 | } |
| 162 | |
| 163 | if (!_dbus_string_append_printf (&str, "member='%s'", rule->member)) |
| 164 | goto nomem; |
| 165 | } |
| 166 | |
| 167 | if (rule->flags & BUS_MATCH_PATH) |
| 168 | { |
| 169 | if (_dbus_string_get_length (&str) > 0) |
| 170 | { |
| 171 | if (!_dbus_string_append (&str, ",")) |
| 172 | goto nomem; |
| 173 | } |
| 174 | |
| 175 | if (!_dbus_string_append_printf (&str, "path='%s'", rule->path)) |
| 176 | goto nomem; |
| 177 | } |
| 178 | |
| 179 | if (rule->flags & BUS_MATCH_SENDER) |
| 180 | { |
| 181 | if (_dbus_string_get_length (&str) > 0) |
| 182 | { |
| 183 | if (!_dbus_string_append (&str, ",")) |
| 184 | goto nomem; |
| 185 | } |
| 186 | |
| 187 | if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender)) |
| 188 | goto nomem; |
| 189 | } |
| 190 | |
| 191 | if (rule->flags & BUS_MATCH_DESTINATION) |
| 192 | { |
| 193 | if (_dbus_string_get_length (&str) > 0) |
| 194 | { |
| 195 | if (!_dbus_string_append (&str, ",")) |
| 196 | goto nomem; |
| 197 | } |
| 198 | |
| 199 | if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination)) |
| 200 | goto nomem; |
| 201 | } |
| 202 | |
| 203 | if (rule->flags & BUS_MATCH_ARGS) |
| 204 | { |
| 205 | int i; |
| 206 | |
| 207 | _dbus_assert (rule->args != NULL); |
| 208 | |
| 209 | i = 0; |
| 210 | while (i < rule->args_len) |
| 211 | { |
| 212 | if (rule->args[i] != NULL) |
| 213 | { |
| 214 | dbus_bool_t is_path; |
| 215 | |
| 216 | if (_dbus_string_get_length (&str) > 0) |
| 217 | { |
| 218 | if (!_dbus_string_append (&str, ",")) |
| 219 | goto nomem; |
| 220 | } |
| 221 | |
| 222 | is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0; |
| 223 | |
| 224 | if (!_dbus_string_append_printf (&str, |
| 225 | "arg%d%s='%s'", |
| 226 | i, is_path ? "path" : "", |
| 227 | rule->args[i])) |
| 228 | goto nomem; |
| 229 | } |
| 230 | |
| 231 | ++i; |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | if (!_dbus_string_steal_data (&str, &ret)) |
| 236 | goto nomem; |
| 237 | |
| 238 | _dbus_string_free (&str); |
| 239 | return ret; |
| 240 | |
| 241 | nomem: |
| 242 | _dbus_string_free (&str); |
| 243 | { |
| 244 | char *s; |
| 245 | while ((s = _dbus_strdup ("nomem")) == NULL) |
| 246 | ; /* only OK for debug spew... */ |
| 247 | return s; |
| 248 | } |
| 249 | } |
| 250 | #endif /* DBUS_ENABLE_VERBOSE_MODE */ |
| 251 | |
| 252 | dbus_bool_t |
| 253 | bus_match_rule_set_message_type (BusMatchRule *rule, |
| 254 | int type) |
| 255 | { |
| 256 | rule->flags |= BUS_MATCH_MESSAGE_TYPE; |
| 257 | |
| 258 | rule->message_type = type; |
| 259 | |
| 260 | return TRUE; |
| 261 | } |
| 262 | |
| 263 | dbus_bool_t |
| 264 | bus_match_rule_set_interface (BusMatchRule *rule, |
| 265 | const char *interface) |
| 266 | { |
| 267 | char *new; |
| 268 | |
| 269 | _dbus_assert (interface != NULL); |
| 270 | |
| 271 | new = _dbus_strdup (interface); |
| 272 | if (new == NULL) |
| 273 | return FALSE; |
| 274 | |
| 275 | rule->flags |= BUS_MATCH_INTERFACE; |
| 276 | dbus_free (rule->interface); |
| 277 | rule->interface = new; |
| 278 | |
| 279 | return TRUE; |
| 280 | } |
| 281 | |
| 282 | dbus_bool_t |
| 283 | bus_match_rule_set_member (BusMatchRule *rule, |
| 284 | const char *member) |
| 285 | { |
| 286 | char *new; |
| 287 | |
| 288 | _dbus_assert (member != NULL); |
| 289 | |
| 290 | new = _dbus_strdup (member); |
| 291 | if (new == NULL) |
| 292 | return FALSE; |
| 293 | |
| 294 | rule->flags |= BUS_MATCH_MEMBER; |
| 295 | dbus_free (rule->member); |
| 296 | rule->member = new; |
| 297 | |
| 298 | return TRUE; |
| 299 | } |
| 300 | |
| 301 | dbus_bool_t |
| 302 | bus_match_rule_set_sender (BusMatchRule *rule, |
| 303 | const char *sender) |
| 304 | { |
| 305 | char *new; |
| 306 | |
| 307 | _dbus_assert (sender != NULL); |
| 308 | |
| 309 | new = _dbus_strdup (sender); |
| 310 | if (new == NULL) |
| 311 | return FALSE; |
| 312 | |
| 313 | rule->flags |= BUS_MATCH_SENDER; |
| 314 | dbus_free (rule->sender); |
| 315 | rule->sender = new; |
| 316 | |
| 317 | return TRUE; |
| 318 | } |
| 319 | |
| 320 | dbus_bool_t |
| 321 | bus_match_rule_set_destination (BusMatchRule *rule, |
| 322 | const char *destination) |
| 323 | { |
| 324 | char *new; |
| 325 | |
| 326 | _dbus_assert (destination != NULL); |
| 327 | |
| 328 | new = _dbus_strdup (destination); |
| 329 | if (new == NULL) |
| 330 | return FALSE; |
| 331 | |
| 332 | rule->flags |= BUS_MATCH_DESTINATION; |
| 333 | dbus_free (rule->destination); |
| 334 | rule->destination = new; |
| 335 | |
| 336 | return TRUE; |
| 337 | } |
| 338 | |
| 339 | dbus_bool_t |
| 340 | bus_match_rule_set_path (BusMatchRule *rule, |
| 341 | const char *path) |
| 342 | { |
| 343 | char *new; |
| 344 | |
| 345 | _dbus_assert (path != NULL); |
| 346 | |
| 347 | new = _dbus_strdup (path); |
| 348 | if (new == NULL) |
| 349 | return FALSE; |
| 350 | |
| 351 | rule->flags |= BUS_MATCH_PATH; |
| 352 | dbus_free (rule->path); |
| 353 | rule->path = new; |
| 354 | |
| 355 | return TRUE; |
| 356 | } |
| 357 | |
| 358 | dbus_bool_t |
| 359 | bus_match_rule_set_arg (BusMatchRule *rule, |
| 360 | int arg, |
| 361 | const DBusString *value, |
| 362 | dbus_bool_t is_path) |
| 363 | { |
| 364 | int length; |
| 365 | char *new; |
| 366 | |
| 367 | _dbus_assert (value != NULL); |
| 368 | |
| 369 | /* args_len is the number of args not including null termination |
| 370 | * in the char** |
| 371 | */ |
| 372 | if (arg >= rule->args_len) |
| 373 | { |
| 374 | unsigned int *new_arg_lens; |
| 375 | char **new_args; |
| 376 | int new_args_len; |
| 377 | int i; |
| 378 | |
| 379 | new_args_len = arg + 1; |
| 380 | |
| 381 | /* add another + 1 here for null termination */ |
| 382 | new_args = dbus_realloc (rule->args, |
| 383 | sizeof (char *) * (new_args_len + 1)); |
| 384 | if (new_args == NULL) |
| 385 | return FALSE; |
| 386 | |
| 387 | /* NULL the new slots */ |
| 388 | i = rule->args_len; |
| 389 | while (i <= new_args_len) /* <= for null termination */ |
| 390 | { |
| 391 | new_args[i] = NULL; |
| 392 | ++i; |
| 393 | } |
| 394 | |
| 395 | rule->args = new_args; |
| 396 | |
| 397 | /* and now add to the lengths */ |
| 398 | new_arg_lens = dbus_realloc (rule->arg_lens, |
| 399 | sizeof (int) * (new_args_len + 1)); |
| 400 | |
| 401 | if (new_arg_lens == NULL) |
| 402 | return FALSE; |
| 403 | |
| 404 | /* zero the new slots */ |
| 405 | i = rule->args_len; |
| 406 | while (i <= new_args_len) /* <= for null termination */ |
| 407 | { |
| 408 | new_arg_lens[i] = 0; |
| 409 | ++i; |
| 410 | } |
| 411 | |
| 412 | rule->arg_lens = new_arg_lens; |
| 413 | rule->args_len = new_args_len; |
| 414 | } |
| 415 | |
| 416 | length = _dbus_string_get_length (value); |
| 417 | if (!_dbus_string_copy_data (value, &new)) |
| 418 | return FALSE; |
| 419 | |
| 420 | rule->flags |= BUS_MATCH_ARGS; |
| 421 | |
| 422 | dbus_free (rule->args[arg]); |
| 423 | rule->arg_lens[arg] = length; |
| 424 | rule->args[arg] = new; |
| 425 | |
| 426 | if (is_path) |
| 427 | rule->arg_lens[arg] |= BUS_MATCH_ARG_IS_PATH; |
| 428 | |
| 429 | /* NULL termination didn't get busted */ |
| 430 | _dbus_assert (rule->args[rule->args_len] == NULL); |
| 431 | _dbus_assert (rule->arg_lens[rule->args_len] == 0); |
| 432 | |
| 433 | return TRUE; |
| 434 | } |
| 435 | |
| 436 | #define ISWHITE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r')) |
| 437 | |
| 438 | static dbus_bool_t |
| 439 | find_key (const DBusString *str, |
| 440 | int start, |
| 441 | DBusString *key, |
| 442 | int *value_pos, |
| 443 | DBusError *error) |
| 444 | { |
| 445 | const char *p; |
| 446 | const char *s; |
| 447 | const char *key_start; |
| 448 | const char *key_end; |
| 449 | |
| 450 | _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| 451 | |
| 452 | s = _dbus_string_get_const_data (str); |
| 453 | |
| 454 | p = s + start; |
| 455 | |
| 456 | while (*p && ISWHITE (*p)) |
| 457 | ++p; |
| 458 | |
| 459 | key_start = p; |
| 460 | |
| 461 | while (*p && *p != '=' && !ISWHITE (*p)) |
| 462 | ++p; |
| 463 | |
| 464 | key_end = p; |
| 465 | |
| 466 | while (*p && ISWHITE (*p)) |
| 467 | ++p; |
| 468 | |
| 469 | if (key_start == key_end) |
| 470 | { |
| 471 | /* Empty match rules or trailing whitespace are OK */ |
| 472 | *value_pos = p - s; |
| 473 | return TRUE; |
| 474 | } |
| 475 | |
| 476 | if (*p != '=') |
| 477 | { |
| 478 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 479 | "Match rule has a key with no subsequent '=' character"); |
| 480 | return FALSE; |
| 481 | } |
| 482 | ++p; |
| 483 | |
| 484 | if (!_dbus_string_append_len (key, key_start, key_end - key_start)) |
| 485 | { |
| 486 | BUS_SET_OOM (error); |
| 487 | return FALSE; |
| 488 | } |
| 489 | |
| 490 | *value_pos = p - s; |
| 491 | |
| 492 | return TRUE; |
| 493 | } |
| 494 | |
| 495 | static dbus_bool_t |
| 496 | find_value (const DBusString *str, |
| 497 | int start, |
| 498 | const char *key, |
| 499 | DBusString *value, |
| 500 | int *value_end, |
| 501 | DBusError *error) |
| 502 | { |
| 503 | const char *p; |
| 504 | const char *s; |
| 505 | char quote_char; |
| 506 | int orig_len; |
| 507 | |
| 508 | _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| 509 | |
| 510 | orig_len = _dbus_string_get_length (value); |
| 511 | |
| 512 | s = _dbus_string_get_const_data (str); |
| 513 | |
| 514 | p = s + start; |
| 515 | |
| 516 | quote_char = '\0'; |
| 517 | |
| 518 | while (*p) |
| 519 | { |
| 520 | if (quote_char == '\0') |
| 521 | { |
| 522 | switch (*p) |
| 523 | { |
| 524 | case '\0': |
| 525 | goto done; |
| 526 | |
| 527 | case '\'': |
| 528 | quote_char = '\''; |
| 529 | goto next; |
| 530 | |
| 531 | case ',': |
| 532 | ++p; |
| 533 | goto done; |
| 534 | |
| 535 | case '\\': |
| 536 | quote_char = '\\'; |
| 537 | goto next; |
| 538 | |
| 539 | default: |
| 540 | if (!_dbus_string_append_byte (value, *p)) |
| 541 | { |
| 542 | BUS_SET_OOM (error); |
| 543 | goto failed; |
| 544 | } |
| 545 | } |
| 546 | } |
| 547 | else if (quote_char == '\\') |
| 548 | { |
| 549 | /* \ only counts as an escape if escaping a quote mark */ |
| 550 | if (*p != '\'') |
| 551 | { |
| 552 | if (!_dbus_string_append_byte (value, '\\')) |
| 553 | { |
| 554 | BUS_SET_OOM (error); |
| 555 | goto failed; |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | if (!_dbus_string_append_byte (value, *p)) |
| 560 | { |
| 561 | BUS_SET_OOM (error); |
| 562 | goto failed; |
| 563 | } |
| 564 | |
| 565 | quote_char = '\0'; |
| 566 | } |
| 567 | else |
| 568 | { |
| 569 | _dbus_assert (quote_char == '\''); |
| 570 | |
| 571 | if (*p == '\'') |
| 572 | { |
| 573 | quote_char = '\0'; |
| 574 | } |
| 575 | else |
| 576 | { |
| 577 | if (!_dbus_string_append_byte (value, *p)) |
| 578 | { |
| 579 | BUS_SET_OOM (error); |
| 580 | goto failed; |
| 581 | } |
| 582 | } |
| 583 | } |
| 584 | |
| 585 | next: |
| 586 | ++p; |
| 587 | } |
| 588 | |
| 589 | done: |
| 590 | |
| 591 | if (quote_char == '\\') |
| 592 | { |
| 593 | if (!_dbus_string_append_byte (value, '\\')) |
| 594 | { |
| 595 | BUS_SET_OOM (error); |
| 596 | goto failed; |
| 597 | } |
| 598 | } |
| 599 | else if (quote_char == '\'') |
| 600 | { |
| 601 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 602 | "Unbalanced quotation marks in match rule"); |
| 603 | goto failed; |
| 604 | } |
| 605 | else |
| 606 | _dbus_assert (quote_char == '\0'); |
| 607 | |
| 608 | /* Zero-length values are allowed */ |
| 609 | |
| 610 | *value_end = p - s; |
| 611 | |
| 612 | return TRUE; |
| 613 | |
| 614 | failed: |
| 615 | _DBUS_ASSERT_ERROR_IS_SET (error); |
| 616 | _dbus_string_set_length (value, orig_len); |
| 617 | return FALSE; |
| 618 | } |
| 619 | |
| 620 | /* duplicates aren't allowed so the real legitimate max is only 6 or |
| 621 | * so. Leaving extra so we don't have to bother to update it. |
| 622 | * FIXME this is sort of busted now with arg matching, but we let |
| 623 | * you match on up to 10 args for now |
| 624 | */ |
| 625 | #define MAX_RULE_TOKENS 16 |
| 626 | |
| 627 | /* this is slightly too high level to be termed a "token" |
| 628 | * but let's not be pedantic. |
| 629 | */ |
| 630 | typedef struct |
| 631 | { |
| 632 | char *key; |
| 633 | char *value; |
| 634 | } RuleToken; |
| 635 | |
| 636 | static dbus_bool_t |
| 637 | tokenize_rule (const DBusString *rule_text, |
| 638 | RuleToken tokens[MAX_RULE_TOKENS], |
| 639 | DBusError *error) |
| 640 | { |
| 641 | int i; |
| 642 | int pos; |
| 643 | DBusString key; |
| 644 | DBusString value; |
| 645 | dbus_bool_t retval; |
| 646 | |
| 647 | retval = FALSE; |
| 648 | |
| 649 | if (!_dbus_string_init (&key)) |
| 650 | { |
| 651 | BUS_SET_OOM (error); |
| 652 | return FALSE; |
| 653 | } |
| 654 | |
| 655 | if (!_dbus_string_init (&value)) |
| 656 | { |
| 657 | _dbus_string_free (&key); |
| 658 | BUS_SET_OOM (error); |
| 659 | return FALSE; |
| 660 | } |
| 661 | |
| 662 | i = 0; |
| 663 | pos = 0; |
| 664 | while (i < MAX_RULE_TOKENS && |
| 665 | pos < _dbus_string_get_length (rule_text)) |
| 666 | { |
| 667 | _dbus_assert (tokens[i].key == NULL); |
| 668 | _dbus_assert (tokens[i].value == NULL); |
| 669 | |
| 670 | if (!find_key (rule_text, pos, &key, &pos, error)) |
| 671 | goto out; |
| 672 | |
| 673 | if (_dbus_string_get_length (&key) == 0) |
| 674 | goto next; |
| 675 | |
| 676 | if (!_dbus_string_steal_data (&key, &tokens[i].key)) |
| 677 | { |
| 678 | BUS_SET_OOM (error); |
| 679 | goto out; |
| 680 | } |
| 681 | |
| 682 | if (!find_value (rule_text, pos, tokens[i].key, &value, &pos, error)) |
| 683 | goto out; |
| 684 | |
| 685 | if (!_dbus_string_steal_data (&value, &tokens[i].value)) |
| 686 | { |
| 687 | BUS_SET_OOM (error); |
| 688 | goto out; |
| 689 | } |
| 690 | |
| 691 | next: |
| 692 | ++i; |
| 693 | } |
| 694 | |
| 695 | retval = TRUE; |
| 696 | |
| 697 | out: |
| 698 | if (!retval) |
| 699 | { |
| 700 | i = 0; |
| 701 | while (tokens[i].key || tokens[i].value) |
| 702 | { |
| 703 | dbus_free (tokens[i].key); |
| 704 | dbus_free (tokens[i].value); |
| 705 | tokens[i].key = NULL; |
| 706 | tokens[i].value = NULL; |
| 707 | ++i; |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | _dbus_string_free (&key); |
| 712 | _dbus_string_free (&value); |
| 713 | |
| 714 | return retval; |
| 715 | } |
| 716 | |
| 717 | static dbus_bool_t |
| 718 | bus_match_rule_parse_arg_match (BusMatchRule *rule, |
| 719 | const char *key, |
| 720 | const DBusString *value, |
| 721 | DBusError *error) |
| 722 | { |
| 723 | dbus_bool_t is_path; |
| 724 | DBusString key_str; |
| 725 | unsigned long arg; |
| 726 | int length; |
| 727 | int end; |
| 728 | |
| 729 | /* For now, arg0='foo' always implies that 'foo' is a |
| 730 | * DBUS_TYPE_STRING. Someday we could add an arg0type='int32' thing |
| 731 | * if we wanted, which would specify another type, in which case |
| 732 | * arg0='5' would have the 5 parsed as an int rather than string. |
| 733 | */ |
| 734 | |
| 735 | /* First we need to parse arg0 = 0, arg27 = 27 */ |
| 736 | |
| 737 | _dbus_string_init_const (&key_str, key); |
| 738 | length = _dbus_string_get_length (&key_str); |
| 739 | |
| 740 | if (_dbus_string_get_length (&key_str) < 4) |
| 741 | { |
| 742 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 743 | "Key '%s' in match rule starts with 'arg' but lacks an arg number. Should be 'arg0' or 'arg7' for example.\n", key); |
| 744 | goto failed; |
| 745 | } |
| 746 | |
| 747 | if (!_dbus_string_parse_uint (&key_str, 3, &arg, &end)) |
| 748 | { |
| 749 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 750 | "Key '%s' in match rule starts with 'arg' but could not parse arg number. Should be 'arg0' or 'arg7' for example.\n", key); |
| 751 | goto failed; |
| 752 | } |
| 753 | |
| 754 | if (end != length && |
| 755 | ((end + 4) != length || |
| 756 | !_dbus_string_ends_with_c_str (&key_str, "path"))) |
| 757 | { |
| 758 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 759 | "Key '%s' in match rule contains junk after argument number. Only 'path' is optionally valid ('arg0path' for example).\n", key); |
| 760 | goto failed; |
| 761 | } |
| 762 | |
| 763 | is_path = end != length; |
| 764 | |
| 765 | /* If we didn't check this we could allocate a huge amount of RAM */ |
| 766 | if (arg > DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER) |
| 767 | { |
| 768 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 769 | "Key '%s' in match rule has arg number %lu but the maximum is %d.\n", key, (unsigned long) arg, DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER); |
| 770 | goto failed; |
| 771 | } |
| 772 | |
| 773 | if ((rule->flags & BUS_MATCH_ARGS) && |
| 774 | rule->args_len > (int) arg && |
| 775 | rule->args[arg] != NULL) |
| 776 | { |
| 777 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 778 | "Argument %d matched more than once in match rule\n", key); |
| 779 | goto failed; |
| 780 | } |
| 781 | |
| 782 | if (!bus_match_rule_set_arg (rule, arg, value, is_path)) |
| 783 | { |
| 784 | BUS_SET_OOM (error); |
| 785 | goto failed; |
| 786 | } |
| 787 | |
| 788 | return TRUE; |
| 789 | |
| 790 | failed: |
| 791 | _DBUS_ASSERT_ERROR_IS_SET (error); |
| 792 | return FALSE; |
| 793 | } |
| 794 | |
| 795 | /* |
| 796 | * The format is comma-separated with strings quoted with single quotes |
| 797 | * as for the shell (to escape a literal single quote, use '\''). |
| 798 | * |
| 799 | * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo', |
| 800 | * path='/bar/foo',destination=':452345.34' |
| 801 | * |
| 802 | */ |
| 803 | BusMatchRule* |
| 804 | bus_match_rule_parse (DBusConnection *matches_go_to, |
| 805 | const DBusString *rule_text, |
| 806 | DBusError *error) |
| 807 | { |
| 808 | BusMatchRule *rule; |
| 809 | RuleToken tokens[MAX_RULE_TOKENS+1]; /* NULL termination + 1 */ |
| 810 | int i; |
| 811 | |
| 812 | _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| 813 | |
| 814 | if (_dbus_string_get_length (rule_text) > DBUS_MAXIMUM_MATCH_RULE_LENGTH) |
| 815 | { |
| 816 | dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, |
| 817 | "Match rule text is %d bytes, maximum is %d", |
| 818 | _dbus_string_get_length (rule_text), |
| 819 | DBUS_MAXIMUM_MATCH_RULE_LENGTH); |
| 820 | return NULL; |
| 821 | } |
| 822 | |
| 823 | memset (tokens, '\0', sizeof (tokens)); |
| 824 | |
| 825 | rule = bus_match_rule_new (matches_go_to); |
| 826 | if (rule == NULL) |
| 827 | { |
| 828 | BUS_SET_OOM (error); |
| 829 | goto failed; |
| 830 | } |
| 831 | |
| 832 | if (!tokenize_rule (rule_text, tokens, error)) |
| 833 | goto failed; |
| 834 | |
| 835 | i = 0; |
| 836 | while (tokens[i].key != NULL) |
| 837 | { |
| 838 | DBusString tmp_str; |
| 839 | int len; |
| 840 | const char *key = tokens[i].key; |
| 841 | const char *value = tokens[i].value; |
| 842 | |
| 843 | _dbus_string_init_const (&tmp_str, value); |
| 844 | len = _dbus_string_get_length (&tmp_str); |
| 845 | |
| 846 | if (strcmp (key, "type") == 0) |
| 847 | { |
| 848 | int t; |
| 849 | |
| 850 | if (rule->flags & BUS_MATCH_MESSAGE_TYPE) |
| 851 | { |
| 852 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 853 | "Key %s specified twice in match rule\n", key); |
| 854 | goto failed; |
| 855 | } |
| 856 | |
| 857 | t = dbus_message_type_from_string (value); |
| 858 | |
| 859 | if (t == DBUS_MESSAGE_TYPE_INVALID) |
| 860 | { |
| 861 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 862 | "Invalid message type (%s) in match rule\n", value); |
| 863 | goto failed; |
| 864 | } |
| 865 | |
| 866 | if (!bus_match_rule_set_message_type (rule, t)) |
| 867 | { |
| 868 | BUS_SET_OOM (error); |
| 869 | goto failed; |
| 870 | } |
| 871 | } |
| 872 | else if (strcmp (key, "sender") == 0) |
| 873 | { |
| 874 | if (rule->flags & BUS_MATCH_SENDER) |
| 875 | { |
| 876 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 877 | "Key %s specified twice in match rule\n", key); |
| 878 | goto failed; |
| 879 | } |
| 880 | |
| 881 | if (!_dbus_validate_bus_name (&tmp_str, 0, len)) |
| 882 | { |
| 883 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 884 | "Sender name '%s' is invalid\n", value); |
| 885 | goto failed; |
| 886 | } |
| 887 | |
| 888 | if (!bus_match_rule_set_sender (rule, value)) |
| 889 | { |
| 890 | BUS_SET_OOM (error); |
| 891 | goto failed; |
| 892 | } |
| 893 | } |
| 894 | else if (strcmp (key, "interface") == 0) |
| 895 | { |
| 896 | if (rule->flags & BUS_MATCH_INTERFACE) |
| 897 | { |
| 898 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 899 | "Key %s specified twice in match rule\n", key); |
| 900 | goto failed; |
| 901 | } |
| 902 | |
| 903 | if (!_dbus_validate_interface (&tmp_str, 0, len)) |
| 904 | { |
| 905 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 906 | "Interface name '%s' is invalid\n", value); |
| 907 | goto failed; |
| 908 | } |
| 909 | |
| 910 | if (!bus_match_rule_set_interface (rule, value)) |
| 911 | { |
| 912 | BUS_SET_OOM (error); |
| 913 | goto failed; |
| 914 | } |
| 915 | } |
| 916 | else if (strcmp (key, "member") == 0) |
| 917 | { |
| 918 | if (rule->flags & BUS_MATCH_MEMBER) |
| 919 | { |
| 920 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 921 | "Key %s specified twice in match rule\n", key); |
| 922 | goto failed; |
| 923 | } |
| 924 | |
| 925 | if (!_dbus_validate_member (&tmp_str, 0, len)) |
| 926 | { |
| 927 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 928 | "Member name '%s' is invalid\n", value); |
| 929 | goto failed; |
| 930 | } |
| 931 | |
| 932 | if (!bus_match_rule_set_member (rule, value)) |
| 933 | { |
| 934 | BUS_SET_OOM (error); |
| 935 | goto failed; |
| 936 | } |
| 937 | } |
| 938 | else if (strcmp (key, "path") == 0) |
| 939 | { |
| 940 | if (rule->flags & BUS_MATCH_PATH) |
| 941 | { |
| 942 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 943 | "Key %s specified twice in match rule\n", key); |
| 944 | goto failed; |
| 945 | } |
| 946 | |
| 947 | if (!_dbus_validate_path (&tmp_str, 0, len)) |
| 948 | { |
| 949 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 950 | "Path '%s' is invalid\n", value); |
| 951 | goto failed; |
| 952 | } |
| 953 | |
| 954 | if (!bus_match_rule_set_path (rule, value)) |
| 955 | { |
| 956 | BUS_SET_OOM (error); |
| 957 | goto failed; |
| 958 | } |
| 959 | } |
| 960 | else if (strcmp (key, "destination") == 0) |
| 961 | { |
| 962 | if (rule->flags & BUS_MATCH_DESTINATION) |
| 963 | { |
| 964 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 965 | "Key %s specified twice in match rule\n", key); |
| 966 | goto failed; |
| 967 | } |
| 968 | |
| 969 | if (!_dbus_validate_bus_name (&tmp_str, 0, len)) |
| 970 | { |
| 971 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 972 | "Destination name '%s' is invalid\n", value); |
| 973 | goto failed; |
| 974 | } |
| 975 | |
| 976 | if (!bus_match_rule_set_destination (rule, value)) |
| 977 | { |
| 978 | BUS_SET_OOM (error); |
| 979 | goto failed; |
| 980 | } |
| 981 | } |
| 982 | else if (strncmp (key, "arg", 3) == 0) |
| 983 | { |
| 984 | if (!bus_match_rule_parse_arg_match (rule, key, &tmp_str, error)) |
| 985 | goto failed; |
| 986 | } |
| 987 | else |
| 988 | { |
| 989 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, |
| 990 | "Unknown key \"%s\" in match rule", |
| 991 | key); |
| 992 | goto failed; |
| 993 | } |
| 994 | |
| 995 | ++i; |
| 996 | } |
| 997 | |
| 998 | |
| 999 | goto out; |
| 1000 | |
| 1001 | failed: |
| 1002 | _DBUS_ASSERT_ERROR_IS_SET (error); |
| 1003 | if (rule) |
| 1004 | { |
| 1005 | bus_match_rule_unref (rule); |
| 1006 | rule = NULL; |
| 1007 | } |
| 1008 | |
| 1009 | out: |
| 1010 | |
| 1011 | i = 0; |
| 1012 | while (tokens[i].key || tokens[i].value) |
| 1013 | { |
| 1014 | _dbus_assert (i < MAX_RULE_TOKENS); |
| 1015 | dbus_free (tokens[i].key); |
| 1016 | dbus_free (tokens[i].value); |
| 1017 | ++i; |
| 1018 | } |
| 1019 | |
| 1020 | return rule; |
| 1021 | } |
| 1022 | |
| 1023 | typedef struct RulePool RulePool; |
| 1024 | struct RulePool |
| 1025 | { |
| 1026 | /* Maps non-NULL interface names to non-NULL (DBusList **)s */ |
| 1027 | DBusHashTable *rules_by_iface; |
| 1028 | |
| 1029 | /* List of BusMatchRules which don't specify an interface */ |
| 1030 | DBusList *rules_without_iface; |
| 1031 | }; |
| 1032 | |
| 1033 | struct BusMatchmaker |
| 1034 | { |
| 1035 | int refcount; |
| 1036 | |
| 1037 | /* Pools of rules, grouped by the type of message they match. 0 |
| 1038 | * (DBUS_MESSAGE_TYPE_INVALID) represents rules that do not specify a message |
| 1039 | * type. |
| 1040 | */ |
| 1041 | RulePool rules_by_type[DBUS_NUM_MESSAGE_TYPES]; |
| 1042 | }; |
| 1043 | |
| 1044 | static void |
| 1045 | rule_list_free (DBusList **rules) |
| 1046 | { |
| 1047 | while (*rules != NULL) |
| 1048 | { |
| 1049 | BusMatchRule *rule; |
| 1050 | |
| 1051 | rule = (*rules)->data; |
| 1052 | bus_match_rule_unref (rule); |
| 1053 | _dbus_list_remove_link (rules, *rules); |
| 1054 | } |
| 1055 | } |
| 1056 | |
| 1057 | static void |
| 1058 | rule_list_ptr_free (DBusList **list) |
| 1059 | { |
| 1060 | /* We have to cope with NULL because the hash table frees the "existing" |
| 1061 | * value (which is NULL) when creating a new table entry... |
| 1062 | */ |
| 1063 | if (list != NULL) |
| 1064 | { |
| 1065 | rule_list_free (list); |
| 1066 | dbus_free (list); |
| 1067 | } |
| 1068 | } |
| 1069 | |
| 1070 | BusMatchmaker* |
| 1071 | bus_matchmaker_new (void) |
| 1072 | { |
| 1073 | BusMatchmaker *matchmaker; |
| 1074 | int i; |
| 1075 | |
| 1076 | matchmaker = dbus_new0 (BusMatchmaker, 1); |
| 1077 | if (matchmaker == NULL) |
| 1078 | return NULL; |
| 1079 | |
| 1080 | matchmaker->refcount = 1; |
| 1081 | |
| 1082 | for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++) |
| 1083 | { |
| 1084 | RulePool *p = matchmaker->rules_by_type + i; |
| 1085 | |
| 1086 | p->rules_by_iface = _dbus_hash_table_new (DBUS_HASH_STRING, |
| 1087 | dbus_free, (DBusFreeFunction) rule_list_ptr_free); |
| 1088 | |
| 1089 | if (p->rules_by_iface == NULL) |
| 1090 | goto nomem; |
| 1091 | } |
| 1092 | |
| 1093 | return matchmaker; |
| 1094 | |
| 1095 | nomem: |
| 1096 | for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++) |
| 1097 | { |
| 1098 | RulePool *p = matchmaker->rules_by_type + i; |
| 1099 | |
| 1100 | if (p->rules_by_iface == NULL) |
| 1101 | break; |
| 1102 | else |
| 1103 | _dbus_hash_table_unref (p->rules_by_iface); |
| 1104 | } |
| 1105 | |
| 1106 | return NULL; |
| 1107 | } |
| 1108 | |
| 1109 | static DBusList ** |
| 1110 | bus_matchmaker_get_rules (BusMatchmaker *matchmaker, |
| 1111 | int message_type, |
| 1112 | const char *interface, |
| 1113 | dbus_bool_t create) |
| 1114 | { |
| 1115 | RulePool *p; |
| 1116 | |
| 1117 | _dbus_assert (message_type >= 0); |
| 1118 | _dbus_assert (message_type < DBUS_NUM_MESSAGE_TYPES); |
| 1119 | |
| 1120 | _dbus_verbose ("Looking up rules for message_type %d, interface %s\n", |
| 1121 | message_type, |
| 1122 | interface != NULL ? interface : "<null>"); |
| 1123 | |
| 1124 | p = matchmaker->rules_by_type + message_type; |
| 1125 | |
| 1126 | if (interface == NULL) |
| 1127 | { |
| 1128 | return &p->rules_without_iface; |
| 1129 | } |
| 1130 | else |
| 1131 | { |
| 1132 | DBusList **list; |
| 1133 | |
| 1134 | list = _dbus_hash_table_lookup_string (p->rules_by_iface, interface); |
| 1135 | |
| 1136 | if (list == NULL && create) |
| 1137 | { |
| 1138 | char *dupped_interface; |
| 1139 | |
| 1140 | list = dbus_new0 (DBusList *, 1); |
| 1141 | if (list == NULL) |
| 1142 | return NULL; |
| 1143 | |
| 1144 | dupped_interface = _dbus_strdup (interface); |
| 1145 | if (dupped_interface == NULL) |
| 1146 | { |
| 1147 | dbus_free (list); |
| 1148 | return NULL; |
| 1149 | } |
| 1150 | |
| 1151 | _dbus_verbose ("Adding list for type %d, iface %s\n", message_type, |
| 1152 | interface); |
| 1153 | |
| 1154 | if (!_dbus_hash_table_insert_string (p->rules_by_iface, |
| 1155 | dupped_interface, list)) |
| 1156 | { |
| 1157 | dbus_free (list); |
| 1158 | dbus_free (dupped_interface); |
| 1159 | return NULL; |
| 1160 | } |
| 1161 | } |
| 1162 | |
| 1163 | return list; |
| 1164 | } |
| 1165 | } |
| 1166 | |
| 1167 | static void |
| 1168 | bus_matchmaker_gc_rules (BusMatchmaker *matchmaker, |
| 1169 | int message_type, |
| 1170 | const char *interface, |
| 1171 | DBusList **rules) |
| 1172 | { |
| 1173 | RulePool *p; |
| 1174 | |
| 1175 | if (interface == NULL) |
| 1176 | return; |
| 1177 | |
| 1178 | if (*rules != NULL) |
| 1179 | return; |
| 1180 | |
| 1181 | _dbus_verbose ("GCing HT entry for message_type %u, interface %s\n", |
| 1182 | message_type, interface); |
| 1183 | |
| 1184 | p = matchmaker->rules_by_type + message_type; |
| 1185 | |
| 1186 | _dbus_assert (_dbus_hash_table_lookup_string (p->rules_by_iface, interface) |
| 1187 | == rules); |
| 1188 | |
| 1189 | _dbus_hash_table_remove_string (p->rules_by_iface, interface); |
| 1190 | } |
| 1191 | |
| 1192 | BusMatchmaker * |
| 1193 | bus_matchmaker_ref (BusMatchmaker *matchmaker) |
| 1194 | { |
| 1195 | _dbus_assert (matchmaker->refcount > 0); |
| 1196 | |
| 1197 | matchmaker->refcount += 1; |
| 1198 | |
| 1199 | return matchmaker; |
| 1200 | } |
| 1201 | |
| 1202 | void |
| 1203 | bus_matchmaker_unref (BusMatchmaker *matchmaker) |
| 1204 | { |
| 1205 | _dbus_assert (matchmaker->refcount > 0); |
| 1206 | |
| 1207 | matchmaker->refcount -= 1; |
| 1208 | if (matchmaker->refcount == 0) |
| 1209 | { |
| 1210 | int i; |
| 1211 | |
| 1212 | for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++) |
| 1213 | { |
| 1214 | RulePool *p = matchmaker->rules_by_type + i; |
| 1215 | |
| 1216 | _dbus_hash_table_unref (p->rules_by_iface); |
| 1217 | rule_list_free (&p->rules_without_iface); |
| 1218 | } |
| 1219 | |
| 1220 | dbus_free (matchmaker); |
| 1221 | } |
| 1222 | } |
| 1223 | |
| 1224 | /* The rule can't be modified after it's added. */ |
| 1225 | dbus_bool_t |
| 1226 | bus_matchmaker_add_rule (BusMatchmaker *matchmaker, |
| 1227 | BusMatchRule *rule) |
| 1228 | { |
| 1229 | DBusList **rules; |
| 1230 | |
| 1231 | _dbus_assert (bus_connection_is_active (rule->matches_go_to)); |
| 1232 | |
| 1233 | _dbus_verbose ("Adding rule with message_type %d, interface %s\n", |
| 1234 | rule->message_type, |
| 1235 | rule->interface != NULL ? rule->interface : "<null>"); |
| 1236 | |
| 1237 | rules = bus_matchmaker_get_rules (matchmaker, rule->message_type, |
| 1238 | rule->interface, TRUE); |
| 1239 | |
| 1240 | if (rules == NULL) |
| 1241 | return FALSE; |
| 1242 | |
| 1243 | if (!_dbus_list_append (rules, rule)) |
| 1244 | return FALSE; |
| 1245 | |
| 1246 | if (!bus_connection_add_match_rule (rule->matches_go_to, rule)) |
| 1247 | { |
| 1248 | _dbus_list_remove_last (rules, rule); |
| 1249 | bus_matchmaker_gc_rules (matchmaker, rule->message_type, |
| 1250 | rule->interface, rules); |
| 1251 | return FALSE; |
| 1252 | } |
| 1253 | |
| 1254 | bus_match_rule_ref (rule); |
| 1255 | |
| 1256 | #ifdef DBUS_ENABLE_VERBOSE_MODE |
| 1257 | { |
| 1258 | char *s = match_rule_to_string (rule); |
| 1259 | |
| 1260 | _dbus_verbose ("Added match rule %s to connection %p\n", |
| 1261 | s, rule->matches_go_to); |
| 1262 | dbus_free (s); |
| 1263 | } |
| 1264 | #endif |
| 1265 | |
| 1266 | return TRUE; |
| 1267 | } |
| 1268 | |
| 1269 | static dbus_bool_t |
| 1270 | match_rule_equal (BusMatchRule *a, |
| 1271 | BusMatchRule *b) |
| 1272 | { |
| 1273 | if (a->flags != b->flags) |
| 1274 | return FALSE; |
| 1275 | |
| 1276 | if (a->matches_go_to != b->matches_go_to) |
| 1277 | return FALSE; |
| 1278 | |
| 1279 | if ((a->flags & BUS_MATCH_MESSAGE_TYPE) && |
| 1280 | a->message_type != b->message_type) |
| 1281 | return FALSE; |
| 1282 | |
| 1283 | if ((a->flags & BUS_MATCH_MEMBER) && |
| 1284 | strcmp (a->member, b->member) != 0) |
| 1285 | return FALSE; |
| 1286 | |
| 1287 | if ((a->flags & BUS_MATCH_PATH) && |
| 1288 | strcmp (a->path, b->path) != 0) |
| 1289 | return FALSE; |
| 1290 | |
| 1291 | if ((a->flags & BUS_MATCH_INTERFACE) && |
| 1292 | strcmp (a->interface, b->interface) != 0) |
| 1293 | return FALSE; |
| 1294 | |
| 1295 | if ((a->flags & BUS_MATCH_SENDER) && |
| 1296 | strcmp (a->sender, b->sender) != 0) |
| 1297 | return FALSE; |
| 1298 | |
| 1299 | if ((a->flags & BUS_MATCH_DESTINATION) && |
| 1300 | strcmp (a->destination, b->destination) != 0) |
| 1301 | return FALSE; |
| 1302 | |
| 1303 | if (a->flags & BUS_MATCH_ARGS) |
| 1304 | { |
| 1305 | int i; |
| 1306 | |
| 1307 | if (a->args_len != b->args_len) |
| 1308 | return FALSE; |
| 1309 | |
| 1310 | i = 0; |
| 1311 | while (i < a->args_len) |
| 1312 | { |
| 1313 | int length; |
| 1314 | |
| 1315 | if ((a->args[i] != NULL) != (b->args[i] != NULL)) |
| 1316 | return FALSE; |
| 1317 | |
| 1318 | if (a->arg_lens[i] != b->arg_lens[i]) |
| 1319 | return FALSE; |
| 1320 | |
| 1321 | length = a->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH; |
| 1322 | |
| 1323 | if (a->args[i] != NULL) |
| 1324 | { |
| 1325 | _dbus_assert (b->args[i] != NULL); |
| 1326 | if (memcmp (a->args[i], b->args[i], length) != 0) |
| 1327 | return FALSE; |
| 1328 | } |
| 1329 | |
| 1330 | ++i; |
| 1331 | } |
| 1332 | } |
| 1333 | |
| 1334 | return TRUE; |
| 1335 | } |
| 1336 | |
| 1337 | static void |
| 1338 | bus_matchmaker_remove_rule_link (DBusList **rules, |
| 1339 | DBusList *link) |
| 1340 | { |
| 1341 | BusMatchRule *rule = link->data; |
| 1342 | |
| 1343 | bus_connection_remove_match_rule (rule->matches_go_to, rule); |
| 1344 | _dbus_list_remove_link (rules, link); |
| 1345 | |
| 1346 | #ifdef DBUS_ENABLE_VERBOSE_MODE |
| 1347 | { |
| 1348 | char *s = match_rule_to_string (rule); |
| 1349 | |
| 1350 | _dbus_verbose ("Removed match rule %s for connection %p\n", |
| 1351 | s, rule->matches_go_to); |
| 1352 | dbus_free (s); |
| 1353 | } |
| 1354 | #endif |
| 1355 | |
| 1356 | bus_match_rule_unref (rule); |
| 1357 | } |
| 1358 | |
| 1359 | void |
| 1360 | bus_matchmaker_remove_rule (BusMatchmaker *matchmaker, |
| 1361 | BusMatchRule *rule) |
| 1362 | { |
| 1363 | DBusList **rules; |
| 1364 | |
| 1365 | _dbus_verbose ("Removing rule with message_type %d, interface %s\n", |
| 1366 | rule->message_type, |
| 1367 | rule->interface != NULL ? rule->interface : "<null>"); |
| 1368 | |
| 1369 | bus_connection_remove_match_rule (rule->matches_go_to, rule); |
| 1370 | |
| 1371 | rules = bus_matchmaker_get_rules (matchmaker, rule->message_type, |
| 1372 | rule->interface, FALSE); |
| 1373 | |
| 1374 | /* We should only be asked to remove a rule by identity right after it was |
| 1375 | * added, so there should be a list for it. |
| 1376 | */ |
| 1377 | _dbus_assert (rules != NULL); |
| 1378 | |
| 1379 | _dbus_list_remove (rules, rule); |
| 1380 | bus_matchmaker_gc_rules (matchmaker, rule->message_type, rule->interface, |
| 1381 | rules); |
| 1382 | |
| 1383 | #ifdef DBUS_ENABLE_VERBOSE_MODE |
| 1384 | { |
| 1385 | char *s = match_rule_to_string (rule); |
| 1386 | |
| 1387 | _dbus_verbose ("Removed match rule %s for connection %p\n", |
| 1388 | s, rule->matches_go_to); |
| 1389 | dbus_free (s); |
| 1390 | } |
| 1391 | #endif |
| 1392 | |
| 1393 | bus_match_rule_unref (rule); |
| 1394 | } |
| 1395 | |
| 1396 | /* Remove a single rule which is equal to the given rule by value */ |
| 1397 | dbus_bool_t |
| 1398 | bus_matchmaker_remove_rule_by_value (BusMatchmaker *matchmaker, |
| 1399 | BusMatchRule *value, |
| 1400 | DBusError *error) |
| 1401 | { |
| 1402 | DBusList **rules; |
| 1403 | DBusList *link = NULL; |
| 1404 | |
| 1405 | _dbus_verbose ("Removing rule by value with message_type %d, interface %s\n", |
| 1406 | value->message_type, |
| 1407 | value->interface != NULL ? value->interface : "<null>"); |
| 1408 | |
| 1409 | rules = bus_matchmaker_get_rules (matchmaker, value->message_type, |
| 1410 | value->interface, FALSE); |
| 1411 | |
| 1412 | if (rules != NULL) |
| 1413 | { |
| 1414 | /* we traverse backward because bus_connection_remove_match_rule() |
| 1415 | * removes the most-recently-added rule |
| 1416 | */ |
| 1417 | link = _dbus_list_get_last_link (rules); |
| 1418 | while (link != NULL) |
| 1419 | { |
| 1420 | BusMatchRule *rule; |
| 1421 | DBusList *prev; |
| 1422 | |
| 1423 | rule = link->data; |
| 1424 | prev = _dbus_list_get_prev_link (rules, link); |
| 1425 | |
| 1426 | if (match_rule_equal (rule, value)) |
| 1427 | { |
| 1428 | bus_matchmaker_remove_rule_link (rules, link); |
| 1429 | break; |
| 1430 | } |
| 1431 | |
| 1432 | link = prev; |
| 1433 | } |
| 1434 | } |
| 1435 | |
| 1436 | if (link == NULL) |
| 1437 | { |
| 1438 | dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND, |
| 1439 | "The given match rule wasn't found and can't be removed"); |
| 1440 | return FALSE; |
| 1441 | } |
| 1442 | |
| 1443 | bus_matchmaker_gc_rules (matchmaker, value->message_type, value->interface, |
| 1444 | rules); |
| 1445 | |
| 1446 | return TRUE; |
| 1447 | } |
| 1448 | |
| 1449 | static void |
| 1450 | rule_list_remove_by_connection (DBusList **rules, |
| 1451 | DBusConnection *connection) |
| 1452 | { |
| 1453 | DBusList *link; |
| 1454 | |
| 1455 | link = _dbus_list_get_first_link (rules); |
| 1456 | while (link != NULL) |
| 1457 | { |
| 1458 | BusMatchRule *rule; |
| 1459 | DBusList *next; |
| 1460 | |
| 1461 | rule = link->data; |
| 1462 | next = _dbus_list_get_next_link (rules, link); |
| 1463 | |
| 1464 | if (rule->matches_go_to == connection) |
| 1465 | { |
| 1466 | bus_matchmaker_remove_rule_link (rules, link); |
| 1467 | } |
| 1468 | else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') || |
| 1469 | ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':')) |
| 1470 | { |
| 1471 | /* The rule matches to/from a base service, see if it's the |
| 1472 | * one being disconnected, since we know this service name |
| 1473 | * will never be recycled. |
| 1474 | */ |
| 1475 | const char *name; |
| 1476 | |
| 1477 | name = bus_connection_get_name (connection); |
| 1478 | _dbus_assert (name != NULL); /* because we're an active connection */ |
| 1479 | |
| 1480 | if (((rule->flags & BUS_MATCH_SENDER) && |
| 1481 | strcmp (rule->sender, name) == 0) || |
| 1482 | ((rule->flags & BUS_MATCH_DESTINATION) && |
| 1483 | strcmp (rule->destination, name) == 0)) |
| 1484 | { |
| 1485 | bus_matchmaker_remove_rule_link (rules, link); |
| 1486 | } |
| 1487 | } |
| 1488 | |
| 1489 | link = next; |
| 1490 | } |
| 1491 | } |
| 1492 | |
| 1493 | void |
| 1494 | bus_matchmaker_disconnected (BusMatchmaker *matchmaker, |
| 1495 | DBusConnection *connection) |
| 1496 | { |
| 1497 | int i; |
| 1498 | |
| 1499 | /* FIXME |
| 1500 | * |
| 1501 | * This scans all match rules on the bus. We could avoid that |
| 1502 | * for the rules belonging to the connection, since we keep |
| 1503 | * a list of those; but for the rules that just refer to |
| 1504 | * the connection we'd need to do something more elaborate. |
| 1505 | */ |
| 1506 | |
| 1507 | _dbus_assert (bus_connection_is_active (connection)); |
| 1508 | |
| 1509 | _dbus_verbose ("Removing all rules for connection %p\n", connection); |
| 1510 | |
| 1511 | for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++) |
| 1512 | { |
| 1513 | RulePool *p = matchmaker->rules_by_type + i; |
| 1514 | DBusHashIter iter; |
| 1515 | |
| 1516 | rule_list_remove_by_connection (&p->rules_without_iface, connection); |
| 1517 | |
| 1518 | _dbus_hash_iter_init (p->rules_by_iface, &iter); |
| 1519 | while (_dbus_hash_iter_next (&iter)) |
| 1520 | { |
| 1521 | DBusList **items = _dbus_hash_iter_get_value (&iter); |
| 1522 | |
| 1523 | rule_list_remove_by_connection (items, connection); |
| 1524 | |
| 1525 | if (*items == NULL) |
| 1526 | _dbus_hash_iter_remove_entry (&iter); |
| 1527 | } |
| 1528 | } |
| 1529 | } |
| 1530 | |
| 1531 | static dbus_bool_t |
| 1532 | connection_is_primary_owner (DBusConnection *connection, |
| 1533 | const char *service_name) |
| 1534 | { |
| 1535 | BusService *service; |
| 1536 | DBusString str; |
| 1537 | BusRegistry *registry; |
| 1538 | |
| 1539 | _dbus_assert (connection != NULL); |
| 1540 | |
| 1541 | registry = bus_connection_get_registry (connection); |
| 1542 | |
| 1543 | _dbus_string_init_const (&str, service_name); |
| 1544 | service = bus_registry_lookup (registry, &str); |
| 1545 | |
| 1546 | if (service == NULL) |
| 1547 | return FALSE; /* Service doesn't exist so connection can't own it. */ |
| 1548 | |
| 1549 | return bus_service_get_primary_owners_connection (service) == connection; |
| 1550 | } |
| 1551 | |
| 1552 | static dbus_bool_t |
| 1553 | match_rule_matches (BusMatchRule *rule, |
| 1554 | DBusConnection *sender, |
| 1555 | DBusConnection *addressed_recipient, |
| 1556 | DBusMessage *message, |
| 1557 | BusMatchFlags already_matched) |
| 1558 | { |
| 1559 | int flags; |
| 1560 | |
| 1561 | /* All features of the match rule are AND'd together, |
| 1562 | * so FALSE if any of them don't match. |
| 1563 | */ |
| 1564 | |
| 1565 | /* sender/addressed_recipient of #NULL may mean bus driver, |
| 1566 | * or for addressed_recipient may mean a message with no |
| 1567 | * specific recipient (i.e. a signal) |
| 1568 | */ |
| 1569 | |
| 1570 | /* Don't bother re-matching features we've already checked implicitly. */ |
| 1571 | flags = rule->flags & (~already_matched); |
| 1572 | |
| 1573 | if (flags & BUS_MATCH_MESSAGE_TYPE) |
| 1574 | { |
| 1575 | _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID); |
| 1576 | |
| 1577 | if (rule->message_type != dbus_message_get_type (message)) |
| 1578 | return FALSE; |
| 1579 | } |
| 1580 | |
| 1581 | if (flags & BUS_MATCH_INTERFACE) |
| 1582 | { |
| 1583 | const char *iface; |
| 1584 | |
| 1585 | _dbus_assert (rule->interface != NULL); |
| 1586 | |
| 1587 | iface = dbus_message_get_interface (message); |
| 1588 | if (iface == NULL) |
| 1589 | return FALSE; |
| 1590 | |
| 1591 | if (strcmp (iface, rule->interface) != 0) |
| 1592 | return FALSE; |
| 1593 | } |
| 1594 | |
| 1595 | if (flags & BUS_MATCH_MEMBER) |
| 1596 | { |
| 1597 | const char *member; |
| 1598 | |
| 1599 | _dbus_assert (rule->member != NULL); |
| 1600 | |
| 1601 | member = dbus_message_get_member (message); |
| 1602 | if (member == NULL) |
| 1603 | return FALSE; |
| 1604 | |
| 1605 | if (strcmp (member, rule->member) != 0) |
| 1606 | return FALSE; |
| 1607 | } |
| 1608 | |
| 1609 | if (flags & BUS_MATCH_SENDER) |
| 1610 | { |
| 1611 | _dbus_assert (rule->sender != NULL); |
| 1612 | |
| 1613 | if (sender == NULL) |
| 1614 | { |
| 1615 | if (strcmp (rule->sender, |
| 1616 | DBUS_SERVICE_DBUS) != 0) |
| 1617 | return FALSE; |
| 1618 | } |
| 1619 | else |
| 1620 | { |
| 1621 | if (!connection_is_primary_owner (sender, rule->sender)) |
| 1622 | return FALSE; |
| 1623 | } |
| 1624 | } |
| 1625 | |
| 1626 | if (flags & BUS_MATCH_DESTINATION) |
| 1627 | { |
| 1628 | const char *destination; |
| 1629 | |
| 1630 | _dbus_assert (rule->destination != NULL); |
| 1631 | |
| 1632 | destination = dbus_message_get_destination (message); |
| 1633 | if (destination == NULL) |
| 1634 | return FALSE; |
| 1635 | |
| 1636 | if (addressed_recipient == NULL) |
| 1637 | { |
| 1638 | if (strcmp (rule->destination, |
| 1639 | DBUS_SERVICE_DBUS) != 0) |
| 1640 | return FALSE; |
| 1641 | } |
| 1642 | else |
| 1643 | { |
| 1644 | if (!connection_is_primary_owner (addressed_recipient, rule->destination)) |
| 1645 | return FALSE; |
| 1646 | } |
| 1647 | } |
| 1648 | |
| 1649 | if (flags & BUS_MATCH_PATH) |
| 1650 | { |
| 1651 | const char *path; |
| 1652 | |
| 1653 | _dbus_assert (rule->path != NULL); |
| 1654 | |
| 1655 | path = dbus_message_get_path (message); |
| 1656 | if (path == NULL) |
| 1657 | return FALSE; |
| 1658 | |
| 1659 | if (strcmp (path, rule->path) != 0) |
| 1660 | return FALSE; |
| 1661 | } |
| 1662 | |
| 1663 | if (flags & BUS_MATCH_ARGS) |
| 1664 | { |
| 1665 | int i; |
| 1666 | DBusMessageIter iter; |
| 1667 | |
| 1668 | _dbus_assert (rule->args != NULL); |
| 1669 | |
| 1670 | dbus_message_iter_init (message, &iter); |
| 1671 | |
| 1672 | i = 0; |
| 1673 | while (i < rule->args_len) |
| 1674 | { |
| 1675 | int current_type; |
| 1676 | const char *expected_arg; |
| 1677 | int expected_length; |
| 1678 | dbus_bool_t is_path; |
| 1679 | |
| 1680 | expected_arg = rule->args[i]; |
| 1681 | expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH; |
| 1682 | is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0; |
| 1683 | |
| 1684 | current_type = dbus_message_iter_get_arg_type (&iter); |
| 1685 | |
| 1686 | if (expected_arg != NULL) |
| 1687 | { |
| 1688 | const char *actual_arg; |
| 1689 | int actual_length; |
| 1690 | |
| 1691 | if (current_type != DBUS_TYPE_STRING) |
| 1692 | return FALSE; |
| 1693 | |
| 1694 | actual_arg = NULL; |
| 1695 | dbus_message_iter_get_basic (&iter, &actual_arg); |
| 1696 | _dbus_assert (actual_arg != NULL); |
| 1697 | |
| 1698 | actual_length = strlen (actual_arg); |
| 1699 | |
| 1700 | if (is_path) |
| 1701 | { |
| 1702 | if (actual_length < expected_length && |
| 1703 | actual_arg[actual_length - 1] != '/') |
| 1704 | return FALSE; |
| 1705 | |
| 1706 | if (expected_length < actual_length && |
| 1707 | expected_arg[expected_length - 1] != '/') |
| 1708 | return FALSE; |
| 1709 | |
| 1710 | if (memcmp (actual_arg, expected_arg, |
| 1711 | MIN (actual_length, expected_length)) != 0) |
| 1712 | return FALSE; |
| 1713 | } |
| 1714 | else |
| 1715 | { |
| 1716 | if (expected_length != actual_length || |
| 1717 | memcmp (expected_arg, actual_arg, expected_length) != 0) |
| 1718 | return FALSE; |
| 1719 | } |
| 1720 | |
| 1721 | } |
| 1722 | |
| 1723 | if (current_type != DBUS_TYPE_INVALID) |
| 1724 | dbus_message_iter_next (&iter); |
| 1725 | |
| 1726 | ++i; |
| 1727 | } |
| 1728 | } |
| 1729 | |
| 1730 | return TRUE; |
| 1731 | } |
| 1732 | |
| 1733 | static dbus_bool_t |
| 1734 | get_recipients_from_list (DBusList **rules, |
| 1735 | DBusConnection *sender, |
| 1736 | DBusConnection *addressed_recipient, |
| 1737 | DBusMessage *message, |
| 1738 | DBusList **recipients_p) |
| 1739 | { |
| 1740 | DBusList *link; |
| 1741 | |
| 1742 | if (rules == NULL) |
| 1743 | return TRUE; |
| 1744 | |
| 1745 | link = _dbus_list_get_first_link (rules); |
| 1746 | while (link != NULL) |
| 1747 | { |
| 1748 | BusMatchRule *rule; |
| 1749 | |
| 1750 | rule = link->data; |
| 1751 | |
| 1752 | #ifdef DBUS_ENABLE_VERBOSE_MODE |
| 1753 | { |
| 1754 | char *s = match_rule_to_string (rule); |
| 1755 | |
| 1756 | _dbus_verbose ("Checking whether message matches rule %s for connection %p\n", |
| 1757 | s, rule->matches_go_to); |
| 1758 | dbus_free (s); |
| 1759 | } |
| 1760 | #endif |
| 1761 | |
| 1762 | if (match_rule_matches (rule, |
| 1763 | sender, addressed_recipient, message, |
| 1764 | BUS_MATCH_MESSAGE_TYPE | BUS_MATCH_INTERFACE)) |
| 1765 | { |
| 1766 | _dbus_verbose ("Rule matched\n"); |
| 1767 | |
| 1768 | /* Append to the list if we haven't already */ |
| 1769 | if (bus_connection_mark_stamp (rule->matches_go_to)) |
| 1770 | { |
| 1771 | if (!_dbus_list_append (recipients_p, rule->matches_go_to)) |
| 1772 | return FALSE; |
| 1773 | } |
| 1774 | #ifdef DBUS_ENABLE_VERBOSE_MODE |
| 1775 | else |
| 1776 | { |
| 1777 | _dbus_verbose ("Connection already receiving this message, so not adding again\n"); |
| 1778 | } |
| 1779 | #endif /* DBUS_ENABLE_VERBOSE_MODE */ |
| 1780 | } |
| 1781 | |
| 1782 | link = _dbus_list_get_next_link (rules, link); |
| 1783 | } |
| 1784 | |
| 1785 | return TRUE; |
| 1786 | } |
| 1787 | |
| 1788 | dbus_bool_t |
| 1789 | bus_matchmaker_get_recipients (BusMatchmaker *matchmaker, |
| 1790 | BusConnections *connections, |
| 1791 | DBusConnection *sender, |
| 1792 | DBusConnection *addressed_recipient, |
| 1793 | DBusMessage *message, |
| 1794 | DBusList **recipients_p) |
| 1795 | { |
| 1796 | int type; |
| 1797 | const char *interface; |
| 1798 | DBusList **neither, **just_type, **just_iface, **both; |
| 1799 | |
| 1800 | _dbus_assert (*recipients_p == NULL); |
| 1801 | |
| 1802 | /* This avoids sending same message to the same connection twice. |
| 1803 | * Purpose of the stamp instead of a bool is to avoid iterating over |
| 1804 | * all connections resetting the bool each time. |
| 1805 | */ |
| 1806 | bus_connections_increment_stamp (connections); |
| 1807 | |
| 1808 | /* addressed_recipient is already receiving the message, don't add to list. |
| 1809 | * NULL addressed_recipient means either bus driver, or this is a signal |
| 1810 | * and thus lacks a specific addressed_recipient. |
| 1811 | */ |
| 1812 | if (addressed_recipient != NULL) |
| 1813 | bus_connection_mark_stamp (addressed_recipient); |
| 1814 | |
| 1815 | type = dbus_message_get_type (message); |
| 1816 | interface = dbus_message_get_interface (message); |
| 1817 | |
| 1818 | neither = bus_matchmaker_get_rules (matchmaker, DBUS_MESSAGE_TYPE_INVALID, |
| 1819 | NULL, FALSE); |
| 1820 | just_type = just_iface = both = NULL; |
| 1821 | |
| 1822 | if (interface != NULL) |
| 1823 | just_iface = bus_matchmaker_get_rules (matchmaker, |
| 1824 | DBUS_MESSAGE_TYPE_INVALID, interface, FALSE); |
| 1825 | |
| 1826 | if (type > DBUS_MESSAGE_TYPE_INVALID && type < DBUS_NUM_MESSAGE_TYPES) |
| 1827 | { |
| 1828 | just_type = bus_matchmaker_get_rules (matchmaker, type, NULL, FALSE); |
| 1829 | |
| 1830 | if (interface != NULL) |
| 1831 | both = bus_matchmaker_get_rules (matchmaker, type, interface, FALSE); |
| 1832 | } |
| 1833 | |
| 1834 | if (!(get_recipients_from_list (neither, sender, addressed_recipient, |
| 1835 | message, recipients_p) && |
| 1836 | get_recipients_from_list (just_iface, sender, addressed_recipient, |
| 1837 | message, recipients_p) && |
| 1838 | get_recipients_from_list (just_type, sender, addressed_recipient, |
| 1839 | message, recipients_p) && |
| 1840 | get_recipients_from_list (both, sender, addressed_recipient, |
| 1841 | message, recipients_p))) |
| 1842 | { |
| 1843 | _dbus_list_clear (recipients_p); |
| 1844 | return FALSE; |
| 1845 | } |
| 1846 | |
| 1847 | return TRUE; |
| 1848 | } |
| 1849 | |
| 1850 | #ifdef DBUS_BUILD_TESTS |
| 1851 | #include "test.h" |
| 1852 | #include <stdlib.h> |
| 1853 | |
| 1854 | static BusMatchRule* |
| 1855 | check_parse (dbus_bool_t should_succeed, |
| 1856 | const char *text) |
| 1857 | { |
| 1858 | BusMatchRule *rule; |
| 1859 | DBusString str; |
| 1860 | DBusError error; |
| 1861 | |
| 1862 | dbus_error_init (&error); |
| 1863 | |
| 1864 | _dbus_string_init_const (&str, text); |
| 1865 | |
| 1866 | rule = bus_match_rule_parse (NULL, &str, &error); |
| 1867 | if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) |
| 1868 | { |
| 1869 | dbus_error_free (&error); |
| 1870 | return NULL; |
| 1871 | } |
| 1872 | |
| 1873 | if (should_succeed && rule == NULL) |
| 1874 | { |
| 1875 | _dbus_warn ("Failed to parse: %s: %s: \"%s\"\n", |
| 1876 | error.name, error.message, |
| 1877 | _dbus_string_get_const_data (&str)); |
| 1878 | exit (1); |
| 1879 | } |
| 1880 | |
| 1881 | if (!should_succeed && rule != NULL) |
| 1882 | { |
| 1883 | _dbus_warn ("Failed to fail to parse: \"%s\"\n", |
| 1884 | _dbus_string_get_const_data (&str)); |
| 1885 | exit (1); |
| 1886 | } |
| 1887 | |
| 1888 | dbus_error_free (&error); |
| 1889 | |
| 1890 | return rule; |
| 1891 | } |
| 1892 | |
| 1893 | static void |
| 1894 | assert_large_rule (BusMatchRule *rule) |
| 1895 | { |
| 1896 | _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE); |
| 1897 | _dbus_assert (rule->flags & BUS_MATCH_SENDER); |
| 1898 | _dbus_assert (rule->flags & BUS_MATCH_INTERFACE); |
| 1899 | _dbus_assert (rule->flags & BUS_MATCH_MEMBER); |
| 1900 | _dbus_assert (rule->flags & BUS_MATCH_DESTINATION); |
| 1901 | _dbus_assert (rule->flags & BUS_MATCH_PATH); |
| 1902 | |
| 1903 | _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL); |
| 1904 | _dbus_assert (rule->interface != NULL); |
| 1905 | _dbus_assert (rule->member != NULL); |
| 1906 | _dbus_assert (rule->sender != NULL); |
| 1907 | _dbus_assert (rule->destination != NULL); |
| 1908 | _dbus_assert (rule->path != NULL); |
| 1909 | |
| 1910 | _dbus_assert (strcmp (rule->interface, "org.freedesktop.DBusInterface") == 0); |
| 1911 | _dbus_assert (strcmp (rule->sender, "org.freedesktop.DBusSender") == 0); |
| 1912 | _dbus_assert (strcmp (rule->member, "Foo") == 0); |
| 1913 | _dbus_assert (strcmp (rule->path, "/bar/foo") == 0); |
| 1914 | _dbus_assert (strcmp (rule->destination, ":452345.34") == 0); |
| 1915 | } |
| 1916 | |
| 1917 | static dbus_bool_t |
| 1918 | test_parsing (void *data) |
| 1919 | { |
| 1920 | BusMatchRule *rule; |
| 1921 | |
| 1922 | rule = check_parse (TRUE, "type='signal',sender='org.freedesktop.DBusSender',interface='org.freedesktop.DBusInterface',member='Foo',path='/bar/foo',destination=':452345.34'"); |
| 1923 | if (rule != NULL) |
| 1924 | { |
| 1925 | assert_large_rule (rule); |
| 1926 | bus_match_rule_unref (rule); |
| 1927 | } |
| 1928 | |
| 1929 | /* With extra whitespace and useless quotes */ |
| 1930 | rule = check_parse (TRUE, " type='signal', \tsender='org.freedes''ktop.DBusSender', interface='org.freedesktop.DBusInterface''''', \tmember='Foo',path='/bar/foo',destination=':452345.34'''''"); |
| 1931 | if (rule != NULL) |
| 1932 | { |
| 1933 | assert_large_rule (rule); |
| 1934 | bus_match_rule_unref (rule); |
| 1935 | } |
| 1936 | |
| 1937 | |
| 1938 | /* A simple signal connection */ |
| 1939 | rule = check_parse (TRUE, "type='signal',path='/foo',interface='org.Bar'"); |
| 1940 | if (rule != NULL) |
| 1941 | { |
| 1942 | _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE); |
| 1943 | _dbus_assert (rule->flags & BUS_MATCH_INTERFACE); |
| 1944 | _dbus_assert (rule->flags & BUS_MATCH_PATH); |
| 1945 | |
| 1946 | _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL); |
| 1947 | _dbus_assert (rule->interface != NULL); |
| 1948 | _dbus_assert (rule->path != NULL); |
| 1949 | |
| 1950 | _dbus_assert (strcmp (rule->interface, "org.Bar") == 0); |
| 1951 | _dbus_assert (strcmp (rule->path, "/foo") == 0); |
| 1952 | |
| 1953 | bus_match_rule_unref (rule); |
| 1954 | } |
| 1955 | |
| 1956 | /* argN */ |
| 1957 | rule = check_parse (TRUE, "arg0='foo'"); |
| 1958 | if (rule != NULL) |
| 1959 | { |
| 1960 | _dbus_assert (rule->flags == BUS_MATCH_ARGS); |
| 1961 | _dbus_assert (rule->args != NULL); |
| 1962 | _dbus_assert (rule->args_len == 1); |
| 1963 | _dbus_assert (rule->args[0] != NULL); |
| 1964 | _dbus_assert (rule->args[1] == NULL); |
| 1965 | _dbus_assert (strcmp (rule->args[0], "foo") == 0); |
| 1966 | |
| 1967 | bus_match_rule_unref (rule); |
| 1968 | } |
| 1969 | |
| 1970 | rule = check_parse (TRUE, "arg1='foo'"); |
| 1971 | if (rule != NULL) |
| 1972 | { |
| 1973 | _dbus_assert (rule->flags == BUS_MATCH_ARGS); |
| 1974 | _dbus_assert (rule->args != NULL); |
| 1975 | _dbus_assert (rule->args_len == 2); |
| 1976 | _dbus_assert (rule->args[0] == NULL); |
| 1977 | _dbus_assert (rule->args[1] != NULL); |
| 1978 | _dbus_assert (rule->args[2] == NULL); |
| 1979 | _dbus_assert (strcmp (rule->args[1], "foo") == 0); |
| 1980 | |
| 1981 | bus_match_rule_unref (rule); |
| 1982 | } |
| 1983 | |
| 1984 | rule = check_parse (TRUE, "arg2='foo'"); |
| 1985 | if (rule != NULL) |
| 1986 | { |
| 1987 | _dbus_assert (rule->flags == BUS_MATCH_ARGS); |
| 1988 | _dbus_assert (rule->args != NULL); |
| 1989 | _dbus_assert (rule->args_len == 3); |
| 1990 | _dbus_assert (rule->args[0] == NULL); |
| 1991 | _dbus_assert (rule->args[1] == NULL); |
| 1992 | _dbus_assert (rule->args[2] != NULL); |
| 1993 | _dbus_assert (rule->args[3] == NULL); |
| 1994 | _dbus_assert (strcmp (rule->args[2], "foo") == 0); |
| 1995 | |
| 1996 | bus_match_rule_unref (rule); |
| 1997 | } |
| 1998 | |
| 1999 | rule = check_parse (TRUE, "arg40='foo'"); |
| 2000 | if (rule != NULL) |
| 2001 | { |
| 2002 | _dbus_assert (rule->flags == BUS_MATCH_ARGS); |
| 2003 | _dbus_assert (rule->args != NULL); |
| 2004 | _dbus_assert (rule->args_len == 41); |
| 2005 | _dbus_assert (rule->args[0] == NULL); |
| 2006 | _dbus_assert (rule->args[1] == NULL); |
| 2007 | _dbus_assert (rule->args[40] != NULL); |
| 2008 | _dbus_assert (rule->args[41] == NULL); |
| 2009 | _dbus_assert (strcmp (rule->args[40], "foo") == 0); |
| 2010 | |
| 2011 | bus_match_rule_unref (rule); |
| 2012 | } |
| 2013 | |
| 2014 | rule = check_parse (TRUE, "arg63='foo'"); |
| 2015 | if (rule != NULL) |
| 2016 | { |
| 2017 | _dbus_assert (rule->flags == BUS_MATCH_ARGS); |
| 2018 | _dbus_assert (rule->args != NULL); |
| 2019 | _dbus_assert (rule->args_len == 64); |
| 2020 | _dbus_assert (rule->args[0] == NULL); |
| 2021 | _dbus_assert (rule->args[1] == NULL); |
| 2022 | _dbus_assert (rule->args[63] != NULL); |
| 2023 | _dbus_assert (rule->args[64] == NULL); |
| 2024 | _dbus_assert (strcmp (rule->args[63], "foo") == 0); |
| 2025 | |
| 2026 | bus_match_rule_unref (rule); |
| 2027 | } |
| 2028 | |
| 2029 | /* Too-large argN */ |
| 2030 | rule = check_parse (FALSE, "arg300='foo'"); |
| 2031 | _dbus_assert (rule == NULL); |
| 2032 | rule = check_parse (FALSE, "arg64='foo'"); |
| 2033 | _dbus_assert (rule == NULL); |
| 2034 | |
| 2035 | /* No N in argN */ |
| 2036 | rule = check_parse (FALSE, "arg='foo'"); |
| 2037 | _dbus_assert (rule == NULL); |
| 2038 | rule = check_parse (FALSE, "argv='foo'"); |
| 2039 | _dbus_assert (rule == NULL); |
| 2040 | rule = check_parse (FALSE, "arg3junk='foo'"); |
| 2041 | _dbus_assert (rule == NULL); |
| 2042 | rule = check_parse (FALSE, "argument='foo'"); |
| 2043 | _dbus_assert (rule == NULL); |
| 2044 | |
| 2045 | /* Reject duplicates */ |
| 2046 | rule = check_parse (FALSE, "type='signal',type='method_call'"); |
| 2047 | _dbus_assert (rule == NULL); |
| 2048 | |
| 2049 | /* Duplicates with the argN code */ |
| 2050 | rule = check_parse (FALSE, "arg0='foo',arg0='bar'"); |
| 2051 | _dbus_assert (rule == NULL); |
| 2052 | rule = check_parse (FALSE, "arg3='foo',arg3='bar'"); |
| 2053 | _dbus_assert (rule == NULL); |
| 2054 | rule = check_parse (FALSE, "arg30='foo',arg30='bar'"); |
| 2055 | _dbus_assert (rule == NULL); |
| 2056 | |
| 2057 | /* Reject broken keys */ |
| 2058 | rule = check_parse (FALSE, "blah='signal'"); |
| 2059 | _dbus_assert (rule == NULL); |
| 2060 | |
| 2061 | /* Reject broken values */ |
| 2062 | rule = check_parse (FALSE, "type='chouin'"); |
| 2063 | _dbus_assert (rule == NULL); |
| 2064 | rule = check_parse (FALSE, "interface='abc@def++'"); |
| 2065 | _dbus_assert (rule == NULL); |
| 2066 | rule = check_parse (FALSE, "service='youpi'"); |
| 2067 | _dbus_assert (rule == NULL); |
| 2068 | |
| 2069 | /* Allow empty rule */ |
| 2070 | rule = check_parse (TRUE, ""); |
| 2071 | if (rule != NULL) |
| 2072 | { |
| 2073 | _dbus_assert (rule->flags == 0); |
| 2074 | |
| 2075 | bus_match_rule_unref (rule); |
| 2076 | } |
| 2077 | |
| 2078 | /* All-whitespace rule is the same as empty */ |
| 2079 | rule = check_parse (TRUE, " \t"); |
| 2080 | if (rule != NULL) |
| 2081 | { |
| 2082 | _dbus_assert (rule->flags == 0); |
| 2083 | |
| 2084 | bus_match_rule_unref (rule); |
| 2085 | } |
| 2086 | |
| 2087 | /* But with non-whitespace chars and no =value, it's not OK */ |
| 2088 | rule = check_parse (FALSE, "type"); |
| 2089 | _dbus_assert (rule == NULL); |
| 2090 | |
| 2091 | return TRUE; |
| 2092 | } |
| 2093 | |
| 2094 | static struct { |
| 2095 | const char *first; |
| 2096 | const char *second; |
| 2097 | } equality_tests[] = { |
| 2098 | { "type='signal'", "type='signal'" }, |
| 2099 | { "type='signal',interface='foo.bar'", "interface='foo.bar',type='signal'" }, |
| 2100 | { "type='signal',member='bar'", "member='bar',type='signal'" }, |
| 2101 | { "type='method_call',sender=':1.0'", "sender=':1.0',type='method_call'" }, |
| 2102 | { "type='method_call',destination=':1.0'", "destination=':1.0',type='method_call'" }, |
| 2103 | { "type='method_call',path='/foo/bar'", "path='/foo/bar',type='method_call'" }, |
| 2104 | { "type='method_call',arg0='blah'", "arg0='blah',type='method_call'" }, |
| 2105 | { "type='method_call',arg0='boo'", "arg0='boo',type='method_call'" }, |
| 2106 | { "type='method_call',arg0='blah',arg1='baz'", "arg0='blah',arg1='baz',type='method_call'" }, |
| 2107 | { "type='method_call',arg3='foosh'", "arg3='foosh',type='method_call'" }, |
| 2108 | { "arg3='fool'", "arg3='fool'" }, |
| 2109 | { "member='food'", "member='food'" } |
| 2110 | }; |
| 2111 | |
| 2112 | static void |
| 2113 | test_equality (void) |
| 2114 | { |
| 2115 | int i; |
| 2116 | |
| 2117 | i = 0; |
| 2118 | while (i < _DBUS_N_ELEMENTS (equality_tests)) |
| 2119 | { |
| 2120 | BusMatchRule *first; |
| 2121 | BusMatchRule *second; |
| 2122 | int j; |
| 2123 | |
| 2124 | first = check_parse (TRUE, equality_tests[i].first); |
| 2125 | _dbus_assert (first != NULL); |
| 2126 | second = check_parse (TRUE, equality_tests[i].second); |
| 2127 | _dbus_assert (second != NULL); |
| 2128 | |
| 2129 | if (!match_rule_equal (first, second)) |
| 2130 | { |
| 2131 | _dbus_warn ("rule %s and %s should have been equal\n", |
| 2132 | equality_tests[i].first, |
| 2133 | equality_tests[i].second); |
| 2134 | exit (1); |
| 2135 | } |
| 2136 | |
| 2137 | bus_match_rule_unref (second); |
| 2138 | |
| 2139 | /* Check that the rule is not equal to any of the |
| 2140 | * others besides its pair match |
| 2141 | */ |
| 2142 | j = 0; |
| 2143 | while (j < _DBUS_N_ELEMENTS (equality_tests)) |
| 2144 | { |
| 2145 | if (i != j) |
| 2146 | { |
| 2147 | second = check_parse (TRUE, equality_tests[j].second); |
| 2148 | |
| 2149 | if (match_rule_equal (first, second)) |
| 2150 | { |
| 2151 | _dbus_warn ("rule %s and %s should not have been equal\n", |
| 2152 | equality_tests[i].first, |
| 2153 | equality_tests[j].second); |
| 2154 | exit (1); |
| 2155 | } |
| 2156 | |
| 2157 | bus_match_rule_unref (second); |
| 2158 | } |
| 2159 | |
| 2160 | ++j; |
| 2161 | } |
| 2162 | |
| 2163 | bus_match_rule_unref (first); |
| 2164 | |
| 2165 | ++i; |
| 2166 | } |
| 2167 | } |
| 2168 | |
| 2169 | static const char* |
| 2170 | should_match_message_1[] = { |
| 2171 | "type='signal'", |
| 2172 | "member='Frobated'", |
| 2173 | "arg0='foobar'", |
| 2174 | "type='signal',member='Frobated'", |
| 2175 | "type='signal',member='Frobated',arg0='foobar'", |
| 2176 | "member='Frobated',arg0='foobar'", |
| 2177 | "type='signal',arg0='foobar'", |
| 2178 | NULL |
| 2179 | }; |
| 2180 | |
| 2181 | static const char* |
| 2182 | should_not_match_message_1[] = { |
| 2183 | "type='method_call'", |
| 2184 | "type='error'", |
| 2185 | "type='method_return'", |
| 2186 | "type='signal',member='Oopsed'", |
| 2187 | "arg0='blah'", |
| 2188 | "arg1='foobar'", |
| 2189 | "arg2='foobar'", |
| 2190 | "arg3='foobar'", |
| 2191 | "arg0='3'", |
| 2192 | "arg1='3'", |
| 2193 | "arg0='foobar',arg1='abcdef'", |
| 2194 | "arg0='foobar',arg1='abcdef',arg2='abcdefghi',arg3='abcdefghi',arg4='abcdefghi'", |
| 2195 | "arg0='foobar',arg1='abcdef',arg4='abcdefghi',arg3='abcdefghi',arg2='abcdefghi'", |
| 2196 | NULL |
| 2197 | }; |
| 2198 | |
| 2199 | static void |
| 2200 | check_matches (dbus_bool_t expected_to_match, |
| 2201 | int number, |
| 2202 | DBusMessage *message, |
| 2203 | const char *rule_text) |
| 2204 | { |
| 2205 | BusMatchRule *rule; |
| 2206 | dbus_bool_t matched; |
| 2207 | |
| 2208 | rule = check_parse (TRUE, rule_text); |
| 2209 | _dbus_assert (rule != NULL); |
| 2210 | |
| 2211 | /* We can't test sender/destination rules since we pass NULL here */ |
| 2212 | matched = match_rule_matches (rule, NULL, NULL, message, 0); |
| 2213 | |
| 2214 | if (matched != expected_to_match) |
| 2215 | { |
| 2216 | _dbus_warn ("Expected rule %s to %s message %d, failed\n", |
| 2217 | rule_text, expected_to_match ? |
| 2218 | "match" : "not match", number); |
| 2219 | exit (1); |
| 2220 | } |
| 2221 | |
| 2222 | bus_match_rule_unref (rule); |
| 2223 | } |
| 2224 | |
| 2225 | static void |
| 2226 | check_matching (DBusMessage *message, |
| 2227 | int number, |
| 2228 | const char **should_match, |
| 2229 | const char **should_not_match) |
| 2230 | { |
| 2231 | int i; |
| 2232 | |
| 2233 | i = 0; |
| 2234 | while (should_match[i] != NULL) |
| 2235 | { |
| 2236 | check_matches (TRUE, number, message, should_match[i]); |
| 2237 | ++i; |
| 2238 | } |
| 2239 | |
| 2240 | i = 0; |
| 2241 | while (should_not_match[i] != NULL) |
| 2242 | { |
| 2243 | check_matches (FALSE, number, message, should_not_match[i]); |
| 2244 | ++i; |
| 2245 | } |
| 2246 | } |
| 2247 | |
| 2248 | static void |
| 2249 | test_matching (void) |
| 2250 | { |
| 2251 | DBusMessage *message1; |
| 2252 | const char *v_STRING; |
| 2253 | dbus_int32_t v_INT32; |
| 2254 | |
| 2255 | message1 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL); |
| 2256 | _dbus_assert (message1 != NULL); |
| 2257 | if (!dbus_message_set_member (message1, "Frobated")) |
| 2258 | _dbus_assert_not_reached ("oom"); |
| 2259 | |
| 2260 | v_STRING = "foobar"; |
| 2261 | v_INT32 = 3; |
| 2262 | if (!dbus_message_append_args (message1, |
| 2263 | DBUS_TYPE_STRING, &v_STRING, |
| 2264 | DBUS_TYPE_INT32, &v_INT32, |
| 2265 | NULL)) |
| 2266 | _dbus_assert_not_reached ("oom"); |
| 2267 | |
| 2268 | check_matching (message1, 1, |
| 2269 | should_match_message_1, |
| 2270 | should_not_match_message_1); |
| 2271 | |
| 2272 | dbus_message_unref (message1); |
| 2273 | } |
| 2274 | |
| 2275 | dbus_bool_t |
| 2276 | bus_signals_test (const DBusString *test_data_dir) |
| 2277 | { |
| 2278 | BusMatchmaker *matchmaker; |
| 2279 | |
| 2280 | matchmaker = bus_matchmaker_new (); |
| 2281 | bus_matchmaker_ref (matchmaker); |
| 2282 | bus_matchmaker_unref (matchmaker); |
| 2283 | bus_matchmaker_unref (matchmaker); |
| 2284 | |
| 2285 | if (!_dbus_test_oom_handling ("parsing match rules", test_parsing, NULL)) |
| 2286 | _dbus_assert_not_reached ("Parsing match rules test failed"); |
| 2287 | |
| 2288 | test_equality (); |
| 2289 | |
| 2290 | test_matching (); |
| 2291 | |
| 2292 | return TRUE; |
| 2293 | } |
| 2294 | |
| 2295 | #endif /* DBUS_BUILD_TESTS */ |
| 2296 | |