2005-01-15 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / signals.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* signals.c  Bus signal connection implementation
3  *
4  * Copyright (C) 2003  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
44 BusMatchRule*
45 bus_match_rule_new (DBusConnection *matches_go_to)
46 {
47   BusMatchRule *rule;
48
49   rule = dbus_new0 (BusMatchRule, 1);
50   if (rule == NULL)
51     return NULL;
52
53   rule->refcount = 1;
54   rule->matches_go_to = matches_go_to;
55
56 #ifndef DBUS_BUILD_TESTS
57   _dbus_assert (rule->matches_go_to != NULL);
58 #endif
59   
60   return rule;
61 }
62
63 BusMatchRule *
64 bus_match_rule_ref (BusMatchRule *rule)
65 {
66   _dbus_assert (rule->refcount > 0);
67
68   rule->refcount += 1;
69
70   return rule;
71 }
72
73 void
74 bus_match_rule_unref (BusMatchRule *rule)
75 {
76   _dbus_assert (rule->refcount > 0);
77
78   rule->refcount -= 1;
79   if (rule->refcount == 0)
80     {
81       dbus_free (rule->interface);
82       dbus_free (rule->member);
83       dbus_free (rule->sender);
84       dbus_free (rule->destination);
85       dbus_free (rule->path);
86       dbus_free (rule);
87     }
88 }
89
90 #ifdef DBUS_ENABLE_VERBOSE_MODE
91 static char*
92 match_rule_to_string (BusMatchRule *rule)
93 {
94   DBusString str;
95   char *ret;
96   
97   if (!_dbus_string_init (&str))
98     {
99       char *s;
100       while ((s = _dbus_strdup ("nomem")) == NULL)
101         ; /* only OK for debug spew... */
102       return s;
103     }
104   
105   if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
106     {
107       /* FIXME make type readable */
108       if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type))
109         goto nomem;
110     }
111
112   if (rule->flags & BUS_MATCH_INTERFACE)
113     {
114       if (_dbus_string_get_length (&str) > 0)
115         {
116           if (!_dbus_string_append (&str, ","))
117             goto nomem;
118         }
119       
120       if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface))
121         goto nomem;
122     }
123
124   if (rule->flags & BUS_MATCH_MEMBER)
125     {
126       if (_dbus_string_get_length (&str) > 0)
127         {
128           if (!_dbus_string_append (&str, ","))
129             goto nomem;
130         }
131       
132       if (!_dbus_string_append_printf (&str, "member='%s'", rule->member))
133         goto nomem;
134     }
135
136   if (rule->flags & BUS_MATCH_PATH)
137     {
138       if (_dbus_string_get_length (&str) > 0)
139         {
140           if (!_dbus_string_append (&str, ","))
141             goto nomem;
142         }
143       
144       if (!_dbus_string_append_printf (&str, "path='%s'", rule->path))
145         goto nomem;
146     }
147
148   if (rule->flags & BUS_MATCH_SENDER)
149     {
150       if (_dbus_string_get_length (&str) > 0)
151         {
152           if (!_dbus_string_append (&str, ","))
153             goto nomem;
154         }
155       
156       if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender))
157         goto nomem;
158     }
159
160   if (rule->flags & BUS_MATCH_DESTINATION)
161     {
162       if (_dbus_string_get_length (&str) > 0)
163         {
164           if (!_dbus_string_append (&str, ","))
165             goto nomem;
166         }
167       
168       if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination))
169         goto nomem;
170     }
171   
172   if (!_dbus_string_steal_data (&str, &ret))
173     goto nomem;
174
175   _dbus_string_free (&str);
176   return ret;
177   
178  nomem:
179   _dbus_string_free (&str);
180   {
181     char *s;
182     while ((s = _dbus_strdup ("nomem")) == NULL)
183       ;  /* only OK for debug spew... */
184     return s;
185   }
186 }
187 #endif /* DBUS_ENABLE_VERBOSE_MODE */
188
189 dbus_bool_t
190 bus_match_rule_set_message_type (BusMatchRule *rule,
191                                  int           type)
192 {
193   rule->flags |= BUS_MATCH_MESSAGE_TYPE;
194
195   rule->message_type = type;
196
197   return TRUE;
198 }
199
200 dbus_bool_t
201 bus_match_rule_set_interface (BusMatchRule *rule,
202                               const char   *interface)
203 {
204   char *new;
205
206   _dbus_assert (interface != NULL);
207
208   new = _dbus_strdup (interface);
209   if (new == NULL)
210     return FALSE;
211
212   rule->flags |= BUS_MATCH_INTERFACE;
213   dbus_free (rule->interface);
214   rule->interface = new;
215
216   return TRUE;
217 }
218
219 dbus_bool_t
220 bus_match_rule_set_member (BusMatchRule *rule,
221                            const char   *member)
222 {
223   char *new;
224
225   _dbus_assert (member != NULL);
226
227   new = _dbus_strdup (member);
228   if (new == NULL)
229     return FALSE;
230
231   rule->flags |= BUS_MATCH_MEMBER;
232   dbus_free (rule->member);
233   rule->member = new;
234
235   return TRUE;
236 }
237
238 dbus_bool_t
239 bus_match_rule_set_sender (BusMatchRule *rule,
240                            const char   *sender)
241 {
242   char *new;
243
244   _dbus_assert (sender != NULL);
245
246   new = _dbus_strdup (sender);
247   if (new == NULL)
248     return FALSE;
249
250   rule->flags |= BUS_MATCH_SENDER;
251   dbus_free (rule->sender);
252   rule->sender = new;
253
254   return TRUE;
255 }
256
257 dbus_bool_t
258 bus_match_rule_set_destination (BusMatchRule *rule,
259                                 const char   *destination)
260 {
261   char *new;
262
263   _dbus_assert (destination != NULL);
264
265   new = _dbus_strdup (destination);
266   if (new == NULL)
267     return FALSE;
268
269   rule->flags |= BUS_MATCH_DESTINATION;
270   dbus_free (rule->destination);
271   rule->destination = new;
272
273   return TRUE;
274 }
275
276 dbus_bool_t
277 bus_match_rule_set_path (BusMatchRule *rule,
278                          const char   *path)
279 {
280   char *new;
281
282   _dbus_assert (path != NULL);
283
284   new = _dbus_strdup (path);
285   if (new == NULL)
286     return FALSE;
287
288   rule->flags |= BUS_MATCH_PATH;
289   dbus_free (rule->path);
290   rule->path = new;
291
292   return TRUE;
293 }
294
295 #define ISWHITE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
296
297 static dbus_bool_t
298 find_key (const DBusString *str,
299           int               start,
300           DBusString       *key,
301           int              *value_pos,
302           DBusError        *error)
303 {
304   const char *p;
305   const char *s;
306   const char *key_start;
307   const char *key_end;
308
309   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
310   
311   s = _dbus_string_get_const_data (str);
312
313   p = s + start;
314
315   while (*p && ISWHITE (*p))
316     ++p;
317
318   key_start = p;
319
320   while (*p && *p != '=' && !ISWHITE (*p))
321     ++p;
322
323   key_end = p;
324
325   while (*p && ISWHITE (*p))
326     ++p;
327   
328   if (key_start == key_end)
329     {
330       /* Empty match rules or trailing whitespace are OK */
331       *value_pos = p - s;
332       return TRUE;
333     }
334
335   if (*p != '=')
336     {
337       dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
338                       "Match rule has a key with no subsequent '=' character");
339       return FALSE;
340     }
341   ++p;
342   
343   if (!_dbus_string_append_len (key, key_start, key_end - key_start))
344     {
345       BUS_SET_OOM (error);
346       return FALSE;
347     }
348
349   *value_pos = p - s;
350   
351   return TRUE;
352 }
353
354 static dbus_bool_t
355 find_value (const DBusString *str,
356             int               start,
357             const char       *key,
358             DBusString       *value,
359             int              *value_end,
360             DBusError        *error)
361 {
362   const char *p;
363   const char *s;
364   char quote_char;
365   int orig_len;
366
367   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
368   
369   orig_len = _dbus_string_get_length (value);
370   
371   s = _dbus_string_get_const_data (str);
372
373   p = s + start;
374
375   quote_char = '\0';
376
377   while (*p)
378     {
379       if (quote_char == '\0')
380         {
381           switch (*p)
382             {
383             case '\0':
384               goto done;
385
386             case '\'':
387               quote_char = '\'';
388               goto next;
389               
390             case ',':
391               ++p;
392               goto done;
393
394             case '\\':
395               quote_char = '\\';
396               goto next;
397               
398             default:
399               if (!_dbus_string_append_byte (value, *p))
400                 {
401                   BUS_SET_OOM (error);
402                   goto failed;
403                 }
404             }
405         }
406       else if (quote_char == '\\')
407         {
408           /* \ only counts as an escape if escaping a quote mark */
409           if (*p != '\'')
410             {
411               if (!_dbus_string_append_byte (value, '\\'))
412                 {
413                   BUS_SET_OOM (error);
414                   goto failed;
415                 }
416             }
417
418           if (!_dbus_string_append_byte (value, *p))
419             {
420               BUS_SET_OOM (error);
421               goto failed;
422             }
423           
424           quote_char = '\0';
425         }
426       else
427         {
428           _dbus_assert (quote_char == '\'');
429
430           if (*p == '\'')
431             {
432               quote_char = '\0';
433             }
434           else
435             {
436               if (!_dbus_string_append_byte (value, *p))
437                 {
438                   BUS_SET_OOM (error);
439                   goto failed;
440                 }
441             }
442         }
443
444     next:
445       ++p;
446     }
447
448  done:
449
450   if (quote_char == '\\')
451     {
452       if (!_dbus_string_append_byte (value, '\\'))
453         {
454           BUS_SET_OOM (error);
455           goto failed;
456         }
457     }
458   else if (quote_char == '\'')
459     {
460       dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
461                       "Unbalanced quotation marks in match rule");
462       goto failed;
463     }
464   else
465     _dbus_assert (quote_char == '\0');
466
467   /* Zero-length values are allowed */
468   
469   *value_end = p - s;
470   
471   return TRUE;
472
473  failed:
474   _DBUS_ASSERT_ERROR_IS_SET (error);
475   _dbus_string_set_length (value, orig_len);
476   return FALSE;
477 }
478
479 /* duplicates aren't allowed so the real legitimate max is only 6 or
480  * so. Leaving extra so we don't have to bother to update it.
481  */
482 #define MAX_RULE_TOKENS 16
483
484 /* this is slightly too high level to be termed a "token"
485  * but let's not be pedantic.
486  */
487 typedef struct
488 {
489   char *key;
490   char *value;
491 } RuleToken;
492
493 static dbus_bool_t
494 tokenize_rule (const DBusString *rule_text,
495                RuleToken         tokens[MAX_RULE_TOKENS],
496                DBusError        *error) 
497 {
498   int i;
499   int pos;
500   DBusString key;
501   DBusString value;
502   dbus_bool_t retval;
503
504   retval = FALSE;
505   
506   if (!_dbus_string_init (&key))
507     {
508       BUS_SET_OOM (error);
509       return FALSE;
510     }
511
512   if (!_dbus_string_init (&value))
513     {
514       _dbus_string_free (&key);
515       BUS_SET_OOM (error);
516       return FALSE;
517     }
518
519   i = 0;
520   pos = 0;
521   while (i < MAX_RULE_TOKENS &&
522          pos < _dbus_string_get_length (rule_text))
523     {
524       _dbus_assert (tokens[i].key == NULL);
525       _dbus_assert (tokens[i].value == NULL);
526
527       if (!find_key (rule_text, pos, &key, &pos, error))
528         goto out;
529
530       if (_dbus_string_get_length (&key) == 0)
531         goto next;
532       
533       if (!_dbus_string_steal_data (&key, &tokens[i].key))
534         {
535           BUS_SET_OOM (error);
536           goto out;
537         }
538
539       if (!find_value (rule_text, pos, tokens[i].key, &value, &pos, error))
540         goto out;
541
542       if (!_dbus_string_steal_data (&value, &tokens[i].value))
543         {
544           BUS_SET_OOM (error);
545           goto out;
546         }
547
548     next:
549       ++i;
550     }
551
552   retval = TRUE;
553   
554  out:
555   if (!retval)
556     {
557       i = 0;
558       while (tokens[i].key || tokens[i].value)
559         {
560           dbus_free (tokens[i].key);
561           dbus_free (tokens[i].value);
562           tokens[i].key = NULL;
563           tokens[i].value = NULL;
564           ++i;
565         }
566     }
567   
568   _dbus_string_free (&key);
569   _dbus_string_free (&value);
570   
571   return retval;
572 }
573
574 /*
575  * The format is comma-separated with strings quoted with single quotes
576  * as for the shell (to escape a literal single quote, use '\'').
577  *
578  * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',
579  * path='/bar/foo',destination=':452345.34'
580  *
581  */
582 BusMatchRule*
583 bus_match_rule_parse (DBusConnection   *matches_go_to,
584                       const DBusString *rule_text,
585                       DBusError        *error)
586 {
587   BusMatchRule *rule;
588   RuleToken tokens[MAX_RULE_TOKENS+1]; /* NULL termination + 1 */
589   int i;
590   
591   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
592
593   if (_dbus_string_get_length (rule_text) > DBUS_MAXIMUM_MATCH_RULE_LENGTH)
594     {
595       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
596                       "Match rule text is %d bytes, maximum is %d",
597                       _dbus_string_get_length (rule_text),
598                       DBUS_MAXIMUM_MATCH_RULE_LENGTH);
599       return NULL;
600     }
601   
602   memset (tokens, '\0', sizeof (tokens));
603   
604   rule = bus_match_rule_new (matches_go_to);
605   if (rule == NULL)
606     {
607       BUS_SET_OOM (error);
608       goto failed;
609     }
610   
611   if (!tokenize_rule (rule_text, tokens, error))
612     goto failed;
613   
614   i = 0;
615   while (tokens[i].key != NULL)
616     {
617       DBusString tmp_str;
618       int len;
619       const char *key = tokens[i].key;
620       const char *value = tokens[i].value;
621       
622       _dbus_string_init_const (&tmp_str, value);
623       len = _dbus_string_get_length (&tmp_str);
624
625       if (strcmp (key, "type") == 0)
626         {
627           int t;
628
629           if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
630             {
631               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
632                               "Key %s specified twice in match rule\n", key);
633               goto failed;
634             }
635           
636           t = dbus_message_type_from_string (value);
637           
638           if (t == DBUS_MESSAGE_TYPE_INVALID)
639             {
640               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
641                               "Invalid message type (%s) in match rule\n", value);
642               goto failed;
643             }
644
645           if (!bus_match_rule_set_message_type (rule, t))
646             {
647               BUS_SET_OOM (error);
648               goto failed;
649             }
650         }
651       else if (strcmp (key, "sender") == 0)
652         {
653           if (rule->flags & BUS_MATCH_SENDER)
654             {
655               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
656                               "Key %s specified twice in match rule\n", key);
657               goto failed;
658             }
659
660           if (!_dbus_validate_service (&tmp_str, 0, len))
661             {
662               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
663                               "Sender service name '%s' is invalid\n", value);
664               goto failed;
665             }
666
667           if (!bus_match_rule_set_sender (rule, value))
668             {
669               BUS_SET_OOM (error);
670               goto failed;
671             }
672         }
673       else if (strcmp (key, "interface") == 0)
674         {
675           if (rule->flags & BUS_MATCH_INTERFACE)
676             {
677               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
678                               "Key %s specified twice in match rule\n", key);
679               goto failed;
680             }
681
682           if (!_dbus_validate_interface (&tmp_str, 0, len))
683             {
684               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
685                               "Interface name '%s' is invalid\n", value);
686               goto failed;
687             }
688
689           if (!bus_match_rule_set_interface (rule, value))
690             {
691               BUS_SET_OOM (error);
692               goto failed;
693             }
694         }
695       else if (strcmp (key, "member") == 0)
696         {
697           if (rule->flags & BUS_MATCH_MEMBER)
698             {
699               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
700                               "Key %s specified twice in match rule\n", key);
701               goto failed;
702             }
703
704           if (!_dbus_validate_member (&tmp_str, 0, len))
705             {
706               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
707                               "Member name '%s' is invalid\n", value);
708               goto failed;
709             }
710
711           if (!bus_match_rule_set_member (rule, value))
712             {
713               BUS_SET_OOM (error);
714               goto failed;
715             }
716         }
717       else if (strcmp (key, "path") == 0)
718         {
719           if (rule->flags & BUS_MATCH_PATH)
720             {
721               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
722                               "Key %s specified twice in match rule\n", key);
723               goto failed;
724             }
725
726           if (!_dbus_validate_path (&tmp_str, 0, len))
727             {
728               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
729                               "Path '%s' is invalid\n", value);
730               goto failed;
731             }
732
733           if (!bus_match_rule_set_path (rule, value))
734             {
735               BUS_SET_OOM (error);
736               goto failed;
737             }
738         }
739       else if (strcmp (key, "destination") == 0)
740         {
741           if (rule->flags & BUS_MATCH_DESTINATION)
742             {
743               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
744                               "Key %s specified twice in match rule\n", key);
745               goto failed;
746             }
747
748           if (!_dbus_validate_service (&tmp_str, 0, len))
749             {
750               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
751                               "Destination service name '%s' is invalid\n", value);
752               goto failed;
753             }
754
755           if (!bus_match_rule_set_destination (rule, value))
756             {
757               BUS_SET_OOM (error);
758               goto failed;
759             }
760         }
761       else
762         {
763           dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
764                           "Unknown key \"%s\" in match rule",
765                           key);
766           goto failed;
767         }
768
769       ++i;
770     }
771   
772
773   goto out;
774   
775  failed:
776   _DBUS_ASSERT_ERROR_IS_SET (error);
777   if (rule)
778     {
779       bus_match_rule_unref (rule);
780       rule = NULL;
781     }
782
783  out:
784   
785   i = 0;
786   while (tokens[i].key || tokens[i].value)
787     {
788       _dbus_assert (i < MAX_RULE_TOKENS);
789       dbus_free (tokens[i].key);
790       dbus_free (tokens[i].value);
791       ++i;
792     }
793   
794   return rule;
795 }
796
797 struct BusMatchmaker
798 {
799   int refcount;
800
801   DBusList *all_rules;
802 };
803
804 BusMatchmaker*
805 bus_matchmaker_new (void)
806 {
807   BusMatchmaker *matchmaker;
808
809   matchmaker = dbus_new0 (BusMatchmaker, 1);
810   if (matchmaker == NULL)
811     return NULL;
812
813   matchmaker->refcount = 1;
814   
815   return matchmaker;
816 }
817
818 BusMatchmaker *
819 bus_matchmaker_ref (BusMatchmaker *matchmaker)
820 {
821   _dbus_assert (matchmaker->refcount > 0);
822
823   matchmaker->refcount += 1;
824
825   return matchmaker;
826 }
827
828 void
829 bus_matchmaker_unref (BusMatchmaker *matchmaker)
830 {
831   _dbus_assert (matchmaker->refcount > 0);
832
833   matchmaker->refcount -= 1;
834   if (matchmaker->refcount == 0)
835     {
836       while (matchmaker->all_rules != NULL)
837         {
838           BusMatchRule *rule;
839
840           rule = matchmaker->all_rules->data;
841           bus_match_rule_unref (rule);
842           _dbus_list_remove_link (&matchmaker->all_rules,
843                                   matchmaker->all_rules);
844         }
845
846       dbus_free (matchmaker);
847     }
848 }
849
850 /* The rule can't be modified after it's added. */
851 dbus_bool_t
852 bus_matchmaker_add_rule (BusMatchmaker   *matchmaker,
853                          BusMatchRule    *rule)
854 {
855   _dbus_assert (bus_connection_is_active (rule->matches_go_to));
856
857   if (!_dbus_list_append (&matchmaker->all_rules, rule))
858     return FALSE;
859
860   if (!bus_connection_add_match_rule (rule->matches_go_to, rule))
861     {
862       _dbus_list_remove_last (&matchmaker->all_rules, rule);
863       return FALSE;
864     }
865   
866   bus_match_rule_ref (rule);
867
868 #ifdef DBUS_ENABLE_VERBOSE_MODE
869   {
870     char *s = match_rule_to_string (rule);
871
872     _dbus_verbose ("Added match rule %s to connection %p\n",
873                    s, rule->matches_go_to);
874     dbus_free (s);
875   }
876 #endif
877   
878   return TRUE;
879 }
880
881 static dbus_bool_t
882 match_rule_equal (BusMatchRule *a,
883                   BusMatchRule *b)
884 {
885   if (a->flags != b->flags)
886     return FALSE;
887
888   if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
889       a->message_type != b->message_type)
890     return FALSE;
891
892   if ((a->flags & BUS_MATCH_MEMBER) &&
893       strcmp (a->member, b->member) != 0)
894     return FALSE;
895
896   if ((a->flags & BUS_MATCH_PATH) &&
897       strcmp (a->path, b->path) != 0)
898     return FALSE;
899   
900   if ((a->flags & BUS_MATCH_INTERFACE) &&
901       strcmp (a->interface, b->interface) != 0)
902     return FALSE;
903
904   if ((a->flags & BUS_MATCH_SENDER) &&
905       strcmp (a->sender, b->sender) != 0)
906     return FALSE;
907
908   if ((a->flags & BUS_MATCH_DESTINATION) &&
909       strcmp (a->destination, b->destination) != 0)
910     return FALSE;
911
912   return TRUE;
913 }
914
915 static void
916 bus_matchmaker_remove_rule_link (BusMatchmaker   *matchmaker,
917                                  DBusList        *link)
918 {
919   BusMatchRule *rule = link->data;
920   
921   bus_connection_remove_match_rule (rule->matches_go_to, rule);
922   _dbus_list_remove_link (&matchmaker->all_rules, link);
923
924 #ifdef DBUS_ENABLE_VERBOSE_MODE
925   {
926     char *s = match_rule_to_string (rule);
927
928     _dbus_verbose ("Removed match rule %s for connection %p\n",
929                    s, rule->matches_go_to);
930     dbus_free (s);
931   }
932 #endif
933   
934   bus_match_rule_unref (rule);  
935 }
936
937 void
938 bus_matchmaker_remove_rule (BusMatchmaker   *matchmaker,
939                             BusMatchRule    *rule)
940 {
941   bus_connection_remove_match_rule (rule->matches_go_to, rule);
942   _dbus_list_remove (&matchmaker->all_rules, rule);
943
944 #ifdef DBUS_ENABLE_VERBOSE_MODE
945   {
946     char *s = match_rule_to_string (rule);
947
948     _dbus_verbose ("Removed match rule %s for connection %p\n",
949                    s, rule->matches_go_to);
950     dbus_free (s);
951   }
952 #endif
953   
954   bus_match_rule_unref (rule);
955 }
956
957 /* Remove a single rule which is equal to the given rule by value */
958 dbus_bool_t
959 bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker,
960                                      BusMatchRule    *value,
961                                      DBusError       *error)
962 {
963   /* FIXME this is an unoptimized linear scan */
964
965   DBusList *link;
966
967   /* we traverse backward because bus_connection_remove_match_rule()
968    * removes the most-recently-added rule
969    */
970   link = _dbus_list_get_last_link (&matchmaker->all_rules);
971   while (link != NULL)
972     {
973       BusMatchRule *rule;
974       DBusList *prev;
975
976       rule = link->data;
977       prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link);
978
979       if (match_rule_equal (rule, value))
980         {
981           bus_matchmaker_remove_rule_link (matchmaker, link);
982           break;
983         }
984
985       link = prev;
986     }
987
988   if (link == NULL)
989     {
990       dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
991                       "The given match rule wasn't found and can't be removed");
992       return FALSE;
993     }
994
995   return TRUE;
996 }
997
998 void
999 bus_matchmaker_disconnected (BusMatchmaker   *matchmaker,
1000                              DBusConnection  *disconnected)
1001 {
1002   DBusList *link;
1003
1004   /* FIXME
1005    *
1006    * This scans all match rules on the bus. We could avoid that
1007    * for the rules belonging to the connection, since we keep
1008    * a list of those; but for the rules that just refer to
1009    * the connection we'd need to do something more elaborate.
1010    * 
1011    */
1012   
1013   _dbus_assert (bus_connection_is_active (disconnected));
1014
1015   link = _dbus_list_get_first_link (&matchmaker->all_rules);
1016   while (link != NULL)
1017     {
1018       BusMatchRule *rule;
1019       DBusList *next;
1020
1021       rule = link->data;
1022       next = _dbus_list_get_next_link (&matchmaker->all_rules, link);
1023
1024       if (rule->matches_go_to == disconnected)
1025         {
1026           bus_matchmaker_remove_rule_link (matchmaker, link);
1027         }
1028       else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') ||
1029                ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':'))
1030         {
1031           /* The rule matches to/from a base service, see if it's the
1032            * one being disconnected, since we know this service name
1033            * will never be recycled.
1034            */
1035           const char *name;
1036
1037           name = bus_connection_get_name (disconnected);
1038           _dbus_assert (name != NULL); /* because we're an active connection */
1039
1040           if (((rule->flags & BUS_MATCH_SENDER) &&
1041                strcmp (rule->sender, name) == 0) ||
1042               ((rule->flags & BUS_MATCH_DESTINATION) &&
1043                strcmp (rule->destination, name) == 0))
1044             {
1045               bus_matchmaker_remove_rule_link (matchmaker, link);
1046             }
1047         }
1048
1049       link = next;
1050     }
1051 }
1052
1053 static dbus_bool_t
1054 connection_is_primary_owner (DBusConnection *connection,
1055                              const char     *service_name)
1056 {
1057   BusService *service;
1058   DBusString str;
1059   BusRegistry *registry;
1060
1061   _dbus_assert (connection != NULL);
1062   
1063   registry = bus_connection_get_registry (connection);
1064
1065   _dbus_string_init_const (&str, service_name);
1066   service = bus_registry_lookup (registry, &str);
1067
1068   if (service == NULL)
1069     return FALSE; /* Service doesn't exist so connection can't own it. */
1070
1071   return bus_service_get_primary_owner (service) == connection;
1072 }
1073
1074 static dbus_bool_t
1075 match_rule_matches (BusMatchRule    *rule,
1076                     BusConnections  *connections,
1077                     DBusConnection  *sender,
1078                     DBusConnection  *addressed_recipient,
1079                     DBusMessage     *message)
1080 {
1081   /* All features of the match rule are AND'd together,
1082    * so FALSE if any of them don't match.
1083    */
1084
1085   /* sender/addressed_recipient of #NULL may mean bus driver,
1086    * or for addressed_recipient may mean a message with no
1087    * specific recipient (i.e. a signal)
1088    */
1089   
1090   if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
1091     {
1092       _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
1093
1094       if (rule->message_type != dbus_message_get_type (message))
1095         return FALSE;
1096     }
1097
1098   if (rule->flags & BUS_MATCH_INTERFACE)
1099     {
1100       const char *iface;
1101
1102       _dbus_assert (rule->interface != NULL);
1103
1104       iface = dbus_message_get_interface (message);
1105       if (iface == NULL)
1106         return FALSE;
1107
1108       if (strcmp (iface, rule->interface) != 0)
1109         return FALSE;
1110     }
1111
1112   if (rule->flags & BUS_MATCH_MEMBER)
1113     {
1114       const char *member;
1115
1116       _dbus_assert (rule->member != NULL);
1117
1118       member = dbus_message_get_member (message);
1119       if (member == NULL)
1120         return FALSE;
1121
1122       if (strcmp (member, rule->member) != 0)
1123         return FALSE;
1124     }
1125
1126   if (rule->flags & BUS_MATCH_SENDER)
1127     {
1128       _dbus_assert (rule->sender != NULL);
1129
1130       if (sender == NULL)
1131         {
1132           if (strcmp (rule->sender,
1133                       DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) != 0)
1134             return FALSE;
1135         }
1136       else
1137         {
1138           if (!connection_is_primary_owner (sender, rule->sender))
1139             return FALSE;
1140         }
1141     }
1142
1143   if (rule->flags & BUS_MATCH_DESTINATION)
1144     {
1145       const char *destination;
1146
1147       _dbus_assert (rule->destination != NULL);
1148
1149       destination = dbus_message_get_destination (message);
1150       if (destination == NULL)
1151         return FALSE;
1152
1153       if (addressed_recipient == NULL)
1154         {          
1155           if (strcmp (rule->destination,
1156                       DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) != 0)
1157             return FALSE;
1158         }
1159       else
1160         {
1161           if (!connection_is_primary_owner (addressed_recipient, rule->destination))
1162             return FALSE;
1163         }
1164     }
1165
1166   if (rule->flags & BUS_MATCH_PATH)
1167     {
1168       const char *path;
1169
1170       _dbus_assert (rule->path != NULL);
1171
1172       path = dbus_message_get_path (message);
1173       if (path == NULL)
1174         return FALSE;
1175
1176       if (strcmp (path, rule->path) != 0)
1177         return FALSE;
1178     }
1179
1180   return TRUE;
1181 }
1182
1183 dbus_bool_t
1184 bus_matchmaker_get_recipients (BusMatchmaker   *matchmaker,
1185                                BusConnections  *connections,
1186                                DBusConnection  *sender,
1187                                DBusConnection  *addressed_recipient,
1188                                DBusMessage     *message,
1189                                DBusList       **recipients_p)
1190 {
1191   /* FIXME for now this is a wholly unoptimized linear search */
1192   /* Guessing the important optimization is to skip the signal-related
1193    * match lists when processing method call and exception messages.
1194    * So separate match rule lists for signals?
1195    */
1196   
1197   DBusList *link;
1198
1199   _dbus_assert (*recipients_p == NULL);
1200
1201   /* This avoids sending same message to the same connection twice.
1202    * Purpose of the stamp instead of a bool is to avoid iterating over
1203    * all connections resetting the bool each time.
1204    */
1205   bus_connections_increment_stamp (connections);
1206
1207   /* addressed_recipient is already receiving the message, don't add to list.
1208    * NULL addressed_recipient means either bus driver, or this is a signal
1209    * and thus lacks a specific addressed_recipient.
1210    */
1211   if (addressed_recipient != NULL)
1212     bus_connection_mark_stamp (addressed_recipient);
1213
1214   link = _dbus_list_get_first_link (&matchmaker->all_rules);
1215   while (link != NULL)
1216     {
1217       BusMatchRule *rule;
1218
1219       rule = link->data;
1220
1221 #ifdef DBUS_ENABLE_VERBOSE_MODE
1222       {
1223         char *s = match_rule_to_string (rule);
1224         
1225         _dbus_verbose ("Checking whether message matches rule %s for connection %p\n",
1226                        s, rule->matches_go_to);
1227         dbus_free (s);
1228       }
1229 #endif
1230       
1231       if (match_rule_matches (rule, connections,
1232                               sender, addressed_recipient, message))
1233         {
1234           _dbus_verbose ("Rule matched\n");
1235           
1236           /* Append to the list if we haven't already */
1237           if (bus_connection_mark_stamp (rule->matches_go_to))
1238             {
1239               if (!_dbus_list_append (recipients_p, rule->matches_go_to))
1240                 goto nomem;
1241             }
1242 #ifdef DBUS_ENABLE_VERBOSE_MODE
1243           else
1244             {
1245               _dbus_verbose ("Connection already receiving this message, so not adding again\n");
1246             }
1247 #endif /* DBUS_ENABLE_VERBOSE_MODE */
1248         }
1249
1250       link = _dbus_list_get_next_link (&matchmaker->all_rules, link);
1251     }
1252
1253   return TRUE;
1254
1255  nomem:
1256   _dbus_list_clear (recipients_p);
1257   return FALSE;
1258 }
1259
1260 #ifdef DBUS_BUILD_TESTS
1261 #include "test.h"
1262 #include <stdlib.h>
1263
1264 static BusMatchRule*
1265 check_parse (dbus_bool_t should_succeed,
1266              const char *text)
1267 {
1268   BusMatchRule *rule;
1269   DBusString str;
1270   DBusError error;
1271
1272   dbus_error_init (&error);
1273
1274   _dbus_string_init_const (&str, text);
1275   
1276   rule = bus_match_rule_parse (NULL, &str, &error);
1277   if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
1278     {
1279       dbus_error_free (&error);
1280       return NULL;
1281     }
1282
1283   if (should_succeed && rule == NULL)
1284     {
1285       _dbus_warn ("Failed to parse: %s: %s: \"%s\"\n",
1286                   error.name, error.message,
1287                   _dbus_string_get_const_data (&str));
1288       exit (1);
1289     }
1290
1291   if (!should_succeed && rule != NULL)
1292     {
1293       _dbus_warn ("Failed to fail to parse: \"%s\"\n",
1294                   _dbus_string_get_const_data (&str));
1295       exit (1);
1296     }
1297
1298   dbus_error_free (&error);
1299
1300   return rule;
1301 }
1302
1303 static void
1304 assert_large_rule (BusMatchRule *rule)
1305 {
1306   _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
1307   _dbus_assert (rule->flags & BUS_MATCH_SENDER);
1308   _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
1309   _dbus_assert (rule->flags & BUS_MATCH_MEMBER);
1310   _dbus_assert (rule->flags & BUS_MATCH_DESTINATION);
1311   _dbus_assert (rule->flags & BUS_MATCH_PATH);
1312
1313   _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
1314   _dbus_assert (rule->interface != NULL);
1315   _dbus_assert (rule->member != NULL);
1316   _dbus_assert (rule->sender != NULL);
1317   _dbus_assert (rule->destination != NULL);
1318   _dbus_assert (rule->path != NULL);
1319
1320   _dbus_assert (strcmp (rule->interface, "org.freedesktop.DBusInterface") == 0);
1321   _dbus_assert (strcmp (rule->sender, "org.freedesktop.DBusSender") == 0);
1322   _dbus_assert (strcmp (rule->member, "Foo") == 0);
1323   _dbus_assert (strcmp (rule->path, "/bar/foo") == 0);
1324   _dbus_assert (strcmp (rule->destination, ":452345.34") == 0);
1325 }
1326
1327 static dbus_bool_t
1328 test_parsing (void *data)
1329 {
1330   BusMatchRule *rule;
1331
1332   rule = check_parse (TRUE, "type='signal',sender='org.freedesktop.DBusSender',interface='org.freedesktop.DBusInterface',member='Foo',path='/bar/foo',destination=':452345.34'");
1333   if (rule != NULL)
1334     {
1335       assert_large_rule (rule);
1336       bus_match_rule_unref (rule);
1337     }
1338
1339   /* With extra whitespace and useless quotes */
1340   rule = check_parse (TRUE, "    type='signal',  \tsender='org.freedes''ktop.DBusSender',   interface='org.freedesktop.DBusInterface''''', \tmember='Foo',path='/bar/foo',destination=':452345.34'''''");
1341   if (rule != NULL)
1342     {
1343       assert_large_rule (rule);
1344       bus_match_rule_unref (rule);
1345     }
1346
1347
1348   /* A simple signal connection */
1349   rule = check_parse (TRUE, "type='signal',path='/foo',interface='org.Bar'");
1350   if (rule != NULL)
1351     {
1352       _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
1353       _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
1354       _dbus_assert (rule->flags & BUS_MATCH_PATH);
1355
1356       _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
1357       _dbus_assert (rule->interface != NULL);
1358       _dbus_assert (rule->path != NULL);
1359
1360       _dbus_assert (strcmp (rule->interface, "org.Bar") == 0);
1361       _dbus_assert (strcmp (rule->path, "/foo") == 0);
1362   
1363       bus_match_rule_unref (rule);
1364     }
1365
1366   /* Reject duplicates */
1367   rule = check_parse (FALSE, "type='signal',type='method_call'");
1368   _dbus_assert (rule == NULL);
1369
1370   /* Reject broken keys */
1371   rule = check_parse (FALSE, "blah='signal'");
1372   _dbus_assert (rule == NULL);
1373
1374   /* Reject broken valuess */
1375   rule = check_parse (FALSE, "type='chouin'");
1376   _dbus_assert (rule == NULL);
1377   rule = check_parse (FALSE, "interface='abc@def++'");
1378   _dbus_assert (rule == NULL);
1379   rule = check_parse (FALSE, "service='youpi'");
1380   _dbus_assert (rule == NULL);
1381
1382   /* Allow empty rule */
1383   rule = check_parse (TRUE, "");
1384   if (rule != NULL)
1385     {
1386       _dbus_assert (rule->flags == 0);
1387       
1388       bus_match_rule_unref (rule);
1389     }
1390
1391   /* All-whitespace rule is the same as empty */
1392   rule = check_parse (TRUE, "    \t");
1393   if (rule != NULL)
1394     {
1395       _dbus_assert (rule->flags == 0);
1396       
1397       bus_match_rule_unref (rule);
1398     }
1399
1400   /* But with non-whitespace chars and no =value, it's not OK */
1401   rule = check_parse (FALSE, "type");
1402   _dbus_assert (rule == NULL);
1403
1404   return TRUE;
1405 }
1406
1407 dbus_bool_t
1408 bus_signals_test (const DBusString *test_data_dir)
1409 {
1410   BusMatchmaker *matchmaker;
1411
1412   matchmaker = bus_matchmaker_new ();
1413   bus_matchmaker_ref (matchmaker);
1414   bus_matchmaker_unref (matchmaker);
1415   bus_matchmaker_unref (matchmaker);
1416
1417   if (!_dbus_test_oom_handling ("parsing match rules", test_parsing, NULL))
1418     _dbus_assert_not_reached ("Parsing match rules test failed");
1419   
1420   return TRUE;
1421 }
1422
1423 #endif /* DBUS_BUILD_TESTS */
1424