From d8fd6b8bd2fbe52a715b2ad79166e970e850c956 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 6 Nov 2015 17:36:31 +0100 Subject: [PATCH 3/3] signals: add support for arg0has keyword Bug: https://bugs.freedesktop.org/show_bug.cgi?id=91755 Signed-off-by: Simon McVittie --- bus/signals.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ bus/signals.h | 5 -- 2 files changed, 235 insertions(+), 29 deletions(-) diff --git a/bus/signals.c b/bus/signals.c index f284355..cde30a2 100644 --- a/bus/signals.c +++ b/bus/signals.c @@ -45,15 +45,23 @@ struct BusMatchRule char *destination; char *path; + /* The low bits of each integer are the actual length; the high bits + * are flags. We can assume that the max length is less than + * DBUS_MAXIMUM_MATCH_RULE_LENGTH, which is 1024, so we only need 10 bits + * for the actual length. */ unsigned int *arg_lens; + char **args; int args_len; }; +#define BUS_MATCH_ARG_IS_HAS 0x2000000u #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) +#define BUS_MATCH_ARG_FLAGS (BUS_MATCH_ARG_NAMESPACE | \ + BUS_MATCH_ARG_IS_PATH | \ + BUS_MATCH_ARG_IS_HAS) BusMatchRule* bus_match_rule_new (DBusConnection *matches_go_to) @@ -276,7 +284,7 @@ match_rule_to_string (BusMatchRule *rule) { if (rule->args[i] != NULL) { - dbus_bool_t is_path, is_namespace; + const char *flag = ""; if (_dbus_string_get_length (&str) > 0) { @@ -284,14 +292,17 @@ match_rule_to_string (BusMatchRule *rule) goto nomem; } - is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0; - is_namespace = (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) != 0; - + if (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) + flag = "path"; + else if (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) + flag = "namespace"; + else if (rule->arg_lens[i] & BUS_MATCH_ARG_IS_HAS) + flag = "has"; + if (!_dbus_string_append_printf (&str, "arg%d%s", i, - is_path ? "path" : - is_namespace ? "namespace" : "")) + flag)) goto nomem; if (!append_key_and_escaped_value (&str, "", rule->args[i])) goto nomem; @@ -445,12 +456,11 @@ bus_match_rule_set_path (BusMatchRule *rule, return TRUE; } -dbus_bool_t +static dbus_bool_t bus_match_rule_set_arg (BusMatchRule *rule, int arg, const DBusString *value, - dbus_bool_t is_path, - dbus_bool_t is_namespace) + unsigned int flag) { int length; char *new; @@ -514,11 +524,7 @@ bus_match_rule_set_arg (BusMatchRule *rule, rule->arg_lens[arg] = length; rule->args[arg] = new; - if (is_path) - rule->arg_lens[arg] |= BUS_MATCH_ARG_IS_PATH; - - if (is_namespace) - rule->arg_lens[arg] |= BUS_MATCH_ARG_NAMESPACE; + rule->arg_lens[arg] |= flag; /* NULL termination didn't get busted */ _dbus_assert (rule->args[rule->args_len] == NULL); @@ -814,8 +820,7 @@ bus_match_rule_parse_arg_match (BusMatchRule *rule, const DBusString *value, DBusError *error) { - dbus_bool_t is_path = FALSE; - dbus_bool_t is_namespace = FALSE; + unsigned int flag = 0; DBusString key_str; unsigned long arg; int length; @@ -848,17 +853,16 @@ bus_match_rule_parse_arg_match (BusMatchRule *rule, if (end != length) { - int len1 = strlen ("path"); - if ((end + len1) == length && + if ((end + (int) strlen ("path")) == length && _dbus_string_ends_with_c_str (&key_str, "path")) { - is_path = TRUE; + flag = BUS_MATCH_ARG_IS_PATH; } else if (_dbus_string_equal_c_str (&key_str, "arg0namespace")) { int value_len = _dbus_string_get_length (value); - is_namespace = TRUE; + flag = BUS_MATCH_ARG_NAMESPACE; if (!_dbus_validate_bus_namespace (value, 0, value_len)) { @@ -868,6 +872,11 @@ bus_match_rule_parse_arg_match (BusMatchRule *rule, goto failed; } } + else if ((end + (int) strlen ("has")) == length && + _dbus_string_ends_with_c_str (&key_str, "has")) + { + flag = BUS_MATCH_ARG_IS_HAS; + } else { dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID, @@ -892,8 +901,8 @@ bus_match_rule_parse_arg_match (BusMatchRule *rule, "Argument %d matched more than once in match rule\n", key); goto failed; } - - if (!bus_match_rule_set_arg (rule, arg, value, is_path, is_namespace)) + + if (!bus_match_rule_set_arg (rule, arg, value, flag)) { BUS_SET_OOM (error); goto failed; @@ -1775,6 +1784,26 @@ str_has_prefix (const char *str, const char *prefix) } static dbus_bool_t +match_rule_matches_has (const char *expected_arg, + DBusMessageIter *array_iter, + unsigned int flags) +{ + while (dbus_message_iter_get_arg_type (array_iter) != DBUS_TYPE_INVALID) + { + const char *next_string; + + dbus_message_iter_get_basic (array_iter, &next_string); + + if (strcmp (next_string, expected_arg) == 0) + return TRUE; + + dbus_message_iter_next (array_iter); + } + + return FALSE; +} + +static dbus_bool_t match_rule_matches_string_arg (const char *expected_arg, int expected_length, const char *actual_arg, @@ -2039,7 +2068,23 @@ match_rule_matches (BusMatchRule *rule, current_type = dbus_message_iter_get_arg_type (&iter); - if (expected_arg != NULL) + if (expected_arg != NULL && (flags & BUS_MATCH_ARG_IS_HAS) != 0) + { + DBusMessageIter array_iter; + + if (current_type != DBUS_TYPE_ARRAY) + return FALSE; + + dbus_message_iter_recurse (&iter, &array_iter); + + if (dbus_message_iter_get_arg_type (&array_iter) != + DBUS_TYPE_STRING) + return FALSE; + + if (!match_rule_matches_has (expected_arg, &array_iter, flags)) + return FALSE; + } + else if (expected_arg != NULL) { const char *actual_arg; @@ -2963,6 +3008,171 @@ test_matching_path_namespace (void) dbus_message_unref (message1); } +static const char * const contains_keyword1[] = { + "keyword", NULL +}; +static const char * const contains_keyword2[] = { + "first", "keyword", NULL +}; +static const char * const contains_keyword3[] = { + "keyword", "last", NULL +}; +static const char * const contains_keyword4[] = { + "first", "keyword", "last", NULL +}; +static const char * const contains_keyword5[] = { + "keyword", "keyword", NULL +}; +static const char * const * const contains_keyword[] = { + contains_keyword1, + contains_keyword2, + contains_keyword3, + contains_keyword4, + contains_keyword5 +}; + +static const char * const contains_empty1[] = { + "", NULL +}; +static const char * const contains_empty2[] = { + "first", "", NULL +}; +static const char * const contains_empty3[] = { + "", "last", NULL +}; +static const char * const contains_empty4[] = { + "first", "", "last", NULL +}; +static const char * const contains_empty5[] = { + "", "", NULL +}; +static const char * const * const contains_empty[] = { + contains_empty1, + contains_empty2, + contains_empty3, + contains_empty4, + contains_empty5 +}; + +static const char * const doesnt_contain_empty1[] = { + "a", NULL +}; +static const char * const doesnt_contain_empty2[] = { + "a", "b", "c", NULL +}; +static const char * const doesnt_contain_empty3[] = { + NULL +}; +const char * const * const doesnt_contain_empty[] = { + doesnt_contain_empty1, + doesnt_contain_empty2, + doesnt_contain_empty3 +}; + +static const char * const doesnt_contain_keyword1[] = { + "", NULL +}; +static const char * const doesnt_contain_keyword2[] = { + "keywords", NULL +}; +static const char * const doesnt_contain_keyword3[] = { + "notmykeywords", "last", NULL +}; +static const char * const doesnt_contain_keyword4[] = { + "first", "notmykeyword", NULL +}; +static const char * const doesnt_contain_keyword5[] = { + "first", "keywords", "last", NULL +}; +static const char * const doesnt_contain_keyword6[] = { + NULL +}; +const char * const * const doesnt_contain_keyword[] = { + doesnt_contain_keyword1, + doesnt_contain_keyword2, + doesnt_contain_keyword3, + doesnt_contain_keyword4, + doesnt_contain_keyword5, + doesnt_contain_keyword6 +}; + +static void +test_arr_match (const char *rule_text, + BusMatchRule *rule, + int which, + const char * const * keywords, + dbus_bool_t should_match) +{ + DBusMessage *message; + DBusMessageIter iter; + DBusMessageIter array_iter; + const char * const *keyword_iter; + dbus_bool_t matched; + + message = dbus_message_new_signal ("/", "com.example.Foo", "Foo"); + _dbus_assert (message != NULL); + + dbus_message_iter_init_append (message, &iter); + + if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", + &array_iter)) + _dbus_assert_not_reached ("OOM"); + + for (keyword_iter = keywords; *keyword_iter != NULL; keyword_iter++) + { + if (!dbus_message_iter_append_basic (&array_iter, DBUS_TYPE_STRING, + keyword_iter)) + _dbus_assert_not_reached ("OOM"); + } + + if (!dbus_message_iter_close_container (&iter, &array_iter)) + _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 in test #%d", + rule_text, + should_match ? "match" : "not match", + which); + exit (1); + } + + dbus_message_unref (message); +} + +static void +test_matching_has (void) +{ + const char *rule_text = "arg0has='keyword'"; + BusMatchRule *rule; + int i; + + rule = check_parse (TRUE, rule_text); + _dbus_assert (rule != NULL); + + for (i = 0; i < _DBUS_N_ELEMENTS (contains_keyword); i++) + test_arr_match (rule_text, rule, i, contains_keyword[i], TRUE); + + for (i = 0; i < _DBUS_N_ELEMENTS (doesnt_contain_keyword); i++) + test_arr_match (rule_text, rule, i, doesnt_contain_keyword[i], FALSE); + + bus_match_rule_unref (rule); + + rule_text = "arg0has=''"; + rule = check_parse (TRUE, rule_text); + _dbus_assert (rule != NULL); + + for (i = 0; i < _DBUS_N_ELEMENTS (contains_empty); i++) + test_arr_match (rule_text, rule, i, contains_empty[i], TRUE); + + for (i = 0; i < _DBUS_N_ELEMENTS (doesnt_contain_empty); i++) + test_arr_match (rule_text, rule, i, doesnt_contain_empty[i], FALSE); + + bus_match_rule_unref (rule); +} + dbus_bool_t bus_signals_test (const DBusString *test_data_dir) { @@ -2980,6 +3190,7 @@ bus_signals_test (const DBusString *test_data_dir) test_matching (); test_path_matching (); test_matching_path_namespace (); + test_matching_has (); return TRUE; } diff --git a/bus/signals.h b/bus/signals.h index 0edfb07..b16f2ee 100644 --- a/bus/signals.h +++ b/bus/signals.h @@ -59,11 +59,6 @@ dbus_bool_t bus_match_rule_set_destination (BusMatchRule *rule, dbus_bool_t bus_match_rule_set_path (BusMatchRule *rule, 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_namespace); /* Calling this methods a client declares that it is creating a rule which * needs to eavesdrop (e.g., dbus-monitor), any other created rules not -- 2.6.2