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