os/ossrv/ofdbus/dbus/bus/signals.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/ossrv/ofdbus/dbus/bus/signals.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,1960 @@
     1.4 +/* -*- mode: C; c-file-style: "gnu" -*- */
     1.5 +/* signals.c  Bus signal connection implementation
     1.6 + *
     1.7 + * Copyright (C) 2003, 2005  Red Hat, Inc.
     1.8 + * Portion Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
     1.9 + * Licensed under the Academic Free License version 2.1
    1.10 + *
    1.11 + * This program is free software; you can redistribute it and/or modify
    1.12 + * it under the terms of the GNU General Public License as published by
    1.13 + * the Free Software Foundation; either version 2 of the License, or
    1.14 + * (at your option) any later version.
    1.15 + *
    1.16 + * This program is distributed in the hope that it will be useful,
    1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.19 + * GNU General Public License for more details.
    1.20 + *
    1.21 + * You should have received a copy of the GNU General Public License
    1.22 + * along with this program; if not, write to the Free Software
    1.23 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.24 + *
    1.25 + */
    1.26 +#include "signals.h"
    1.27 +#include "services.h"
    1.28 +#include "utils.h"
    1.29 +#ifndef __SYMBIAN32__
    1.30 +#include <dbus/dbus-marshal-validate.h>
    1.31 +#else
    1.32 +#include "dbus-marshal-validate.h"
    1.33 +#include "config.h"
    1.34 +#endif //__SYMBIAN32__
    1.35 +
    1.36 +struct BusMatchRule
    1.37 +{
    1.38 +  int refcount;       /**< reference count */
    1.39 +
    1.40 +  DBusConnection *matches_go_to; /**< Owner of the rule */
    1.41 +
    1.42 +  unsigned int flags; /**< BusMatchFlags */
    1.43 +
    1.44 +  int   message_type;
    1.45 +  char *interface;
    1.46 +  char *member;
    1.47 +  char *sender;
    1.48 +  char *destination;
    1.49 +  char *path;
    1.50 +
    1.51 +  char **args;
    1.52 +  int args_len;
    1.53 +};
    1.54 +
    1.55 +BusMatchRule*
    1.56 +bus_match_rule_new (DBusConnection *matches_go_to)
    1.57 +{
    1.58 +  BusMatchRule *rule;
    1.59 +
    1.60 +  rule = dbus_new0 (BusMatchRule, 1);
    1.61 +  if (rule == NULL)
    1.62 +    return NULL;
    1.63 +
    1.64 +  rule->refcount = 1;
    1.65 +  rule->matches_go_to = matches_go_to;
    1.66 +
    1.67 +#ifndef DBUS_BUILD_TESTS
    1.68 +  _dbus_assert (rule->matches_go_to != NULL);
    1.69 +#endif
    1.70 +  
    1.71 +  return rule;
    1.72 +}
    1.73 +
    1.74 +BusMatchRule *
    1.75 +bus_match_rule_ref (BusMatchRule *rule)
    1.76 +{
    1.77 +  _dbus_assert (rule->refcount > 0);
    1.78 +
    1.79 +  rule->refcount += 1;
    1.80 +
    1.81 +  return rule;
    1.82 +}
    1.83 +
    1.84 +void
    1.85 +bus_match_rule_unref (BusMatchRule *rule)
    1.86 +{
    1.87 +  _dbus_assert (rule->refcount > 0);
    1.88 +
    1.89 +  rule->refcount -= 1;
    1.90 +  if (rule->refcount == 0)
    1.91 +    {
    1.92 +      dbus_free (rule->interface);
    1.93 +      dbus_free (rule->member);
    1.94 +      dbus_free (rule->sender);
    1.95 +      dbus_free (rule->destination);
    1.96 +      dbus_free (rule->path);
    1.97 +
    1.98 +      /* can't use dbus_free_string_array() since there
    1.99 +       * are embedded NULL
   1.100 +       */
   1.101 +      if (rule->args)
   1.102 +        {
   1.103 +          int i;
   1.104 +
   1.105 +          i = 0;
   1.106 +          while (i < rule->args_len)
   1.107 +            {
   1.108 +              if (rule->args[i])
   1.109 +                dbus_free (rule->args[i]);
   1.110 +              ++i;
   1.111 +            }
   1.112 +
   1.113 +          dbus_free (rule->args);
   1.114 +        }
   1.115 +      
   1.116 +      dbus_free (rule);
   1.117 +    }
   1.118 +}
   1.119 +
   1.120 +#ifdef DBUS_ENABLE_VERBOSE_MODE
   1.121 +/* Note this function does not do escaping, so it's only
   1.122 + * good for debug spew at the moment
   1.123 + */
   1.124 +static char*
   1.125 +match_rule_to_string (BusMatchRule *rule)
   1.126 +{
   1.127 +  DBusString str;
   1.128 +  char *ret;
   1.129 +  
   1.130 +  if (!_dbus_string_init (&str))
   1.131 +    {
   1.132 +      char *s;
   1.133 +      while ((s = _dbus_strdup ("nomem")) == NULL)
   1.134 +        ; /* only OK for debug spew... */
   1.135 +      return s;
   1.136 +    }
   1.137 +  
   1.138 +  if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
   1.139 +    {
   1.140 +      /* FIXME make type readable */
   1.141 +      if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type))
   1.142 +        goto nomem;
   1.143 +    }
   1.144 +
   1.145 +  if (rule->flags & BUS_MATCH_INTERFACE)
   1.146 +    {
   1.147 +      if (_dbus_string_get_length (&str) > 0)
   1.148 +        {
   1.149 +          if (!_dbus_string_append (&str, ","))
   1.150 +            goto nomem;
   1.151 +        }
   1.152 +      
   1.153 +      if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface))
   1.154 +        goto nomem;
   1.155 +    }
   1.156 +
   1.157 +  if (rule->flags & BUS_MATCH_MEMBER)
   1.158 +    {
   1.159 +      if (_dbus_string_get_length (&str) > 0)
   1.160 +        {
   1.161 +          if (!_dbus_string_append (&str, ","))
   1.162 +            goto nomem;
   1.163 +        }
   1.164 +      
   1.165 +      if (!_dbus_string_append_printf (&str, "member='%s'", rule->member))
   1.166 +        goto nomem;
   1.167 +    }
   1.168 +
   1.169 +  if (rule->flags & BUS_MATCH_PATH)
   1.170 +    {
   1.171 +      if (_dbus_string_get_length (&str) > 0)
   1.172 +        {
   1.173 +          if (!_dbus_string_append (&str, ","))
   1.174 +            goto nomem;
   1.175 +        }
   1.176 +      
   1.177 +      if (!_dbus_string_append_printf (&str, "path='%s'", rule->path))
   1.178 +        goto nomem;
   1.179 +    }
   1.180 +
   1.181 +  if (rule->flags & BUS_MATCH_SENDER)
   1.182 +    {
   1.183 +      if (_dbus_string_get_length (&str) > 0)
   1.184 +        {
   1.185 +          if (!_dbus_string_append (&str, ","))
   1.186 +            goto nomem;
   1.187 +        }
   1.188 +      
   1.189 +      if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender))
   1.190 +        goto nomem;
   1.191 +    }
   1.192 +
   1.193 +  if (rule->flags & BUS_MATCH_DESTINATION)
   1.194 +    {
   1.195 +      if (_dbus_string_get_length (&str) > 0)
   1.196 +        {
   1.197 +          if (!_dbus_string_append (&str, ","))
   1.198 +            goto nomem;
   1.199 +        }
   1.200 +      
   1.201 +      if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination))
   1.202 +        goto nomem;
   1.203 +    }
   1.204 +
   1.205 +  if (rule->flags & BUS_MATCH_ARGS)
   1.206 +    {
   1.207 +      int i;
   1.208 +      
   1.209 +      _dbus_assert (rule->args != NULL);
   1.210 +
   1.211 +      i = 0;
   1.212 +      while (i < rule->args_len)
   1.213 +        {
   1.214 +          if (rule->args[i] != NULL)
   1.215 +            {
   1.216 +              if (_dbus_string_get_length (&str) > 0)
   1.217 +                {
   1.218 +                  if (!_dbus_string_append (&str, ","))
   1.219 +                    goto nomem;
   1.220 +                }
   1.221 +              
   1.222 +              if (!_dbus_string_append_printf (&str,
   1.223 +                                               "arg%d='%s'",
   1.224 +                                               i,
   1.225 +                                               rule->args[i]))
   1.226 +                goto nomem;
   1.227 +            }
   1.228 +          
   1.229 +          ++i;
   1.230 +        }
   1.231 +    }
   1.232 +  
   1.233 +  if (!_dbus_string_steal_data (&str, &ret))
   1.234 +    goto nomem;
   1.235 +
   1.236 +  _dbus_string_free (&str);
   1.237 +  return ret;
   1.238 +  
   1.239 + nomem:
   1.240 +  _dbus_string_free (&str);
   1.241 +  {
   1.242 +    char *s;
   1.243 +    while ((s = _dbus_strdup ("nomem")) == NULL)
   1.244 +      ;  /* only OK for debug spew... */
   1.245 +    return s;
   1.246 +  }
   1.247 +}
   1.248 +#endif /* DBUS_ENABLE_VERBOSE_MODE */
   1.249 +
   1.250 +dbus_bool_t
   1.251 +bus_match_rule_set_message_type (BusMatchRule *rule,
   1.252 +                                 int           type)
   1.253 +{
   1.254 +  rule->flags |= BUS_MATCH_MESSAGE_TYPE;
   1.255 +
   1.256 +  rule->message_type = type;
   1.257 +
   1.258 +  return TRUE;
   1.259 +}
   1.260 +
   1.261 +dbus_bool_t
   1.262 +bus_match_rule_set_interface (BusMatchRule *rule,
   1.263 +                              const char   *interface)
   1.264 +{
   1.265 +  char *new;
   1.266 +
   1.267 +  _dbus_assert (interface != NULL);
   1.268 +
   1.269 +  new = _dbus_strdup (interface);
   1.270 +  if (new == NULL)
   1.271 +    return FALSE;
   1.272 +
   1.273 +  rule->flags |= BUS_MATCH_INTERFACE;
   1.274 +  dbus_free (rule->interface);
   1.275 +  rule->interface = new;
   1.276 +
   1.277 +  return TRUE;
   1.278 +}
   1.279 +
   1.280 +dbus_bool_t
   1.281 +bus_match_rule_set_member (BusMatchRule *rule,
   1.282 +                           const char   *member)
   1.283 +{
   1.284 +  char *new;
   1.285 +
   1.286 +  _dbus_assert (member != NULL);
   1.287 +
   1.288 +  new = _dbus_strdup (member);
   1.289 +  if (new == NULL)
   1.290 +    return FALSE;
   1.291 +
   1.292 +  rule->flags |= BUS_MATCH_MEMBER;
   1.293 +  dbus_free (rule->member);
   1.294 +  rule->member = new;
   1.295 +
   1.296 +  return TRUE;
   1.297 +}
   1.298 +
   1.299 +dbus_bool_t
   1.300 +bus_match_rule_set_sender (BusMatchRule *rule,
   1.301 +                           const char   *sender)
   1.302 +{
   1.303 +  char *new;
   1.304 +
   1.305 +  _dbus_assert (sender != NULL);
   1.306 +
   1.307 +  new = _dbus_strdup (sender);
   1.308 +  if (new == NULL)
   1.309 +    return FALSE;
   1.310 +
   1.311 +  rule->flags |= BUS_MATCH_SENDER;
   1.312 +  dbus_free (rule->sender);
   1.313 +  rule->sender = new;
   1.314 +
   1.315 +  return TRUE;
   1.316 +}
   1.317 +
   1.318 +dbus_bool_t
   1.319 +bus_match_rule_set_destination (BusMatchRule *rule,
   1.320 +                                const char   *destination)
   1.321 +{
   1.322 +  char *new;
   1.323 +
   1.324 +  _dbus_assert (destination != NULL);
   1.325 +
   1.326 +  new = _dbus_strdup (destination);
   1.327 +  if (new == NULL)
   1.328 +    return FALSE;
   1.329 +
   1.330 +  rule->flags |= BUS_MATCH_DESTINATION;
   1.331 +  dbus_free (rule->destination);
   1.332 +  rule->destination = new;
   1.333 +
   1.334 +  return TRUE;
   1.335 +}
   1.336 +
   1.337 +dbus_bool_t
   1.338 +bus_match_rule_set_path (BusMatchRule *rule,
   1.339 +                         const char   *path)
   1.340 +{
   1.341 +  char *new;
   1.342 +
   1.343 +  _dbus_assert (path != NULL);
   1.344 +
   1.345 +  new = _dbus_strdup (path);
   1.346 +  if (new == NULL)
   1.347 +    return FALSE;
   1.348 +
   1.349 +  rule->flags |= BUS_MATCH_PATH;
   1.350 +  dbus_free (rule->path);
   1.351 +  rule->path = new;
   1.352 +
   1.353 +  return TRUE;
   1.354 +}
   1.355 +
   1.356 +dbus_bool_t
   1.357 +bus_match_rule_set_arg (BusMatchRule *rule,
   1.358 +                        int           arg,
   1.359 +                        const char   *value)
   1.360 +{
   1.361 +  char *new;
   1.362 +
   1.363 +  _dbus_assert (value != NULL);
   1.364 +
   1.365 +  new = _dbus_strdup (value);
   1.366 +  if (new == NULL)
   1.367 +    return FALSE;
   1.368 +
   1.369 +  /* args_len is the number of args not including null termination
   1.370 +   * in the char**
   1.371 +   */
   1.372 +  if (arg >= rule->args_len)
   1.373 +    {
   1.374 +      char **new_args;
   1.375 +      int new_args_len;
   1.376 +      int i;
   1.377 +
   1.378 +      new_args_len = arg + 1;
   1.379 +
   1.380 +      /* add another + 1 here for null termination */
   1.381 +      new_args = dbus_realloc (rule->args,
   1.382 +                               sizeof(rule->args[0]) * (new_args_len + 1));
   1.383 +      if (new_args == NULL)
   1.384 +        {
   1.385 +          dbus_free (new);
   1.386 +          return FALSE;
   1.387 +        }
   1.388 +
   1.389 +      /* NULL the new slots */
   1.390 +      i = rule->args_len;
   1.391 +      while (i <= new_args_len) /* <= for null termination */
   1.392 +        {
   1.393 +          new_args[i] = NULL;
   1.394 +          ++i;
   1.395 +        }
   1.396 +      
   1.397 +      rule->args = new_args;
   1.398 +      rule->args_len = new_args_len;
   1.399 +    }
   1.400 +
   1.401 +  rule->flags |= BUS_MATCH_ARGS;
   1.402 +
   1.403 +  dbus_free (rule->args[arg]);
   1.404 +  rule->args[arg] = new;
   1.405 +
   1.406 +  /* NULL termination didn't get busted */
   1.407 +  _dbus_assert (rule->args[rule->args_len] == NULL);
   1.408 +
   1.409 +  return TRUE;
   1.410 +}
   1.411 +
   1.412 +#define ISWHITE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
   1.413 +
   1.414 +static dbus_bool_t
   1.415 +find_key (const DBusString *str,
   1.416 +          int               start,
   1.417 +          DBusString       *key,
   1.418 +          int              *value_pos,
   1.419 +          DBusError        *error)
   1.420 +{
   1.421 +  const char *p;
   1.422 +  const char *s;
   1.423 +  const char *key_start;
   1.424 +  const char *key_end;
   1.425 +
   1.426 +  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
   1.427 +  
   1.428 +  s = _dbus_string_get_const_data (str);
   1.429 +
   1.430 +  p = s + start;
   1.431 +
   1.432 +  while (*p && ISWHITE (*p))
   1.433 +    ++p;
   1.434 +
   1.435 +  key_start = p;
   1.436 +
   1.437 +  while (*p && *p != '=' && !ISWHITE (*p))
   1.438 +    ++p;
   1.439 +
   1.440 +  key_end = p;
   1.441 +
   1.442 +  while (*p && ISWHITE (*p))
   1.443 +    ++p;
   1.444 +  
   1.445 +  if (key_start == key_end)
   1.446 +    {
   1.447 +      /* Empty match rules or trailing whitespace are OK */
   1.448 +      *value_pos = p - s;
   1.449 +      return TRUE;
   1.450 +    }
   1.451 +
   1.452 +  if (*p != '=')
   1.453 +    {
   1.454 +      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.455 +                      "Match rule has a key with no subsequent '=' character");
   1.456 +      return FALSE;
   1.457 +    }
   1.458 +  ++p;
   1.459 +  
   1.460 +  if (!_dbus_string_append_len (key, key_start, key_end - key_start))
   1.461 +    {
   1.462 +      BUS_SET_OOM (error);
   1.463 +      return FALSE;
   1.464 +    }
   1.465 +
   1.466 +  *value_pos = p - s;
   1.467 +  
   1.468 +  return TRUE;
   1.469 +}
   1.470 +
   1.471 +static dbus_bool_t
   1.472 +find_value (const DBusString *str,
   1.473 +            int               start,
   1.474 +            const char       *key,
   1.475 +            DBusString       *value,
   1.476 +            int              *value_end,
   1.477 +            DBusError        *error)
   1.478 +{
   1.479 +  const char *p;
   1.480 +  const char *s;
   1.481 +  char quote_char;
   1.482 +  int orig_len;
   1.483 +
   1.484 +  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
   1.485 +  
   1.486 +  orig_len = _dbus_string_get_length (value);
   1.487 +  
   1.488 +  s = _dbus_string_get_const_data (str);
   1.489 +
   1.490 +  p = s + start;
   1.491 +
   1.492 +  quote_char = '\0';
   1.493 +
   1.494 +  while (*p)
   1.495 +    {
   1.496 +      if (quote_char == '\0')
   1.497 +        {
   1.498 +          switch (*p)
   1.499 +            {
   1.500 +            case '\0':
   1.501 +              goto done;
   1.502 +
   1.503 +            case '\'':
   1.504 +              quote_char = '\'';
   1.505 +              goto next;
   1.506 +              
   1.507 +            case ',':
   1.508 +              ++p;
   1.509 +              goto done;
   1.510 +
   1.511 +            case '\\':
   1.512 +              quote_char = '\\';
   1.513 +              goto next;
   1.514 +              
   1.515 +            default:
   1.516 +              if (!_dbus_string_append_byte (value, *p))
   1.517 +                {
   1.518 +                  BUS_SET_OOM (error);
   1.519 +                  goto failed;
   1.520 +                }
   1.521 +            }
   1.522 +        }
   1.523 +      else if (quote_char == '\\')
   1.524 +        {
   1.525 +          /* \ only counts as an escape if escaping a quote mark */
   1.526 +          if (*p != '\'')
   1.527 +            {
   1.528 +              if (!_dbus_string_append_byte (value, '\\'))
   1.529 +                {
   1.530 +                  BUS_SET_OOM (error);
   1.531 +                  goto failed;
   1.532 +                }
   1.533 +            }
   1.534 +
   1.535 +          if (!_dbus_string_append_byte (value, *p))
   1.536 +            {
   1.537 +              BUS_SET_OOM (error);
   1.538 +              goto failed;
   1.539 +            }
   1.540 +          
   1.541 +          quote_char = '\0';
   1.542 +        }
   1.543 +      else
   1.544 +        {
   1.545 +          _dbus_assert (quote_char == '\'');
   1.546 +
   1.547 +          if (*p == '\'')
   1.548 +            {
   1.549 +              quote_char = '\0';
   1.550 +            }
   1.551 +          else
   1.552 +            {
   1.553 +              if (!_dbus_string_append_byte (value, *p))
   1.554 +                {
   1.555 +                  BUS_SET_OOM (error);
   1.556 +                  goto failed;
   1.557 +                }
   1.558 +            }
   1.559 +        }
   1.560 +
   1.561 +    next:
   1.562 +      ++p;
   1.563 +    }
   1.564 +
   1.565 + done:
   1.566 +
   1.567 +  if (quote_char == '\\')
   1.568 +    {
   1.569 +      if (!_dbus_string_append_byte (value, '\\'))
   1.570 +        {
   1.571 +          BUS_SET_OOM (error);
   1.572 +          goto failed;
   1.573 +        }
   1.574 +    }
   1.575 +  else if (quote_char == '\'')
   1.576 +    {
   1.577 +      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.578 +                      "Unbalanced quotation marks in match rule");
   1.579 +      goto failed;
   1.580 +    }
   1.581 +  else
   1.582 +    _dbus_assert (quote_char == '\0');
   1.583 +
   1.584 +  /* Zero-length values are allowed */
   1.585 +  
   1.586 +  *value_end = p - s;
   1.587 +  
   1.588 +  return TRUE;
   1.589 +
   1.590 + failed:
   1.591 +  _DBUS_ASSERT_ERROR_IS_SET (error);
   1.592 +  _dbus_string_set_length (value, orig_len);
   1.593 +  return FALSE;
   1.594 +}
   1.595 +
   1.596 +/* duplicates aren't allowed so the real legitimate max is only 6 or
   1.597 + * so. Leaving extra so we don't have to bother to update it.
   1.598 + * FIXME this is sort of busted now with arg matching, but we let
   1.599 + * you match on up to 10 args for now
   1.600 + */
   1.601 +#define MAX_RULE_TOKENS 16
   1.602 +
   1.603 +/* this is slightly too high level to be termed a "token"
   1.604 + * but let's not be pedantic.
   1.605 + */
   1.606 +typedef struct
   1.607 +{
   1.608 +  char *key;
   1.609 +  char *value;
   1.610 +} RuleToken;
   1.611 +
   1.612 +static dbus_bool_t
   1.613 +tokenize_rule (const DBusString *rule_text,
   1.614 +               RuleToken         tokens[MAX_RULE_TOKENS],
   1.615 +               DBusError        *error) 
   1.616 +{
   1.617 +  int i;
   1.618 +  int pos;
   1.619 +  DBusString key;
   1.620 +  DBusString value;
   1.621 +  dbus_bool_t retval;
   1.622 +
   1.623 +  retval = FALSE;
   1.624 +  
   1.625 +  if (!_dbus_string_init (&key))
   1.626 +    {
   1.627 +      BUS_SET_OOM (error);
   1.628 +      return FALSE;
   1.629 +    }
   1.630 +
   1.631 +  if (!_dbus_string_init (&value))
   1.632 +    {
   1.633 +      _dbus_string_free (&key);
   1.634 +      BUS_SET_OOM (error);
   1.635 +      return FALSE;
   1.636 +    }
   1.637 +
   1.638 +  i = 0;
   1.639 +  pos = 0;
   1.640 +  while (i < MAX_RULE_TOKENS &&
   1.641 +         pos < _dbus_string_get_length (rule_text))
   1.642 +    {
   1.643 +      _dbus_assert (tokens[i].key == NULL);
   1.644 +      _dbus_assert (tokens[i].value == NULL);
   1.645 +
   1.646 +      if (!find_key (rule_text, pos, &key, &pos, error))
   1.647 +        goto out;
   1.648 +
   1.649 +      if (_dbus_string_get_length (&key) == 0)
   1.650 +        goto next;
   1.651 +      
   1.652 +      if (!_dbus_string_steal_data (&key, &tokens[i].key))
   1.653 +        {
   1.654 +          BUS_SET_OOM (error);
   1.655 +          goto out;
   1.656 +        }
   1.657 +
   1.658 +      if (!find_value (rule_text, pos, tokens[i].key, &value, &pos, error))
   1.659 +        goto out;
   1.660 +
   1.661 +      if (!_dbus_string_steal_data (&value, &tokens[i].value))
   1.662 +        {
   1.663 +          BUS_SET_OOM (error);
   1.664 +          goto out;
   1.665 +        }
   1.666 +
   1.667 +    next:
   1.668 +      ++i;
   1.669 +    }
   1.670 +
   1.671 +  retval = TRUE;
   1.672 +  
   1.673 + out:
   1.674 +  if (!retval)
   1.675 +    {
   1.676 +      i = 0;
   1.677 +      while (tokens[i].key || tokens[i].value)
   1.678 +        {
   1.679 +          dbus_free (tokens[i].key);
   1.680 +          dbus_free (tokens[i].value);
   1.681 +          tokens[i].key = NULL;
   1.682 +          tokens[i].value = NULL;
   1.683 +          ++i;
   1.684 +        }
   1.685 +    }
   1.686 +  
   1.687 +  _dbus_string_free (&key);
   1.688 +  _dbus_string_free (&value);
   1.689 +  
   1.690 +  return retval;
   1.691 +}
   1.692 +
   1.693 +static dbus_bool_t
   1.694 +bus_match_rule_parse_arg_match (BusMatchRule     *rule,
   1.695 +                                const char       *key,
   1.696 +                                const DBusString *value,
   1.697 +                                DBusError        *error)
   1.698 +{
   1.699 +  DBusString key_str;
   1.700 +  unsigned long arg;
   1.701 +  int end;
   1.702 +
   1.703 +  /* For now, arg0='foo' always implies that 'foo' is a
   1.704 +   * DBUS_TYPE_STRING. Someday we could add an arg0type='int32' thing
   1.705 +   * if we wanted, which would specify another type, in which case
   1.706 +   * arg0='5' would have the 5 parsed as an int rather than string.
   1.707 +   */
   1.708 +  
   1.709 +  /* First we need to parse arg0 = 0, arg27 = 27 */
   1.710 +
   1.711 +  _dbus_string_init_const (&key_str, key);
   1.712 +
   1.713 +  if (_dbus_string_get_length (&key_str) < 4)
   1.714 +    {
   1.715 +      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.716 +                      "Key '%s' in match rule starts with 'arg' but lacks an arg number. Should be 'arg0' or 'arg7' for example.\n", key);
   1.717 +      goto failed;
   1.718 +    }
   1.719 +
   1.720 +  if (!_dbus_string_parse_uint (&key_str, 3, &arg, &end) ||
   1.721 +      end != _dbus_string_get_length (&key_str))
   1.722 +    {
   1.723 +      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.724 +                      "Key '%s' in match rule starts with 'arg' but could not parse arg number. Should be 'arg0' or 'arg7' for example.\n", key);
   1.725 +      goto failed;
   1.726 +    }
   1.727 +
   1.728 +  /* If we didn't check this we could allocate a huge amount of RAM */
   1.729 +  if (arg > DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER)
   1.730 +    {
   1.731 +      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.732 +                      "Key '%s' in match rule has arg number %lu but the maximum is %d.\n", key, (unsigned long) arg, DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER);
   1.733 +      goto failed;
   1.734 +    }
   1.735 +  
   1.736 +  if ((rule->flags & BUS_MATCH_ARGS) &&
   1.737 +      rule->args_len > (int) arg &&
   1.738 +      rule->args[arg] != NULL)
   1.739 +    {
   1.740 +      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.741 +                      "Key '%s' specified twice in match rule\n", key);
   1.742 +      goto failed;
   1.743 +    }
   1.744 +  
   1.745 +  if (!bus_match_rule_set_arg (rule, arg,
   1.746 +                               _dbus_string_get_const_data (value)))
   1.747 +    {
   1.748 +      BUS_SET_OOM (error);
   1.749 +      goto failed;
   1.750 +    }
   1.751 +
   1.752 +  return TRUE;
   1.753 +
   1.754 + failed:
   1.755 +  _DBUS_ASSERT_ERROR_IS_SET (error);
   1.756 +  return FALSE;
   1.757 +}
   1.758 +
   1.759 +/*
   1.760 + * The format is comma-separated with strings quoted with single quotes
   1.761 + * as for the shell (to escape a literal single quote, use '\'').
   1.762 + *
   1.763 + * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',
   1.764 + * path='/bar/foo',destination=':452345.34'
   1.765 + *
   1.766 + */
   1.767 +BusMatchRule*
   1.768 +bus_match_rule_parse (DBusConnection   *matches_go_to,
   1.769 +                      const DBusString *rule_text,
   1.770 +                      DBusError        *error)
   1.771 +{
   1.772 +  BusMatchRule *rule;
   1.773 +  RuleToken tokens[MAX_RULE_TOKENS+1]; /* NULL termination + 1 */
   1.774 +  int i;
   1.775 +  
   1.776 +  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
   1.777 +
   1.778 +  if (_dbus_string_get_length (rule_text) > DBUS_MAXIMUM_MATCH_RULE_LENGTH)
   1.779 +    {
   1.780 +      dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
   1.781 +                      "Match rule text is %d bytes, maximum is %d",
   1.782 +                      _dbus_string_get_length (rule_text),
   1.783 +                      DBUS_MAXIMUM_MATCH_RULE_LENGTH);
   1.784 +      return NULL;
   1.785 +    }
   1.786 +  
   1.787 +  memset (tokens, '\0', sizeof (tokens));
   1.788 +  
   1.789 +  rule = bus_match_rule_new (matches_go_to);
   1.790 +  if (rule == NULL)
   1.791 +    {
   1.792 +      BUS_SET_OOM (error);
   1.793 +      goto failed;
   1.794 +    }
   1.795 +  
   1.796 +  if (!tokenize_rule (rule_text, tokens, error))
   1.797 +    goto failed;
   1.798 +  
   1.799 +  i = 0;
   1.800 +  while (tokens[i].key != NULL)
   1.801 +    {
   1.802 +      DBusString tmp_str;
   1.803 +      int len;
   1.804 +      const char *key = tokens[i].key;
   1.805 +      const char *value = tokens[i].value;
   1.806 +      
   1.807 +      _dbus_string_init_const (&tmp_str, value);
   1.808 +      len = _dbus_string_get_length (&tmp_str);
   1.809 +
   1.810 +      if (strcmp (key, "type") == 0)
   1.811 +        {
   1.812 +          int t;
   1.813 +
   1.814 +          if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
   1.815 +            {
   1.816 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.817 +                              "Key %s specified twice in match rule\n", key);
   1.818 +              goto failed;
   1.819 +            }
   1.820 +          
   1.821 +          t = dbus_message_type_from_string (value);
   1.822 +          
   1.823 +          if (t == DBUS_MESSAGE_TYPE_INVALID)
   1.824 +            {
   1.825 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.826 +                              "Invalid message type (%s) in match rule\n", value);
   1.827 +              goto failed;
   1.828 +            }
   1.829 +
   1.830 +          if (!bus_match_rule_set_message_type (rule, t))
   1.831 +            {
   1.832 +              BUS_SET_OOM (error);
   1.833 +              goto failed;
   1.834 +            }
   1.835 +        }
   1.836 +      else if (strcmp (key, "sender") == 0)
   1.837 +        {
   1.838 +          if (rule->flags & BUS_MATCH_SENDER)
   1.839 +            {
   1.840 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.841 +                              "Key %s specified twice in match rule\n", key);
   1.842 +              goto failed;
   1.843 +            }
   1.844 +
   1.845 +          if (!_dbus_validate_bus_name (&tmp_str, 0, len))
   1.846 +            {
   1.847 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.848 +                              "Sender name '%s' is invalid\n", value);
   1.849 +              goto failed;
   1.850 +            }
   1.851 +
   1.852 +          if (!bus_match_rule_set_sender (rule, value))
   1.853 +            {
   1.854 +              BUS_SET_OOM (error);
   1.855 +              goto failed;
   1.856 +            }
   1.857 +        }
   1.858 +      else if (strcmp (key, "interface") == 0)
   1.859 +        {
   1.860 +          if (rule->flags & BUS_MATCH_INTERFACE)
   1.861 +            {
   1.862 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.863 +                              "Key %s specified twice in match rule\n", key);
   1.864 +              goto failed;
   1.865 +            }
   1.866 +
   1.867 +          if (!_dbus_validate_interface (&tmp_str, 0, len))
   1.868 +            {
   1.869 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.870 +                              "Interface name '%s' is invalid\n", value);
   1.871 +              goto failed;
   1.872 +            }
   1.873 +
   1.874 +          if (!bus_match_rule_set_interface (rule, value))
   1.875 +            {
   1.876 +              BUS_SET_OOM (error);
   1.877 +              goto failed;
   1.878 +            }
   1.879 +        }
   1.880 +      else if (strcmp (key, "member") == 0)
   1.881 +        {
   1.882 +          if (rule->flags & BUS_MATCH_MEMBER)
   1.883 +            {
   1.884 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.885 +                              "Key %s specified twice in match rule\n", key);
   1.886 +              goto failed;
   1.887 +            }
   1.888 +
   1.889 +          if (!_dbus_validate_member (&tmp_str, 0, len))
   1.890 +            {
   1.891 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.892 +                              "Member name '%s' is invalid\n", value);
   1.893 +              goto failed;
   1.894 +            }
   1.895 +
   1.896 +          if (!bus_match_rule_set_member (rule, value))
   1.897 +            {
   1.898 +              BUS_SET_OOM (error);
   1.899 +              goto failed;
   1.900 +            }
   1.901 +        }
   1.902 +      else if (strcmp (key, "path") == 0)
   1.903 +        {
   1.904 +          if (rule->flags & BUS_MATCH_PATH)
   1.905 +            {
   1.906 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.907 +                              "Key %s specified twice in match rule\n", key);
   1.908 +              goto failed;
   1.909 +            }
   1.910 +
   1.911 +          if (!_dbus_validate_path (&tmp_str, 0, len))
   1.912 +            {
   1.913 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.914 +                              "Path '%s' is invalid\n", value);
   1.915 +              goto failed;
   1.916 +            }
   1.917 +
   1.918 +          if (!bus_match_rule_set_path (rule, value))
   1.919 +            {
   1.920 +              BUS_SET_OOM (error);
   1.921 +              goto failed;
   1.922 +            }
   1.923 +        }
   1.924 +      else if (strcmp (key, "destination") == 0)
   1.925 +        {
   1.926 +          if (rule->flags & BUS_MATCH_DESTINATION)
   1.927 +            {
   1.928 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.929 +                              "Key %s specified twice in match rule\n", key);
   1.930 +              goto failed;
   1.931 +            }
   1.932 +
   1.933 +          if (!_dbus_validate_bus_name (&tmp_str, 0, len))
   1.934 +            {
   1.935 +              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.936 +                              "Destination name '%s' is invalid\n", value);
   1.937 +              goto failed;
   1.938 +            }
   1.939 +
   1.940 +          if (!bus_match_rule_set_destination (rule, value))
   1.941 +            {
   1.942 +              BUS_SET_OOM (error);
   1.943 +              goto failed;
   1.944 +            }
   1.945 +        }
   1.946 +      else if (strncmp (key, "arg", 3) == 0)
   1.947 +        {
   1.948 +          if (!bus_match_rule_parse_arg_match (rule, key, &tmp_str, error))
   1.949 +            goto failed;
   1.950 +        }
   1.951 +      else
   1.952 +        {
   1.953 +          dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
   1.954 +                          "Unknown key \"%s\" in match rule",
   1.955 +                          key);
   1.956 +          goto failed;
   1.957 +        }
   1.958 +
   1.959 +      ++i;
   1.960 +    }
   1.961 +  
   1.962 +
   1.963 +  goto out;
   1.964 +  
   1.965 + failed:
   1.966 +  _DBUS_ASSERT_ERROR_IS_SET (error);
   1.967 +  if (rule)
   1.968 +    {
   1.969 +      bus_match_rule_unref (rule);
   1.970 +      rule = NULL;
   1.971 +    }
   1.972 +
   1.973 + out:
   1.974 +  
   1.975 +  i = 0;
   1.976 +  while (tokens[i].key || tokens[i].value)
   1.977 +    {
   1.978 +      _dbus_assert (i < MAX_RULE_TOKENS);
   1.979 +      dbus_free (tokens[i].key);
   1.980 +      dbus_free (tokens[i].value);
   1.981 +      ++i;
   1.982 +    }
   1.983 +  
   1.984 +  return rule;
   1.985 +}
   1.986 +
   1.987 +struct BusMatchmaker
   1.988 +{
   1.989 +  int refcount;
   1.990 +
   1.991 +  DBusList *all_rules;
   1.992 +};
   1.993 +
   1.994 +BusMatchmaker*
   1.995 +bus_matchmaker_new (void)
   1.996 +{
   1.997 +  BusMatchmaker *matchmaker;
   1.998 +
   1.999 +  matchmaker = dbus_new0 (BusMatchmaker, 1);
  1.1000 +  if (matchmaker == NULL)
  1.1001 +    return NULL;
  1.1002 +
  1.1003 +  matchmaker->refcount = 1;
  1.1004 +  
  1.1005 +  return matchmaker;
  1.1006 +}
  1.1007 +
  1.1008 +BusMatchmaker *
  1.1009 +bus_matchmaker_ref (BusMatchmaker *matchmaker)
  1.1010 +{
  1.1011 +  _dbus_assert (matchmaker->refcount > 0);
  1.1012 +
  1.1013 +  matchmaker->refcount += 1;
  1.1014 +
  1.1015 +  return matchmaker;
  1.1016 +}
  1.1017 +
  1.1018 +void
  1.1019 +bus_matchmaker_unref (BusMatchmaker *matchmaker)
  1.1020 +{
  1.1021 +  _dbus_assert (matchmaker->refcount > 0);
  1.1022 +
  1.1023 +  matchmaker->refcount -= 1;
  1.1024 +  if (matchmaker->refcount == 0)
  1.1025 +    {
  1.1026 +      while (matchmaker->all_rules != NULL)
  1.1027 +        {
  1.1028 +          BusMatchRule *rule;
  1.1029 +
  1.1030 +          rule = matchmaker->all_rules->data;
  1.1031 +          bus_match_rule_unref (rule);
  1.1032 +          _dbus_list_remove_link (&matchmaker->all_rules,
  1.1033 +                                  matchmaker->all_rules);
  1.1034 +        }
  1.1035 +
  1.1036 +      dbus_free (matchmaker);
  1.1037 +    }
  1.1038 +}
  1.1039 +
  1.1040 +/* The rule can't be modified after it's added. */
  1.1041 +dbus_bool_t
  1.1042 +bus_matchmaker_add_rule (BusMatchmaker   *matchmaker,
  1.1043 +                         BusMatchRule    *rule)
  1.1044 +{
  1.1045 +  _dbus_assert (bus_connection_is_active (rule->matches_go_to));
  1.1046 +
  1.1047 +  if (!_dbus_list_append (&matchmaker->all_rules, rule))
  1.1048 +    return FALSE;
  1.1049 +
  1.1050 +  if (!bus_connection_add_match_rule (rule->matches_go_to, rule))
  1.1051 +    {
  1.1052 +      _dbus_list_remove_last (&matchmaker->all_rules, rule);
  1.1053 +      return FALSE;
  1.1054 +    }
  1.1055 +  
  1.1056 +  bus_match_rule_ref (rule);
  1.1057 +
  1.1058 +#ifdef DBUS_ENABLE_VERBOSE_MODE
  1.1059 +  {
  1.1060 +    char *s = match_rule_to_string (rule);
  1.1061 +
  1.1062 +    _dbus_verbose ("Added match rule %s to connection %p\n",
  1.1063 +                   s, rule->matches_go_to);
  1.1064 +    dbus_free (s);
  1.1065 +  }
  1.1066 +#endif
  1.1067 +  
  1.1068 +  return TRUE;
  1.1069 +}
  1.1070 +
  1.1071 +static dbus_bool_t
  1.1072 +match_rule_equal (BusMatchRule *a,
  1.1073 +                  BusMatchRule *b)
  1.1074 +{
  1.1075 +  if (a->flags != b->flags)
  1.1076 +    return FALSE;
  1.1077 +
  1.1078 +  if (a->matches_go_to != b->matches_go_to)
  1.1079 +    return FALSE;
  1.1080 +
  1.1081 +  if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
  1.1082 +      a->message_type != b->message_type)
  1.1083 +    return FALSE;
  1.1084 +
  1.1085 +  if ((a->flags & BUS_MATCH_MEMBER) &&
  1.1086 +      strcmp (a->member, b->member) != 0)
  1.1087 +    return FALSE;
  1.1088 +
  1.1089 +  if ((a->flags & BUS_MATCH_PATH) &&
  1.1090 +      strcmp (a->path, b->path) != 0)
  1.1091 +    return FALSE;
  1.1092 +  
  1.1093 +  if ((a->flags & BUS_MATCH_INTERFACE) &&
  1.1094 +      strcmp (a->interface, b->interface) != 0)
  1.1095 +    return FALSE;
  1.1096 +
  1.1097 +  if ((a->flags & BUS_MATCH_SENDER) &&
  1.1098 +      strcmp (a->sender, b->sender) != 0)
  1.1099 +    return FALSE;
  1.1100 +
  1.1101 +  if ((a->flags & BUS_MATCH_DESTINATION) &&
  1.1102 +      strcmp (a->destination, b->destination) != 0)
  1.1103 +    return FALSE;
  1.1104 +
  1.1105 +  if (a->flags & BUS_MATCH_ARGS)
  1.1106 +    {
  1.1107 +      int i;
  1.1108 +      
  1.1109 +      if (a->args_len != b->args_len)
  1.1110 +        return FALSE;
  1.1111 +      
  1.1112 +      i = 0;
  1.1113 +      while (i < a->args_len)
  1.1114 +        {
  1.1115 +          if ((a->args[i] != NULL) != (b->args[i] != NULL))
  1.1116 +            return FALSE;
  1.1117 +
  1.1118 +          if (a->args[i] != NULL)
  1.1119 +            {
  1.1120 +              _dbus_assert (b->args[i] != NULL);
  1.1121 +              if (strcmp (a->args[i], b->args[i]) != 0)
  1.1122 +                return FALSE;
  1.1123 +            }
  1.1124 +          
  1.1125 +          ++i;
  1.1126 +        }
  1.1127 +    }
  1.1128 +  
  1.1129 +  return TRUE;
  1.1130 +}
  1.1131 +
  1.1132 +static void
  1.1133 +bus_matchmaker_remove_rule_link (BusMatchmaker   *matchmaker,
  1.1134 +                                 DBusList        *link)
  1.1135 +{
  1.1136 +  BusMatchRule *rule = link->data;
  1.1137 +  
  1.1138 +  bus_connection_remove_match_rule (rule->matches_go_to, rule);
  1.1139 +  _dbus_list_remove_link (&matchmaker->all_rules, link);
  1.1140 +
  1.1141 +#ifdef DBUS_ENABLE_VERBOSE_MODE
  1.1142 +  {
  1.1143 +    char *s = match_rule_to_string (rule);
  1.1144 +
  1.1145 +    _dbus_verbose ("Removed match rule %s for connection %p\n",
  1.1146 +                   s, rule->matches_go_to);
  1.1147 +    dbus_free (s);
  1.1148 +  }
  1.1149 +#endif
  1.1150 +  
  1.1151 +  bus_match_rule_unref (rule);  
  1.1152 +}
  1.1153 +
  1.1154 +void
  1.1155 +bus_matchmaker_remove_rule (BusMatchmaker   *matchmaker,
  1.1156 +                            BusMatchRule    *rule)
  1.1157 +{
  1.1158 +  bus_connection_remove_match_rule (rule->matches_go_to, rule);
  1.1159 +  _dbus_list_remove (&matchmaker->all_rules, rule);
  1.1160 +
  1.1161 +#ifdef DBUS_ENABLE_VERBOSE_MODE
  1.1162 +  {
  1.1163 +    char *s = match_rule_to_string (rule);
  1.1164 +
  1.1165 +    _dbus_verbose ("Removed match rule %s for connection %p\n",
  1.1166 +                   s, rule->matches_go_to);
  1.1167 +    dbus_free (s);
  1.1168 +  }
  1.1169 +#endif
  1.1170 +  
  1.1171 +  bus_match_rule_unref (rule);
  1.1172 +}
  1.1173 +
  1.1174 +/* Remove a single rule which is equal to the given rule by value */
  1.1175 +dbus_bool_t
  1.1176 +bus_matchmaker_remove_rule_by_value (BusMatchmaker   *matchmaker,
  1.1177 +                                     BusMatchRule    *value,
  1.1178 +                                     DBusError       *error)
  1.1179 +{
  1.1180 +  /* FIXME this is an unoptimized linear scan */
  1.1181 +
  1.1182 +  DBusList *link;
  1.1183 +
  1.1184 +  /* we traverse backward because bus_connection_remove_match_rule()
  1.1185 +   * removes the most-recently-added rule
  1.1186 +   */
  1.1187 +  link = _dbus_list_get_last_link (&matchmaker->all_rules);
  1.1188 +  while (link != NULL)
  1.1189 +    {
  1.1190 +      BusMatchRule *rule;
  1.1191 +      DBusList *prev;
  1.1192 +
  1.1193 +      rule = link->data;
  1.1194 +      prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link);
  1.1195 +
  1.1196 +      if (match_rule_equal (rule, value))
  1.1197 +        {
  1.1198 +          bus_matchmaker_remove_rule_link (matchmaker, link);
  1.1199 +          break;
  1.1200 +        }
  1.1201 +
  1.1202 +      link = prev;
  1.1203 +    }
  1.1204 +
  1.1205 +  if (link == NULL)
  1.1206 +    {
  1.1207 +      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
  1.1208 +                      "The given match rule wasn't found and can't be removed");
  1.1209 +      return FALSE;
  1.1210 +    }
  1.1211 +
  1.1212 +  return TRUE;
  1.1213 +}
  1.1214 +
  1.1215 +void
  1.1216 +bus_matchmaker_disconnected (BusMatchmaker   *matchmaker,
  1.1217 +                             DBusConnection  *disconnected)
  1.1218 +{
  1.1219 +  DBusList *link;
  1.1220 +
  1.1221 +  /* FIXME
  1.1222 +   *
  1.1223 +   * This scans all match rules on the bus. We could avoid that
  1.1224 +   * for the rules belonging to the connection, since we keep
  1.1225 +   * a list of those; but for the rules that just refer to
  1.1226 +   * the connection we'd need to do something more elaborate.
  1.1227 +   * 
  1.1228 +   */
  1.1229 +  
  1.1230 +  _dbus_assert (bus_connection_is_active (disconnected));
  1.1231 +
  1.1232 +  link = _dbus_list_get_first_link (&matchmaker->all_rules);
  1.1233 +  while (link != NULL)
  1.1234 +    {
  1.1235 +      BusMatchRule *rule;
  1.1236 +      DBusList *next;
  1.1237 +
  1.1238 +      rule = link->data;
  1.1239 +      next = _dbus_list_get_next_link (&matchmaker->all_rules, link);
  1.1240 +
  1.1241 +      if (rule->matches_go_to == disconnected)
  1.1242 +        {
  1.1243 +          bus_matchmaker_remove_rule_link (matchmaker, link);
  1.1244 +        }
  1.1245 +      else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') ||
  1.1246 +               ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':'))
  1.1247 +        {
  1.1248 +          /* The rule matches to/from a base service, see if it's the
  1.1249 +           * one being disconnected, since we know this service name
  1.1250 +           * will never be recycled.
  1.1251 +           */
  1.1252 +          const char *name;
  1.1253 +
  1.1254 +          name = bus_connection_get_name (disconnected);
  1.1255 +          _dbus_assert (name != NULL); /* because we're an active connection */
  1.1256 +
  1.1257 +          if (((rule->flags & BUS_MATCH_SENDER) &&
  1.1258 +               strcmp (rule->sender, name) == 0) ||
  1.1259 +              ((rule->flags & BUS_MATCH_DESTINATION) &&
  1.1260 +               strcmp (rule->destination, name) == 0))
  1.1261 +            {
  1.1262 +              bus_matchmaker_remove_rule_link (matchmaker, link);
  1.1263 +            }
  1.1264 +        }
  1.1265 +
  1.1266 +      link = next;
  1.1267 +    }
  1.1268 +}
  1.1269 +
  1.1270 +static dbus_bool_t
  1.1271 +connection_is_primary_owner (DBusConnection *connection,
  1.1272 +                             const char     *service_name)
  1.1273 +{
  1.1274 +  BusService *service;
  1.1275 +  DBusString str;
  1.1276 +  BusRegistry *registry;
  1.1277 +
  1.1278 +  _dbus_assert (connection != NULL);
  1.1279 +  
  1.1280 +  registry = bus_connection_get_registry (connection);
  1.1281 +
  1.1282 +  _dbus_string_init_const (&str, service_name);
  1.1283 +  service = bus_registry_lookup (registry, &str);
  1.1284 +
  1.1285 +  if (service == NULL)
  1.1286 +    return FALSE; /* Service doesn't exist so connection can't own it. */
  1.1287 +
  1.1288 +  return bus_service_get_primary_owners_connection (service) == connection;
  1.1289 +}
  1.1290 +
  1.1291 +static dbus_bool_t
  1.1292 +match_rule_matches (BusMatchRule    *rule,
  1.1293 +                    DBusConnection  *sender,
  1.1294 +                    DBusConnection  *addressed_recipient,
  1.1295 +                    DBusMessage     *message)
  1.1296 +{
  1.1297 +  /* All features of the match rule are AND'd together,
  1.1298 +   * so FALSE if any of them don't match.
  1.1299 +   */
  1.1300 +
  1.1301 +  /* sender/addressed_recipient of #NULL may mean bus driver,
  1.1302 +   * or for addressed_recipient may mean a message with no
  1.1303 +   * specific recipient (i.e. a signal)
  1.1304 +   */
  1.1305 +  
  1.1306 +  if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
  1.1307 +    {
  1.1308 +      _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
  1.1309 +
  1.1310 +      if (rule->message_type != dbus_message_get_type (message))
  1.1311 +        return FALSE;
  1.1312 +    }
  1.1313 +
  1.1314 +  if (rule->flags & BUS_MATCH_INTERFACE)
  1.1315 +    {
  1.1316 +      const char *iface;
  1.1317 +
  1.1318 +      _dbus_assert (rule->interface != NULL);
  1.1319 +
  1.1320 +      iface = dbus_message_get_interface (message);
  1.1321 +      if (iface == NULL)
  1.1322 +        return FALSE;
  1.1323 +
  1.1324 +      if (strcmp (iface, rule->interface) != 0)
  1.1325 +        return FALSE;
  1.1326 +    }
  1.1327 +
  1.1328 +  if (rule->flags & BUS_MATCH_MEMBER)
  1.1329 +    {
  1.1330 +      const char *member;
  1.1331 +
  1.1332 +      _dbus_assert (rule->member != NULL);
  1.1333 +
  1.1334 +      member = dbus_message_get_member (message);
  1.1335 +      if (member == NULL)
  1.1336 +        return FALSE;
  1.1337 +
  1.1338 +      if (strcmp (member, rule->member) != 0)
  1.1339 +        return FALSE;
  1.1340 +    }
  1.1341 +
  1.1342 +  if (rule->flags & BUS_MATCH_SENDER)
  1.1343 +    {
  1.1344 +      _dbus_assert (rule->sender != NULL);
  1.1345 +
  1.1346 +      if (sender == NULL)
  1.1347 +        {
  1.1348 +          if (strcmp (rule->sender,
  1.1349 +                      DBUS_SERVICE_DBUS) != 0)
  1.1350 +            return FALSE;
  1.1351 +        }
  1.1352 +      else
  1.1353 +        {
  1.1354 +          if (!connection_is_primary_owner (sender, rule->sender))
  1.1355 +            return FALSE;
  1.1356 +        }
  1.1357 +    }
  1.1358 +
  1.1359 +  if (rule->flags & BUS_MATCH_DESTINATION)
  1.1360 +    {
  1.1361 +      const char *destination;
  1.1362 +
  1.1363 +      _dbus_assert (rule->destination != NULL);
  1.1364 +
  1.1365 +      destination = dbus_message_get_destination (message);
  1.1366 +      if (destination == NULL)
  1.1367 +        return FALSE;
  1.1368 +
  1.1369 +      if (addressed_recipient == NULL)
  1.1370 +        {          
  1.1371 +          if (strcmp (rule->destination,
  1.1372 +                      DBUS_SERVICE_DBUS) != 0)
  1.1373 +            return FALSE;
  1.1374 +        }
  1.1375 +      else
  1.1376 +        {
  1.1377 +          if (!connection_is_primary_owner (addressed_recipient, rule->destination))
  1.1378 +            return FALSE;
  1.1379 +        }
  1.1380 +    }
  1.1381 +
  1.1382 +  if (rule->flags & BUS_MATCH_PATH)
  1.1383 +    {
  1.1384 +      const char *path;
  1.1385 +
  1.1386 +      _dbus_assert (rule->path != NULL);
  1.1387 +
  1.1388 +      path = dbus_message_get_path (message);
  1.1389 +      if (path == NULL)
  1.1390 +        return FALSE;
  1.1391 +
  1.1392 +      if (strcmp (path, rule->path) != 0)
  1.1393 +        return FALSE;
  1.1394 +    }
  1.1395 +
  1.1396 +  if (rule->flags & BUS_MATCH_ARGS)
  1.1397 +    {
  1.1398 +      int i;
  1.1399 +      DBusMessageIter iter;
  1.1400 +      
  1.1401 +      _dbus_assert (rule->args != NULL);
  1.1402 +
  1.1403 +      dbus_message_iter_init (message, &iter);
  1.1404 +      
  1.1405 +      i = 0;
  1.1406 +      while (i < rule->args_len)
  1.1407 +        {
  1.1408 +          int current_type;
  1.1409 +          const char *expected_arg;
  1.1410 +
  1.1411 +          expected_arg = rule->args[i];
  1.1412 +          
  1.1413 +          current_type = dbus_message_iter_get_arg_type (&iter);
  1.1414 +
  1.1415 +          if (expected_arg != NULL)
  1.1416 +            {
  1.1417 +              const char *actual_arg;
  1.1418 +              
  1.1419 +              if (current_type != DBUS_TYPE_STRING)
  1.1420 +                return FALSE;
  1.1421 +
  1.1422 +              actual_arg = NULL;
  1.1423 +              dbus_message_iter_get_basic (&iter, &actual_arg);
  1.1424 +              _dbus_assert (actual_arg != NULL);
  1.1425 +
  1.1426 +              if (strcmp (expected_arg, actual_arg) != 0)
  1.1427 +                return FALSE;
  1.1428 +            }
  1.1429 +          
  1.1430 +          if (current_type != DBUS_TYPE_INVALID)
  1.1431 +            dbus_message_iter_next (&iter);
  1.1432 +
  1.1433 +          ++i;
  1.1434 +        }
  1.1435 +    }
  1.1436 +  
  1.1437 +  return TRUE;
  1.1438 +}
  1.1439 +
  1.1440 +dbus_bool_t
  1.1441 +bus_matchmaker_get_recipients (BusMatchmaker   *matchmaker,
  1.1442 +                               BusConnections  *connections,
  1.1443 +                               DBusConnection  *sender,
  1.1444 +                               DBusConnection  *addressed_recipient,
  1.1445 +                               DBusMessage     *message,
  1.1446 +                               DBusList       **recipients_p)
  1.1447 +{
  1.1448 +  /* FIXME for now this is a wholly unoptimized linear search */
  1.1449 +  /* Guessing the important optimization is to skip the signal-related
  1.1450 +   * match lists when processing method call and exception messages.
  1.1451 +   * So separate match rule lists for signals?
  1.1452 +   */
  1.1453 +  
  1.1454 +  DBusList *link;
  1.1455 +
  1.1456 +  _dbus_assert (*recipients_p == NULL);
  1.1457 +
  1.1458 +  /* This avoids sending same message to the same connection twice.
  1.1459 +   * Purpose of the stamp instead of a bool is to avoid iterating over
  1.1460 +   * all connections resetting the bool each time.
  1.1461 +   */
  1.1462 +  bus_connections_increment_stamp (connections);
  1.1463 +
  1.1464 +  /* addressed_recipient is already receiving the message, don't add to list.
  1.1465 +   * NULL addressed_recipient means either bus driver, or this is a signal
  1.1466 +   * and thus lacks a specific addressed_recipient.
  1.1467 +   */
  1.1468 +  if (addressed_recipient != NULL)
  1.1469 +    bus_connection_mark_stamp (addressed_recipient);
  1.1470 +
  1.1471 +  link = _dbus_list_get_first_link (&matchmaker->all_rules);
  1.1472 +  while (link != NULL)
  1.1473 +    {
  1.1474 +      BusMatchRule *rule;
  1.1475 +
  1.1476 +      rule = link->data;
  1.1477 +
  1.1478 +#ifdef DBUS_ENABLE_VERBOSE_MODE
  1.1479 +      {
  1.1480 +        char *s = match_rule_to_string (rule);
  1.1481 +        
  1.1482 +        _dbus_verbose ("Checking whether message matches rule %s for connection %p\n",
  1.1483 +                       s, rule->matches_go_to);
  1.1484 +        dbus_free (s);
  1.1485 +      }
  1.1486 +#endif
  1.1487 +      
  1.1488 +      if (match_rule_matches (rule,
  1.1489 +                              sender, addressed_recipient, message))
  1.1490 +        {
  1.1491 +          _dbus_verbose ("Rule matched\n");
  1.1492 +          
  1.1493 +          /* Append to the list if we haven't already */
  1.1494 +          if (bus_connection_mark_stamp (rule->matches_go_to))
  1.1495 +            {
  1.1496 +              if (!_dbus_list_append (recipients_p, rule->matches_go_to))
  1.1497 +                goto nomem;
  1.1498 +            }
  1.1499 +#ifdef DBUS_ENABLE_VERBOSE_MODE
  1.1500 +          else
  1.1501 +            {
  1.1502 +              _dbus_verbose ("Connection already receiving this message, so not adding again\n");
  1.1503 +            }
  1.1504 +#endif /* DBUS_ENABLE_VERBOSE_MODE */
  1.1505 +        }
  1.1506 +
  1.1507 +      link = _dbus_list_get_next_link (&matchmaker->all_rules, link);
  1.1508 +    }
  1.1509 +
  1.1510 +  return TRUE;
  1.1511 +
  1.1512 + nomem:
  1.1513 +  _dbus_list_clear (recipients_p);
  1.1514 +  return FALSE;
  1.1515 +}
  1.1516 +
  1.1517 +#ifdef DBUS_BUILD_TESTS
  1.1518 +#include "test.h"
  1.1519 +#include <stdlib.h>
  1.1520 +
  1.1521 +static BusMatchRule*
  1.1522 +check_parse (dbus_bool_t should_succeed,
  1.1523 +             const char *text)
  1.1524 +{
  1.1525 +  BusMatchRule *rule;
  1.1526 +  DBusString str;
  1.1527 +  DBusError error;
  1.1528 +
  1.1529 +  dbus_error_init (&error);
  1.1530 +
  1.1531 +  _dbus_string_init_const (&str, text);
  1.1532 +  
  1.1533 +  rule = bus_match_rule_parse (NULL, &str, &error);
  1.1534 +  if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
  1.1535 +    {
  1.1536 +      dbus_error_free (&error);
  1.1537 +      return NULL;
  1.1538 +    }
  1.1539 +
  1.1540 +  if (should_succeed && rule == NULL)
  1.1541 +    {
  1.1542 +      _dbus_warn ("Failed to parse: %s: %s: \"%s\"\n",
  1.1543 +                  error.name, error.message,
  1.1544 +                  _dbus_string_get_const_data (&str));
  1.1545 +      exit (1);
  1.1546 +    }
  1.1547 +
  1.1548 +  if (!should_succeed && rule != NULL)
  1.1549 +    {
  1.1550 +      _dbus_warn ("Failed to fail to parse: \"%s\"\n",
  1.1551 +                  _dbus_string_get_const_data (&str));
  1.1552 +      exit (1);
  1.1553 +    }
  1.1554 +
  1.1555 +  dbus_error_free (&error);
  1.1556 +
  1.1557 +  return rule;
  1.1558 +}
  1.1559 +
  1.1560 +static void
  1.1561 +assert_large_rule (BusMatchRule *rule)
  1.1562 +{
  1.1563 +  _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
  1.1564 +  _dbus_assert (rule->flags & BUS_MATCH_SENDER);
  1.1565 +  _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
  1.1566 +  _dbus_assert (rule->flags & BUS_MATCH_MEMBER);
  1.1567 +  _dbus_assert (rule->flags & BUS_MATCH_DESTINATION);
  1.1568 +  _dbus_assert (rule->flags & BUS_MATCH_PATH);
  1.1569 +
  1.1570 +  _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
  1.1571 +  _dbus_assert (rule->interface != NULL);
  1.1572 +  _dbus_assert (rule->member != NULL);
  1.1573 +  _dbus_assert (rule->sender != NULL);
  1.1574 +  _dbus_assert (rule->destination != NULL);
  1.1575 +  _dbus_assert (rule->path != NULL);
  1.1576 +
  1.1577 +  _dbus_assert (strcmp (rule->interface, "org.freedesktop.DBusInterface") == 0);
  1.1578 +  _dbus_assert (strcmp (rule->sender, "org.freedesktop.DBusSender") == 0);
  1.1579 +  _dbus_assert (strcmp (rule->member, "Foo") == 0);
  1.1580 +  _dbus_assert (strcmp (rule->path, "/bar/foo") == 0);
  1.1581 +  _dbus_assert (strcmp (rule->destination, ":452345.34") == 0);
  1.1582 +}
  1.1583 +
  1.1584 +static dbus_bool_t
  1.1585 +test_parsing (void *data)
  1.1586 +{
  1.1587 +  BusMatchRule *rule;
  1.1588 +
  1.1589 +  rule = check_parse (TRUE, "type='signal',sender='org.freedesktop.DBusSender',interface='org.freedesktop.DBusInterface',member='Foo',path='/bar/foo',destination=':452345.34'");
  1.1590 +  if (rule != NULL)
  1.1591 +    {
  1.1592 +      assert_large_rule (rule);
  1.1593 +      bus_match_rule_unref (rule);
  1.1594 +    }
  1.1595 +
  1.1596 +  /* With extra whitespace and useless quotes */
  1.1597 +  rule = check_parse (TRUE, "    type='signal',  \tsender='org.freedes''ktop.DBusSender',   interface='org.freedesktop.DBusInterface''''', \tmember='Foo',path='/bar/foo',destination=':452345.34'''''");
  1.1598 +  if (rule != NULL)
  1.1599 +    {
  1.1600 +      assert_large_rule (rule);
  1.1601 +      bus_match_rule_unref (rule);
  1.1602 +    }
  1.1603 +
  1.1604 +
  1.1605 +  /* A simple signal connection */
  1.1606 +  rule = check_parse (TRUE, "type='signal',path='/foo',interface='org.Bar'");
  1.1607 +  if (rule != NULL)
  1.1608 +    {
  1.1609 +      _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
  1.1610 +      _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
  1.1611 +      _dbus_assert (rule->flags & BUS_MATCH_PATH);
  1.1612 +
  1.1613 +      _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
  1.1614 +      _dbus_assert (rule->interface != NULL);
  1.1615 +      _dbus_assert (rule->path != NULL);
  1.1616 +
  1.1617 +      _dbus_assert (strcmp (rule->interface, "org.Bar") == 0);
  1.1618 +      _dbus_assert (strcmp (rule->path, "/foo") == 0);
  1.1619 +  
  1.1620 +      bus_match_rule_unref (rule);
  1.1621 +    }
  1.1622 +
  1.1623 +  /* argN */
  1.1624 +  rule = check_parse (TRUE, "arg0='foo'");
  1.1625 +  if (rule != NULL)
  1.1626 +    {
  1.1627 +      _dbus_assert (rule->flags == BUS_MATCH_ARGS);
  1.1628 +      _dbus_assert (rule->args != NULL);
  1.1629 +      _dbus_assert (rule->args_len == 1);
  1.1630 +      _dbus_assert (rule->args[0] != NULL);
  1.1631 +      _dbus_assert (rule->args[1] == NULL);
  1.1632 +      _dbus_assert (strcmp (rule->args[0], "foo") == 0);
  1.1633 +
  1.1634 +      bus_match_rule_unref (rule);
  1.1635 +    }
  1.1636 +  
  1.1637 +  rule = check_parse (TRUE, "arg1='foo'");
  1.1638 +  if (rule != NULL)
  1.1639 +    {
  1.1640 +      _dbus_assert (rule->flags == BUS_MATCH_ARGS);
  1.1641 +      _dbus_assert (rule->args != NULL);
  1.1642 +      _dbus_assert (rule->args_len == 2);
  1.1643 +      _dbus_assert (rule->args[0] == NULL);
  1.1644 +      _dbus_assert (rule->args[1] != NULL);
  1.1645 +      _dbus_assert (rule->args[2] == NULL);
  1.1646 +      _dbus_assert (strcmp (rule->args[1], "foo") == 0);
  1.1647 +
  1.1648 +      bus_match_rule_unref (rule);
  1.1649 +    }
  1.1650 +
  1.1651 +  rule = check_parse (TRUE, "arg2='foo'");
  1.1652 +  if (rule != NULL)
  1.1653 +    {
  1.1654 +      _dbus_assert (rule->flags == BUS_MATCH_ARGS);
  1.1655 +      _dbus_assert (rule->args != NULL);
  1.1656 +      _dbus_assert (rule->args_len == 3);
  1.1657 +      _dbus_assert (rule->args[0] == NULL);
  1.1658 +      _dbus_assert (rule->args[1] == NULL);
  1.1659 +      _dbus_assert (rule->args[2] != NULL);
  1.1660 +      _dbus_assert (rule->args[3] == NULL);
  1.1661 +      _dbus_assert (strcmp (rule->args[2], "foo") == 0);
  1.1662 +
  1.1663 +      bus_match_rule_unref (rule);
  1.1664 +    }
  1.1665 +  
  1.1666 +  rule = check_parse (TRUE, "arg40='foo'");
  1.1667 +  if (rule != NULL)
  1.1668 +    {
  1.1669 +      _dbus_assert (rule->flags == BUS_MATCH_ARGS);
  1.1670 +      _dbus_assert (rule->args != NULL);
  1.1671 +      _dbus_assert (rule->args_len == 41);
  1.1672 +      _dbus_assert (rule->args[0] == NULL);
  1.1673 +      _dbus_assert (rule->args[1] == NULL);
  1.1674 +      _dbus_assert (rule->args[40] != NULL);
  1.1675 +      _dbus_assert (rule->args[41] == NULL);
  1.1676 +      _dbus_assert (strcmp (rule->args[40], "foo") == 0);
  1.1677 +
  1.1678 +      bus_match_rule_unref (rule);
  1.1679 +    }
  1.1680 +  
  1.1681 +  rule = check_parse (TRUE, "arg63='foo'");
  1.1682 +  if (rule != NULL)
  1.1683 +    {
  1.1684 +      _dbus_assert (rule->flags == BUS_MATCH_ARGS);
  1.1685 +      _dbus_assert (rule->args != NULL);
  1.1686 +      _dbus_assert (rule->args_len == 64);
  1.1687 +      _dbus_assert (rule->args[0] == NULL);
  1.1688 +      _dbus_assert (rule->args[1] == NULL);
  1.1689 +      _dbus_assert (rule->args[63] != NULL);
  1.1690 +      _dbus_assert (rule->args[64] == NULL);
  1.1691 +      _dbus_assert (strcmp (rule->args[63], "foo") == 0);
  1.1692 +
  1.1693 +      bus_match_rule_unref (rule);
  1.1694 +    }
  1.1695 +  
  1.1696 +  /* Too-large argN */
  1.1697 +  rule = check_parse (FALSE, "arg300='foo'");
  1.1698 +  _dbus_assert (rule == NULL);
  1.1699 +  rule = check_parse (FALSE, "arg64='foo'");
  1.1700 +  _dbus_assert (rule == NULL);
  1.1701 +
  1.1702 +  /* No N in argN */
  1.1703 +  rule = check_parse (FALSE, "arg='foo'");
  1.1704 +  _dbus_assert (rule == NULL);
  1.1705 +  rule = check_parse (FALSE, "argv='foo'");
  1.1706 +  _dbus_assert (rule == NULL);
  1.1707 +  rule = check_parse (FALSE, "arg3junk='foo'");
  1.1708 +  _dbus_assert (rule == NULL);
  1.1709 +  rule = check_parse (FALSE, "argument='foo'");
  1.1710 +  _dbus_assert (rule == NULL);
  1.1711 +  
  1.1712 +  /* Reject duplicates */
  1.1713 +  rule = check_parse (FALSE, "type='signal',type='method_call'");
  1.1714 +  _dbus_assert (rule == NULL);
  1.1715 +
  1.1716 +  /* Duplicates with the argN code */
  1.1717 +  rule = check_parse (FALSE, "arg0='foo',arg0='bar'");
  1.1718 +  _dbus_assert (rule == NULL);
  1.1719 +  rule = check_parse (FALSE, "arg3='foo',arg3='bar'");
  1.1720 +  _dbus_assert (rule == NULL);
  1.1721 +  rule = check_parse (FALSE, "arg30='foo',arg30='bar'");
  1.1722 +  _dbus_assert (rule == NULL);
  1.1723 +  
  1.1724 +  /* Reject broken keys */
  1.1725 +  rule = check_parse (FALSE, "blah='signal'");
  1.1726 +  _dbus_assert (rule == NULL);
  1.1727 +
  1.1728 +  /* Reject broken values */
  1.1729 +  rule = check_parse (FALSE, "type='chouin'");
  1.1730 +  _dbus_assert (rule == NULL);
  1.1731 +  rule = check_parse (FALSE, "interface='abc@def++'");
  1.1732 +  _dbus_assert (rule == NULL);
  1.1733 +  rule = check_parse (FALSE, "service='youpi'");
  1.1734 +  _dbus_assert (rule == NULL);
  1.1735 +
  1.1736 +  /* Allow empty rule */
  1.1737 +  rule = check_parse (TRUE, "");
  1.1738 +  if (rule != NULL)
  1.1739 +    {
  1.1740 +      _dbus_assert (rule->flags == 0);
  1.1741 +      
  1.1742 +      bus_match_rule_unref (rule);
  1.1743 +    }
  1.1744 +
  1.1745 +  /* All-whitespace rule is the same as empty */
  1.1746 +  rule = check_parse (TRUE, "    \t");
  1.1747 +  if (rule != NULL)
  1.1748 +    {
  1.1749 +      _dbus_assert (rule->flags == 0);
  1.1750 +      
  1.1751 +      bus_match_rule_unref (rule);
  1.1752 +    }
  1.1753 +
  1.1754 +  /* But with non-whitespace chars and no =value, it's not OK */
  1.1755 +  rule = check_parse (FALSE, "type");
  1.1756 +  _dbus_assert (rule == NULL);
  1.1757 +  
  1.1758 +  return TRUE;
  1.1759 +}
  1.1760 +
  1.1761 +static struct {
  1.1762 +  const char *first;
  1.1763 +  const char *second;
  1.1764 +} equality_tests[] = {
  1.1765 +  { "type='signal'", "type='signal'" },
  1.1766 +  { "type='signal',interface='foo.bar'", "interface='foo.bar',type='signal'" },
  1.1767 +  { "type='signal',member='bar'", "member='bar',type='signal'" },
  1.1768 +  { "type='method_call',sender=':1.0'", "sender=':1.0',type='method_call'" },
  1.1769 +  { "type='method_call',destination=':1.0'", "destination=':1.0',type='method_call'" },
  1.1770 +  { "type='method_call',path='/foo/bar'", "path='/foo/bar',type='method_call'" },
  1.1771 +  { "type='method_call',arg0='blah'", "arg0='blah',type='method_call'" },
  1.1772 +  { "type='method_call',arg0='boo'", "arg0='boo',type='method_call'" },
  1.1773 +  { "type='method_call',arg0='blah',arg1='baz'", "arg0='blah',arg1='baz',type='method_call'" },
  1.1774 +  { "type='method_call',arg3='foosh'", "arg3='foosh',type='method_call'" },
  1.1775 +  { "arg3='fool'", "arg3='fool'" },
  1.1776 +  { "member='food'", "member='food'" }
  1.1777 +};
  1.1778 +
  1.1779 +static void
  1.1780 +test_equality (void)
  1.1781 +{
  1.1782 +  int i;
  1.1783 +  
  1.1784 +  i = 0;
  1.1785 +  while (i < _DBUS_N_ELEMENTS (equality_tests))
  1.1786 +    {
  1.1787 +      BusMatchRule *first;
  1.1788 +      BusMatchRule *second;
  1.1789 +      int j;
  1.1790 +      
  1.1791 +      first = check_parse (TRUE, equality_tests[i].first);
  1.1792 +      _dbus_assert (first != NULL);
  1.1793 +      second = check_parse (TRUE, equality_tests[i].second);
  1.1794 +      _dbus_assert (second != NULL);
  1.1795 +
  1.1796 +      if (!match_rule_equal (first, second))
  1.1797 +        {
  1.1798 +          _dbus_warn ("rule %s and %s should have been equal\n",
  1.1799 +                      equality_tests[i].first,
  1.1800 +                      equality_tests[i].second);
  1.1801 +          exit (1);
  1.1802 +        }
  1.1803 +
  1.1804 +      bus_match_rule_unref (second);
  1.1805 +
  1.1806 +      /* Check that the rule is not equal to any of the
  1.1807 +       * others besides its pair match
  1.1808 +       */
  1.1809 +      j = 0;
  1.1810 +      while (j < _DBUS_N_ELEMENTS (equality_tests))
  1.1811 +        {
  1.1812 +          if (i != j)
  1.1813 +            {
  1.1814 +              second = check_parse (TRUE, equality_tests[j].second);
  1.1815 +
  1.1816 +              if (match_rule_equal (first, second))
  1.1817 +                {
  1.1818 +                  _dbus_warn ("rule %s and %s should not have been equal\n",
  1.1819 +                              equality_tests[i].first,
  1.1820 +                              equality_tests[j].second);
  1.1821 +                  exit (1);
  1.1822 +                }
  1.1823 +              
  1.1824 +              bus_match_rule_unref (second);
  1.1825 +            }
  1.1826 +          
  1.1827 +          ++j;
  1.1828 +        }
  1.1829 +
  1.1830 +      bus_match_rule_unref (first);
  1.1831 +
  1.1832 +      ++i;
  1.1833 +    }
  1.1834 +}
  1.1835 +
  1.1836 +static const char*
  1.1837 +should_match_message_1[] = {
  1.1838 +  "type='signal'",
  1.1839 +  "member='Frobated'",
  1.1840 +  "arg0='foobar'",
  1.1841 +  "type='signal',member='Frobated'",
  1.1842 +  "type='signal',member='Frobated',arg0='foobar'",
  1.1843 +  "member='Frobated',arg0='foobar'",
  1.1844 +  "type='signal',arg0='foobar'",
  1.1845 +  NULL
  1.1846 +};
  1.1847 +
  1.1848 +static const char*
  1.1849 +should_not_match_message_1[] = {
  1.1850 +  "type='method_call'",
  1.1851 +  "type='error'",
  1.1852 +  "type='method_return'",
  1.1853 +  "type='signal',member='Oopsed'",
  1.1854 +  "arg0='blah'",
  1.1855 +  "arg1='foobar'",
  1.1856 +  "arg2='foobar'",
  1.1857 +  "arg3='foobar'",
  1.1858 +  "arg0='3'",
  1.1859 +  "arg1='3'",
  1.1860 +  "arg0='foobar',arg1='abcdef'",
  1.1861 +  "arg0='foobar',arg1='abcdef',arg2='abcdefghi',arg3='abcdefghi',arg4='abcdefghi'",
  1.1862 +  "arg0='foobar',arg1='abcdef',arg4='abcdefghi',arg3='abcdefghi',arg2='abcdefghi'",
  1.1863 +  NULL
  1.1864 +};
  1.1865 +
  1.1866 +static void
  1.1867 +check_matches (dbus_bool_t  expected_to_match,
  1.1868 +               int          number,
  1.1869 +               DBusMessage *message,
  1.1870 +               const char  *rule_text)
  1.1871 +{
  1.1872 +  BusMatchRule *rule;
  1.1873 +  dbus_bool_t matched;
  1.1874 +
  1.1875 +  rule = check_parse (TRUE, rule_text);
  1.1876 +  _dbus_assert (rule != NULL);
  1.1877 +
  1.1878 +  /* We can't test sender/destination rules since we pass NULL here */
  1.1879 +  matched = match_rule_matches (rule, NULL, NULL, message);
  1.1880 +
  1.1881 +  if (matched != expected_to_match)
  1.1882 +    {
  1.1883 +      _dbus_warn ("Expected rule %s to %s message %d, failed\n",
  1.1884 +                  rule_text, expected_to_match ?
  1.1885 +                  "match" : "not match", number);
  1.1886 +      exit (1);
  1.1887 +    }
  1.1888 +
  1.1889 +  bus_match_rule_unref (rule);
  1.1890 +}
  1.1891 +
  1.1892 +static void
  1.1893 +check_matching (DBusMessage *message,
  1.1894 +                int          number,
  1.1895 +                const char **should_match,
  1.1896 +                const char **should_not_match)
  1.1897 +{
  1.1898 +  int i;
  1.1899 +
  1.1900 +  i = 0;
  1.1901 +  while (should_match[i] != NULL)
  1.1902 +    {
  1.1903 +      check_matches (TRUE, number, message, should_match[i]);
  1.1904 +      ++i;
  1.1905 +    }
  1.1906 +
  1.1907 +  i = 0;
  1.1908 +  while (should_not_match[i] != NULL)
  1.1909 +    {
  1.1910 +      check_matches (FALSE, number, message, should_not_match[i]);
  1.1911 +      ++i;
  1.1912 +    }
  1.1913 +}
  1.1914 +
  1.1915 +static void
  1.1916 +test_matching (void)
  1.1917 +{
  1.1918 +  DBusMessage *message1;
  1.1919 +  const char *v_STRING;
  1.1920 +  dbus_int32_t v_INT32;
  1.1921 +
  1.1922 +  message1 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
  1.1923 +  _dbus_assert (message1 != NULL);
  1.1924 +  if (!dbus_message_set_member (message1, "Frobated"))
  1.1925 +    _dbus_assert_not_reached ("oom");
  1.1926 +
  1.1927 +  v_STRING = "foobar";
  1.1928 +  v_INT32 = 3;
  1.1929 +  if (!dbus_message_append_args (message1,
  1.1930 +                                 DBUS_TYPE_STRING, &v_STRING,
  1.1931 +                                 DBUS_TYPE_INT32, &v_INT32,
  1.1932 +                                 NULL))
  1.1933 +    _dbus_assert_not_reached ("oom");
  1.1934 +  
  1.1935 +  check_matching (message1, 1,
  1.1936 +                  should_match_message_1,
  1.1937 +                  should_not_match_message_1);
  1.1938 +  
  1.1939 +  dbus_message_unref (message1);
  1.1940 +}
  1.1941 +
  1.1942 +dbus_bool_t
  1.1943 +bus_signals_test (const DBusString *test_data_dir)
  1.1944 +{
  1.1945 +  BusMatchmaker *matchmaker;
  1.1946 +
  1.1947 +  matchmaker = bus_matchmaker_new ();
  1.1948 +  bus_matchmaker_ref (matchmaker);
  1.1949 +  bus_matchmaker_unref (matchmaker);
  1.1950 +  bus_matchmaker_unref (matchmaker);
  1.1951 +
  1.1952 +  if (!_dbus_test_oom_handling ("parsing match rules", test_parsing, NULL))
  1.1953 +    _dbus_assert_not_reached ("Parsing match rules test failed");
  1.1954 +
  1.1955 +  test_equality ();
  1.1956 +
  1.1957 +  test_matching ();
  1.1958 +  
  1.1959 +  return TRUE;
  1.1960 +}
  1.1961 +
  1.1962 +#endif /* DBUS_BUILD_TESTS */
  1.1963 +