Add path_prefix match rule
authorDavid Zeuthen <davidz@redhat.com>
Tue, 8 Mar 2011 15:32:34 +0000 (10:32 -0500)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Thu, 7 Apr 2011 13:06:05 +0000 (14:06 +0100)
Add a new path_prefix match rule that can be used for efficient
implementations of the org.freedesktop.DBus.ObjectManager interface
(see bug 34869).

https://bugs.freedesktop.org/show_bug.cgi?id=34870

Signed-off-by: David Zeuthen <davidz@redhat.com>
bus/signals.c
bus/signals.h
doc/dbus-specification.xml

index 56a7d6c..0d18dc6 100644 (file)
@@ -41,6 +41,7 @@ struct BusMatchRule
   char *sender;
   char *destination;
   char *path;
+  char *path_prefix;
 
   unsigned int *arg_lens;
   char **args;
@@ -94,6 +95,7 @@ bus_match_rule_unref (BusMatchRule *rule)
       dbus_free (rule->sender);
       dbus_free (rule->destination);
       dbus_free (rule->path);
+      dbus_free (rule->path_prefix);
       dbus_free (rule->arg_lens);
 
       /* can't use dbus_free_string_array() since there
@@ -206,6 +208,18 @@ match_rule_to_string (BusMatchRule *rule)
         goto nomem;
     }
 
+  if (rule->flags & BUS_MATCH_PATH_PREFIX)
+    {
+      if (_dbus_string_get_length (&str) > 0)
+        {
+          if (!_dbus_string_append (&str, ","))
+            goto nomem;
+        }
+
+      if (!_dbus_string_append_printf (&str, "path_prefix='%s'", rule->path_prefix))
+        goto nomem;
+    }
+
   if (rule->flags & BUS_MATCH_SENDER)
     {
       if (_dbus_string_get_length (&str) > 0)
@@ -389,6 +403,25 @@ bus_match_rule_set_path (BusMatchRule *rule,
 }
 
 dbus_bool_t
+bus_match_rule_set_path_prefix (BusMatchRule *rule,
+                                const char   *path_prefix)
+{
+  char *new;
+
+  _dbus_assert (path_prefix != NULL);
+
+  new = _dbus_strdup (path_prefix);
+  if (new == NULL)
+    return FALSE;
+
+  rule->flags |= BUS_MATCH_PATH_PREFIX;
+  dbus_free (rule->path_prefix);
+  rule->path_prefix = new;
+
+  return TRUE;
+}
+
+dbus_bool_t
 bus_match_rule_set_arg (BusMatchRule     *rule,
                         int                arg,
                         const DBusString *value,
@@ -1018,6 +1051,34 @@ bus_match_rule_parse (DBusConnection   *matches_go_to,
               goto failed;
             }
         }
+      else if (strcmp (key, "path_prefix") == 0)
+        {
+          int path_prefix_len;
+
+          if (rule->flags & BUS_MATCH_PATH_PREFIX)
+            {
+              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+                              "Key %s specified twice in match rule\n", key);
+              goto failed;
+            }
+
+          path_prefix_len = len;
+          if (_dbus_string_ends_with_c_str (&tmp_str, "/"))
+            path_prefix_len--;
+
+          if (!_dbus_validate_path (&tmp_str, 0, path_prefix_len))
+            {
+              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+                              "Path prefix '%s' is invalid\n", value);
+              goto failed;
+            }
+
+          if (!bus_match_rule_set_path_prefix (rule, value))
+            {
+              BUS_SET_OOM (error);
+              goto failed;
+            }
+        }
       else if (strcmp (key, "destination") == 0)
         {
           if (rule->flags & BUS_MATCH_DESTINATION)
@@ -1349,6 +1410,10 @@ match_rule_equal (BusMatchRule *a,
   if ((a->flags & BUS_MATCH_PATH) &&
       strcmp (a->path, b->path) != 0)
     return FALSE;
+
+  if ((a->flags & BUS_MATCH_PATH_PREFIX) &&
+      strcmp (a->path_prefix, b->path_prefix) != 0)
+    return FALSE;
   
   if ((a->flags & BUS_MATCH_INTERFACE) &&
       strcmp (a->interface, b->interface) != 0)
@@ -1612,6 +1677,17 @@ 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,
@@ -1722,6 +1798,20 @@ match_rule_matches (BusMatchRule    *rule,
         return FALSE;
     }
 
+  if (flags & BUS_MATCH_PATH_PREFIX)
+    {
+      const char *path;
+
+      _dbus_assert (rule->path_prefix != NULL);
+
+      path = dbus_message_get_path (message);
+      if (path == NULL)
+        return FALSE;
+
+      if (!str_has_prefix (path, rule->path_prefix))
+        return FALSE;
+    }
+
   if (flags & BUS_MATCH_ARGS)
     {
       int i;
@@ -2616,6 +2706,83 @@ test_path_matching (void)
   bus_match_rule_unref (rule);
 }
 
+static const char*
+path_prefix_should_match_message_1[] = {
+  "type='signal',path_prefix='/foo'",
+  "type='signal',path_prefix='/foo/'",
+  "type='signal',path_prefix='/foo/TheObjectManager'",
+  NULL
+};
+
+static const char*
+path_prefix_should_not_match_message_1[] = {
+  "type='signal',path_prefix='/bar'",
+  "type='signal',path_prefix='/bar/'",
+  "type='signal',path_prefix='/bar/TheObjectManager'",
+  NULL
+};
+
+static const char*
+path_prefix_should_match_message_2[] = {
+  "type='signal',path_prefix='/foo/TheObjectManager'",
+  "type='signal',path_prefix='/foo/TheObjectManager/'",
+  NULL
+};
+
+static const char*
+path_prefix_should_not_match_message_2[] = {
+  NULL
+};
+
+static const char*
+path_prefix_should_match_message_3[] = {
+  "type='signal',path_prefix='/foo/TheObjectManager'",
+  NULL
+};
+
+static const char*
+path_prefix_should_not_match_message_3[] = {
+  "type='signal',path_prefix='/foo/TheObjectManager/'",
+  NULL
+};
+
+static void
+test_matching_path_prefix (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_prefix_should_match_message_1,
+                  path_prefix_should_not_match_message_1);
+  check_matching (message2, 2,
+                  path_prefix_should_match_message_2,
+                  path_prefix_should_not_match_message_2);
+  check_matching (message3, 3,
+                  path_prefix_should_match_message_3,
+                  path_prefix_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)
 {
@@ -2632,6 +2799,7 @@ bus_signals_test (const DBusString *test_data_dir)
   test_equality ();
   test_matching ();
   test_path_matching ();
+  test_matching_path_prefix ();
 
   return TRUE;
 }
index 4702b9e..0053bd5 100644 (file)
@@ -37,7 +37,8 @@ typedef enum
   BUS_MATCH_SENDER       = 1 << 3,
   BUS_MATCH_DESTINATION  = 1 << 4,
   BUS_MATCH_PATH         = 1 << 5,
-  BUS_MATCH_ARGS         = 1 << 6
+  BUS_MATCH_ARGS         = 1 << 6,
+  BUS_MATCH_PATH_PREFIX  = 1 << 7
 } BusMatchFlags;
 
 BusMatchRule* bus_match_rule_new   (DBusConnection *matches_go_to);
@@ -56,6 +57,8 @@ dbus_bool_t bus_match_rule_set_destination  (BusMatchRule     *rule,
                                              const char       *destination);
 dbus_bool_t bus_match_rule_set_path         (BusMatchRule     *rule,
                                              const char       *path);
+dbus_bool_t bus_match_rule_set_path_prefix  (BusMatchRule     *rule,
+                                             const char       *path_prefix);
 dbus_bool_t bus_match_rule_set_arg          (BusMatchRule     *rule,
                                              int               arg,
                                              const DBusString *value,
index ad731c9..1c19a4a 100644 (file)
                   path match is path='/org/freedesktop/Hal/Manager'</entry>
                 </row>
                 <row>
+                  <entry><literal>path_prefix</literal></entry>
+                  <entry>An object path optionally ending in a slash</entry>
+                  <entry>
+                    <para>
+                      Matches messages which are sent from or to an
+                      object for which the object path is a prefix of
+                      the given value. Examples of matches are
+                      path_prefix='/org/Application/ObjectManager' or
+                      path_prefix='/org/Application/ContactObjects/'.
+                    </para>
+                    <para>
+                      <emphasis>
+                        This match key was added in version 0.15 of the
+                        D-Bus specification and implemented by the bus
+                        daemon in dbus 1.4.7 and later.
+                      </emphasis>
+                    </para>
+                </entry>
+                </row>
+                <row>
                   <entry><literal>destination</literal></entry>
                   <entry>A unique name (see <xref linkend="term-unique-name"/>)</entry>
                   <entry>Matches messages which are being sent to the given unique name. An