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