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