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