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