signals.c revision 6a65f4802e95ba442c520f8e225da837e0a9f73b
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
27struct 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
43BusMatchRule*
44bus_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
62void
63bus_match_rule_ref (BusMatchRule *rule)
64{
65  _dbus_assert (rule->refcount > 0);
66
67  rule->refcount += 1;
68}
69
70void
71bus_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
88static char*
89match_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
186dbus_bool_t
187bus_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
197dbus_bool_t
198bus_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
216dbus_bool_t
217bus_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
235dbus_bool_t
236bus_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
254dbus_bool_t
255bus_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
273dbus_bool_t
274bus_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
294static dbus_bool_t
295find_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
351static dbus_bool_t
352find_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 */
484typedef struct
485{
486  char *key;
487  char *value;
488} RuleToken;
489
490static dbus_bool_t
491tokenize_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 */
579BusMatchRule*
580bus_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  memset (tokens, '\0', sizeof (tokens));
591
592  rule = bus_match_rule_new (matches_go_to);
593  if (rule == NULL)
594    {
595      BUS_SET_OOM (error);
596      goto failed;
597    }
598
599  if (!tokenize_rule (rule_text, tokens, error))
600    goto failed;
601
602  i = 0;
603  while (tokens[i].key != NULL)
604    {
605      const char *key = tokens[i].key;
606      const char *value = tokens[i].value;
607
608      if (strcmp (key, "type") == 0)
609        {
610          int t;
611
612          if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
613            {
614              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
615                              "Key %s specified twice in match rule\n", key);
616              goto failed;
617            }
618
619          t = dbus_message_type_from_string (value);
620
621          if (!bus_match_rule_set_message_type (rule, t))
622            {
623              BUS_SET_OOM (error);
624              goto failed;
625            }
626        }
627      else if (strcmp (key, "sender") == 0)
628        {
629          if (rule->flags & BUS_MATCH_SENDER)
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          if (!bus_match_rule_set_sender (rule, value))
637            {
638              BUS_SET_OOM (error);
639              goto failed;
640            }
641        }
642      else if (strcmp (key, "interface") == 0)
643        {
644          if (rule->flags & BUS_MATCH_INTERFACE)
645            {
646              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
647                              "Key %s specified twice in match rule\n", key);
648              goto failed;
649            }
650
651          if (!bus_match_rule_set_interface (rule, value))
652            {
653              BUS_SET_OOM (error);
654              goto failed;
655            }
656        }
657      else if (strcmp (key, "member") == 0)
658        {
659          if (rule->flags & BUS_MATCH_MEMBER)
660            {
661              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
662                              "Key %s specified twice in match rule\n", key);
663              goto failed;
664            }
665
666          if (!bus_match_rule_set_member (rule, value))
667            {
668              BUS_SET_OOM (error);
669              goto failed;
670            }
671        }
672      else if (strcmp (key, "path") == 0)
673        {
674          if (rule->flags & BUS_MATCH_PATH)
675            {
676              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
677                              "Key %s specified twice in match rule\n", key);
678              goto failed;
679            }
680
681          if (!bus_match_rule_set_path (rule, value))
682            {
683              BUS_SET_OOM (error);
684              goto failed;
685            }
686        }
687      else if (strcmp (key, "destination") == 0)
688        {
689          if (rule->flags & BUS_MATCH_DESTINATION)
690            {
691              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
692                              "Key %s specified twice in match rule\n", key);
693              goto failed;
694            }
695
696          if (!bus_match_rule_set_destination (rule, value))
697            {
698              BUS_SET_OOM (error);
699              goto failed;
700            }
701        }
702      else
703        {
704          dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
705                          "Unknown key \"%s\" in match rule",
706                          key);
707          goto failed;
708        }
709
710      ++i;
711    }
712
713
714  goto out;
715
716 failed:
717  _DBUS_ASSERT_ERROR_IS_SET (error);
718  if (rule)
719    {
720      bus_match_rule_unref (rule);
721      rule = NULL;
722    }
723
724 out:
725
726  i = 0;
727  while (tokens[i].key || tokens[i].value)
728    {
729      _dbus_assert (i < MAX_RULE_TOKENS);
730      dbus_free (tokens[i].key);
731      dbus_free (tokens[i].value);
732      ++i;
733    }
734
735  return rule;
736}
737
738struct BusMatchmaker
739{
740  int refcount;
741
742  DBusList *all_rules;
743};
744
745BusMatchmaker*
746bus_matchmaker_new (void)
747{
748  BusMatchmaker *matchmaker;
749
750  matchmaker = dbus_new0 (BusMatchmaker, 1);
751  if (matchmaker == NULL)
752    return NULL;
753
754  matchmaker->refcount = 1;
755
756  return matchmaker;
757}
758
759void
760bus_matchmaker_ref (BusMatchmaker *matchmaker)
761{
762  _dbus_assert (matchmaker->refcount > 0);
763
764  matchmaker->refcount += 1;
765}
766
767void
768bus_matchmaker_unref (BusMatchmaker *matchmaker)
769{
770  _dbus_assert (matchmaker->refcount > 0);
771
772  matchmaker->refcount -= 1;
773  if (matchmaker->refcount == 0)
774    {
775      while (matchmaker->all_rules != NULL)
776        {
777          BusMatchRule *rule;
778
779          rule = matchmaker->all_rules->data;
780          bus_match_rule_unref (rule);
781          _dbus_list_remove_link (&matchmaker->all_rules,
782                                  matchmaker->all_rules);
783        }
784
785      dbus_free (matchmaker);
786    }
787}
788
789/* The rule can't be modified after it's added. */
790dbus_bool_t
791bus_matchmaker_add_rule (BusMatchmaker   *matchmaker,
792                         BusMatchRule    *rule)
793{
794  _dbus_assert (bus_connection_is_active (rule->matches_go_to));
795
796  if (!_dbus_list_append (&matchmaker->all_rules, rule))
797    return FALSE;
798
799  if (!bus_connection_add_match_rule (rule->matches_go_to, rule))
800    {
801      _dbus_list_remove_last (&matchmaker->all_rules, rule);
802      return FALSE;
803    }
804
805  bus_match_rule_ref (rule);
806
807#ifdef DBUS_ENABLE_VERBOSE_MODE
808  {
809    char *s = match_rule_to_string (rule);
810
811    _dbus_verbose ("Added match rule %s to connection %p\n",
812                   s, rule->matches_go_to);
813    dbus_free (s);
814  }
815#endif
816
817  return TRUE;
818}
819
820static dbus_bool_t
821match_rule_equal (BusMatchRule *a,
822                  BusMatchRule *b)
823{
824  if (a->flags != b->flags)
825    return FALSE;
826
827  if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
828      a->message_type != b->message_type)
829    return FALSE;
830
831  if ((a->flags & BUS_MATCH_MEMBER) &&
832      strcmp (a->member, b->member) != 0)
833    return FALSE;
834
835  if ((a->flags & BUS_MATCH_PATH) &&
836      strcmp (a->path, b->path) != 0)
837    return FALSE;
838
839  if ((a->flags & BUS_MATCH_INTERFACE) &&
840      strcmp (a->interface, b->interface) != 0)
841    return FALSE;
842
843  if ((a->flags & BUS_MATCH_SENDER) &&
844      strcmp (a->sender, b->sender) != 0)
845    return FALSE;
846
847  if ((a->flags & BUS_MATCH_DESTINATION) &&
848      strcmp (a->destination, b->destination) != 0)
849    return FALSE;
850
851  return TRUE;
852}
853
854static void
855bus_matchmaker_remove_rule_link (BusMatchmaker   *matchmaker,
856                                 DBusList        *link)
857{
858  BusMatchRule *rule = link->data;
859
860  bus_connection_remove_match_rule (rule->matches_go_to, rule);
861  _dbus_list_remove_link (&matchmaker->all_rules, link);
862
863#ifdef DBUS_ENABLE_VERBOSE_MODE
864  {
865    char *s = match_rule_to_string (rule);
866
867    _dbus_verbose ("Removed match rule %s for connection %p\n",
868                   s, rule->matches_go_to);
869    dbus_free (s);
870  }
871#endif
872
873  bus_match_rule_unref (rule);
874}
875
876void
877bus_matchmaker_remove_rule (BusMatchmaker   *matchmaker,
878                            BusMatchRule    *rule)
879{
880  bus_connection_remove_match_rule (rule->matches_go_to, rule);
881  _dbus_list_remove (&matchmaker->all_rules, rule);
882
883#ifdef DBUS_ENABLE_VERBOSE_MODE
884  {
885    char *s = match_rule_to_string (rule);
886
887    _dbus_verbose ("Removed match rule %s for connection %p\n",
888                   s, rule->matches_go_to);
889    dbus_free (s);
890  }
891#endif
892
893  bus_match_rule_unref (rule);
894}
895
896/* Remove a single rule which is equal to the given rule by value */
897dbus_bool_t
898bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker,
899                                     BusMatchRule    *value,
900                                     DBusError       *error)
901{
902  /* FIXME this is an unoptimized linear scan */
903
904  DBusList *link;
905
906  /* we traverse backward because bus_connection_remove_match_rule()
907   * removes the most-recently-added rule
908   */
909  link = _dbus_list_get_last_link (&matchmaker->all_rules);
910  while (link != NULL)
911    {
912      BusMatchRule *rule;
913      DBusList *prev;
914
915      rule = link->data;
916      prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link);
917
918      if (match_rule_equal (rule, value))
919        {
920          bus_matchmaker_remove_rule_link (matchmaker, link);
921          break;
922        }
923
924      link = prev;
925    }
926
927  if (link == NULL)
928    {
929      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
930                      "The given match rule wasn't found and can't be removed");
931      return FALSE;
932    }
933
934  return TRUE;
935}
936
937void
938bus_matchmaker_disconnected (BusMatchmaker   *matchmaker,
939                             DBusConnection  *disconnected)
940{
941  DBusList *link;
942
943  /* FIXME
944   *
945   * This scans all match rules on the bus. We could avoid that
946   * for the rules belonging to the connection, since we keep
947   * a list of those; but for the rules that just refer to
948   * the connection we'd need to do something more elaborate.
949   *
950   */
951
952  _dbus_assert (bus_connection_is_active (disconnected));
953
954  link = _dbus_list_get_first_link (&matchmaker->all_rules);
955  while (link != NULL)
956    {
957      BusMatchRule *rule;
958      DBusList *next;
959
960      rule = link->data;
961      next = _dbus_list_get_next_link (&matchmaker->all_rules, link);
962
963      if (rule->matches_go_to == disconnected)
964        {
965          bus_matchmaker_remove_rule_link (matchmaker, link);
966        }
967      else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') ||
968               ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':'))
969        {
970          /* The rule matches to/from a base service, see if it's the
971           * one being disconnected, since we know this service name
972           * will never be recycled.
973           */
974          const char *name;
975
976          name = bus_connection_get_name (disconnected);
977          _dbus_assert (name != NULL); /* because we're an active connection */
978
979          if (((rule->flags & BUS_MATCH_SENDER) &&
980               strcmp (rule->sender, name) == 0) ||
981              ((rule->flags & BUS_MATCH_DESTINATION) &&
982               strcmp (rule->destination, name) == 0))
983            {
984              bus_matchmaker_remove_rule_link (matchmaker, link);
985            }
986        }
987
988      link = next;
989    }
990}
991
992static dbus_bool_t
993connection_is_primary_owner (DBusConnection *connection,
994                             const char     *service_name)
995{
996  BusService *service;
997  DBusString str;
998  BusRegistry *registry;
999
1000  registry = bus_connection_get_registry (connection);
1001
1002  _dbus_string_init_const (&str, service_name);
1003  service = bus_registry_lookup (registry, &str);
1004
1005  if (service == NULL)
1006    return FALSE; /* Service doesn't exist so connection can't own it. */
1007
1008  return bus_service_get_primary_owner (service) == connection;
1009}
1010
1011static dbus_bool_t
1012match_rule_matches (BusMatchRule    *rule,
1013                    BusConnections  *connections,
1014                    DBusConnection  *sender,
1015                    DBusConnection  *addressed_recipient,
1016                    DBusMessage     *message)
1017{
1018  /* All features of the match rule are AND'd together,
1019   * so FALSE if any of them don't match.
1020   */
1021
1022  if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
1023    {
1024      _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
1025
1026      if (rule->message_type != dbus_message_get_type (message))
1027        return FALSE;
1028    }
1029
1030  if (rule->flags & BUS_MATCH_INTERFACE)
1031    {
1032      const char *iface;
1033
1034      _dbus_assert (rule->interface != NULL);
1035
1036      iface = dbus_message_get_interface (message);
1037      if (iface == NULL)
1038        return FALSE;
1039
1040      if (strcmp (iface, rule->interface) != 0)
1041        return FALSE;
1042    }
1043
1044  if (rule->flags & BUS_MATCH_MEMBER)
1045    {
1046      const char *member;
1047
1048      _dbus_assert (rule->member != NULL);
1049
1050      member = dbus_message_get_member (message);
1051      if (member == NULL)
1052        return FALSE;
1053
1054      if (strcmp (member, rule->member) != 0)
1055        return FALSE;
1056    }
1057
1058  if (rule->flags & BUS_MATCH_SENDER)
1059    {
1060      _dbus_assert (rule->sender != NULL);
1061
1062      if (!connection_is_primary_owner (sender, rule->sender))
1063        return FALSE;
1064    }
1065
1066  if (rule->flags & BUS_MATCH_DESTINATION)
1067    {
1068      const char *destination;
1069
1070      _dbus_assert (rule->destination != NULL);
1071
1072      if (addressed_recipient == NULL)
1073        return FALSE;
1074
1075      destination = dbus_message_get_destination (message);
1076      if (destination == NULL)
1077        return FALSE;
1078
1079      if (!connection_is_primary_owner (addressed_recipient, rule->destination))
1080        return FALSE;
1081    }
1082
1083  if (rule->flags & BUS_MATCH_PATH)
1084    {
1085      const char *path;
1086
1087      _dbus_assert (rule->path != NULL);
1088
1089      path = dbus_message_get_path (message);
1090      if (path == NULL)
1091        return FALSE;
1092
1093      if (strcmp (path, rule->path) != 0)
1094        return FALSE;
1095    }
1096
1097  return TRUE;
1098}
1099
1100dbus_bool_t
1101bus_matchmaker_get_recipients (BusMatchmaker   *matchmaker,
1102                               BusConnections  *connections,
1103                               DBusConnection  *sender,
1104                               DBusConnection  *addressed_recipient,
1105                               DBusMessage     *message,
1106                               DBusList       **recipients_p)
1107{
1108  /* FIXME for now this is a wholly unoptimized linear search */
1109  /* Guessing the important optimization is to skip the signal-related
1110   * match lists when processing method call and exception messages.
1111   * So separate match rule lists for signals?
1112   */
1113
1114  DBusList *link;
1115
1116  _dbus_assert (*recipients_p == NULL);
1117
1118  /* This avoids sending same message to the same connection twice.
1119   * Purpose of the stamp instead of a bool is to avoid iterating over
1120   * all connections resetting the bool each time.
1121   */
1122  bus_connections_increment_stamp (connections);
1123
1124  /* addressed_recipient is already receiving the message, don't add to list.
1125   * NULL addressed_recipient means either bus driver, or this is a signal
1126   * and thus lacks a specific addressed_recipient.
1127   */
1128  if (addressed_recipient != NULL)
1129    bus_connection_mark_stamp (addressed_recipient);
1130
1131  link = _dbus_list_get_first_link (&matchmaker->all_rules);
1132  while (link != NULL)
1133    {
1134      BusMatchRule *rule;
1135
1136      rule = link->data;
1137
1138#ifdef DBUS_ENABLE_VERBOSE_MODE
1139      {
1140        char *s = match_rule_to_string (rule);
1141
1142        _dbus_verbose ("Checking whether message matches rule %s for connection %p\n",
1143                       s, rule->matches_go_to);
1144        dbus_free (s);
1145      }
1146#endif
1147
1148      if (match_rule_matches (rule, connections,
1149                              sender, addressed_recipient, message))
1150        {
1151          _dbus_verbose ("Rule matched\n");
1152
1153          /* Append to the list if we haven't already */
1154          if (bus_connection_mark_stamp (rule->matches_go_to))
1155            {
1156              if (!_dbus_list_append (recipients_p, rule->matches_go_to))
1157                goto nomem;
1158            }
1159#ifdef DBUS_ENABLE_VERBOSE_MODE
1160          else
1161            {
1162              _dbus_verbose ("Connection already receiving this message, so not adding again\n");
1163            }
1164#endif /* DBUS_ENABLE_VERBOSE_MODE */
1165        }
1166
1167      link = _dbus_list_get_next_link (&matchmaker->all_rules, link);
1168    }
1169
1170  return TRUE;
1171
1172 nomem:
1173  _dbus_list_clear (recipients_p);
1174  return FALSE;
1175}
1176
1177#ifdef DBUS_BUILD_TESTS
1178#include "test.h"
1179#include <stdlib.h>
1180
1181static BusMatchRule*
1182check_parse (dbus_bool_t should_succeed,
1183             const char *text)
1184{
1185  BusMatchRule *rule;
1186  DBusString str;
1187  DBusError error;
1188
1189  dbus_error_init (&error);
1190
1191  _dbus_string_init_const (&str, text);
1192
1193  rule = bus_match_rule_parse (NULL, &str, &error);
1194  if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
1195    {
1196      dbus_error_free (&error);
1197      return NULL;
1198    }
1199
1200  if (should_succeed && rule == NULL)
1201    {
1202      _dbus_warn ("Failed to parse: %s: %s: \"%s\"\n",
1203                  error.name, error.message,
1204                  _dbus_string_get_const_data (&str));
1205      exit (1);
1206    }
1207
1208  if (!should_succeed && rule != NULL)
1209    {
1210      _dbus_warn ("Failed to fail to parse: \"%s\"\n",
1211                  _dbus_string_get_const_data (&str));
1212      exit (1);
1213    }
1214
1215  dbus_error_free (&error);
1216
1217  return rule;
1218}
1219
1220static void
1221assert_large_rule (BusMatchRule *rule)
1222{
1223  _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
1224  _dbus_assert (rule->flags & BUS_MATCH_SENDER);
1225  _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
1226  _dbus_assert (rule->flags & BUS_MATCH_MEMBER);
1227  _dbus_assert (rule->flags & BUS_MATCH_DESTINATION);
1228  _dbus_assert (rule->flags & BUS_MATCH_PATH);
1229
1230  _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
1231  _dbus_assert (rule->interface != NULL);
1232  _dbus_assert (rule->member != NULL);
1233  _dbus_assert (rule->sender != NULL);
1234  _dbus_assert (rule->destination != NULL);
1235  _dbus_assert (rule->path != NULL);
1236
1237  _dbus_assert (strcmp (rule->interface, "org.freedesktop.DBusInterface") == 0);
1238  _dbus_assert (strcmp (rule->sender, "org.freedesktop.DBusSender") == 0);
1239  _dbus_assert (strcmp (rule->member, "Foo") == 0);
1240  _dbus_assert (strcmp (rule->path, "/bar/foo") == 0);
1241  _dbus_assert (strcmp (rule->destination, ":452345-34") == 0);
1242}
1243
1244static dbus_bool_t
1245test_parsing (void *data)
1246{
1247  BusMatchRule *rule;
1248
1249  rule = check_parse (TRUE, "type='signal',sender='org.freedesktop.DBusSender',interface='org.freedesktop.DBusInterface',member='Foo',path='/bar/foo',destination=':452345-34'");
1250  if (rule != NULL)
1251    {
1252      assert_large_rule (rule);
1253      bus_match_rule_unref (rule);
1254    }
1255
1256  /* With extra whitespace and useless quotes */
1257  rule = check_parse (TRUE, "    type='signal',  \tsender='org.freedes''ktop.DBusSender',   interface='org.freedesktop.DBusInterface''''', \tmember='Foo',path='/bar/foo',destination=':452345-34'''''");
1258  if (rule != NULL)
1259    {
1260      assert_large_rule (rule);
1261      bus_match_rule_unref (rule);
1262    }
1263
1264
1265  /* A simple signal connection */
1266  rule = check_parse (TRUE, "type='signal',path='/foo',interface='org.Bar'");
1267  if (rule != NULL)
1268    {
1269      _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
1270      _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
1271      _dbus_assert (rule->flags & BUS_MATCH_PATH);
1272
1273      _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
1274      _dbus_assert (rule->interface != NULL);
1275      _dbus_assert (rule->path != NULL);
1276
1277      _dbus_assert (strcmp (rule->interface, "org.Bar") == 0);
1278      _dbus_assert (strcmp (rule->path, "/foo") == 0);
1279
1280      bus_match_rule_unref (rule);
1281    }
1282
1283  /* Reject duplicates */
1284  rule = check_parse (FALSE, "type='signal',type='method_call'");
1285  _dbus_assert (rule == NULL);
1286
1287  /* Reject broken keys */
1288  rule = check_parse (FALSE, "blah='signal'");
1289  _dbus_assert (rule == NULL);
1290
1291  /* Allow empty rule */
1292  rule = check_parse (TRUE, "");
1293  if (rule != NULL)
1294    {
1295      _dbus_assert (rule->flags == 0);
1296
1297      bus_match_rule_unref (rule);
1298    }
1299
1300  /* All-whitespace rule is the same as empty */
1301  rule = check_parse (TRUE, "    \t");
1302  if (rule != NULL)
1303    {
1304      _dbus_assert (rule->flags == 0);
1305
1306      bus_match_rule_unref (rule);
1307    }
1308
1309  /* But with non-whitespace chars and no =value, it's not OK */
1310  rule = check_parse (FALSE, "type");
1311  _dbus_assert (rule == NULL);
1312
1313  /* Empty string values are allowed at the moment */
1314  rule = check_parse (TRUE, "interface=");
1315  if (rule != NULL)
1316    {
1317      _dbus_assert (rule->flags == BUS_MATCH_INTERFACE);
1318      _dbus_assert (rule->interface);
1319      _dbus_assert (strlen (rule->interface) == 0);
1320
1321      bus_match_rule_unref (rule);
1322    }
1323
1324  /* Empty string expressed with quotes */
1325  rule = check_parse (TRUE, "interface=''");
1326  if (rule != NULL)
1327    {
1328      _dbus_assert (rule->flags == BUS_MATCH_INTERFACE);
1329      _dbus_assert (rule->interface);
1330      _dbus_assert (strlen (rule->interface) == 0);
1331
1332      bus_match_rule_unref (rule);
1333    }
1334
1335  /* Check whitespace in a value */
1336  rule = check_parse (TRUE, "interface=   ");
1337  if (rule != NULL)
1338    {
1339      _dbus_assert (rule->flags == BUS_MATCH_INTERFACE);
1340      _dbus_assert (rule->interface);
1341      _dbus_assert (strcmp (rule->interface, "   ") == 0);
1342
1343      bus_match_rule_unref (rule);
1344    }
1345
1346  /* Check whitespace mixed with non-whitespace in a value */
1347  rule = check_parse (TRUE, "interface= foo ");
1348  if (rule != NULL)
1349    {
1350      _dbus_assert (rule->flags == BUS_MATCH_INTERFACE);
1351      _dbus_assert (rule->interface);
1352      _dbus_assert (strcmp (rule->interface, " foo ") == 0);
1353
1354      bus_match_rule_unref (rule);
1355    }
1356
1357  return TRUE;
1358}
1359
1360dbus_bool_t
1361bus_signals_test (const DBusString *test_data_dir)
1362{
1363  BusMatchmaker *matchmaker;
1364
1365  matchmaker = bus_matchmaker_new ();
1366  bus_matchmaker_ref (matchmaker);
1367  bus_matchmaker_unref (matchmaker);
1368  bus_matchmaker_unref (matchmaker);
1369
1370  if (!_dbus_test_oom_handling ("parsing match rules", test_parsing, NULL))
1371    _dbus_assert_not_reached ("Parsing match rules test failed");
1372
1373  return TRUE;
1374}
1375
1376#endif /* DBUS_BUILD_TESTS */
1377
1378