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