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