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