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