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