2007-08-03 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / signals.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* signals.c  Bus signal connection implementation
3  *
4  * Copyright (C) 2003, 2005  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 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->matches_go_to != b->matches_go_to)
1071     return FALSE;
1072
1073   if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
1074       a->message_type != b->message_type)
1075     return FALSE;
1076
1077   if ((a->flags & BUS_MATCH_MEMBER) &&
1078       strcmp (a->member, b->member) != 0)
1079     return FALSE;
1080
1081   if ((a->flags & BUS_MATCH_PATH) &&
1082       strcmp (a->path, b->path) != 0)
1083     return FALSE;
1084   
1085   if ((a->flags & BUS_MATCH_INTERFACE) &&
1086       strcmp (a->interface, b->interface) != 0)
1087     return FALSE;
1088
1089   if ((a->flags & BUS_MATCH_SENDER) &&
1090       strcmp (a->sender, b->sender) != 0)
1091     return FALSE;
1092
1093   if ((a->flags & BUS_MATCH_DESTINATION) &&
1094       strcmp (a->destination, b->destination) != 0)
1095     return FALSE;
1096
1097   if (a->flags & BUS_MATCH_ARGS)
1098     {
1099       int i;
1100       
1101       if (a->args_len != b->args_len)
1102         return FALSE;
1103       
1104       i = 0;
1105       while (i < a->args_len)
1106         {
1107           if ((a->args[i] != NULL) != (b->args[i] != NULL))
1108             return FALSE;
1109
1110           if (a->args[i] != NULL)
1111             {
1112               _dbus_assert (b->args[i] != NULL);
1113               if (strcmp (a->args[i], b->args[i]) != 0)
1114                 return FALSE;
1115             }
1116           
1117           ++i;
1118         }
1119     }
1120   
1121   return TRUE;
1122 }
1123
1124 static void
1125 bus_matchmaker_remove_rule_link (BusMatchmaker   *matchmaker,
1126                                  DBusList        *link)
1127 {
1128   BusMatchRule *rule = link->data;
1129   
1130   bus_connection_remove_match_rule (rule->matches_go_to, rule);
1131   _dbus_list_remove_link (&matchmaker->all_rules, link);
1132
1133 #ifdef DBUS_ENABLE_VERBOSE_MODE
1134   {
1135     char *s = match_rule_to_string (rule);
1136
1137     _dbus_verbose ("Removed match rule %s for connection %p\n",
1138                    s, rule->matches_go_to);
1139     dbus_free (s);
1140   }
1141 #endif
1142   
1143   bus_match_rule_unref (rule);  
1144 }
1145
1146 void
1147 bus_matchmaker_remove_rule (BusMatchmaker   *matchmaker,
1148                             BusMatchRule    *rule)
1149 {
1150   bus_connection_remove_match_rule (rule->matches_go_to, rule);
1151   _dbus_list_remove (&matchmaker->all_rules, rule);
1152
1153 #ifdef DBUS_ENABLE_VERBOSE_MODE
1154   {
1155     char *s = match_rule_to_string (rule);
1156
1157     _dbus_verbose ("Removed match rule %s for connection %p\n",
1158                    s, rule->matches_go_to);
1159     dbus_free (s);
1160   }
1161 #endif
1162   
1163   bus_match_rule_unref (rule);
1164 }
1165
1166 /* Remove a single rule which is equal to the given rule by value */
1167 dbus_bool_t
1168 bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker,
1169                                      BusMatchRule    *value,
1170                                      DBusError       *error)
1171 {
1172   /* FIXME this is an unoptimized linear scan */
1173
1174   DBusList *link;
1175
1176   /* we traverse backward because bus_connection_remove_match_rule()
1177    * removes the most-recently-added rule
1178    */
1179   link = _dbus_list_get_last_link (&matchmaker->all_rules);
1180   while (link != NULL)
1181     {
1182       BusMatchRule *rule;
1183       DBusList *prev;
1184
1185       rule = link->data;
1186       prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link);
1187
1188       if (match_rule_equal (rule, value))
1189         {
1190           bus_matchmaker_remove_rule_link (matchmaker, link);
1191           break;
1192         }
1193
1194       link = prev;
1195     }
1196
1197   if (link == NULL)
1198     {
1199       dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
1200                       "The given match rule wasn't found and can't be removed");
1201       return FALSE;
1202     }
1203
1204   return TRUE;
1205 }
1206
1207 void
1208 bus_matchmaker_disconnected (BusMatchmaker   *matchmaker,
1209                              DBusConnection  *disconnected)
1210 {
1211   DBusList *link;
1212
1213   /* FIXME
1214    *
1215    * This scans all match rules on the bus. We could avoid that
1216    * for the rules belonging to the connection, since we keep
1217    * a list of those; but for the rules that just refer to
1218    * the connection we'd need to do something more elaborate.
1219    * 
1220    */
1221   
1222   _dbus_assert (bus_connection_is_active (disconnected));
1223
1224   link = _dbus_list_get_first_link (&matchmaker->all_rules);
1225   while (link != NULL)
1226     {
1227       BusMatchRule *rule;
1228       DBusList *next;
1229
1230       rule = link->data;
1231       next = _dbus_list_get_next_link (&matchmaker->all_rules, link);
1232
1233       if (rule->matches_go_to == disconnected)
1234         {
1235           bus_matchmaker_remove_rule_link (matchmaker, link);
1236         }
1237       else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') ||
1238                ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':'))
1239         {
1240           /* The rule matches to/from a base service, see if it's the
1241            * one being disconnected, since we know this service name
1242            * will never be recycled.
1243            */
1244           const char *name;
1245
1246           name = bus_connection_get_name (disconnected);
1247           _dbus_assert (name != NULL); /* because we're an active connection */
1248
1249           if (((rule->flags & BUS_MATCH_SENDER) &&
1250                strcmp (rule->sender, name) == 0) ||
1251               ((rule->flags & BUS_MATCH_DESTINATION) &&
1252                strcmp (rule->destination, name) == 0))
1253             {
1254               bus_matchmaker_remove_rule_link (matchmaker, link);
1255             }
1256         }
1257
1258       link = next;
1259     }
1260 }
1261
1262 static dbus_bool_t
1263 connection_is_primary_owner (DBusConnection *connection,
1264                              const char     *service_name)
1265 {
1266   BusService *service;
1267   DBusString str;
1268   BusRegistry *registry;
1269
1270   _dbus_assert (connection != NULL);
1271   
1272   registry = bus_connection_get_registry (connection);
1273
1274   _dbus_string_init_const (&str, service_name);
1275   service = bus_registry_lookup (registry, &str);
1276
1277   if (service == NULL)
1278     return FALSE; /* Service doesn't exist so connection can't own it. */
1279
1280   return bus_service_get_primary_owners_connection (service) == connection;
1281 }
1282
1283 static dbus_bool_t
1284 match_rule_matches (BusMatchRule    *rule,
1285                     DBusConnection  *sender,
1286                     DBusConnection  *addressed_recipient,
1287                     DBusMessage     *message)
1288 {
1289   /* All features of the match rule are AND'd together,
1290    * so FALSE if any of them don't match.
1291    */
1292
1293   /* sender/addressed_recipient of #NULL may mean bus driver,
1294    * or for addressed_recipient may mean a message with no
1295    * specific recipient (i.e. a signal)
1296    */
1297   
1298   if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
1299     {
1300       _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
1301
1302       if (rule->message_type != dbus_message_get_type (message))
1303         return FALSE;
1304     }
1305
1306   if (rule->flags & BUS_MATCH_INTERFACE)
1307     {
1308       const char *iface;
1309
1310       _dbus_assert (rule->interface != NULL);
1311
1312       iface = dbus_message_get_interface (message);
1313       if (iface == NULL)
1314         return FALSE;
1315
1316       if (strcmp (iface, rule->interface) != 0)
1317         return FALSE;
1318     }
1319
1320   if (rule->flags & BUS_MATCH_MEMBER)
1321     {
1322       const char *member;
1323
1324       _dbus_assert (rule->member != NULL);
1325
1326       member = dbus_message_get_member (message);
1327       if (member == NULL)
1328         return FALSE;
1329
1330       if (strcmp (member, rule->member) != 0)
1331         return FALSE;
1332     }
1333
1334   if (rule->flags & BUS_MATCH_SENDER)
1335     {
1336       _dbus_assert (rule->sender != NULL);
1337
1338       if (sender == NULL)
1339         {
1340           if (strcmp (rule->sender,
1341                       DBUS_SERVICE_DBUS) != 0)
1342             return FALSE;
1343         }
1344       else
1345         {
1346           if (!connection_is_primary_owner (sender, rule->sender))
1347             return FALSE;
1348         }
1349     }
1350
1351   if (rule->flags & BUS_MATCH_DESTINATION)
1352     {
1353       const char *destination;
1354
1355       _dbus_assert (rule->destination != NULL);
1356
1357       destination = dbus_message_get_destination (message);
1358       if (destination == NULL)
1359         return FALSE;
1360
1361       if (addressed_recipient == NULL)
1362         {          
1363           if (strcmp (rule->destination,
1364                       DBUS_SERVICE_DBUS) != 0)
1365             return FALSE;
1366         }
1367       else
1368         {
1369           if (!connection_is_primary_owner (addressed_recipient, rule->destination))
1370             return FALSE;
1371         }
1372     }
1373
1374   if (rule->flags & BUS_MATCH_PATH)
1375     {
1376       const char *path;
1377
1378       _dbus_assert (rule->path != NULL);
1379
1380       path = dbus_message_get_path (message);
1381       if (path == NULL)
1382         return FALSE;
1383
1384       if (strcmp (path, rule->path) != 0)
1385         return FALSE;
1386     }
1387
1388   if (rule->flags & BUS_MATCH_ARGS)
1389     {
1390       int i;
1391       DBusMessageIter iter;
1392       
1393       _dbus_assert (rule->args != NULL);
1394
1395       dbus_message_iter_init (message, &iter);
1396       
1397       i = 0;
1398       while (i < rule->args_len)
1399         {
1400           int current_type;
1401           const char *expected_arg;
1402
1403           expected_arg = rule->args[i];
1404           
1405           current_type = dbus_message_iter_get_arg_type (&iter);
1406
1407           if (expected_arg != NULL)
1408             {
1409               const char *actual_arg;
1410               
1411               if (current_type != DBUS_TYPE_STRING)
1412                 return FALSE;
1413
1414               actual_arg = NULL;
1415               dbus_message_iter_get_basic (&iter, &actual_arg);
1416               _dbus_assert (actual_arg != NULL);
1417
1418               if (strcmp (expected_arg, actual_arg) != 0)
1419                 return FALSE;
1420             }
1421           
1422           if (current_type != DBUS_TYPE_INVALID)
1423             dbus_message_iter_next (&iter);
1424
1425           ++i;
1426         }
1427     }
1428   
1429   return TRUE;
1430 }
1431
1432 dbus_bool_t
1433 bus_matchmaker_get_recipients (BusMatchmaker   *matchmaker,
1434                                BusConnections  *connections,
1435                                DBusConnection  *sender,
1436                                DBusConnection  *addressed_recipient,
1437                                DBusMessage     *message,
1438                                DBusList       **recipients_p)
1439 {
1440   /* FIXME for now this is a wholly unoptimized linear search */
1441   /* Guessing the important optimization is to skip the signal-related
1442    * match lists when processing method call and exception messages.
1443    * So separate match rule lists for signals?
1444    */
1445   
1446   DBusList *link;
1447
1448   _dbus_assert (*recipients_p == NULL);
1449
1450   /* This avoids sending same message to the same connection twice.
1451    * Purpose of the stamp instead of a bool is to avoid iterating over
1452    * all connections resetting the bool each time.
1453    */
1454   bus_connections_increment_stamp (connections);
1455
1456   /* addressed_recipient is already receiving the message, don't add to list.
1457    * NULL addressed_recipient means either bus driver, or this is a signal
1458    * and thus lacks a specific addressed_recipient.
1459    */
1460   if (addressed_recipient != NULL)
1461     bus_connection_mark_stamp (addressed_recipient);
1462
1463   link = _dbus_list_get_first_link (&matchmaker->all_rules);
1464   while (link != NULL)
1465     {
1466       BusMatchRule *rule;
1467
1468       rule = link->data;
1469
1470 #ifdef DBUS_ENABLE_VERBOSE_MODE
1471       {
1472         char *s = match_rule_to_string (rule);
1473         
1474         _dbus_verbose ("Checking whether message matches rule %s for connection %p\n",
1475                        s, rule->matches_go_to);
1476         dbus_free (s);
1477       }
1478 #endif
1479       
1480       if (match_rule_matches (rule,
1481                               sender, addressed_recipient, message))
1482         {
1483           _dbus_verbose ("Rule matched\n");
1484           
1485           /* Append to the list if we haven't already */
1486           if (bus_connection_mark_stamp (rule->matches_go_to))
1487             {
1488               if (!_dbus_list_append (recipients_p, rule->matches_go_to))
1489                 goto nomem;
1490             }
1491 #ifdef DBUS_ENABLE_VERBOSE_MODE
1492           else
1493             {
1494               _dbus_verbose ("Connection already receiving this message, so not adding again\n");
1495             }
1496 #endif /* DBUS_ENABLE_VERBOSE_MODE */
1497         }
1498
1499       link = _dbus_list_get_next_link (&matchmaker->all_rules, link);
1500     }
1501
1502   return TRUE;
1503
1504  nomem:
1505   _dbus_list_clear (recipients_p);
1506   return FALSE;
1507 }
1508
1509 #ifdef DBUS_BUILD_TESTS
1510 #include "test.h"
1511 #include <stdlib.h>
1512
1513 static BusMatchRule*
1514 check_parse (dbus_bool_t should_succeed,
1515              const char *text)
1516 {
1517   BusMatchRule *rule;
1518   DBusString str;
1519   DBusError error;
1520
1521   dbus_error_init (&error);
1522
1523   _dbus_string_init_const (&str, text);
1524   
1525   rule = bus_match_rule_parse (NULL, &str, &error);
1526   if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
1527     {
1528       dbus_error_free (&error);
1529       return NULL;
1530     }
1531
1532   if (should_succeed && rule == NULL)
1533     {
1534       _dbus_warn ("Failed to parse: %s: %s: \"%s\"\n",
1535                   error.name, error.message,
1536                   _dbus_string_get_const_data (&str));
1537       exit (1);
1538     }
1539
1540   if (!should_succeed && rule != NULL)
1541     {
1542       _dbus_warn ("Failed to fail to parse: \"%s\"\n",
1543                   _dbus_string_get_const_data (&str));
1544       exit (1);
1545     }
1546
1547   dbus_error_free (&error);
1548
1549   return rule;
1550 }
1551
1552 static void
1553 assert_large_rule (BusMatchRule *rule)
1554 {
1555   _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
1556   _dbus_assert (rule->flags & BUS_MATCH_SENDER);
1557   _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
1558   _dbus_assert (rule->flags & BUS_MATCH_MEMBER);
1559   _dbus_assert (rule->flags & BUS_MATCH_DESTINATION);
1560   _dbus_assert (rule->flags & BUS_MATCH_PATH);
1561
1562   _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
1563   _dbus_assert (rule->interface != NULL);
1564   _dbus_assert (rule->member != NULL);
1565   _dbus_assert (rule->sender != NULL);
1566   _dbus_assert (rule->destination != NULL);
1567   _dbus_assert (rule->path != NULL);
1568
1569   _dbus_assert (strcmp (rule->interface, "org.freedesktop.DBusInterface") == 0);
1570   _dbus_assert (strcmp (rule->sender, "org.freedesktop.DBusSender") == 0);
1571   _dbus_assert (strcmp (rule->member, "Foo") == 0);
1572   _dbus_assert (strcmp (rule->path, "/bar/foo") == 0);
1573   _dbus_assert (strcmp (rule->destination, ":452345.34") == 0);
1574 }
1575
1576 static dbus_bool_t
1577 test_parsing (void *data)
1578 {
1579   BusMatchRule *rule;
1580
1581   rule = check_parse (TRUE, "type='signal',sender='org.freedesktop.DBusSender',interface='org.freedesktop.DBusInterface',member='Foo',path='/bar/foo',destination=':452345.34'");
1582   if (rule != NULL)
1583     {
1584       assert_large_rule (rule);
1585       bus_match_rule_unref (rule);
1586     }
1587
1588   /* With extra whitespace and useless quotes */
1589   rule = check_parse (TRUE, "    type='signal',  \tsender='org.freedes''ktop.DBusSender',   interface='org.freedesktop.DBusInterface''''', \tmember='Foo',path='/bar/foo',destination=':452345.34'''''");
1590   if (rule != NULL)
1591     {
1592       assert_large_rule (rule);
1593       bus_match_rule_unref (rule);
1594     }
1595
1596
1597   /* A simple signal connection */
1598   rule = check_parse (TRUE, "type='signal',path='/foo',interface='org.Bar'");
1599   if (rule != NULL)
1600     {
1601       _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
1602       _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
1603       _dbus_assert (rule->flags & BUS_MATCH_PATH);
1604
1605       _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
1606       _dbus_assert (rule->interface != NULL);
1607       _dbus_assert (rule->path != NULL);
1608
1609       _dbus_assert (strcmp (rule->interface, "org.Bar") == 0);
1610       _dbus_assert (strcmp (rule->path, "/foo") == 0);
1611   
1612       bus_match_rule_unref (rule);
1613     }
1614
1615   /* argN */
1616   rule = check_parse (TRUE, "arg0='foo'");
1617   if (rule != NULL)
1618     {
1619       _dbus_assert (rule->flags == BUS_MATCH_ARGS);
1620       _dbus_assert (rule->args != NULL);
1621       _dbus_assert (rule->args_len == 1);
1622       _dbus_assert (rule->args[0] != NULL);
1623       _dbus_assert (rule->args[1] == NULL);
1624       _dbus_assert (strcmp (rule->args[0], "foo") == 0);
1625
1626       bus_match_rule_unref (rule);
1627     }
1628   
1629   rule = check_parse (TRUE, "arg1='foo'");
1630   if (rule != NULL)
1631     {
1632       _dbus_assert (rule->flags == BUS_MATCH_ARGS);
1633       _dbus_assert (rule->args != NULL);
1634       _dbus_assert (rule->args_len == 2);
1635       _dbus_assert (rule->args[0] == NULL);
1636       _dbus_assert (rule->args[1] != NULL);
1637       _dbus_assert (rule->args[2] == NULL);
1638       _dbus_assert (strcmp (rule->args[1], "foo") == 0);
1639
1640       bus_match_rule_unref (rule);
1641     }
1642
1643   rule = check_parse (TRUE, "arg2='foo'");
1644   if (rule != NULL)
1645     {
1646       _dbus_assert (rule->flags == BUS_MATCH_ARGS);
1647       _dbus_assert (rule->args != NULL);
1648       _dbus_assert (rule->args_len == 3);
1649       _dbus_assert (rule->args[0] == NULL);
1650       _dbus_assert (rule->args[1] == NULL);
1651       _dbus_assert (rule->args[2] != NULL);
1652       _dbus_assert (rule->args[3] == NULL);
1653       _dbus_assert (strcmp (rule->args[2], "foo") == 0);
1654
1655       bus_match_rule_unref (rule);
1656     }
1657   
1658   rule = check_parse (TRUE, "arg40='foo'");
1659   if (rule != NULL)
1660     {
1661       _dbus_assert (rule->flags == BUS_MATCH_ARGS);
1662       _dbus_assert (rule->args != NULL);
1663       _dbus_assert (rule->args_len == 41);
1664       _dbus_assert (rule->args[0] == NULL);
1665       _dbus_assert (rule->args[1] == NULL);
1666       _dbus_assert (rule->args[40] != NULL);
1667       _dbus_assert (rule->args[41] == NULL);
1668       _dbus_assert (strcmp (rule->args[40], "foo") == 0);
1669
1670       bus_match_rule_unref (rule);
1671     }
1672   
1673   rule = check_parse (TRUE, "arg63='foo'");
1674   if (rule != NULL)
1675     {
1676       _dbus_assert (rule->flags == BUS_MATCH_ARGS);
1677       _dbus_assert (rule->args != NULL);
1678       _dbus_assert (rule->args_len == 64);
1679       _dbus_assert (rule->args[0] == NULL);
1680       _dbus_assert (rule->args[1] == NULL);
1681       _dbus_assert (rule->args[63] != NULL);
1682       _dbus_assert (rule->args[64] == NULL);
1683       _dbus_assert (strcmp (rule->args[63], "foo") == 0);
1684
1685       bus_match_rule_unref (rule);
1686     }
1687   
1688   /* Too-large argN */
1689   rule = check_parse (FALSE, "arg300='foo'");
1690   _dbus_assert (rule == NULL);
1691   rule = check_parse (FALSE, "arg64='foo'");
1692   _dbus_assert (rule == NULL);
1693
1694   /* No N in argN */
1695   rule = check_parse (FALSE, "arg='foo'");
1696   _dbus_assert (rule == NULL);
1697   rule = check_parse (FALSE, "argv='foo'");
1698   _dbus_assert (rule == NULL);
1699   rule = check_parse (FALSE, "arg3junk='foo'");
1700   _dbus_assert (rule == NULL);
1701   rule = check_parse (FALSE, "argument='foo'");
1702   _dbus_assert (rule == NULL);
1703   
1704   /* Reject duplicates */
1705   rule = check_parse (FALSE, "type='signal',type='method_call'");
1706   _dbus_assert (rule == NULL);
1707
1708   /* Duplicates with the argN code */
1709   rule = check_parse (FALSE, "arg0='foo',arg0='bar'");
1710   _dbus_assert (rule == NULL);
1711   rule = check_parse (FALSE, "arg3='foo',arg3='bar'");
1712   _dbus_assert (rule == NULL);
1713   rule = check_parse (FALSE, "arg30='foo',arg30='bar'");
1714   _dbus_assert (rule == NULL);
1715   
1716   /* Reject broken keys */
1717   rule = check_parse (FALSE, "blah='signal'");
1718   _dbus_assert (rule == NULL);
1719
1720   /* Reject broken values */
1721   rule = check_parse (FALSE, "type='chouin'");
1722   _dbus_assert (rule == NULL);
1723   rule = check_parse (FALSE, "interface='abc@def++'");
1724   _dbus_assert (rule == NULL);
1725   rule = check_parse (FALSE, "service='youpi'");
1726   _dbus_assert (rule == NULL);
1727
1728   /* Allow empty rule */
1729   rule = check_parse (TRUE, "");
1730   if (rule != NULL)
1731     {
1732       _dbus_assert (rule->flags == 0);
1733       
1734       bus_match_rule_unref (rule);
1735     }
1736
1737   /* All-whitespace rule is the same as empty */
1738   rule = check_parse (TRUE, "    \t");
1739   if (rule != NULL)
1740     {
1741       _dbus_assert (rule->flags == 0);
1742       
1743       bus_match_rule_unref (rule);
1744     }
1745
1746   /* But with non-whitespace chars and no =value, it's not OK */
1747   rule = check_parse (FALSE, "type");
1748   _dbus_assert (rule == NULL);
1749   
1750   return TRUE;
1751 }
1752
1753 static struct {
1754   const char *first;
1755   const char *second;
1756 } equality_tests[] = {
1757   { "type='signal'", "type='signal'" },
1758   { "type='signal',interface='foo.bar'", "interface='foo.bar',type='signal'" },
1759   { "type='signal',member='bar'", "member='bar',type='signal'" },
1760   { "type='method_call',sender=':1.0'", "sender=':1.0',type='method_call'" },
1761   { "type='method_call',destination=':1.0'", "destination=':1.0',type='method_call'" },
1762   { "type='method_call',path='/foo/bar'", "path='/foo/bar',type='method_call'" },
1763   { "type='method_call',arg0='blah'", "arg0='blah',type='method_call'" },
1764   { "type='method_call',arg0='boo'", "arg0='boo',type='method_call'" },
1765   { "type='method_call',arg0='blah',arg1='baz'", "arg0='blah',arg1='baz',type='method_call'" },
1766   { "type='method_call',arg3='foosh'", "arg3='foosh',type='method_call'" },
1767   { "arg3='fool'", "arg3='fool'" },
1768   { "member='food'", "member='food'" }
1769 };
1770
1771 static void
1772 test_equality (void)
1773 {
1774   int i;
1775   
1776   i = 0;
1777   while (i < _DBUS_N_ELEMENTS (equality_tests))
1778     {
1779       BusMatchRule *first;
1780       BusMatchRule *second;
1781       int j;
1782       
1783       first = check_parse (TRUE, equality_tests[i].first);
1784       _dbus_assert (first != NULL);
1785       second = check_parse (TRUE, equality_tests[i].second);
1786       _dbus_assert (second != NULL);
1787
1788       if (!match_rule_equal (first, second))
1789         {
1790           _dbus_warn ("rule %s and %s should have been equal\n",
1791                       equality_tests[i].first,
1792                       equality_tests[i].second);
1793           exit (1);
1794         }
1795
1796       bus_match_rule_unref (second);
1797
1798       /* Check that the rule is not equal to any of the
1799        * others besides its pair match
1800        */
1801       j = 0;
1802       while (j < _DBUS_N_ELEMENTS (equality_tests))
1803         {
1804           if (i != j)
1805             {
1806               second = check_parse (TRUE, equality_tests[j].second);
1807
1808               if (match_rule_equal (first, second))
1809                 {
1810                   _dbus_warn ("rule %s and %s should not have been equal\n",
1811                               equality_tests[i].first,
1812                               equality_tests[j].second);
1813                   exit (1);
1814                 }
1815               
1816               bus_match_rule_unref (second);
1817             }
1818           
1819           ++j;
1820         }
1821
1822       bus_match_rule_unref (first);
1823
1824       ++i;
1825     }
1826 }
1827
1828 static const char*
1829 should_match_message_1[] = {
1830   "type='signal'",
1831   "member='Frobated'",
1832   "arg0='foobar'",
1833   "type='signal',member='Frobated'",
1834   "type='signal',member='Frobated',arg0='foobar'",
1835   "member='Frobated',arg0='foobar'",
1836   "type='signal',arg0='foobar'",
1837   NULL
1838 };
1839
1840 static const char*
1841 should_not_match_message_1[] = {
1842   "type='method_call'",
1843   "type='error'",
1844   "type='method_return'",
1845   "type='signal',member='Oopsed'",
1846   "arg0='blah'",
1847   "arg1='foobar'",
1848   "arg2='foobar'",
1849   "arg3='foobar'",
1850   "arg0='3'",
1851   "arg1='3'",
1852   "arg0='foobar',arg1='abcdef'",
1853   "arg0='foobar',arg1='abcdef',arg2='abcdefghi',arg3='abcdefghi',arg4='abcdefghi'",
1854   "arg0='foobar',arg1='abcdef',arg4='abcdefghi',arg3='abcdefghi',arg2='abcdefghi'",
1855   NULL
1856 };
1857
1858 static void
1859 check_matches (dbus_bool_t  expected_to_match,
1860                int          number,
1861                DBusMessage *message,
1862                const char  *rule_text)
1863 {
1864   BusMatchRule *rule;
1865   dbus_bool_t matched;
1866
1867   rule = check_parse (TRUE, rule_text);
1868   _dbus_assert (rule != NULL);
1869
1870   /* We can't test sender/destination rules since we pass NULL here */
1871   matched = match_rule_matches (rule, NULL, NULL, message);
1872
1873   if (matched != expected_to_match)
1874     {
1875       _dbus_warn ("Expected rule %s to %s message %d, failed\n",
1876                   rule_text, expected_to_match ?
1877                   "match" : "not match", number);
1878       exit (1);
1879     }
1880
1881   bus_match_rule_unref (rule);
1882 }
1883
1884 static void
1885 check_matching (DBusMessage *message,
1886                 int          number,
1887                 const char **should_match,
1888                 const char **should_not_match)
1889 {
1890   int i;
1891
1892   i = 0;
1893   while (should_match[i] != NULL)
1894     {
1895       check_matches (TRUE, number, message, should_match[i]);
1896       ++i;
1897     }
1898
1899   i = 0;
1900   while (should_not_match[i] != NULL)
1901     {
1902       check_matches (FALSE, number, message, should_not_match[i]);
1903       ++i;
1904     }
1905 }
1906
1907 static void
1908 test_matching (void)
1909 {
1910   DBusMessage *message1;
1911   const char *v_STRING;
1912   dbus_int32_t v_INT32;
1913
1914   message1 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
1915   _dbus_assert (message1 != NULL);
1916   if (!dbus_message_set_member (message1, "Frobated"))
1917     _dbus_assert_not_reached ("oom");
1918
1919   v_STRING = "foobar";
1920   v_INT32 = 3;
1921   if (!dbus_message_append_args (message1,
1922                                  DBUS_TYPE_STRING, &v_STRING,
1923                                  DBUS_TYPE_INT32, &v_INT32,
1924                                  NULL))
1925     _dbus_assert_not_reached ("oom");
1926   
1927   check_matching (message1, 1,
1928                   should_match_message_1,
1929                   should_not_match_message_1);
1930   
1931   dbus_message_unref (message1);
1932 }
1933
1934 dbus_bool_t
1935 bus_signals_test (const DBusString *test_data_dir)
1936 {
1937   BusMatchmaker *matchmaker;
1938
1939   matchmaker = bus_matchmaker_new ();
1940   bus_matchmaker_ref (matchmaker);
1941   bus_matchmaker_unref (matchmaker);
1942   bus_matchmaker_unref (matchmaker);
1943
1944   if (!_dbus_test_oom_handling ("parsing match rules", test_parsing, NULL))
1945     _dbus_assert_not_reached ("Parsing match rules test failed");
1946
1947   test_equality ();
1948
1949   test_matching ();
1950   
1951   return TRUE;
1952 }
1953
1954 #endif /* DBUS_BUILD_TESTS */
1955