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