summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2011-04-07 17:16:01 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2011-04-07 17:16:24 +0100
commit74072479a05dbf9208a5f92495e7eb32f471428c (patch)
tree68bcfacfada594fbbfb056607c9d7ba39c6c99ff
parentc99ead074a5c0917716c28c2ff1dc2e0c74a1b53 (diff)
parentf15c6d66ca73d3fbee77118b94f1a5617e3577a2 (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--HACKING7
-rw-r--r--bus/signals.c483
-rw-r--r--bus/signals.h21
-rw-r--r--dbus/dbus-marshal-validate.c66
-rw-r--r--dbus/dbus-marshal-validate.h3
-rw-r--r--doc/dbus-specification.xml118
6 files changed, 627 insertions, 71 deletions
diff --git a/HACKING b/HACKING
index 7a5a792c..64dc0409 100644
--- a/HACKING
+++ b/HACKING
@@ -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>