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