diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2011-04-07 12:40:30 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2011-04-07 12:40:30 +0100 |
commit | bf3a7d865307627136bf30dfa2558d7d208c7460 (patch) | |
tree | 009ab957ba29cde75a0a31dbec35a6210e55beef /bus | |
parent | 22b821bf4a806192f891ee1f33b541b61ea03510 (diff) | |
parent | 610549a679ac2efd7b4e0f15eed62a282240c57c (diff) |
Merge remote-tracking branch 'wjt/arg0namespace' into arg0namespace-24317
Diffstat (limited to 'bus')
-rw-r--r-- | bus/signals.c | 360 | ||||
-rw-r--r-- | bus/signals.h | 3 |
2 files changed, 340 insertions, 23 deletions
diff --git a/bus/signals.c b/bus/signals.c index c85a88df..56a7d6c1 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) { @@ -238,7 +241,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) { @@ -247,10 +250,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; } @@ -386,7 +392,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; @@ -453,6 +460,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); @@ -747,7 +757,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; @@ -778,16 +789,39 @@ 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")) + { + int value_len = _dbus_string_get_length (value); - is_path = end != length; + is_namespace = TRUE; + + if (value_len > 0 && + _dbus_string_get_byte (value, value_len - 1) == '.') + value_len--; + + if (!_dbus_validate_bus_namespace (value, 0, value_len)) + { + dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, + "arg0namespace='%s' is not a valid (optionally " + "period-terminated) prefix of a bus name", + _dbus_string_get_const_data (value)); + goto failed; + } + } + 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; + } + } /* If we didn't check this we could allocate a huge amount of RAM */ if (arg > DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER) @@ -806,7 +840,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; @@ -1346,7 +1380,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) { @@ -1703,11 +1737,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); @@ -1715,8 +1750,9 @@ match_rule_matches (BusMatchRule *rule, { const char *actual_arg; int actual_length; - - if (current_type != DBUS_TYPE_STRING) + + if (current_type != DBUS_TYPE_STRING && + (!is_path || current_type != DBUS_TYPE_OBJECT_PATH)) return FALSE; actual_arg = NULL; @@ -1739,6 +1775,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 || @@ -2053,7 +2122,92 @@ test_parsing (void *data) bus_match_rule_unref (rule); } - + + rule = check_parse (TRUE, "arg7path='/foo'"); + if (rule != NULL) + { + _dbus_assert (rule->flags = BUS_MATCH_ARGS); + _dbus_assert (rule->args != NULL); + _dbus_assert (rule->args_len == 8); + _dbus_assert (rule->args[7] != NULL); + _dbus_assert (rule->args[8] == NULL); + _dbus_assert (strcmp (rule->args[7], "/foo") == 0); + _dbus_assert ((rule->arg_lens[7] & BUS_MATCH_ARG_IS_PATH) + == BUS_MATCH_ARG_IS_PATH); + + 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 (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 (TRUE, "arg0namespace='foo.bar'"); + 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.bar") == 0); + _dbus_assert ((rule->arg_lens[0] & BUS_MATCH_ARG_NAMESPACE) + == BUS_MATCH_ARG_NAMESPACE); + + bus_match_rule_unref (rule); + } + + rule = check_parse (TRUE, "arg0namespace='foo.bar.'"); + 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.bar.") == 0); + _dbus_assert ((rule->arg_lens[0] & BUS_MATCH_ARG_NAMESPACE) + == BUS_MATCH_ARG_NAMESPACE); + + bus_match_rule_unref (rule); + } + + /* Only arg0namespace is supported. */ + rule = check_parse (FALSE, "arg1namespace='foo'"); + _dbus_assert (rule == NULL); + + /* An empty string isn't a valid namespace prefix (you should just not + * specify this key at all). + */ + rule = check_parse (FALSE, "arg0namespace=''"); + _dbus_assert (rule == NULL); + + /* Two trailing periods on otherwise-valid namespaces aren't allowed. */ + rule = check_parse (FALSE, "arg0namespace='foo..'"); + _dbus_assert (rule == NULL); + + rule = check_parse (FALSE, "arg0namespace='foo.bar..'"); + _dbus_assert (rule == NULL); + /* Too-large argN */ rule = check_parse (FALSE, "arg300='foo'"); _dbus_assert (rule == NULL); @@ -2134,6 +2288,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'" } }; @@ -2203,6 +2358,13 @@ should_match_message_1[] = { "type='signal',member='Frobated',arg0='foobar'", "member='Frobated',arg0='foobar'", "type='signal',arg0='foobar'", + /* The definition of argXpath matches says: "As with normal argument matches, + * if the argument is exactly equal to the string given in the match rule + * then the rule is satisfied." So this should match (even though the + * argument is not a valid path)! + */ + "arg0path='foobar'", + "arg0namespace='foobar'", NULL }; @@ -2221,6 +2383,54 @@ should_not_match_message_1[] = { "arg0='foobar',arg1='abcdef'", "arg0='foobar',arg1='abcdef',arg2='abcdefghi',arg3='abcdefghi',arg4='abcdefghi'", "arg0='foobar',arg1='abcdef',arg4='abcdefghi',arg3='abcdefghi',arg2='abcdefghi'", + "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 }; @@ -2276,7 +2486,7 @@ check_matching (DBusMessage *message, static void test_matching (void) { - DBusMessage *message1; + DBusMessage *message1, *message2; const char *v_STRING; dbus_int32_t v_INT32; @@ -2298,6 +2508,112 @@ 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/'" + +/* This is a list of paths that should be matched by PATH_MATCH_RULE, taken + * from the specification. Notice that not all of them are actually legal D-Bus + * paths. + * + * The author of this test takes no responsibility for the semantics of + * this match rule key. + */ +static const char *paths_that_should_be_matched[] = { + "/aa/", + "/aa/bb/", + "/aa/bb/cc/", +#define FIRST_VALID_PATH_WHICH_SHOULD_MATCH 3 + "/", + "/aa/bb/cc", + NULL +}; + +/* These paths should not be matched by PATH_MATCH_RULE. */ +static const char *paths_that_should_not_be_matched[] = { + "/aa/b", + "/aa", + /* or even... */ + "/aa/bb", + NULL +}; + +static void +test_path_match (int type, + const char *path, + const char *rule_text, + BusMatchRule *rule, + dbus_bool_t should_match) +{ + DBusMessage *message = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL); + dbus_bool_t matched; + + _dbus_assert (message != NULL); + if (!dbus_message_set_member (message, "Foo")) + _dbus_assert_not_reached ("oom"); + + if (!dbus_message_append_args (message, + type, &path, + NULL)) + _dbus_assert_not_reached ("oom"); + + matched = match_rule_matches (rule, NULL, NULL, message, 0); + + if (matched != should_match) + { + _dbus_warn ("Expected rule %s to %s message " + "with first arg %s of type '%c', failed\n", + rule_text, + should_match ? "match" : "not match", + path, + (char) type); + exit (1); + } + + dbus_message_unref (message); +} + +static void +test_path_matching (void) +{ + BusMatchRule *rule; + const char **s; + + rule = check_parse (TRUE, PATH_MATCH_RULE); + _dbus_assert (rule != NULL); + + for (s = paths_that_should_be_matched; *s != NULL; s++) + test_path_match (DBUS_TYPE_STRING, *s, PATH_MATCH_RULE, rule, TRUE); + + for (s = paths_that_should_be_matched + FIRST_VALID_PATH_WHICH_SHOULD_MATCH; + *s != NULL; s++) + test_path_match (DBUS_TYPE_OBJECT_PATH, *s, PATH_MATCH_RULE, rule, TRUE); + + for (s = paths_that_should_not_be_matched; *s != NULL; s++) + { + test_path_match (DBUS_TYPE_STRING, *s, PATH_MATCH_RULE, rule, FALSE); + test_path_match (DBUS_TYPE_OBJECT_PATH, *s, PATH_MATCH_RULE, rule, FALSE); + } + + bus_match_rule_unref (rule); } dbus_bool_t @@ -2314,9 +2630,9 @@ bus_signals_test (const DBusString *test_data_dir) _dbus_assert_not_reached ("Parsing match rules test failed"); test_equality (); - test_matching (); - + test_path_matching (); + return TRUE; } 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, |