summaryrefslogtreecommitdiff
path: root/bus
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2011-04-07 12:40:30 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2011-04-07 12:40:30 +0100
commitbf3a7d865307627136bf30dfa2558d7d208c7460 (patch)
tree009ab957ba29cde75a0a31dbec35a6210e55beef /bus
parent22b821bf4a806192f891ee1f33b541b61ea03510 (diff)
parent610549a679ac2efd7b4e0f15eed62a282240c57c (diff)
Merge remote-tracking branch 'wjt/arg0namespace' into arg0namespace-24317
Diffstat (limited to 'bus')
-rw-r--r--bus/signals.c360
-rw-r--r--bus/signals.h3
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,