[rename] renamed kdbus related macros
[platform/upstream/dbus.git] / bus / signals.c
index daf8540..dab7154 100644 (file)
@@ -64,7 +64,7 @@ bus_match_rule_new (DBusConnection *matches_go_to)
   rule->refcount = 1;
   rule->matches_go_to = matches_go_to;
 
-#ifndef DBUS_BUILD_TESTS
+#ifndef DBUS_ENABLE_EMBEDDED_TESTS
   _dbus_assert (rule->matches_go_to != NULL);
 #endif
   
@@ -138,8 +138,8 @@ match_rule_to_string (BusMatchRule *rule)
   
   if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
     {
-      /* FIXME make type readable */
-      if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type))
+      if (!_dbus_string_append_printf (&str, "type='%s'",
+            dbus_message_type_to_string (rule->message_type)))
         goto nomem;
     }
 
@@ -179,6 +179,18 @@ match_rule_to_string (BusMatchRule *rule)
         goto nomem;
     }
 
+  if (rule->flags & BUS_MATCH_PATH_NAMESPACE)
+    {
+      if (_dbus_string_get_length (&str) > 0)
+        {
+          if (!_dbus_string_append (&str, ","))
+            goto nomem;
+        }
+
+      if (!_dbus_string_append_printf (&str, "path_namespace='%s'", rule->path))
+        goto nomem;
+    }
+
   if (rule->flags & BUS_MATCH_SENDER)
     {
       if (_dbus_string_get_length (&str) > 0)
@@ -203,6 +215,20 @@ match_rule_to_string (BusMatchRule *rule)
         goto nomem;
     }
 
+  if (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+    {
+      if (_dbus_string_get_length (&str) > 0)
+        {
+          if (!_dbus_string_append (&str, ","))
+            goto nomem;
+        }
+
+      if (!_dbus_string_append_printf (&str, "eavesdrop='%s'",
+            (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING) ?
+            "true" : "false"))
+        goto nomem;
+    }
+
   if (rule->flags & BUS_MATCH_ARGS)
     {
       int i;
@@ -342,9 +368,20 @@ bus_match_rule_set_destination (BusMatchRule *rule,
   return TRUE;
 }
 
+void
+bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule,
+                                            dbus_bool_t is_eavesdropping)
+{
+  if (is_eavesdropping)
+    rule->flags |= BUS_MATCH_CLIENT_IS_EAVESDROPPING;
+  else
+    rule->flags &= ~(BUS_MATCH_CLIENT_IS_EAVESDROPPING);
+}
+
 dbus_bool_t
 bus_match_rule_set_path (BusMatchRule *rule,
-                         const char   *path)
+                         const char   *path,
+                         dbus_bool_t   is_namespace)
 {
   char *new;
 
@@ -354,7 +391,13 @@ bus_match_rule_set_path (BusMatchRule *rule,
   if (new == NULL)
     return FALSE;
 
-  rule->flags |= BUS_MATCH_PATH;
+  rule->flags &= ~(BUS_MATCH_PATH|BUS_MATCH_PATH_NAMESPACE);
+
+  if (is_namespace)
+    rule->flags |= BUS_MATCH_PATH_NAMESPACE;
+  else
+    rule->flags |= BUS_MATCH_PATH;
+
   dbus_free (rule->path);
   rule->path = new;
 
@@ -775,15 +818,10 @@ bus_match_rule_parse_arg_match (BusMatchRule     *rule,
 
           is_namespace = TRUE;
 
-          if (value_len > 0 &&
-              _dbus_string_get_byte (value, value_len - 1) == '.')
-            value_len--;
-
           if (!_dbus_validate_bus_namespace (value, 0, value_len))
             {
               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
-                  "arg0namespace='%s' is not a valid (optionally "
-                  "period-terminated) prefix of a bus name",
+                  "arg0namespace='%s' is not a valid prefix of a bus name",
                   _dbus_string_get_const_data (value));
               goto failed;
             }
@@ -969,12 +1007,15 @@ bus_match_rule_parse (DBusConnection   *matches_go_to,
               goto failed;
             }
         }
-      else if (strcmp (key, "path") == 0)
+      else if (strcmp (key, "path") == 0 ||
+          strcmp (key, "path_namespace") == 0)
         {
-          if (rule->flags & BUS_MATCH_PATH)
+          dbus_bool_t is_namespace = (strcmp (key, "path_namespace") == 0);
+
+          if (rule->flags & (BUS_MATCH_PATH | BUS_MATCH_PATH_NAMESPACE))
             {
               dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
-                              "Key %s specified twice in match rule\n", key);
+                              "path or path_namespace specified twice in match rule\n");
               goto failed;
             }
 
@@ -985,7 +1026,7 @@ bus_match_rule_parse (DBusConnection   *matches_go_to,
               goto failed;
             }
 
-          if (!bus_match_rule_set_path (rule, value))
+          if (!bus_match_rule_set_path (rule, value, is_namespace))
             {
               BUS_SET_OOM (error);
               goto failed;
@@ -1013,6 +1054,31 @@ bus_match_rule_parse (DBusConnection   *matches_go_to,
               goto failed;
             }
         }
+      else if (strcmp (key, "eavesdrop") == 0)
+        {
+          /* do not detect "eavesdrop" being used more than once in rule:
+           * 1) it's not possible, it's only in the flags
+           * 2) it might be used twice to disable eavesdropping when it's
+           * automatically added (eg dbus-monitor/bustle) */
+
+          /* we accept only "true|false" as possible values */
+          if ((strcmp (value, "true") == 0))
+            {
+              bus_match_rule_set_client_is_eavesdropping (rule, TRUE);
+            }
+          else if (strcmp (value, "false") == 0)
+            {
+              bus_match_rule_set_client_is_eavesdropping (rule, FALSE);
+            }
+          else
+            {
+              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+                              "eavesdrop='%s' is invalid, "
+                              "it should be 'true' or 'false'\n",
+                              value);
+              goto failed;
+            }
+        }
       else if (strncmp (key, "arg", 3) == 0)
         {
           if (!bus_match_rule_parse_arg_match (rule, key, &tmp_str, error))
@@ -1136,6 +1202,7 @@ bus_matchmaker_new (void)
       else
         _dbus_hash_table_unref (p->rules_by_iface);
     }
+  dbus_free (matchmaker);
 
   return NULL;
 }
@@ -1321,7 +1388,7 @@ match_rule_equal (BusMatchRule *a,
   if ((a->flags & BUS_MATCH_PATH) &&
       strcmp (a->path, b->path) != 0)
     return FALSE;
-  
+
   if ((a->flags & BUS_MATCH_INTERFACE) &&
       strcmp (a->interface, b->interface) != 0)
     return FALSE;
@@ -1334,6 +1401,9 @@ match_rule_equal (BusMatchRule *a,
       strcmp (a->destination, b->destination) != 0)
     return FALSE;
 
+  /* we already compared the value of flags, and
+   * BUS_MATCH_CLIENT_IS_EAVESDROPPING does not have another struct member */
+
   if (a->flags & BUS_MATCH_ARGS)
     {
       int i;
@@ -1584,12 +1654,24 @@ connection_is_primary_owner (DBusConnection *connection,
 }
 
 static dbus_bool_t
+str_has_prefix (const char *str, const char *prefix)
+{
+  size_t prefix_len;
+  prefix_len = strlen (prefix);
+  if (strncmp (str, prefix, prefix_len) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static dbus_bool_t
 match_rule_matches (BusMatchRule    *rule,
                     DBusConnection  *sender,
                     DBusConnection  *addressed_recipient,
                     DBusMessage     *message,
                     BusMatchFlags    already_matched)
 {
+  dbus_bool_t wants_to_eavesdrop = FALSE;
   int flags;
 
   /* All features of the match rule are AND'd together,
@@ -1604,6 +1686,9 @@ match_rule_matches (BusMatchRule    *rule,
   /* Don't bother re-matching features we've already checked implicitly. */
   flags = rule->flags & (~already_matched);
 
+  if (flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+    wants_to_eavesdrop = TRUE;
+
   if (flags & BUS_MATCH_MESSAGE_TYPE)
     {
       _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
@@ -1657,6 +1742,24 @@ match_rule_matches (BusMatchRule    *rule,
         }
     }
 
+  /* Note: this part is relevant for eavesdropper rules:
+   * Two cases:
+   * 1) rule has a destination to be matched
+   *   (flag BUS_MATCH_DESTINATION present). Rule will match if:
+   *   - rule->destination matches the addressed_recipient
+   *   AND
+   *   - wants_to_eavesdrop=TRUE
+   *
+   *   Note: (the case in which addressed_recipient is the actual rule owner
+   *   is handled elsewere in dispatch.c:bus_dispatch_matches().
+   *
+   * 2) rule has no destination. Rule will match if:
+   *    - message has no specified destination (ie broadcasts)
+   *      (Note: this will rule out unicast method calls and unicast signals,
+   *      fixing FDO#269748)
+   *    OR
+   *    - wants_to_eavesdrop=TRUE (destination-catch-all situation)
+   */
   if (flags & BUS_MATCH_DESTINATION)
     {
       const char *destination;
@@ -1665,6 +1768,12 @@ match_rule_matches (BusMatchRule    *rule,
 
       destination = dbus_message_get_destination (message);
       if (destination == NULL)
+        /* broadcast, but this rule specified a destination: no match */
+        return FALSE;
+
+      /* rule owner does not intend to eavesdrop: we'll deliver only msgs
+       * directed to it, NOT MATCHING */
+      if (!wants_to_eavesdrop)
         return FALSE;
 
       if (addressed_recipient == NULL)
@@ -1678,6 +1787,19 @@ match_rule_matches (BusMatchRule    *rule,
           if (!connection_is_primary_owner (addressed_recipient, rule->destination))
             return FALSE;
         }
+    } else { /* no destination in rule */
+        dbus_bool_t msg_is_broadcast;
+
+        _dbus_assert (rule->destination == NULL);
+
+        msg_is_broadcast = (dbus_message_get_destination (message) == NULL);
+
+        if (!wants_to_eavesdrop && !msg_is_broadcast)
+          return FALSE;
+
+        /* if we are here rule owner intends to eavesdrop
+         * OR
+         * message is being broadcasted */
     }
 
   if (flags & BUS_MATCH_PATH)
@@ -1694,6 +1816,31 @@ match_rule_matches (BusMatchRule    *rule,
         return FALSE;
     }
 
+  if (flags & BUS_MATCH_PATH_NAMESPACE)
+    {
+      const char *path;
+      int len;
+
+      _dbus_assert (rule->path != NULL);
+
+      path = dbus_message_get_path (message);
+      if (path == NULL)
+        return FALSE;
+
+      if (!str_has_prefix (path, rule->path))
+        return FALSE;
+
+      len = strlen (rule->path);
+
+      /* Check that the actual argument is within the expected
+       * namespace, rather than just starting with that string,
+       * by checking that the matched prefix is followed by a '/'
+       * or the end of the path.
+       */
+      if (path[len] != '\0' && path[len] != '/')
+        return FALSE;
+    }
+
   if (flags & BUS_MATCH_ARGS)
     {
       int i;
@@ -1768,14 +1915,7 @@ match_rule_matches (BusMatchRule    *rule,
                        * which is an invalid namespace, but at some point the
                        * daemon can't cover up for broken services.
                        */
-                      int expected_period_index;
-
-                      if (expected_arg[expected_length - 1] == '.')
-                        expected_period_index = expected_length - 1;
-                      else
-                        expected_period_index = expected_length;
-
-                      if (actual_arg[expected_period_index] != '.')
+                      if (actual_arg[expected_length] != '.')
                         return FALSE;
                     }
                   /* otherwise we had an exact match. */
@@ -1840,12 +1980,10 @@ get_recipients_from_list (DBusList       **rules,
               if (!_dbus_list_append (recipients_p, rule->matches_go_to))
                 return FALSE;
             }
-#ifdef DBUS_ENABLE_VERBOSE_MODE
           else
             {
               _dbus_verbose ("Connection already receiving this message, so not adding again\n");
             }
-#endif /* DBUS_ENABLE_VERBOSE_MODE */
         }
 
       link = _dbus_list_get_next_link (rules, link);
@@ -1916,7 +2054,7 @@ bus_matchmaker_get_recipients (BusMatchmaker   *matchmaker,
   return TRUE;
 }
 
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
 #include "test.h"
 #include <stdlib.h>
 
@@ -2124,19 +2262,6 @@ test_parsing (void *data)
       bus_match_rule_unref (rule);
     }
 
-  rule = check_parse (TRUE, "arg0namespace='foo.'");
-  if (rule != NULL)
-    {
-      _dbus_assert (rule->flags == BUS_MATCH_ARGS);
-      _dbus_assert (rule->args != NULL);
-      _dbus_assert (rule->args_len == 1);
-      _dbus_assert (strcmp (rule->args[0], "foo.") == 0);
-      _dbus_assert ((rule->arg_lens[0] & BUS_MATCH_ARG_NAMESPACE)
-          == BUS_MATCH_ARG_NAMESPACE);
-
-      bus_match_rule_unref (rule);
-    }
-
   rule = check_parse (TRUE, "arg0namespace='foo.bar'");
   if (rule != NULL)
     {
@@ -2150,19 +2275,6 @@ test_parsing (void *data)
       bus_match_rule_unref (rule);
     }
 
-  rule = check_parse (TRUE, "arg0namespace='foo.bar.'");
-  if (rule != NULL)
-    {
-      _dbus_assert (rule->flags == BUS_MATCH_ARGS);
-      _dbus_assert (rule->args != NULL);
-      _dbus_assert (rule->args_len == 1);
-      _dbus_assert (strcmp (rule->args[0], "foo.bar.") == 0);
-      _dbus_assert ((rule->arg_lens[0] & BUS_MATCH_ARG_NAMESPACE)
-          == BUS_MATCH_ARG_NAMESPACE);
-
-      bus_match_rule_unref (rule);
-    }
-
   /* Only arg0namespace is supported. */
   rule = check_parse (FALSE, "arg1namespace='foo'");
   _dbus_assert (rule == NULL);
@@ -2173,7 +2285,14 @@ test_parsing (void *data)
   rule = check_parse (FALSE, "arg0namespace=''");
   _dbus_assert (rule == NULL);
 
-  /* Two trailing periods on otherwise-valid namespaces aren't allowed. */
+  /* Trailing periods aren't allowed (earlier versions of the arg0namespace
+   * spec allowed a single trailing period, which altered the semantics) */
+  rule = check_parse (FALSE, "arg0namespace='foo.'");
+  _dbus_assert (rule == NULL);
+
+  rule = check_parse (FALSE, "arg0namespace='foo.bar.'");
+  _dbus_assert (rule == NULL);
+
   rule = check_parse (FALSE, "arg0namespace='foo..'");
   _dbus_assert (rule == NULL);
 
@@ -2200,6 +2319,24 @@ test_parsing (void *data)
   rule = check_parse (FALSE, "type='signal',type='method_call'");
   _dbus_assert (rule == NULL);
 
+  rule = check_parse (TRUE, "path_namespace='/foo/bar'");
+  if (rule != NULL)
+    {
+      _dbus_assert (rule->flags == BUS_MATCH_PATH_NAMESPACE);
+      _dbus_assert (rule->path != NULL);
+      _dbus_assert (strcmp (rule->path, "/foo/bar") == 0);
+
+      bus_match_rule_unref (rule);
+    }
+
+  /* Almost a duplicate */
+  rule = check_parse (FALSE, "path='/foo',path_namespace='/foo'");
+  _dbus_assert (rule == NULL);
+
+  /* Trailing / was supported in the initial proposal, but now isn't */
+  rule = check_parse (FALSE, "path_namespace='/foo/'");
+  _dbus_assert (rule == NULL);
+
   /* Duplicates with the argN code */
   rule = check_parse (FALSE, "arg0='foo',arg0='bar'");
   _dbus_assert (rule == NULL);
@@ -2368,13 +2505,9 @@ should_not_match_message_1[] = {
 
 static const char *
 should_match_message_2[] = {
-  /* EXAMPLE_NAME is in all of these namespaces, specified with and without a
-   * trailing period */
-  "arg0namespace='com.example.backend.'",
+  /* EXAMPLE_NAME is in all of these namespaces */
   "arg0namespace='com.example.backend'",
-  "arg0namespace='com.example.'",
   "arg0namespace='com.example'",
-  "arg0namespace='com.'",
   "arg0namespace='com'",
 
   /* If the client specifies the name exactly, with no trailing period, then
@@ -2390,12 +2523,6 @@ should_not_match_message_2[] = {
   /* These are not even prefixes */
   "arg0namespace='com.example.backend.foo.bar'",
   "arg0namespace='com.example.backend.foobar'",
-  "arg0namespace='com.example.backend.fo.'",
-
-  /* This should match anything within the namespace com.example.backend.foo,
-   * not including com.example.backend.foo itself.
-   */
-  "arg0namespace='com.example.backend.foo.'",
 
   /* These are prefixes, but they're not parent namespaces. */
   "arg0namespace='com.example.backend.fo'",
@@ -2588,6 +2715,79 @@ test_path_matching (void)
   bus_match_rule_unref (rule);
 }
 
+static const char*
+path_namespace_should_match_message_1[] = {
+  "type='signal',path_namespace='/foo'",
+  "type='signal',path_namespace='/foo/TheObjectManager'",
+  NULL
+};
+
+static const char*
+path_namespace_should_not_match_message_1[] = {
+  "type='signal',path_namespace='/bar'",
+  "type='signal',path_namespace='/bar/TheObjectManager'",
+  NULL
+};
+
+static const char*
+path_namespace_should_match_message_2[] = {
+  "type='signal',path_namespace='/foo/TheObjectManager'",
+  NULL
+};
+
+static const char*
+path_namespace_should_not_match_message_2[] = {
+  NULL
+};
+
+static const char*
+path_namespace_should_match_message_3[] = {
+  NULL
+};
+
+static const char*
+path_namespace_should_not_match_message_3[] = {
+  "type='signal',path_namespace='/foo/TheObjectManager'",
+  NULL
+};
+
+static void
+test_matching_path_namespace (void)
+{
+  DBusMessage *message1;
+  DBusMessage *message2;
+  DBusMessage *message3;
+
+  message1 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+  _dbus_assert (message1 != NULL);
+  if (!dbus_message_set_path (message1, "/foo/TheObjectManager"))
+    _dbus_assert_not_reached ("oom");
+
+  message2 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+  _dbus_assert (message2 != NULL);
+  if (!dbus_message_set_path (message2, "/foo/TheObjectManager/child_object"))
+    _dbus_assert_not_reached ("oom");
+
+  message3 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+  _dbus_assert (message3 != NULL);
+  if (!dbus_message_set_path (message3, "/foo/TheObjectManagerOther"))
+    _dbus_assert_not_reached ("oom");
+
+  check_matching (message1, 1,
+                  path_namespace_should_match_message_1,
+                  path_namespace_should_not_match_message_1);
+  check_matching (message2, 2,
+                  path_namespace_should_match_message_2,
+                  path_namespace_should_not_match_message_2);
+  check_matching (message3, 3,
+                  path_namespace_should_match_message_3,
+                  path_namespace_should_not_match_message_3);
+
+  dbus_message_unref (message3);
+  dbus_message_unref (message2);
+  dbus_message_unref (message1);
+}
+
 dbus_bool_t
 bus_signals_test (const DBusString *test_data_dir)
 {
@@ -2604,9 +2804,10 @@ bus_signals_test (const DBusString *test_data_dir)
   test_equality ();
   test_matching ();
   test_path_matching ();
+  test_matching_path_namespace ();
 
   return TRUE;
 }
 
-#endif /* DBUS_BUILD_TESTS */
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */