*/
#include <config.h>
+
+#include <string.h>
+
#include "signals.h"
#include "services.h"
#include "utils.h"
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
}
}
-#ifdef DBUS_ENABLE_VERBOSE_MODE
-/* Note this function does not do escaping, so it's only
- * good for debug spew at the moment
- */
+#if defined(DBUS_ENABLE_VERBOSE_MODE) || defined(DBUS_ENABLE_STATS)
+static dbus_bool_t
+append_key_and_escaped_value (DBusString *str, const char *token, const char *value)
+{
+ const char *p = value;
+
+ if (!_dbus_string_append_printf (str, "%s='", token))
+ return FALSE;
+
+ while (*p != '\0')
+ {
+ const char *next = strchr (p, '\'');
+
+ if (next)
+ {
+ if (!_dbus_string_append_printf (str, "%.*s", (int) (next - p), p))
+ return FALSE;
+ /* Horrible escape sequence: single quote cannot be escaped inside
+ * a single quoted string. So we close the single quote, escape the
+ * single quote, and reopen a single quote.
+ */
+ if (!_dbus_string_append_printf (str, "'\\''"))
+ return FALSE;
+ p = next + 1;
+ }
+ else
+ {
+ if (!_dbus_string_append_printf (str, "%s", p))
+ return FALSE;
+ break;
+ }
+ }
+
+ if (!_dbus_string_append_byte (str, '\''))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* returns NULL if no memory */
static char*
match_rule_to_string (BusMatchRule *rule)
{
if (!_dbus_string_init (&str))
{
- char *s;
- while ((s = _dbus_strdup ("nomem")) == NULL)
- ; /* only OK for debug spew... */
- return s;
+ return NULL;
}
if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
{
- if (!_dbus_string_append_printf (&str, "type='%s'",
+ if (!append_key_and_escaped_value (&str, "type",
dbus_message_type_to_string (rule->message_type)))
goto nomem;
}
goto nomem;
}
- if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface))
+ if (!append_key_and_escaped_value (&str, "interface", rule->interface))
goto nomem;
}
goto nomem;
}
- if (!_dbus_string_append_printf (&str, "member='%s'", rule->member))
+ if (!append_key_and_escaped_value (&str, "member", rule->member))
goto nomem;
}
goto nomem;
}
- if (!_dbus_string_append_printf (&str, "path='%s'", rule->path))
+ if (!append_key_and_escaped_value (&str, "path", rule->path))
goto nomem;
}
goto nomem;
}
- if (!_dbus_string_append_printf (&str, "path_namespace='%s'", rule->path))
+ if (!append_key_and_escaped_value (&str, "path_namespace", rule->path))
goto nomem;
}
goto nomem;
}
- if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender))
+ if (!append_key_and_escaped_value (&str, "sender", rule->sender))
goto nomem;
}
goto nomem;
}
- if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination))
+ if (!append_key_and_escaped_value (&str, "destination", rule->destination))
goto nomem;
}
goto nomem;
}
- if (!_dbus_string_append_printf (&str, "eavesdrop='%s'",
+ if (!append_key_and_escaped_value (&str, "eavesdrop",
(rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING) ?
"true" : "false"))
goto nomem;
is_namespace = (rule->arg_lens[i] & BUS_MATCH_ARG_NAMESPACE) != 0;
if (!_dbus_string_append_printf (&str,
- "arg%d%s='%s'",
+ "arg%d%s",
i,
is_path ? "path" :
- is_namespace ? "namespace" : "",
- rule->args[i]))
+ is_namespace ? "namespace" : ""))
+ goto nomem;
+ if (!append_key_and_escaped_value (&str, "", rule->args[i]))
goto nomem;
}
nomem:
_dbus_string_free (&str);
- {
- char *s;
- while ((s = _dbus_strdup ("nomem")) == NULL)
- ; /* only OK for debug spew... */
- return s;
- }
+ return NULL;
}
-#endif /* DBUS_ENABLE_VERBOSE_MODE */
+#endif /* defined(DBUS_ENABLE_VERBOSE_MODE) || defined(DBUS_ENABLE_STATS) */
dbus_bool_t
bus_match_rule_set_message_type (BusMatchRule *rule,
}
dbus_bool_t
+bus_match_rule_get_client_is_eavesdropping (BusMatchRule *rule)
+{
+ if (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+dbus_bool_t
bus_match_rule_set_path (BusMatchRule *rule,
const char *path,
dbus_bool_t is_namespace)
if (end != length)
{
- if ((end + strlen ("path")) == length &&
+ int len1 = strlen ("path");
+ if ((end + len1) == length &&
_dbus_string_ends_with_c_str (&key_str, "path"))
{
is_path = 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);
+ "Key '%s' in match rule contains junk after argument number (%lu). Only 'arg%lupath' (for example) or 'arg0namespace' are valid", key, arg, arg);
goto failed;
}
}
rule->args[arg] != NULL)
{
dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
- "Argument %d matched more than once in match rule\n", key);
+ "Argument %s matched more than once in match rule\n", key);
goto failed;
}
RulePool rules_by_type[DBUS_NUM_MESSAGE_TYPES];
};
+#ifdef DBUS_ENABLE_STATS
+dbus_bool_t
+bus_match_rule_dump (BusMatchmaker *matchmaker,
+ DBusConnection *conn_filter,
+ DBusMessageIter *arr_iter)
+{
+ int i;
+
+ for (i = 0 ; i < DBUS_NUM_MESSAGE_TYPES ; i++)
+ {
+ DBusHashIter iter;
+ DBusList **list;
+ DBusList *link;
+
+ _dbus_hash_iter_init (matchmaker->rules_by_type[i].rules_by_iface, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ list = _dbus_hash_iter_get_value (&iter);
+ for (link = _dbus_list_get_first_link (list);
+ link != NULL;
+ link = _dbus_list_get_next_link (list, link))
+ {
+ BusMatchRule *rule = link->data;
+
+ if (rule->matches_go_to == conn_filter)
+ {
+ char *s = match_rule_to_string (rule);
+
+ if (s == NULL)
+ return FALSE;
+
+ if (!dbus_message_iter_append_basic (arr_iter, DBUS_TYPE_STRING, &s))
+ {
+ dbus_free (s);
+ return FALSE;
+ }
+ dbus_free (s);
+ }
+ }
+ }
+ list = &matchmaker->rules_by_type[i].rules_without_iface;
+ for (link = _dbus_list_get_first_link (list);
+ link != NULL;
+ link = _dbus_list_get_next_link (list, link))
+ {
+ BusMatchRule *rule = link->data;
+
+ if (rule->matches_go_to == conn_filter)
+ {
+ char *s = match_rule_to_string (rule);
+
+ if (s == NULL)
+ return FALSE;
+
+ if (!dbus_message_iter_append_basic (arr_iter, DBUS_TYPE_STRING, &s))
+ {
+ dbus_free (s);
+ return FALSE;
+ }
+ dbus_free (s);
+ }
+ }
+ }
+
+ return TRUE;
+}
+#endif
+
static void
rule_list_free (DBusList **rules)
{
char *s = match_rule_to_string (rule);
_dbus_verbose ("Added match rule %s to connection %p\n",
- s, rule->matches_go_to);
+ s ? s : "nomem", rule->matches_go_to);
dbus_free (s);
}
#endif
char *s = match_rule_to_string (rule);
_dbus_verbose ("Removed match rule %s for connection %p\n",
- s, rule->matches_go_to);
+ s ? s : "nomem", rule->matches_go_to);
dbus_free (s);
}
#endif
char *s = match_rule_to_string (rule);
_dbus_verbose ("Removed match rule %s for connection %p\n",
- s, rule->matches_go_to);
+ s ? s : "nomem", rule->matches_go_to);
dbus_free (s);
}
#endif
return FALSE;
if (addressed_recipient == NULL)
- {
- if (strcmp (rule->destination,
- DBUS_SERVICE_DBUS) != 0)
+ {
+ /* If the message is going to be delivered to the dbus-daemon
+ * itself, its destination will be "org.freedesktop.DBus",
+ * which we again match against the rule (see bus_dispatch()
+ * in bus/dispatch.c, which checks for o.fd.DBus first).
+ *
+ * If we are monitoring and we don't know who is going to receive
+ * the message (for instance because they haven't been activated yet),
+ * assume they will own the requested destination name and no other,
+ * and match the rule's destination against that.
+ */
+ if (strcmp (rule->destination, destination) != 0)
return FALSE;
}
else
* namespace, rather than just starting with that string,
* by checking that the matched prefix is followed by a '/'
* or the end of the path.
+ *
+ * Special case: the only valid path of length 1, "/",
+ * matches everything.
*/
- if (path[len] != '\0' && path[len] != '/')
+ if (len > 1 && path[len] != '\0' && path[len] != '/')
return FALSE;
}
char *s = match_rule_to_string (rule);
_dbus_verbose ("Checking whether message matches rule %s for connection %p\n",
- s, rule->matches_go_to);
+ s ? s : "nomem", rule->matches_go_to);
dbus_free (s);
}
#endif
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>
if (should_succeed && rule == NULL)
{
- _dbus_warn ("Failed to parse: %s: %s: \"%s\"\n",
+ _dbus_warn ("Failed to parse: %s: %s: \"%s\"",
error.name, error.message,
_dbus_string_get_const_data (&str));
exit (1);
if (!should_succeed && rule != NULL)
{
- _dbus_warn ("Failed to fail to parse: \"%s\"\n",
+ _dbus_warn ("Failed to fail to parse: \"%s\"",
_dbus_string_get_const_data (&str));
exit (1);
}
rule = check_parse (TRUE, "arg7path='/foo'");
if (rule != NULL)
{
- _dbus_assert (rule->flags = BUS_MATCH_ARGS);
+ _dbus_assert (rule->flags == BUS_MATCH_ARGS);
_dbus_assert (rule->args != NULL);
_dbus_assert (rule->args_len == 8);
_dbus_assert (rule->args[7] != NULL);
{ "type='method_call',arg3='foosh'", "arg3='foosh',type='method_call'" },
{ "arg3='fool'", "arg3='fool'" },
{ "arg0namespace='fool'", "arg0namespace='fool'" },
- { "member='food'", "member='food'" }
+ { "member='food'", "member='food'" },
+ { "member=escape", "member='escape'" },
+ { "member=icecream", "member=ice'cream'" },
+ { "arg0='comma,type=comma',type=signal", "type=signal,arg0='comma,type=comma'" },
+ { "arg0=escap\\e", "arg0='escap\\e'" },
+ { "arg0=Time: 8 o\\'clock", "arg0='Time: 8 o'\\''clock'" },
};
static void
{
BusMatchRule *first;
BusMatchRule *second;
+ char *first_str, *second_str;
+ BusMatchRule *first_reparsed, *second_reparsed;
int j;
first = check_parse (TRUE, equality_tests[i].first);
if (!match_rule_equal (first, second))
{
- _dbus_warn ("rule %s and %s should have been equal\n",
+ _dbus_warn ("rule %s and %s should have been equal",
equality_tests[i].first,
equality_tests[i].second);
exit (1);
}
+ /* Check match_rule_to_string */
+ first_str = match_rule_to_string (first);
+ _dbus_assert (first_str != NULL);
+ second_str = match_rule_to_string (second);
+ _dbus_assert (second_str != NULL);
+ _dbus_assert (strcmp (first_str, second_str) == 0);
+ first_reparsed = check_parse (TRUE, first_str);
+ _dbus_assert (first_reparsed != NULL);
+ second_reparsed = check_parse (TRUE, second_str);
+ _dbus_assert (second_reparsed != NULL);
+ _dbus_assert (match_rule_equal (first, first_reparsed));
+ _dbus_assert (match_rule_equal (second, second_reparsed));
+ bus_match_rule_unref (first_reparsed);
+ bus_match_rule_unref (second_reparsed);
+ dbus_free (first_str);
+ dbus_free (second_str);
+
bus_match_rule_unref (second);
/* Check that the rule is not equal to any of the
if (i != j)
{
second = check_parse (TRUE, equality_tests[j].second);
+ _dbus_assert (second != NULL);
if (match_rule_equal (first, second))
{
- _dbus_warn ("rule %s and %s should not have been equal\n",
+ _dbus_warn ("rule %s and %s should not have been equal",
equality_tests[i].first,
equality_tests[j].second);
exit (1);
if (matched != expected_to_match)
{
- _dbus_warn ("Expected rule %s to %s message %d, failed\n",
+ _dbus_warn ("Expected rule %s to %s message %d, failed",
rule_text, expected_to_match ?
"match" : "not match", number);
exit (1);
if (matched != should_match)
{
_dbus_warn ("Expected rule %s to %s message "
- "with first arg %s of type '%c', failed\n",
+ "with first arg %s of type '%c', failed",
rule_text,
should_match ? "match" : "not match",
path,
static const char*
path_namespace_should_match_message_1[] = {
+ "type='signal',path_namespace='/'",
"type='signal',path_namespace='/foo'",
"type='signal',path_namespace='/foo/TheObjectManager'",
NULL
static const char*
path_namespace_should_match_message_2[] = {
+ "type='signal',path_namespace='/'",
"type='signal',path_namespace='/foo/TheObjectManager'",
NULL
};
static const char*
path_namespace_should_match_message_3[] = {
+ "type='signal',path_namespace='/'",
NULL
};
NULL
};
+static const char*
+path_namespace_should_match_message_4[] = {
+ "type='signal',path_namespace='/'",
+ NULL
+};
+
+static const char*
+path_namespace_should_not_match_message_4[] = {
+ "type='signal',path_namespace='/foo/TheObjectManager'",
+ NULL
+};
+
static void
test_matching_path_namespace (void)
{
DBusMessage *message1;
DBusMessage *message2;
DBusMessage *message3;
+ DBusMessage *message4;
message1 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
_dbus_assert (message1 != NULL);
if (!dbus_message_set_path (message3, "/foo/TheObjectManagerOther"))
_dbus_assert_not_reached ("oom");
+ message4 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+ _dbus_assert (message4 != NULL);
+ if (!dbus_message_set_path (message4, "/"))
+ _dbus_assert_not_reached ("oom");
+
check_matching (message1, 1,
path_namespace_should_match_message_1,
path_namespace_should_not_match_message_1);
check_matching (message3, 3,
path_namespace_should_match_message_3,
path_namespace_should_not_match_message_3);
+ check_matching (message4, 4,
+ path_namespace_should_match_message_4,
+ path_namespace_should_not_match_message_4);
+ dbus_message_unref (message4);
dbus_message_unref (message3);
dbus_message_unref (message2);
dbus_message_unref (message1);
return TRUE;
}
-#endif /* DBUS_BUILD_TESTS */
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */