diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2011-04-07 17:16:01 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2011-04-07 17:16:24 +0100 |
commit | 74072479a05dbf9208a5f92495e7eb32f471428c (patch) | |
tree | 68bcfacfada594fbbfb056607c9d7ba39c6c99ff | |
parent | c99ead074a5c0917716c28c2ff1dc2e0c74a1b53 (diff) | |
parent | f15c6d66ca73d3fbee77118b94f1a5617e3577a2 (diff) |
Merge branch 'arg0namespace-24317'
Reviewed-by: David Zeuthen <davidz@redhat.com>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=24317
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=31818
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=34870
-rw-r--r-- | HACKING | 7 | ||||
-rw-r--r-- | bus/signals.c | 483 | ||||
-rw-r--r-- | bus/signals.h | 21 | ||||
-rw-r--r-- | dbus/dbus-marshal-validate.c | 66 | ||||
-rw-r--r-- | dbus/dbus-marshal-validate.h | 3 | ||||
-rw-r--r-- | doc/dbus-specification.xml | 118 |
6 files changed, 627 insertions, 71 deletions
@@ -154,6 +154,10 @@ To make a release of D-Bus, do the following: - update the file NEWS based on the git history + - verify that the version number of dbus-specification.xml is + changed if it needs to be; if changes have been made, update the + release date in that file + - update the AUTHORS file with "make update-authors" if necessary - the version number should have major.minor.micro, even @@ -175,7 +179,8 @@ To make a release of D-Bus, do the following: - bump the version number up in configure.ac (so the micro version is odd), and commit it. Make sure you do this *after* tagging the previous release! The idea is that git has a newer version number - than anything released. + than anything released. Similarly, bump the version number of + dbus-specification.xml and set the release date to "(not finalized)". - merge the branch you've released to the chronologically-later branch (usually "master"). You'll probably have to fix a merge diff --git a/bus/signals.c b/bus/signals.c index c85a88df..4d34ca14 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) { @@ -203,6 +206,18 @@ match_rule_to_string (BusMatchRule *rule) goto nomem; } + if (rule->flags & BUS_MATCH_PATH_NAMESPACE) + { + if (_dbus_string_get_length (&str) > 0) + { + if (!_dbus_string_append (&str, ",")) + goto nomem; + } + + if (!_dbus_string_append_printf (&str, "path_namespace='%s'", rule->path)) + goto nomem; + } + if (rule->flags & BUS_MATCH_SENDER) { if (_dbus_string_get_length (&str) > 0) @@ -238,7 +253,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 +262,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; } @@ -365,7 +383,8 @@ bus_match_rule_set_destination (BusMatchRule *rule, dbus_bool_t bus_match_rule_set_path (BusMatchRule *rule, - const char *path) + const char *path, + dbus_bool_t is_namespace) { char *new; @@ -375,7 +394,13 @@ bus_match_rule_set_path (BusMatchRule *rule, if (new == NULL) return FALSE; - rule->flags |= BUS_MATCH_PATH; + rule->flags &= ~(BUS_MATCH_PATH|BUS_MATCH_PATH_NAMESPACE); + + if (is_namespace) + rule->flags |= BUS_MATCH_PATH_NAMESPACE; + else + rule->flags |= BUS_MATCH_PATH; + dbus_free (rule->path); rule->path = new; @@ -386,7 +411,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 +479,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 +776,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 +808,34 @@ 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 (!_dbus_validate_bus_namespace (value, 0, value_len)) + { + dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, + "arg0namespace='%s' is not a valid 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 +854,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; @@ -962,12 +1010,15 @@ bus_match_rule_parse (DBusConnection *matches_go_to, goto failed; } } - else if (strcmp (key, "path") == 0) + else if (strcmp (key, "path") == 0 || + strcmp (key, "path_namespace") == 0) { - if (rule->flags & BUS_MATCH_PATH) + dbus_bool_t is_namespace = (strcmp (key, "path_namespace") == 0); + + if (rule->flags & (BUS_MATCH_PATH | BUS_MATCH_PATH_NAMESPACE)) { dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, - "Key %s specified twice in match rule\n", key); + "path or path_namespace specified twice in match rule\n"); goto failed; } @@ -978,7 +1029,7 @@ bus_match_rule_parse (DBusConnection *matches_go_to, goto failed; } - if (!bus_match_rule_set_path (rule, value)) + if (!bus_match_rule_set_path (rule, value, is_namespace)) { BUS_SET_OOM (error); goto failed; @@ -1315,7 +1366,7 @@ match_rule_equal (BusMatchRule *a, if ((a->flags & BUS_MATCH_PATH) && strcmp (a->path, b->path) != 0) return FALSE; - + if ((a->flags & BUS_MATCH_INTERFACE) && strcmp (a->interface, b->interface) != 0) return FALSE; @@ -1346,7 +1397,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) { @@ -1578,6 +1629,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, @@ -1688,6 +1750,31 @@ match_rule_matches (BusMatchRule *rule, return FALSE; } + if (flags & BUS_MATCH_PATH_NAMESPACE) + { + const char *path; + int len; + + _dbus_assert (rule->path != NULL); + + path = dbus_message_get_path (message); + if (path == NULL) + return FALSE; + + if (!str_has_prefix (path, rule->path)) + return FALSE; + + len = strlen (rule->path); + + /* Check that the actual argument is within the expected + * namespace, rather than just starting with that string, + * by checking that the matched prefix is followed by a '/' + * or the end of the path. + */ + if (path[len] != '\0' && path[len] != '/') + return FALSE; + } + if (flags & BUS_MATCH_ARGS) { int i; @@ -1703,11 +1790,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 +1803,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 +1828,32 @@ 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. + */ + if (actual_arg[expected_length] != '.') + return FALSE; + } + /* otherwise we had an exact match. */ + } else { if (expected_length != actual_length || @@ -2053,7 +2168,73 @@ 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.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); + + /* Trailing periods aren't allowed (earlier versions of the arg0namespace + * spec allowed a single trailing period, which altered the semantics) */ + rule = check_parse (FALSE, "arg0namespace='foo.'"); + _dbus_assert (rule == NULL); + + rule = check_parse (FALSE, "arg0namespace='foo.bar.'"); + _dbus_assert (rule == NULL); + + 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); @@ -2074,6 +2255,24 @@ test_parsing (void *data) rule = check_parse (FALSE, "type='signal',type='method_call'"); _dbus_assert (rule == NULL); + rule = check_parse (TRUE, "path_namespace='/foo/bar'"); + if (rule != NULL) + { + _dbus_assert (rule->flags == BUS_MATCH_PATH_NAMESPACE); + _dbus_assert (rule->path != NULL); + _dbus_assert (strcmp (rule->path, "/foo/bar") == 0); + + bus_match_rule_unref (rule); + } + + /* Almost a duplicate */ + rule = check_parse (FALSE, "path='/foo',path_namespace='/foo'"); + _dbus_assert (rule == NULL); + + /* Trailing / was supported in the initial proposal, but now isn't */ + rule = check_parse (FALSE, "path_namespace='/foo/'"); + _dbus_assert (rule == NULL); + /* Duplicates with the argN code */ rule = check_parse (FALSE, "arg0='foo',arg0='bar'"); _dbus_assert (rule == NULL); @@ -2134,6 +2333,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 +2403,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 +2428,44 @@ 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 */ + "arg0namespace='com.example.backend'", + "arg0namespace='com.example'", + "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'", + + /* 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 +2521,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 +2543,185 @@ 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); +} + +static const char* +path_namespace_should_match_message_1[] = { + "type='signal',path_namespace='/foo'", + "type='signal',path_namespace='/foo/TheObjectManager'", + NULL +}; + +static const char* +path_namespace_should_not_match_message_1[] = { + "type='signal',path_namespace='/bar'", + "type='signal',path_namespace='/bar/TheObjectManager'", + NULL +}; + +static const char* +path_namespace_should_match_message_2[] = { + "type='signal',path_namespace='/foo/TheObjectManager'", + NULL +}; + +static const char* +path_namespace_should_not_match_message_2[] = { + NULL +}; + +static const char* +path_namespace_should_match_message_3[] = { + NULL +}; + +static const char* +path_namespace_should_not_match_message_3[] = { + "type='signal',path_namespace='/foo/TheObjectManager'", + NULL +}; + +static void +test_matching_path_namespace (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_namespace_should_match_message_1, + path_namespace_should_not_match_message_1); + check_matching (message2, 2, + path_namespace_should_match_message_2, + path_namespace_should_not_match_message_2); + check_matching (message3, 3, + path_namespace_should_match_message_3, + path_namespace_should_not_match_message_3); + + dbus_message_unref (message3); + dbus_message_unref (message2); + dbus_message_unref (message1); } dbus_bool_t @@ -2314,9 +2738,10 @@ bus_signals_test (const DBusString *test_data_dir) _dbus_assert_not_reached ("Parsing match rules test failed"); test_equality (); - test_matching (); - + test_path_matching (); + test_matching_path_namespace (); + return TRUE; } diff --git a/bus/signals.h b/bus/signals.h index eeb1d2d0..5b086f04 100644 --- a/bus/signals.h +++ b/bus/signals.h @@ -31,13 +31,14 @@ typedef enum { - BUS_MATCH_MESSAGE_TYPE = 1 << 0, - BUS_MATCH_INTERFACE = 1 << 1, - BUS_MATCH_MEMBER = 1 << 2, - BUS_MATCH_SENDER = 1 << 3, - BUS_MATCH_DESTINATION = 1 << 4, - BUS_MATCH_PATH = 1 << 5, - BUS_MATCH_ARGS = 1 << 6 + BUS_MATCH_MESSAGE_TYPE = 1 << 0, + BUS_MATCH_INTERFACE = 1 << 1, + BUS_MATCH_MEMBER = 1 << 2, + BUS_MATCH_SENDER = 1 << 3, + BUS_MATCH_DESTINATION = 1 << 4, + BUS_MATCH_PATH = 1 << 5, + BUS_MATCH_ARGS = 1 << 6, + BUS_MATCH_PATH_NAMESPACE = 1 << 7 } BusMatchFlags; BusMatchRule* bus_match_rule_new (DBusConnection *matches_go_to); @@ -55,11 +56,13 @@ dbus_bool_t bus_match_rule_set_sender (BusMatchRule *rule, 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); + const char *path, + dbus_bool_t is_namespace); 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); BusMatchRule* bus_match_rule_parse (DBusConnection *matches_go_to, const DBusString *rule_text, diff --git a/dbus/dbus-marshal-validate.c b/dbus/dbus-marshal-validate.c index d87a27b9..9187a3e9 100644 --- a/dbus/dbus-marshal-validate.c +++ b/dbus/dbus-marshal-validate.c @@ -1082,23 +1082,11 @@ _dbus_validate_error_name (const DBusString *str, ((c) >= 'a' && (c) <= 'z') || \ ((c) == '_') || ((c) == '-')) -/** - * Checks that the given range of the string is a valid bus name in - * the D-Bus protocol. This includes a length restriction, etc., see - * the specification. - * - * @todo this is inconsistent with most of DBusString in that - * it allows a start,len range that extends past the string end. - * - * @param str the string - * @param start first byte index to check - * @param len number of bytes to check - * @returns #TRUE if the byte range exists and is a valid name - */ -dbus_bool_t -_dbus_validate_bus_name (const DBusString *str, - int start, - int len) +static dbus_bool_t +_dbus_validate_bus_name_full (const DBusString *str, + int start, + int len, + dbus_bool_t is_namespace) { const unsigned char *s; const unsigned char *end; @@ -1176,13 +1164,55 @@ _dbus_validate_bus_name (const DBusString *str, ++s; } - if (_DBUS_UNLIKELY (last_dot == NULL)) + if (!is_namespace && _DBUS_UNLIKELY (last_dot == NULL)) return FALSE; return TRUE; } /** + * Checks that the given range of the string is a valid bus name in + * the D-Bus protocol. This includes a length restriction, etc., see + * the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_bus_name (const DBusString *str, + int start, + int len) +{ + return _dbus_validate_bus_name_full (str, start, len, FALSE); +} + +/** + * Checks that the given range of the string is a prefix of a valid bus name in + * the D-Bus protocol. Unlike _dbus_validate_bus_name(), this accepts strings + * with only one period-separated component. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_bus_namespace (const DBusString *str, + int start, + int len) +{ + return _dbus_validate_bus_name_full (str, start, len, TRUE); +} + +/** * Checks that the given range of the string is a valid message type * signature in the D-Bus protocol. * diff --git a/dbus/dbus-marshal-validate.h b/dbus/dbus-marshal-validate.h index 1d2e26b8..06434201 100644 --- a/dbus/dbus-marshal-validate.h +++ b/dbus/dbus-marshal-validate.h @@ -144,6 +144,9 @@ dbus_bool_t _dbus_validate_error_name (const DBusString *str, dbus_bool_t _dbus_validate_bus_name (const DBusString *str, int start, int len); +dbus_bool_t _dbus_validate_bus_namespace (const DBusString *str, + int start, + int len); dbus_bool_t _dbus_validate_signature (const DBusString *str, int start, int len); diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml index ee5aac58..bb8ecd27 100644 --- a/doc/dbus-specification.xml +++ b/doc/dbus-specification.xml @@ -6,8 +6,8 @@ <article id="index"> <articleinfo> <title>D-Bus Specification</title> - <releaseinfo>Version 0.15</releaseinfo> - <date>3 November 2010</date> + <releaseinfo>Version 0.16</releaseinfo> + <date>(not finalized)</date> <authorgroup> <author> <firstname>Havoc</firstname> @@ -3722,6 +3722,43 @@ path match is path='/org/freedesktop/Hal/Manager'</entry> </row> <row> + <entry><literal>path_namespace</literal></entry> + <entry>An object path</entry> + <entry> + <para> + Matches messages which are sent from or to an + object for which the object path is either the + given value, or that value followed by one or + more path components. + </para> + + <para> + For example, + <literal>path_namespace='/com/example/foo'</literal> + would match signals sent by + <literal>/com/example/foo</literal> + or by + <literal>/com/example/foo/bar</literal>, + but not by + <literal>/com/example/foobar</literal>. + </para> + + <para> + Using both <literal>path</literal> and + <literal>path_namespace</literal> in the same match + rule is not allowed. + </para> + + <para> + <emphasis> + This match key was added in version 0.16 of the + D-Bus specification and implemented by the bus + daemon in dbus 1.5.0 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 @@ -3731,24 +3768,77 @@ <entry><literal>arg[0, 1, 2, 3, ...]</literal></entry> <entry>Any string</entry> <entry>Arg matches are special and are used for further restricting the - match based on the arguments in the body of a message. As of this time - only string arguments can be matched. An example of an argument match + match based on the arguments in the body of a message. Only arguments of type + STRING can be matched in this way. An example of an argument match 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> + <entry> + <para>Argument path matches provide a specialised form of wildcard matching for + path-like namespaces. They can match arguments whose type is either STRING or + OBJECT_PATH. 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'.</para> + + <para>This is intended for monitoring “directories” in file system-like + hierarchies, as used in the <citetitle>dconf</citetitle> configuration + system. An application interested in all nodes in a particular hierarchy would + monitor <literal>arg0path='/ca/example/foo/'</literal>. Then the service could + emit a signal with zeroth argument <literal>"/ca/example/foo/bar"</literal> to + represent a modification to the “bar” property, or a signal with zeroth + argument <literal>"/ca/example/"</literal> to represent atomic modification of + many properties within that directory, and the interested application would be + notified in both cases.</para> + <para> + <emphasis> + This match key was added in version 0.12 of the + D-Bus specification, implemented for STRING + arguments by the bus daemon in dbus 1.2.0 and later, + and implemented for OBJECT_PATH arguments in dbus 1.5.0 + and later. + </emphasis> + </para> + </entry> + </row> + <row> + <entry><literal>arg0namespace</literal></entry> + <entry>Like a bus name, except that the string is not + required to contain a '.' (period)</entry> + <entry> + <para>Match messages whose first argument is of type STRING, and is a bus name + or interface name within the specified namespace. This is primarily intended + for watching name owner changes for a group of related bus names, rather than + for a single name or all name changes.</para> + + <para>Because every valid interface name is also a valid + bus name, this can also be used for messages whose + first argument is an interface name.</para> + + <para>For example, the match rule + <literal>member='NameOwnerChanged',arg0namespace='com.example.backend'</literal> + matches name owner changes for bus names such as + <literal>com.example.backend.foo</literal>, + <literal>com.example.backend.foo.bar</literal>, and + <literal>com.example.backend</literal> itself.</para> + + <para>See also <xref linkend='bus-messages-name-owner-changed'/>.</para> + <para> + <emphasis> + This match key was added in version 0.16 of the + D-Bus specification and implemented by the bus + daemon in dbus 1.5.0 and later. + </emphasis> + </para> + </entry> </row> </tbody> </tgroup> |