mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-05-07 22:38:08 +02:00
Supporting matching argument 0 as a namespace
Rather like "arg0path='/foo/'" matching all object paths starting with "/foo/", this adds support for matching a prefix of a string argument with "arg0namespace='org.freedesktop.Telepathy.Client.'" (for example). This is mostly intended for use with NameOwnerChanged and PropertiesChanged; thus, only matching the 0th argument is permitted. (This also means it could work with the multicast-plus-socket-filters model being considered for DBus-in-the-kernel without having to hash every period-separated prefix of every string argument.)
This commit is contained in:
parent
2f618faa2d
commit
2f7b11158b
2 changed files with 154 additions and 18 deletions
169
bus/signals.c
169
bus/signals.c
|
|
@ -47,8 +47,11 @@ struct BusMatchRule
|
|||
int args_len;
|
||||
};
|
||||
|
||||
#define BUS_MATCH_ARG_NAMESPACE 0x4000000u
|
||||
#define BUS_MATCH_ARG_IS_PATH 0x8000000u
|
||||
|
||||
#define BUS_MATCH_ARG_FLAGS (BUS_MATCH_ARG_NAMESPACE | BUS_MATCH_ARG_IS_PATH)
|
||||
|
||||
BusMatchRule*
|
||||
bus_match_rule_new (DBusConnection *matches_go_to)
|
||||
{
|
||||
|
|
@ -211,7 +214,7 @@ match_rule_to_string (BusMatchRule *rule)
|
|||
{
|
||||
if (rule->args[i] != NULL)
|
||||
{
|
||||
dbus_bool_t is_path;
|
||||
dbus_bool_t is_path, is_namespace;
|
||||
|
||||
if (_dbus_string_get_length (&str) > 0)
|
||||
{
|
||||
|
|
@ -220,10 +223,13 @@ match_rule_to_string (BusMatchRule *rule)
|
|||
}
|
||||
|
||||
is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
|
||||
is_namespace = (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) != 0;
|
||||
|
||||
if (!_dbus_string_append_printf (&str,
|
||||
"arg%d%s='%s'",
|
||||
i, is_path ? "path" : "",
|
||||
i,
|
||||
is_path ? "path" :
|
||||
is_namespace ? "namespace" : "",
|
||||
rule->args[i]))
|
||||
goto nomem;
|
||||
}
|
||||
|
|
@ -359,7 +365,8 @@ dbus_bool_t
|
|||
bus_match_rule_set_arg (BusMatchRule *rule,
|
||||
int arg,
|
||||
const DBusString *value,
|
||||
dbus_bool_t is_path)
|
||||
dbus_bool_t is_path,
|
||||
dbus_bool_t is_namespace)
|
||||
{
|
||||
int length;
|
||||
char *new;
|
||||
|
|
@ -426,6 +433,9 @@ bus_match_rule_set_arg (BusMatchRule *rule,
|
|||
if (is_path)
|
||||
rule->arg_lens[arg] |= BUS_MATCH_ARG_IS_PATH;
|
||||
|
||||
if (is_namespace)
|
||||
rule->arg_lens[arg] |= BUS_MATCH_ARG_NAMESPACE;
|
||||
|
||||
/* NULL termination didn't get busted */
|
||||
_dbus_assert (rule->args[rule->args_len] == NULL);
|
||||
_dbus_assert (rule->arg_lens[rule->args_len] == 0);
|
||||
|
|
@ -720,7 +730,8 @@ bus_match_rule_parse_arg_match (BusMatchRule *rule,
|
|||
const DBusString *value,
|
||||
DBusError *error)
|
||||
{
|
||||
dbus_bool_t is_path;
|
||||
dbus_bool_t is_path = FALSE;
|
||||
dbus_bool_t is_namespace = FALSE;
|
||||
DBusString key_str;
|
||||
unsigned long arg;
|
||||
int length;
|
||||
|
|
@ -751,17 +762,25 @@ bus_match_rule_parse_arg_match (BusMatchRule *rule,
|
|||
goto failed;
|
||||
}
|
||||
|
||||
if (end != length &&
|
||||
((end + 4) != length ||
|
||||
!_dbus_string_ends_with_c_str (&key_str, "path")))
|
||||
if (end != length)
|
||||
{
|
||||
dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
|
||||
"Key '%s' in match rule contains junk after argument number. Only 'path' is optionally valid ('arg0path' for example).\n", key);
|
||||
goto failed;
|
||||
if ((end + strlen ("path")) == length &&
|
||||
_dbus_string_ends_with_c_str (&key_str, "path"))
|
||||
{
|
||||
is_path = TRUE;
|
||||
}
|
||||
else if (_dbus_string_equal_c_str (&key_str, "arg0namespace"))
|
||||
{
|
||||
is_namespace = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
|
||||
"Key '%s' in match rule contains junk after argument number (%u). Only 'arg%upath' (for example) or 'arg0namespace' are valid", key, arg, arg);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
is_path = end != length;
|
||||
|
||||
/* If we didn't check this we could allocate a huge amount of RAM */
|
||||
if (arg > DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER)
|
||||
{
|
||||
|
|
@ -779,7 +798,7 @@ bus_match_rule_parse_arg_match (BusMatchRule *rule,
|
|||
goto failed;
|
||||
}
|
||||
|
||||
if (!bus_match_rule_set_arg (rule, arg, value, is_path))
|
||||
if (!bus_match_rule_set_arg (rule, arg, value, is_path, is_namespace))
|
||||
{
|
||||
BUS_SET_OOM (error);
|
||||
goto failed;
|
||||
|
|
@ -1318,7 +1337,7 @@ match_rule_equal (BusMatchRule *a,
|
|||
if (a->arg_lens[i] != b->arg_lens[i])
|
||||
return FALSE;
|
||||
|
||||
length = a->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
|
||||
length = a->arg_lens[i] & ~BUS_MATCH_ARG_FLAGS;
|
||||
|
||||
if (a->args[i] != NULL)
|
||||
{
|
||||
|
|
@ -1675,11 +1694,12 @@ match_rule_matches (BusMatchRule *rule,
|
|||
int current_type;
|
||||
const char *expected_arg;
|
||||
int expected_length;
|
||||
dbus_bool_t is_path;
|
||||
dbus_bool_t is_path, is_namespace;
|
||||
|
||||
expected_arg = rule->args[i];
|
||||
expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
|
||||
expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_FLAGS;
|
||||
is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
|
||||
is_namespace = (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) != 0;
|
||||
|
||||
current_type = dbus_message_iter_get_arg_type (&iter);
|
||||
|
||||
|
|
@ -1712,6 +1732,39 @@ match_rule_matches (BusMatchRule *rule,
|
|||
MIN (actual_length, expected_length)) != 0)
|
||||
return FALSE;
|
||||
}
|
||||
else if (is_namespace)
|
||||
{
|
||||
if (expected_length > actual_length)
|
||||
return FALSE;
|
||||
|
||||
/* If the actual argument doesn't start with the expected
|
||||
* namespace, then we don't match.
|
||||
*/
|
||||
if (memcmp (expected_arg, actual_arg, expected_length) != 0)
|
||||
return FALSE;
|
||||
|
||||
if (expected_length < actual_length)
|
||||
{
|
||||
/* Check that the actual argument is within the expected
|
||||
* namespace, rather than just starting with that string,
|
||||
* by checking that the matched prefix ends in a '.'.
|
||||
*
|
||||
* This doesn't stop "foo.bar." matching "foo.bar..baz"
|
||||
* which is an invalid namespace, but at some point the
|
||||
* daemon can't cover up for broken services.
|
||||
*/
|
||||
int expected_period_index;
|
||||
|
||||
if (expected_arg[expected_length - 1] == '.')
|
||||
expected_period_index = expected_length - 1;
|
||||
else
|
||||
expected_period_index = expected_length;
|
||||
|
||||
if (actual_arg[expected_period_index] != '.')
|
||||
return FALSE;
|
||||
}
|
||||
/* otherwise we had an exact match. */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expected_length != actual_length ||
|
||||
|
|
@ -2042,6 +2095,23 @@ test_parsing (void *data)
|
|||
bus_match_rule_unref (rule);
|
||||
}
|
||||
|
||||
/* Arg 0 namespace matches */
|
||||
rule = check_parse (TRUE, "arg0namespace='foo'");
|
||||
if (rule != NULL)
|
||||
{
|
||||
_dbus_assert (rule->flags == BUS_MATCH_ARGS);
|
||||
_dbus_assert (rule->args != NULL);
|
||||
_dbus_assert (rule->args_len == 1);
|
||||
_dbus_assert (strcmp (rule->args[0], "foo") == 0);
|
||||
_dbus_assert ((rule->arg_lens[0] & BUS_MATCH_ARG_NAMESPACE)
|
||||
== BUS_MATCH_ARG_NAMESPACE);
|
||||
|
||||
bus_match_rule_unref (rule);
|
||||
}
|
||||
|
||||
rule = check_parse (FALSE, "arg1namespace='foo'");
|
||||
_dbus_assert (rule == NULL);
|
||||
|
||||
/* Too-large argN */
|
||||
rule = check_parse (FALSE, "arg300='foo'");
|
||||
_dbus_assert (rule == NULL);
|
||||
|
|
@ -2122,6 +2192,7 @@ static struct {
|
|||
{ "type='method_call',arg0='blah',arg1='baz'", "arg0='blah',arg1='baz',type='method_call'" },
|
||||
{ "type='method_call',arg3='foosh'", "arg3='foosh',type='method_call'" },
|
||||
{ "arg3='fool'", "arg3='fool'" },
|
||||
{ "arg0namespace='fool'", "arg0namespace='fool'" },
|
||||
{ "member='food'", "member='food'" }
|
||||
};
|
||||
|
||||
|
|
@ -2197,6 +2268,7 @@ should_match_message_1[] = {
|
|||
* argument is not a valid path)!
|
||||
*/
|
||||
"arg0path='foobar'",
|
||||
"arg0namespace='foobar'",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -2218,6 +2290,51 @@ should_not_match_message_1[] = {
|
|||
"arg0path='foo'",
|
||||
"arg0path='foobar/'",
|
||||
"arg1path='3'",
|
||||
"arg0namespace='foo'",
|
||||
"arg0namespace='foo',arg1='abcdef'",
|
||||
"arg0namespace='moo'",
|
||||
NULL
|
||||
};
|
||||
|
||||
#define EXAMPLE_NAME "com.example.backend.foo"
|
||||
|
||||
static const char *
|
||||
should_match_message_2[] = {
|
||||
/* EXAMPLE_NAME is in all of these namespaces, specified with and without a
|
||||
* trailing period */
|
||||
"arg0namespace='com.example.backend.'",
|
||||
"arg0namespace='com.example.backend'",
|
||||
"arg0namespace='com.example.'",
|
||||
"arg0namespace='com.example'",
|
||||
"arg0namespace='com.'",
|
||||
"arg0namespace='com'",
|
||||
|
||||
/* If the client specifies the name exactly, with no trailing period, then
|
||||
* it should match.
|
||||
*/
|
||||
"arg0namespace='com.example.backend.foo'",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *
|
||||
should_not_match_message_2[] = {
|
||||
/* These are not even prefixes */
|
||||
"arg0namespace='com.example.backend.foo.bar'",
|
||||
"arg0namespace='com.example.backend.foobar'",
|
||||
"arg0namespace='com.example.backend.fo.'",
|
||||
|
||||
/* This should match anything within the namespace com.example.backend.foo,
|
||||
* not including com.example.backend.foo itself.
|
||||
*/
|
||||
"arg0namespace='com.example.backend.foo.'",
|
||||
|
||||
/* These are prefixes, but they're not parent namespaces. */
|
||||
"arg0namespace='com.example.backend.fo'",
|
||||
"arg0namespace='com.example.backen'",
|
||||
"arg0namespace='com.exampl'",
|
||||
"arg0namespace='co'",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -2273,7 +2390,7 @@ check_matching (DBusMessage *message,
|
|||
static void
|
||||
test_matching (void)
|
||||
{
|
||||
DBusMessage *message1;
|
||||
DBusMessage *message1, *message2;
|
||||
const char *v_STRING;
|
||||
dbus_int32_t v_INT32;
|
||||
|
||||
|
|
@ -2295,6 +2412,24 @@ test_matching (void)
|
|||
should_not_match_message_1);
|
||||
|
||||
dbus_message_unref (message1);
|
||||
|
||||
message2 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
|
||||
_dbus_assert (message2 != NULL);
|
||||
if (!dbus_message_set_member (message2, "NameOwnerChanged"))
|
||||
_dbus_assert_not_reached ("oom");
|
||||
|
||||
/* Obviously this isn't really a NameOwnerChanged signal. */
|
||||
v_STRING = EXAMPLE_NAME;
|
||||
if (!dbus_message_append_args (message2,
|
||||
DBUS_TYPE_STRING, &v_STRING,
|
||||
NULL))
|
||||
_dbus_assert_not_reached ("oom");
|
||||
|
||||
check_matching (message2, 2,
|
||||
should_match_message_2,
|
||||
should_not_match_message_2);
|
||||
|
||||
dbus_message_unref (message2);
|
||||
}
|
||||
|
||||
#define PATH_MATCH_RULE "arg0path='/aa/bb/'"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue