From 2f7b11158b497e759cd03664b1881f88157a9f1a Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Thu, 9 Jul 2009 21:33:24 +0100 Subject: Supporting matching argument 0 as a namespace 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 | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ bus/signals.h | 3 +- 2 files changed, 154 insertions(+), 18 deletions(-) (limited to 'bus') diff --git a/bus/signals.c b/bus/signals.c index f059d30f..9939a23e 100644 --- a/bus/signals.c +++ b/bus/signals.c @@ -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/'" diff --git a/bus/signals.h b/bus/signals.h index eeb1d2d0..81590b5f 100644 --- a/bus/signals.h +++ b/bus/signals.h @@ -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, -- cgit v1.2.3