From f6ec4a80abbfd4e4f4969747c39e625b2689df08 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Thu, 20 Sep 2007 13:04:38 -0400 Subject: Add argument path matching support. Bug #11066. 2007-09-20 Ryan Lortie * 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 | 18 +++++++ bus/signals.c | 119 ++++++++++++++++++++++++++++++++++++--------- bus/signals.h | 31 ++++++------ dbus/dbus-bus.c | 7 +++ doc/dbus-specification.xml | 14 ++++++ 5 files changed, 152 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index 32c6436e..fe3af99f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2007-09-20 Ryan Lortie + + 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 Add support for compacting DBusStrings to release wasted memory. diff --git a/bus/signals.c b/bus/signals.c index d0845b1a..c90149b2 100644 --- a/bus/signals.c +++ b/bus/signals.c @@ -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) diff --git a/bus/signals.h b/bus/signals.h index ca74dc99..ab3c33aa 100644 --- a/bus/signals.h +++ b/bus/signals.h @@ -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, diff --git a/dbus/dbus-bus.c b/dbus/dbus-bus.c index fd569c2b..c7f43e8c 100644 --- a/dbus/dbus-bus.c +++ b/dbus/dbus-bus.c @@ -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 diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml index 39bb7723..9e33944c 100644 --- a/doc/dbus-specification.xml +++ b/doc/dbus-specification.xml @@ -3136,6 +3136,20 @@ would be arg3='Foo'. Only argument indexes from 0 to 63 should be accepted. + + arg[0, 1, 2, 3, ...]path + Any string + 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'. + -- cgit