rule->refcount = 1;
rule->matches_go_to = matches_go_to;
-#ifndef DBUS_BUILD_TESTS
+#ifndef DBUS_ENABLE_EMBEDDED_TESTS
_dbus_assert (rule->matches_go_to != NULL);
#endif
if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
{
- /* FIXME make type readable */
- if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type))
+ if (!_dbus_string_append_printf (&str, "type='%s'",
+ dbus_message_type_to_string (rule->message_type)))
goto nomem;
}
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)
goto nomem;
}
+ if (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "eavesdrop='%s'",
+ (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING) ?
+ "true" : "false"))
+ goto nomem;
+ }
+
if (rule->flags & BUS_MATCH_ARGS)
{
int i;
return TRUE;
}
+void
+bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule,
+ dbus_bool_t is_eavesdropping)
+{
+ if (is_eavesdropping)
+ rule->flags |= BUS_MATCH_CLIENT_IS_EAVESDROPPING;
+ else
+ rule->flags &= ~(BUS_MATCH_CLIENT_IS_EAVESDROPPING);
+}
+
dbus_bool_t
bus_match_rule_set_path (BusMatchRule *rule,
- const char *path)
+ const char *path,
+ dbus_bool_t is_namespace)
{
char *new;
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;
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",
+ "arg0namespace='%s' is not a valid prefix of a bus name",
_dbus_string_get_const_data (value));
goto failed;
}
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;
}
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;
goto failed;
}
}
+ else if (strcmp (key, "eavesdrop") == 0)
+ {
+ /* do not detect "eavesdrop" being used more than once in rule:
+ * 1) it's not possible, it's only in the flags
+ * 2) it might be used twice to disable eavesdropping when it's
+ * automatically added (eg dbus-monitor/bustle) */
+
+ /* we accept only "true|false" as possible values */
+ if ((strcmp (value, "true") == 0))
+ {
+ bus_match_rule_set_client_is_eavesdropping (rule, TRUE);
+ }
+ else if (strcmp (value, "false") == 0)
+ {
+ bus_match_rule_set_client_is_eavesdropping (rule, FALSE);
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "eavesdrop='%s' is invalid, "
+ "it should be 'true' or 'false'\n",
+ value);
+ goto failed;
+ }
+ }
else if (strncmp (key, "arg", 3) == 0)
{
if (!bus_match_rule_parse_arg_match (rule, key, &tmp_str, error))
else
_dbus_hash_table_unref (p->rules_by_iface);
}
+ dbus_free (matchmaker);
return NULL;
}
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;
strcmp (a->destination, b->destination) != 0)
return FALSE;
+ /* we already compared the value of flags, and
+ * BUS_MATCH_CLIENT_IS_EAVESDROPPING does not have another struct member */
+
if (a->flags & BUS_MATCH_ARGS)
{
int i;
}
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,
DBusMessage *message,
BusMatchFlags already_matched)
{
+ dbus_bool_t wants_to_eavesdrop = FALSE;
int flags;
/* All features of the match rule are AND'd together,
/* Don't bother re-matching features we've already checked implicitly. */
flags = rule->flags & (~already_matched);
+ if (flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+ wants_to_eavesdrop = TRUE;
+
if (flags & BUS_MATCH_MESSAGE_TYPE)
{
_dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
}
}
+ /* Note: this part is relevant for eavesdropper rules:
+ * Two cases:
+ * 1) rule has a destination to be matched
+ * (flag BUS_MATCH_DESTINATION present). Rule will match if:
+ * - rule->destination matches the addressed_recipient
+ * AND
+ * - wants_to_eavesdrop=TRUE
+ *
+ * Note: (the case in which addressed_recipient is the actual rule owner
+ * is handled elsewere in dispatch.c:bus_dispatch_matches().
+ *
+ * 2) rule has no destination. Rule will match if:
+ * - message has no specified destination (ie broadcasts)
+ * (Note: this will rule out unicast method calls and unicast signals,
+ * fixing FDO#269748)
+ * OR
+ * - wants_to_eavesdrop=TRUE (destination-catch-all situation)
+ */
if (flags & BUS_MATCH_DESTINATION)
{
const char *destination;
destination = dbus_message_get_destination (message);
if (destination == NULL)
+ /* broadcast, but this rule specified a destination: no match */
+ return FALSE;
+
+ /* rule owner does not intend to eavesdrop: we'll deliver only msgs
+ * directed to it, NOT MATCHING */
+ if (!wants_to_eavesdrop)
return FALSE;
if (addressed_recipient == NULL)
if (!connection_is_primary_owner (addressed_recipient, rule->destination))
return FALSE;
}
+ } else { /* no destination in rule */
+ dbus_bool_t msg_is_broadcast;
+
+ _dbus_assert (rule->destination == NULL);
+
+ msg_is_broadcast = (dbus_message_get_destination (message) == NULL);
+
+ if (!wants_to_eavesdrop && !msg_is_broadcast)
+ return FALSE;
+
+ /* if we are here rule owner intends to eavesdrop
+ * OR
+ * message is being broadcasted */
}
if (flags & BUS_MATCH_PATH)
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;
* 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] != '.')
+ if (actual_arg[expected_length] != '.')
return FALSE;
}
/* otherwise we had an exact match. */
if (!_dbus_list_append (recipients_p, rule->matches_go_to))
return FALSE;
}
-#ifdef DBUS_ENABLE_VERBOSE_MODE
else
{
_dbus_verbose ("Connection already receiving this message, so not adding again\n");
}
-#endif /* DBUS_ENABLE_VERBOSE_MODE */
}
link = _dbus_list_get_next_link (rules, link);
return TRUE;
}
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
#include "test.h"
#include <stdlib.h>
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)
{
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);
rule = check_parse (FALSE, "arg0namespace=''");
_dbus_assert (rule == NULL);
- /* Two trailing periods on otherwise-valid namespaces aren't allowed. */
+ /* 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, "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);
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.'",
+ /* EXAMPLE_NAME is in all of these namespaces */
"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
/* 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'",
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
bus_signals_test (const DBusString *test_data_dir)
{
test_equality ();
test_matching ();
test_path_matching ();
+ test_matching_path_namespace ();
return TRUE;
}
-#endif /* DBUS_BUILD_TESTS */
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */