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*
bus_match_rule_new(DBusConnection * matches_go_to)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 *
bus_match_rule_ref(BusMatchRule * rule)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
bus_match_rule_unref(BusMatchRule * rule)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*
match_rule_to_string(BusMatchRule * rule)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
bus_match_rule_set_message_type(BusMatchRule * rule,int type)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
bus_match_rule_set_interface(BusMatchRule * rule,const char * interface)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
bus_match_rule_set_member(BusMatchRule * rule,const char * member)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
bus_match_rule_set_sender(BusMatchRule * rule,const char * sender)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
bus_match_rule_set_destination(BusMatchRule * rule,const char * destination)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
bus_match_rule_set_path(BusMatchRule * rule,const char * path)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
bus_match_rule_set_arg(BusMatchRule * rule,int arg,const char * value)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
find_key(const DBusString * str,int start,DBusString * key,int * value_pos,DBusError * error)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
find_value(const DBusString * str,int start,const char * key,DBusString * value,int * value_end,DBusError * error)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
tokenize_rule(const DBusString * rule_text,RuleToken tokens[MAX_RULE_TOKENS],DBusError * error)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
bus_match_rule_parse_arg_match(BusMatchRule * rule,const char * key,const DBusString * value,DBusError * error)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*
bus_match_rule_parse(DBusConnection * matches_go_to,const DBusString * rule_text,DBusError * error)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*
bus_matchmaker_new(void)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 *
bus_matchmaker_ref(BusMatchmaker * matchmaker)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
bus_matchmaker_unref(BusMatchmaker * matchmaker)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
bus_matchmaker_add_rule(BusMatchmaker * matchmaker,BusMatchRule * rule)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
match_rule_equal(BusMatchRule * a,BusMatchRule * b)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
bus_matchmaker_remove_rule_link(BusMatchmaker * matchmaker,DBusList * link)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
bus_matchmaker_remove_rule(BusMatchmaker * matchmaker,BusMatchRule * rule)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
bus_matchmaker_remove_rule_by_value(BusMatchmaker * matchmaker,BusMatchRule * value,DBusError * error)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
bus_matchmaker_disconnected(BusMatchmaker * matchmaker,DBusConnection * disconnected)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
connection_is_primary_owner(DBusConnection * connection,const char * service_name)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
match_rule_matches(BusMatchRule * rule,DBusConnection * sender,DBusConnection * addressed_recipient,DBusMessage * message)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
bus_matchmaker_get_recipients(BusMatchmaker * matchmaker,BusConnections * connections,DBusConnection * sender,DBusConnection * addressed_recipient,DBusMessage * message,DBusList ** recipients_p)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*
check_parse(dbus_bool_t should_succeed,const char * text)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
assert_large_rule(BusMatchRule * rule)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
test_parsing(void * data)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
test_equality(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
check_matches(dbus_bool_t expected_to_match,int number,DBusMessage * message,const char * rule_text)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
check_matching(DBusMessage * message,int number,const char ** should_match,const char ** should_not_match)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
test_matching(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
bus_signals_test(const DBusString * test_data_dir)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