Add argument path matching support. Bug #11066.
authorRyan Lortie <desrt@desrt.ca>
Thu, 20 Sep 2007 17:04:38 +0000 (13:04 -0400)
committerRyan Lortie <desrt@desrt.ca>
Thu, 20 Sep 2007 17:04:38 +0000 (13:04 -0400)
2007-09-20  Ryan Lortie  <desrt@desrt.ca>

        * dbus/signals.c (struct DBusMatchRule, bus_match_rule_new,
        bus_match_rule_set_arg, bus_match_rule_parse_arg_match,
        match_rule_matches): Add support for parsing and matching on
        arg0path='/some/path' type rules.

        * dbus/signals.h (bus_match_rule_set_arg): change to take const
        DBusString instead of const char * for the string to match against.

        * dbus/dbus-bus.c: add a quick note to dbus_bus_add_match
        documentation about the path matching.

        * doc/dbus-specification.xml: add a more detailed description of the
        changes here.

ChangeLog
bus/signals.c
bus/signals.h
dbus/dbus-bus.c
doc/dbus-specification.xml

index 32c6436..fe3af99 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2007-09-20  Ryan Lortie  <desrt@desrt.ca>
+
+       Add argument path matching support.  Bug #11066.
+
+       * dbus/signals.c (struct DBusMatchRule, bus_match_rule_new,
+       bus_match_rule_set_arg, bus_match_rule_parse_arg_match,
+       match_rule_matches): Add support for parsing and matching on
+       arg0path='/some/path' type rules.
+
+       * dbus/signals.h (bus_match_rule_set_arg): change to take const
+       DBusString instead of const char * for the string to match against.
+
+       * dbus/dbus-bus.c: add a quick note to dbus_bus_add_match
+       documentation about the path matching.
+
+       * doc/dbus-specification.xml: add a more detailed description of the
+       changes here.
+
 2007-09-19  Ryan Lortie  <desrt@desrt.ca>
 
        Add support for compacting DBusStrings to release wasted memory.
index d0845b1..c90149b 100644 (file)
@@ -40,10 +40,13 @@ struct BusMatchRule
   char *destination;
   char *path;
 
+  unsigned int *arg_lens;
   char **args;
   int args_len;
 };
 
+#define BUS_MATCH_ARG_IS_PATH  0x8000000u
+
 BusMatchRule*
 bus_match_rule_new (DBusConnection *matches_go_to)
 {
@@ -86,6 +89,7 @@ bus_match_rule_unref (BusMatchRule *rule)
       dbus_free (rule->sender);
       dbus_free (rule->destination);
       dbus_free (rule->path);
+      dbus_free (rule->arg_lens);
 
       /* can't use dbus_free_string_array() since there
        * are embedded NULL
@@ -205,15 +209,19 @@ match_rule_to_string (BusMatchRule *rule)
         {
           if (rule->args[i] != NULL)
             {
+              dbus_bool_t is_path;
+
               if (_dbus_string_get_length (&str) > 0)
                 {
                   if (!_dbus_string_append (&str, ","))
                     goto nomem;
                 }
+
+              is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
               
               if (!_dbus_string_append_printf (&str,
-                                               "arg%d='%s'",
-                                               i,
+                                               "arg%d%s='%s'",
+                                               i, is_path ? "path" : "",
                                                rule->args[i]))
                 goto nomem;
             }
@@ -346,23 +354,22 @@ bus_match_rule_set_path (BusMatchRule *rule,
 }
 
 dbus_bool_t
-bus_match_rule_set_arg (BusMatchRule *rule,
-                        int           arg,
-                        const char   *value)
+bus_match_rule_set_arg (BusMatchRule     *rule,
+                        int                arg,
+                        const DBusString *value,
+                        dbus_bool_t       is_path)
 {
+  int length;
   char *new;
 
   _dbus_assert (value != NULL);
 
-  new = _dbus_strdup (value);
-  if (new == NULL)
-    return FALSE;
-
   /* args_len is the number of args not including null termination
    * in the char**
    */
   if (arg >= rule->args_len)
     {
+      unsigned int *new_arg_lens;
       char **new_args;
       int new_args_len;
       int i;
@@ -371,12 +378,9 @@ bus_match_rule_set_arg (BusMatchRule *rule,
 
       /* add another + 1 here for null termination */
       new_args = dbus_realloc (rule->args,
-                               sizeof(rule->args[0]) * (new_args_len + 1));
+                               sizeof (char *) * (new_args_len + 1));
       if (new_args == NULL)
-        {
-          dbus_free (new);
-          return FALSE;
-        }
+        return FALSE;
 
       /* NULL the new slots */
       i = rule->args_len;
@@ -387,16 +391,42 @@ bus_match_rule_set_arg (BusMatchRule *rule,
         }
       
       rule->args = new_args;
+
+      /* and now add to the lengths */
+      new_arg_lens = dbus_realloc (rule->arg_lens,
+                                   sizeof (int) * (new_args_len + 1));
+
+      if (new_arg_lens == NULL)
+        return FALSE;
+
+      /* zero the new slots */
+      i = rule->args_len;
+      while (i <= new_args_len) /* <= for null termination */
+        {
+          new_arg_lens[i] = 0;
+          ++i;
+        }
+
+      rule->arg_lens = new_arg_lens;
       rule->args_len = new_args_len;
     }
 
+  length = _dbus_string_get_length (value);
+  if (!_dbus_string_copy_data (value, &new))
+    return FALSE;
+
   rule->flags |= BUS_MATCH_ARGS;
 
   dbus_free (rule->args[arg]);
+  rule->arg_lens[arg] = length;
   rule->args[arg] = new;
 
+  if (is_path)
+    rule->arg_lens[arg] |= BUS_MATCH_ARG_IS_PATH;
+
   /* NULL termination didn't get busted */
   _dbus_assert (rule->args[rule->args_len] == NULL);
+  _dbus_assert (rule->arg_lens[rule->args_len] == 0);
 
   return TRUE;
 }
@@ -688,8 +718,10 @@ bus_match_rule_parse_arg_match (BusMatchRule     *rule,
                                 const DBusString *value,
                                 DBusError        *error)
 {
+  dbus_bool_t is_path;
   DBusString key_str;
   unsigned long arg;
+  int length;
   int end;
 
   /* For now, arg0='foo' always implies that 'foo' is a
@@ -701,6 +733,7 @@ bus_match_rule_parse_arg_match (BusMatchRule     *rule,
   /* First we need to parse arg0 = 0, arg27 = 27 */
 
   _dbus_string_init_const (&key_str, key);
+  length = _dbus_string_get_length (&key_str);
 
   if (_dbus_string_get_length (&key_str) < 4)
     {
@@ -709,14 +742,24 @@ bus_match_rule_parse_arg_match (BusMatchRule     *rule,
       goto failed;
     }
 
-  if (!_dbus_string_parse_uint (&key_str, 3, &arg, &end) ||
-      end != _dbus_string_get_length (&key_str))
+  if (!_dbus_string_parse_uint (&key_str, 3, &arg, &end))
     {
       dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
                       "Key '%s' in match rule starts with 'arg' but could not parse arg number. Should be 'arg0' or 'arg7' for example.\n", key);
       goto failed;
     }
 
+  if (end != length &&
+      ((end + 4) != length ||
+       !_dbus_string_ends_with_c_str (&key_str, "path")))
+    {
+      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;
+    }
+
+  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)
     {
@@ -730,12 +773,11 @@ bus_match_rule_parse_arg_match (BusMatchRule     *rule,
       rule->args[arg] != NULL)
     {
       dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
-                      "Key '%s' specified twice in match rule\n", key);
+                      "Argument %d matched more than once in match rule\n", key);
       goto failed;
     }
   
-  if (!bus_match_rule_set_arg (rule, arg,
-                               _dbus_string_get_const_data (value)))
+  if (!bus_match_rule_set_arg (rule, arg, value, is_path))
     {
       BUS_SET_OOM (error);
       goto failed;
@@ -1104,13 +1146,20 @@ match_rule_equal (BusMatchRule *a,
       i = 0;
       while (i < a->args_len)
         {
+          int length;
+
           if ((a->args[i] != NULL) != (b->args[i] != NULL))
             return FALSE;
 
+          if (a->arg_lens[i] != b->arg_lens[i])
+            return FALSE;
+
+          length = a->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+
           if (a->args[i] != NULL)
             {
               _dbus_assert (b->args[i] != NULL);
-              if (strcmp (a->args[i], b->args[i]) != 0)
+              if (memcmp (a->args[i], b->args[i], length) != 0)
                 return FALSE;
             }
           
@@ -1399,14 +1448,19 @@ match_rule_matches (BusMatchRule    *rule,
         {
           int current_type;
           const char *expected_arg;
+          int expected_length;
+          dbus_bool_t is_path;
 
           expected_arg = rule->args[i];
+          expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+          is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
           
           current_type = dbus_message_iter_get_arg_type (&iter);
 
           if (expected_arg != NULL)
             {
               const char *actual_arg;
+              int actual_length;
               
               if (current_type != DBUS_TYPE_STRING)
                 return FALSE;
@@ -1415,8 +1469,29 @@ match_rule_matches (BusMatchRule    *rule,
               dbus_message_iter_get_basic (&iter, &actual_arg);
               _dbus_assert (actual_arg != NULL);
 
-              if (strcmp (expected_arg, actual_arg) != 0)
-                return FALSE;
+              actual_length = strlen (actual_arg);
+
+              if (is_path)
+                {
+                  if (actual_length < expected_length &&
+                      actual_arg[actual_length - 1] != '/')
+                    return FALSE;
+
+                  if (expected_length < actual_length &&
+                      expected_arg[expected_length - 1] != '/')
+                    return FALSE;
+
+                  if (memcmp (actual_arg, expected_arg,
+                              MIN (actual_length, expected_length)) != 0)
+                    return FALSE;
+                }
+              else
+                {
+                  if (expected_length != actual_length ||
+                      memcmp (expected_arg, actual_arg, expected_length) != 0)
+                    return FALSE;
+                }
+
             }
           
           if (current_type != DBUS_TYPE_INVALID)
index ca74dc9..ab3c33a 100644 (file)
@@ -44,21 +44,22 @@ BusMatchRule* bus_match_rule_new   (DBusConnection *matches_go_to);
 BusMatchRule* bus_match_rule_ref   (BusMatchRule   *rule);
 void          bus_match_rule_unref (BusMatchRule   *rule);
 
-dbus_bool_t bus_match_rule_set_message_type (BusMatchRule *rule,
-                                             int           type);
-dbus_bool_t bus_match_rule_set_interface    (BusMatchRule *rule,
-                                             const char   *interface);
-dbus_bool_t bus_match_rule_set_member       (BusMatchRule *rule,
-                                             const char   *member);
-dbus_bool_t bus_match_rule_set_sender       (BusMatchRule *rule,
-                                             const char   *sender);
-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_arg          (BusMatchRule *rule,
-                                             int           arg,
-                                             const char   *value);
+dbus_bool_t bus_match_rule_set_message_type (BusMatchRule     *rule,
+                                             int               type);
+dbus_bool_t bus_match_rule_set_interface    (BusMatchRule     *rule,
+                                             const char       *interface);
+dbus_bool_t bus_match_rule_set_member       (BusMatchRule     *rule,
+                                             const char       *member);
+dbus_bool_t bus_match_rule_set_sender       (BusMatchRule     *rule,
+                                             const char       *sender);
+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_arg          (BusMatchRule     *rule,
+                                             int               arg,
+                                             const DBusString *value,
+                                             dbus_bool_t       is_path);
 
 BusMatchRule* bus_match_rule_parse (DBusConnection   *matches_go_to,
                                     const DBusString *rule_text,
index fd569c2..c7f43e8 100644 (file)
@@ -1424,6 +1424,13 @@ send_no_return_values (DBusConnection *connection,
  *
  * Currently there is no way to match against non-string arguments.
  *
+ * A specialised form of wildcard matching on arguments is
+ * supported for path-like namespaces.  If your argument match has
+ * a 'path' suffix (eg: "arg0path='/some/path/'") then it is
+ * considered a match if the argument exactly matches the given
+ * string or if one of them ends in a '/' and is a prefix of the
+ * other.
+ *
  * Matching on interface is tricky because method call
  * messages only optionally specify the interface.
  * If a message omits the interface, then it will NOT match
index 39bb772..9e33944 100644 (file)
                   would be arg3='Foo'. Only argument indexes from 0 to 63 should be 
                   accepted.</entry>
                 </row>
+                <row>
+                  <entry><literal>arg[0, 1, 2, 3, ...]path</literal></entry>
+                  <entry>Any string</entry>
+                  <entry>Argument path matches provide a specialised form of wildcard
+                  matching for path-like namespaces. As with normal argument matches,
+                  if the argument is exactly equal to the string given in the match
+                  rule then the rule is satisfied. Additionally, there is also a
+                  match when either the string given in the match rule or the
+                  appropriate message argument ends with '/' and is a prefix of the
+                  other. An example argument path match is arg0path='/aa/bb/'. This
+                  would match messages with first arguments of '/', '/aa/',
+                  '/aa/bb/', '/aa/bb/cc/' and '/aa/bb/cc'. It would not match
+                  messages with first arguments of '/aa/b', '/aa' or even '/aa/bb'.</entry>
+                </row>
               </tbody>
             </tgroup>
           </informaltable>