[glib] g_dbus_connection_signal_subscribe: add path and namespace matching



commit 00f6d78125a02c014bcfb31e47b082ff30ede1d6
Author: Lars Uebernickel <lars uebernickel canonical com>
Date:   Mon Apr 8 08:13:10 2013 +0200

    g_dbus_connection_signal_subscribe: add path and namespace matching
    
    https://bugzilla.gnome.org/show_bug.cgi?id=695156

 gio/gdbusconnection.c        |   93 ++++++++++++++++++++++++++++++++++++-----
 gio/gioenums.h               |    9 ++++-
 gio/tests/gdbus-connection.c |   78 +++++++++++++++++++++++++++++++++++
 3 files changed, 167 insertions(+), 13 deletions(-)
---
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index f4c63a9..0efd2c7 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -3246,6 +3246,7 @@ typedef struct
   gchar *member;
   gchar *object_path;
   gchar *arg0;
+  GDBusSignalFlags flags;
   GArray *subscribers;
 } SignalData;
 
@@ -3273,17 +3274,17 @@ signal_data_free (SignalData *signal_data)
 }
 
 static gchar *
-args_to_rule (const gchar *sender,
-              const gchar *interface_name,
-              const gchar *member,
-              const gchar *object_path,
-              const gchar *arg0,
-              gboolean     negate)
+args_to_rule (const gchar      *sender,
+              const gchar      *interface_name,
+              const gchar      *member,
+              const gchar      *object_path,
+              const gchar      *arg0,
+              GDBusSignalFlags  flags)
 {
   GString *rule;
 
   rule = g_string_new ("type='signal'");
-  if (negate)
+  if (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE)
     g_string_prepend_c (rule, '-');
   if (sender != NULL)
     g_string_append_printf (rule, ",sender='%s'", sender);
@@ -3293,8 +3294,16 @@ args_to_rule (const gchar *sender,
     g_string_append_printf (rule, ",member='%s'", member);
   if (object_path != NULL)
     g_string_append_printf (rule, ",path='%s'", object_path);
+
   if (arg0 != NULL)
-    g_string_append_printf (rule, ",arg0='%s'", arg0);
+    {
+      if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH)
+        g_string_append_printf (rule, ",arg0path='%s'", arg0);
+      else if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)
+        g_string_append_printf (rule, ",arg0namespace='%s'", arg0);
+      else
+        g_string_append_printf (rule, ",arg0='%s'", arg0);
+    }
 
   return g_string_free (rule, FALSE);
 }
@@ -3417,6 +3426,11 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data)
  * tracking the name owner of the well-known name and use that when
  * processing the received signal.
  *
+ * If one of %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE or
+ * %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH are given, @arg0 is
+ * interpreted as part of a namespace or path.  The first argument
+ * of a signal is matched against that part as specified by D-Bus.
+ *
  * Returns: A subscription identifier that can be used with g_dbus_connection_signal_unsubscribe().
  *
  * Since: 2.26
@@ -3456,6 +3470,8 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
   g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), 0);
   g_return_val_if_fail (callback != NULL, 0);
   g_return_val_if_fail (check_initialized (connection), 0);
+  g_return_val_if_fail (!((flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) && (flags & 
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)), 0);
+  g_return_val_if_fail (!(arg0 == NULL && (flags & (G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH | 
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE))), 0);
 
   CONNECTION_LOCK (connection);
 
@@ -3467,8 +3483,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
    * the usual way, but the '-' prevents the match rule from ever
    * actually being send to the bus (either for add or remove).
    */
-  rule = args_to_rule (sender, interface_name, member, object_path, arg0,
-                       (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE) != 0);
+  rule = args_to_rule (sender, interface_name, member, object_path, arg0, flags);
 
   if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0))
     sender_unique_name = sender;
@@ -3498,6 +3513,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
   signal_data->member                = g_strdup (member);
   signal_data->object_path           = g_strdup (object_path);
   signal_data->arg0                  = g_strdup (arg0);
+  signal_data->flags                 = flags;
   signal_data->subscribers           = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
   g_array_append_val (signal_data->subscribers, subscriber);
 
@@ -3733,6 +3749,43 @@ signal_instance_free (SignalInstance *signal_instance)
   g_free (signal_instance);
 }
 
+static gboolean
+namespace_rule_matches (const gchar *namespace,
+                        const gchar *name)
+{
+  gint len_namespace;
+  gint len_name;
+
+  len_namespace = strlen (namespace);
+  len_name = strlen (name);
+
+  if (len_name < len_namespace)
+    return FALSE;
+
+  if (memcmp (namespace, name, len_namespace) != 0)
+    return FALSE;
+
+  return len_namespace == len_name || name[len_namespace] == '.';
+}
+
+static gboolean
+path_rule_matches (const gchar *path_a,
+                   const gchar *path_b)
+{
+  gint len_a, len_b;
+
+  len_a = strlen (path_a);
+  len_b = strlen (path_b);
+
+  if (len_a < len_b && path_a[len_a - 1] != '/')
+    return FALSE;
+
+  if (len_b < len_a && path_b[len_b - 1] != '/')
+    return FALSE;
+
+  return memcmp (path_a, path_b, MIN (len_a, len_b)) == 0;
+}
+
 /* called in GDBusWorker thread WITH lock held */
 static void
 schedule_callbacks (GDBusConnection *connection,
@@ -3786,8 +3839,24 @@ schedule_callbacks (GDBusConnection *connection,
       if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0)
         continue;
 
-      if (signal_data->arg0 != NULL && g_strcmp0 (signal_data->arg0, arg0) != 0)
-        continue;
+      if (signal_data->arg0 != NULL)
+        {
+          if (arg0 == NULL)
+            continue;
+
+          if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)
+            {
+              if (!namespace_rule_matches (signal_data->arg0, arg0))
+                continue;
+            }
+          else if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH)
+            {
+              if (!path_rule_matches (signal_data->arg0, arg0))
+                continue;
+            }
+          else if (!g_str_equal (signal_data->arg0, arg0))
+            continue;
+        }
 
       for (m = 0; m < signal_data->subscribers->len; m++)
         {
diff --git a/gio/gioenums.h b/gio/gioenums.h
index 3831ec9..9767226 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1272,6 +1272,11 @@ typedef enum
  * @G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE: Don't actually send the AddMatch
  * D-Bus call for this signal subscription.  This gives you more control
  * over which match rules you add (but you must add them manually).
+ * @G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE: Match first arguments that
+ * contain a bus or interface name with the given namespace.
+ * @G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH: Match first arguments that
+ * contain an object path that is either equivalent to the given path,
+ * or one of the paths is a subpath of the other.
  *
  * Flags used when subscribing to signals via g_dbus_connection_signal_subscribe().
  *
@@ -1280,7 +1285,9 @@ typedef enum
 typedef enum /*< flags >*/
 {
   G_DBUS_SIGNAL_FLAGS_NONE = 0,
-  G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0)
+  G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0),
+  G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE = (1<<1),
+  G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH = (1<<2)
 } GDBusSignalFlags;
 
 /**
diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c
index e48d61f..f57c776 100644
--- a/gio/tests/gdbus-connection.c
+++ b/gio/tests/gdbus-connection.c
@@ -713,6 +713,83 @@ test_connection_signals (void)
   session_bus_down ();
 }
 
+static void
+test_match_rule (GDBusConnection  *connection,
+                 GDBusSignalFlags  flags,
+                 gchar            *arg0_rule,
+                 gchar            *arg0,
+                 gboolean          should_match)
+{
+  guint subscription_ids[2];
+  gint emissions = 0;
+  gint matches = 0;
+  GError *error = NULL;
+
+  subscription_ids[0] = g_dbus_connection_signal_subscribe (connection,
+                                                            NULL, "org.gtk.ExampleInterface", "Foo", "/",
+                                                            NULL,
+                                                            G_DBUS_SIGNAL_FLAGS_NONE,
+                                                            test_connection_signal_handler,
+                                                            &emissions, NULL);
+  subscription_ids[1] = g_dbus_connection_signal_subscribe (connection,
+                                                            NULL, "org.gtk.ExampleInterface", "Foo", "/",
+                                                            arg0_rule,
+                                                            flags,
+                                                            test_connection_signal_handler,
+                                                            &matches, NULL);
+  g_assert_cmpint (subscription_ids[0], !=, 0);
+  g_assert_cmpint (subscription_ids[1], !=, 0);
+
+  g_dbus_connection_emit_signal (connection,
+                                 NULL, "/", "org.gtk.ExampleInterface",
+                                 "Foo", g_variant_new ("(s)", arg0),
+                                 &error);
+  g_assert_no_error (error);
+
+  /* synchronously ping a non-existent method to make sure the signals are dispatched */
+  g_dbus_connection_call_sync (connection, "org.gtk.ExampleInterface", "/", "org.gtk.ExampleInterface",
+                               "Bar", g_variant_new ("()"), G_VARIANT_TYPE_UNIT, G_DBUS_CALL_FLAGS_NONE,
+                               -1, NULL, NULL);
+
+  while (g_main_context_iteration (NULL, FALSE))
+    ;
+
+  g_assert_cmpint (emissions, ==, 1);
+  g_assert_cmpint (matches, ==, should_match ? 1 : 0);
+
+  g_dbus_connection_signal_unsubscribe (connection, subscription_ids[0]);
+  g_dbus_connection_signal_unsubscribe (connection, subscription_ids[1]);
+}
+
+static void
+test_connection_signal_match_rules (void)
+{
+  GDBusConnection *con;
+
+  session_bus_up ();
+  con = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_NONE, "foo", "foo", TRUE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_NONE, "foo", "bar", FALSE);
+
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "", FALSE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org", FALSE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk", TRUE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk.Example", TRUE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk+", FALSE);
+
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/", "/", TRUE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/", "", FALSE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk/Example", TRUE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/", "/org/gtk/Example", TRUE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk/", TRUE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk", FALSE);
+  test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk+", "/org/gtk", FALSE);
+
+  g_object_unref (con);
+  session_bus_down ();
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 typedef struct
@@ -1153,6 +1230,7 @@ main (int   argc,
   g_test_add_func ("/gdbus/connection/life-cycle", test_connection_life_cycle);
   g_test_add_func ("/gdbus/connection/send", test_connection_send);
   g_test_add_func ("/gdbus/connection/signals", test_connection_signals);
+  g_test_add_func ("/gdbus/connection/signal-match-rules", test_connection_signal_match_rules);
   g_test_add_func ("/gdbus/connection/filter", test_connection_filter);
   g_test_add_func ("/gdbus/connection/serials", test_connection_serials);
   return g_test_run();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]