]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
Supporting matching argument 0 as a namespace
authorWill Thompson <will.thompson@collabora.co.uk>
Thu, 9 Jul 2009 20:33:24 +0000 (21:33 +0100)
committerWill Thompson <will.thompson@collabora.co.uk>
Tue, 23 Nov 2010 10:57:07 +0000 (10:57 +0000)
Rather like "arg0path='/foo/'" matching all object paths starting with
"/foo/", this adds support for matching a prefix of a string argument
with "arg0namespace='org.freedesktop.Telepathy.Client.'" (for example).

This is mostly intended for use with NameOwnerChanged and
PropertiesChanged; thus, only matching the 0th argument is permitted.
(This also means it could work with the multicast-plus-socket-filters
model being considered for DBus-in-the-kernel without having to hash
every period-separated prefix of every string argument.)

bus/signals.c
bus/signals.h

index f059d30fc5a377c486199b68f25c66c9ee030485..9939a23e402bdb1f185faa6a8d412373485c37a1 100644 (file)
@@ -47,8 +47,11 @@ struct BusMatchRule
   int args_len;
 };
 
+#define BUS_MATCH_ARG_NAMESPACE   0x4000000u
 #define BUS_MATCH_ARG_IS_PATH  0x8000000u
 
+#define BUS_MATCH_ARG_FLAGS (BUS_MATCH_ARG_NAMESPACE | BUS_MATCH_ARG_IS_PATH)
+
 BusMatchRule*
 bus_match_rule_new (DBusConnection *matches_go_to)
 {
@@ -211,7 +214,7 @@ match_rule_to_string (BusMatchRule *rule)
         {
           if (rule->args[i] != NULL)
             {
-              dbus_bool_t is_path;
+              dbus_bool_t is_path, is_namespace;
 
               if (_dbus_string_get_length (&str) > 0)
                 {
@@ -220,10 +223,13 @@ match_rule_to_string (BusMatchRule *rule)
                 }
 
               is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
+              is_namespace = (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) != 0;
               
               if (!_dbus_string_append_printf (&str,
                                                "arg%d%s='%s'",
-                                               i, is_path ? "path" : "",
+                                               i,
+                                               is_path ? "path" :
+                                               is_namespace ? "namespace" : "",
                                                rule->args[i]))
                 goto nomem;
             }
@@ -359,7 +365,8 @@ dbus_bool_t
 bus_match_rule_set_arg (BusMatchRule     *rule,
                         int                arg,
                         const DBusString *value,
-                        dbus_bool_t       is_path)
+                        dbus_bool_t       is_path,
+                        dbus_bool_t       is_namespace)
 {
   int length;
   char *new;
@@ -426,6 +433,9 @@ bus_match_rule_set_arg (BusMatchRule     *rule,
   if (is_path)
     rule->arg_lens[arg] |= BUS_MATCH_ARG_IS_PATH;
 
+  if (is_namespace)
+    rule->arg_lens[arg] |= BUS_MATCH_ARG_NAMESPACE;
+
   /* NULL termination didn't get busted */
   _dbus_assert (rule->args[rule->args_len] == NULL);
   _dbus_assert (rule->arg_lens[rule->args_len] == 0);
@@ -720,7 +730,8 @@ bus_match_rule_parse_arg_match (BusMatchRule     *rule,
                                 const DBusString *value,
                                 DBusError        *error)
 {
-  dbus_bool_t is_path;
+  dbus_bool_t is_path = FALSE;
+  dbus_bool_t is_namespace = FALSE;
   DBusString key_str;
   unsigned long arg;
   int length;
@@ -751,17 +762,25 @@ bus_match_rule_parse_arg_match (BusMatchRule     *rule,
       goto failed;
     }
 
-  if (end != length &&
-      ((end + 4) != length ||
-       !_dbus_string_ends_with_c_str (&key_str, "path")))
+  if (end != length)
     {
-      dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
-                      "Key '%s' in match rule contains junk after argument number. Only 'path' is optionally valid ('arg0path' for example).\n", key);
-      goto failed;
+      if ((end + strlen ("path")) == length &&
+          _dbus_string_ends_with_c_str (&key_str, "path"))
+        {
+          is_path = TRUE;
+        }
+      else if (_dbus_string_equal_c_str (&key_str, "arg0namespace"))
+        {
+          is_namespace = TRUE;
+        }
+      else
+        {
+          dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+              "Key '%s' in match rule contains junk after argument number (%u). Only 'arg%upath' (for example) or 'arg0namespace' are valid", key, arg, arg);
+          goto failed;
+        }
     }
 
-  is_path = end != length;
-
   /* If we didn't check this we could allocate a huge amount of RAM */
   if (arg > DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER)
     {
@@ -779,7 +798,7 @@ bus_match_rule_parse_arg_match (BusMatchRule     *rule,
       goto failed;
     }
   
-  if (!bus_match_rule_set_arg (rule, arg, value, is_path))
+  if (!bus_match_rule_set_arg (rule, arg, value, is_path, is_namespace))
     {
       BUS_SET_OOM (error);
       goto failed;
@@ -1318,7 +1337,7 @@ match_rule_equal (BusMatchRule *a,
           if (a->arg_lens[i] != b->arg_lens[i])
             return FALSE;
 
-          length = a->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+          length = a->arg_lens[i] & ~BUS_MATCH_ARG_FLAGS;
 
           if (a->args[i] != NULL)
             {
@@ -1675,11 +1694,12 @@ match_rule_matches (BusMatchRule    *rule,
           int current_type;
           const char *expected_arg;
           int expected_length;
-          dbus_bool_t is_path;
+          dbus_bool_t is_path, is_namespace;
 
           expected_arg = rule->args[i];
-          expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+          expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_FLAGS;
           is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
+          is_namespace = (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) != 0;
           
           current_type = dbus_message_iter_get_arg_type (&iter);
 
@@ -1712,6 +1732,39 @@ match_rule_matches (BusMatchRule    *rule,
                               MIN (actual_length, expected_length)) != 0)
                     return FALSE;
                 }
+              else if (is_namespace)
+                {
+                  if (expected_length > actual_length)
+                    return FALSE;
+
+                  /* If the actual argument doesn't start with the expected
+                   * namespace, then we don't match.
+                   */
+                  if (memcmp (expected_arg, actual_arg, expected_length) != 0)
+                    return FALSE;
+
+                  if (expected_length < actual_length)
+                    {
+                      /* Check that the actual argument is within the expected
+                       * namespace, rather than just starting with that string,
+                       * by checking that the matched prefix ends in a '.'.
+                       *
+                       * This doesn't stop "foo.bar." matching "foo.bar..baz"
+                       * 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] != '.')
+                        return FALSE;
+                    }
+                  /* otherwise we had an exact match. */
+                }
               else
                 {
                   if (expected_length != actual_length ||
@@ -2042,6 +2095,23 @@ test_parsing (void *data)
       bus_match_rule_unref (rule);
     }
 
+  /* Arg 0 namespace matches */
+  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 (FALSE, "arg1namespace='foo'");
+  _dbus_assert (rule == NULL);
+
   /* Too-large argN */
   rule = check_parse (FALSE, "arg300='foo'");
   _dbus_assert (rule == NULL);
@@ -2122,6 +2192,7 @@ static struct {
   { "type='method_call',arg0='blah',arg1='baz'", "arg0='blah',arg1='baz',type='method_call'" },
   { "type='method_call',arg3='foosh'", "arg3='foosh',type='method_call'" },
   { "arg3='fool'", "arg3='fool'" },
+  { "arg0namespace='fool'", "arg0namespace='fool'" },
   { "member='food'", "member='food'" }
 };
 
@@ -2197,6 +2268,7 @@ should_match_message_1[] = {
    * argument is not a valid path)!
    */
   "arg0path='foobar'",
+  "arg0namespace='foobar'",
   NULL
 };
 
@@ -2218,6 +2290,51 @@ should_not_match_message_1[] = {
   "arg0path='foo'",
   "arg0path='foobar/'",
   "arg1path='3'",
+  "arg0namespace='foo'",
+  "arg0namespace='foo',arg1='abcdef'",
+  "arg0namespace='moo'",
+  NULL
+};
+
+#define EXAMPLE_NAME "com.example.backend.foo"
+
+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.'",
+  "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
+   * it should match.
+   */
+  "arg0namespace='com.example.backend.foo'",
+
+  NULL
+};
+
+static const char *
+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'",
+  "arg0namespace='com.example.backen'",
+  "arg0namespace='com.exampl'",
+  "arg0namespace='co'",
+
   NULL
 };
 
@@ -2273,7 +2390,7 @@ check_matching (DBusMessage *message,
 static void
 test_matching (void)
 {
-  DBusMessage *message1;
+  DBusMessage *message1, *message2;
   const char *v_STRING;
   dbus_int32_t v_INT32;
 
@@ -2295,6 +2412,24 @@ test_matching (void)
                   should_not_match_message_1);
   
   dbus_message_unref (message1);
+
+  message2 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+  _dbus_assert (message2 != NULL);
+  if (!dbus_message_set_member (message2, "NameOwnerChanged"))
+    _dbus_assert_not_reached ("oom");
+
+  /* Obviously this isn't really a NameOwnerChanged signal. */
+  v_STRING = EXAMPLE_NAME;
+  if (!dbus_message_append_args (message2,
+                                 DBUS_TYPE_STRING, &v_STRING,
+                                 NULL))
+    _dbus_assert_not_reached ("oom");
+
+  check_matching (message2, 2,
+                  should_match_message_2,
+                  should_not_match_message_2);
+
+  dbus_message_unref (message2);
 }
 
 #define PATH_MATCH_RULE "arg0path='/aa/bb/'"
index eeb1d2d0ce5cc2d195486d46d11b8ae7a244f3bd..81590b5f173658bc58a70fec08f0fd76fc351542 100644 (file)
@@ -59,7 +59,8 @@ dbus_bool_t bus_match_rule_set_path         (BusMatchRule     *rule,
 dbus_bool_t bus_match_rule_set_arg          (BusMatchRule     *rule,
                                              int               arg,
                                              const DBusString *value,
-                                             dbus_bool_t       is_path);
+                                             dbus_bool_t       is_path,
+                                             dbus_bool_t       prefix);
 
 BusMatchRule* bus_match_rule_parse (DBusConnection   *matches_go_to,
                                     const DBusString *rule_text,