summaryrefslogtreecommitdiffstats
path: root/bus/matchrule.c
diff options
context:
space:
mode:
Diffstat (limited to 'bus/matchrule.c')
-rw-r--r--bus/matchrule.c672
1 files changed, 672 insertions, 0 deletions
diff --git a/bus/matchrule.c b/bus/matchrule.c
new file mode 100644
index 0000000..6884fd6
--- /dev/null
+++ b/bus/matchrule.c
@@ -0,0 +1,672 @@
+/* vim:set et sts=4: */
+/* IBus - The Input Bus
+ * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <string.h>
+#include "matchrule.h"
+
+#define BUS_CONFIG_PROXY_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), BUS_TYPE_CONFIG_PROXY, BusMatchRulePrivate))
+
+
+static void bus_match_rule_class_init (BusMatchRuleClass *klass);
+static void bus_match_rule_init (BusMatchRule *rule);
+static void bus_match_rule_destroy (BusMatchRule *rule);
+
+static IBusObjectClass *parent_class = NULL;
+
+GType
+bus_match_rule_get_type (void)
+{
+ static GType type = 0;
+
+ static const GTypeInfo type_info = {
+ sizeof (BusMatchRuleClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) bus_match_rule_class_init,
+ NULL, /* class finalize */
+ NULL, /* class data */
+ sizeof (BusMatchRule),
+ 0,
+ (GInstanceInitFunc) bus_match_rule_init,
+ };
+
+ if (type == 0) {
+ type = g_type_register_static (IBUS_TYPE_OBJECT,
+ "BusMatchRule",
+ &type_info,
+ (GTypeFlags)0);
+ }
+ return type;
+}
+
+static void
+bus_match_rule_class_init (BusMatchRuleClass *klass)
+{
+ IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
+
+ parent_class = (IBusObjectClass *) g_type_class_peek_parent (klass);
+
+ ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_match_rule_destroy;
+}
+
+static void
+bus_match_rule_init (BusMatchRule *rule)
+{
+ rule->flags = 0;
+ rule->message_type = DBUS_MESSAGE_TYPE_INVALID;
+ rule->interface = NULL;
+ rule->member = NULL;
+ rule->sender = NULL;
+ rule->destination = NULL;
+ rule->path = NULL;
+ rule->args = g_array_new (TRUE, TRUE, sizeof (gchar *));
+}
+
+static void
+bus_match_rule_destroy (BusMatchRule *rule)
+{
+ g_free (rule->interface);
+ g_free (rule->member);
+ g_free (rule->sender);
+ g_free (rule->destination);
+ g_free (rule->path);
+
+ gint i;
+ GList *link;
+
+ for (i = 0; i < rule->args->len; i++) {
+ g_free (g_array_index (rule->args, gchar *, i));
+ }
+ g_array_free (rule->args, TRUE);
+
+ for (link = rule->recipients; link != NULL; link = link->next) {
+ BusRecipient *recipient = (BusRecipient *) link->data;
+ g_object_unref (recipient->connection);
+ g_slice_free (BusRecipient, recipient);
+ }
+ g_list_free (rule->recipients);
+
+ IBUS_OBJECT_CLASS(parent_class)->destroy (IBUS_OBJECT (rule));
+}
+
+
+typedef struct _Token {
+ gchar *key;
+ gchar *value;
+} Token;
+
+#define SKIP_WHITE(a) \
+ while (*(a) == ' ' || *(a) == '\t') { (a)++; }
+#define IS_ALPHA(a) \
+ ((*(a) >= 'a' && *(a) <= 'z') || (*(a) >= 'A' && *(a) <= 'Z'))
+#define IS_NUMBER(a) \
+ (*(a) >= '0' && *(a) <= '9')
+
+static gchar *
+find_key (const gchar **p)
+{
+ GString *text;
+
+ text = g_string_new ("");
+
+ SKIP_WHITE(*p)
+ if (!IS_ALPHA (*p))
+ goto failed;
+
+ g_string_append_c (text, **p);
+ (*p) ++;
+
+ while (IS_ALPHA (*p) || IS_NUMBER (*p)) {
+ g_string_append_c (text, **p);
+ (*p) ++;
+ }
+
+ return g_string_free (text, FALSE);
+
+failed:
+ g_string_free (text, TRUE);
+ return NULL;
+
+}
+
+static gchar *
+find_value (const gchar **p)
+{
+ GString *text;
+
+ text = g_string_new ("");
+
+ SKIP_WHITE (*p);
+
+ if (**p != '\'')
+ goto failed;
+ (*p) ++;
+
+ while (**p != '\'') {
+ if (**p == '\0')
+ goto failed;
+ if (**p == '\\')
+ (*p) ++;
+ g_string_append_c (text, **p);
+ (*p) ++;
+ }
+ (*p) ++;
+
+ return g_string_free (text, FALSE);
+
+failed:
+ g_string_free (text, TRUE);
+ return NULL;
+}
+
+static Token *
+tokenize_rule (const gchar *text)
+{
+ GArray *tokens;
+ Token token;
+ const gchar *p;
+ gint i;
+
+ tokens = g_array_new (TRUE, TRUE, sizeof (Token));
+
+ p = text;
+
+ while (*p != '\0') {
+ gchar *key;
+ gchar *value;
+
+ SKIP_WHITE (p);
+ key = find_key (&p);
+ if (key == NULL)
+ goto failed;
+ SKIP_WHITE (p);
+ if (*p != '=')
+ goto failed;
+ p ++;
+ SKIP_WHITE (p);
+ value = find_value (&p);
+ if (value == NULL) {
+ g_free (key);
+ goto failed;
+ }
+ SKIP_WHITE (p);
+ if (*p != ',' && *p != '\0') {
+ g_free (key);
+ g_free (value);
+ goto failed;
+ }
+
+ if (*p == ',')
+ p ++;
+ token.key = key;
+ token.value = value;
+ g_array_append_val (tokens, token);
+ }
+
+ return (Token *)g_array_free (tokens, FALSE);
+
+failed:
+
+ for (i = 0; i < tokens->len; i++) {
+ Token *p = &g_array_index (tokens, Token, i);
+ g_free (p->key);
+ g_free (p->value);
+ }
+
+ g_array_free (tokens, TRUE);
+ return NULL;
+}
+
+static void
+tokens_free (Token *tokens)
+{
+ Token *p;
+ p = tokens;
+
+ while (p != NULL && p->key != NULL) {
+ g_free (p->key);
+ g_free (p->value);
+ p ++;
+ }
+ g_free (tokens);
+}
+
+static gboolean
+_atoi (const gchar *text, gint *i)
+{
+ const gchar *p = text;
+ *i = 0;
+ while (*p != '\0') {
+ if (!IS_NUMBER(p))
+ return FALSE;
+ *i = (*i) * 10 - '0' + *p;
+ p ++;
+ }
+ return TRUE;
+}
+
+BusMatchRule *
+bus_match_rule_new (const gchar *text)
+{
+ g_assert (text != NULL);
+
+ Token *tokens, *p;
+ BusMatchRule *rule;
+
+ rule = BUS_MATCH_RULE (g_object_new (BUS_TYPE_MATCH_RULE, NULL));
+
+ /* parse rule */
+ tokens = tokenize_rule (text);
+
+ for (p = tokens; p != NULL && p->key != 0; p++) {
+ if (g_strcmp0 (p->key, "type") == 0) {
+ if (g_strcmp0 (p->value, "signal") == 0) {
+ bus_match_rule_set_message_type (rule, DBUS_MESSAGE_TYPE_SIGNAL);
+ }
+ else if (g_strcmp0 (p->value, "method_call") == 0) {
+ bus_match_rule_set_message_type (rule, DBUS_MESSAGE_TYPE_METHOD_CALL);
+ }
+ else if (g_strcmp0 (p->value, "method_return") == 0) {
+ bus_match_rule_set_message_type (rule, DBUS_MESSAGE_TYPE_METHOD_RETURN);
+ }
+ else if (g_strcmp0 (p->value, "error") == 0) {
+ bus_match_rule_set_message_type (rule, DBUS_MESSAGE_TYPE_ERROR);
+ }
+ else
+ goto failed;
+ }
+ else if (g_strcmp0 (p->key, "sender") == 0) {
+ bus_match_rule_set_sender (rule, p->value);
+ }
+ else if (g_strcmp0 (p->key, "interface") == 0) {
+ bus_match_rule_set_interface (rule, p->value);
+ }
+ else if (g_strcmp0 (p->key, "member") == 0) {
+ bus_match_rule_set_member (rule, p->value);
+ }
+ else if (g_strcmp0 (p->key, "path") == 0) {
+ bus_match_rule_set_path (rule, p->value);
+ }
+ else if (g_strcmp0 (p->key, "destination") == 0) {
+ bus_match_rule_set_destination (rule, p->value);
+ }
+ else if (strncmp (p->key, "arg", 3) == 0) {
+ gint i;
+ if (! _atoi (p->key + 3, &i))
+ goto failed;
+ bus_match_rule_set_arg (rule, i, p->value);
+ }
+ else
+ goto failed;
+ }
+
+ tokens_free (tokens);
+ return rule;
+
+failed:
+ tokens_free (tokens);
+ g_object_unref (rule);
+ return NULL;
+}
+
+gboolean
+bus_match_rule_set_message_type (BusMatchRule *rule,
+ gint type)
+{
+ g_assert (rule != NULL);
+ g_assert (type == DBUS_MESSAGE_TYPE_SIGNAL ||
+ type == DBUS_MESSAGE_TYPE_METHOD_CALL ||
+ type == DBUS_MESSAGE_TYPE_METHOD_RETURN ||
+ type == DBUS_MESSAGE_TYPE_ERROR);
+
+ rule->flags |= MATCH_TYPE;
+ rule->message_type = type;
+
+ return TRUE;
+}
+
+gboolean
+bus_match_rule_set_sender (BusMatchRule *rule,
+ const gchar *sender)
+{
+ g_assert (rule != NULL);
+ g_assert (sender != NULL);
+
+ rule->flags |= MATCH_SENDER;
+
+ g_free (rule->sender);
+ rule->sender = g_strdup (sender);
+
+ return TRUE;
+}
+
+gboolean
+bus_match_rule_set_interface (BusMatchRule *rule,
+ const gchar *interface)
+{
+ g_assert (rule != NULL);
+ g_assert (interface != NULL);
+
+ rule->flags |= MATCH_INTERFACE;
+
+ g_free (rule->interface);
+ rule->interface = g_strdup (interface);
+ return TRUE;
+}
+
+gboolean
+bus_match_rule_set_member (BusMatchRule *rule,
+ const gchar *member)
+{
+ g_assert (rule != NULL);
+ g_assert (member != NULL);
+
+ rule->flags |= MATCH_MEMBER;
+
+ g_free (rule->member);
+ rule->member = g_strdup (member);
+
+ return TRUE;
+}
+
+gboolean
+bus_match_rule_set_path (BusMatchRule *rule,
+ const gchar *path)
+{
+ g_assert (rule != NULL);
+ g_assert (path != NULL);
+
+ rule->flags |= MATCH_PATH;
+
+ g_free (rule->path);
+ rule->path = g_strdup (path);
+
+ return TRUE;
+}
+
+gboolean
+bus_match_rule_set_destination (BusMatchRule *rule,
+ const gchar *dest)
+{
+ g_assert (rule != NULL);
+ g_assert (dest != NULL);
+
+ rule->flags |= MATCH_DESTINATION;
+
+ g_free (rule->destination);
+ rule->destination = g_strdup (dest);
+
+ return TRUE;
+}
+
+gboolean
+bus_match_rule_set_arg (BusMatchRule *rule,
+ guint arg_i,
+ const gchar *arg)
+{
+ g_assert (rule != NULL);
+ g_assert (arg != NULL);
+
+ rule->flags |= MATCH_ARGS;
+
+ if (arg_i >= rule->args->len) {
+ g_array_set_size (rule->args, arg_i + 1);
+ }
+
+ g_free (g_array_index (rule->args, gchar *, arg_i));
+ g_array_index (rule->args, gchar *, arg_i) = g_strdup (arg);
+ return TRUE;
+}
+
+gboolean
+bus_match_rule_match (BusMatchRule *rule,
+ DBusMessage *message)
+{
+ g_assert (rule != NULL);
+ g_assert (message != NULL);
+
+ if (rule->flags & MATCH_TYPE) {
+ if (ibus_message_get_type (message) != rule->message_type)
+ return FALSE;
+ }
+
+ if (rule->flags & MATCH_INTERFACE) {
+ if (g_strcmp0 (ibus_message_get_interface (message), rule->interface) != 0)
+ return FALSE;
+ }
+
+ if (rule->flags & MATCH_MEMBER) {
+ if (g_strcmp0 (ibus_message_get_member (message), rule->member) != 0)
+ return FALSE;
+ }
+
+ if (rule->flags & MATCH_SENDER) {
+ if (g_strcmp0 (ibus_message_get_sender (message), rule->sender) != 0)
+ return FALSE;
+ }
+
+ if (rule->flags & MATCH_DESTINATION) {
+ if (g_strcmp0 (ibus_message_get_destination (message), rule->destination) != 0)
+ return FALSE;
+ }
+
+ if (rule->flags & MATCH_PATH) {
+ if (g_strcmp0 (ibus_message_get_path (message), rule->path) != 0)
+ return FALSE;
+ }
+
+ if (rule->flags & MATCH_ARGS) {
+ guint i;
+ DBusMessageIter iter;
+
+ ibus_message_iter_init (message, &iter);
+
+ for (i = 0; i < rule->args->len; i++) {
+ gchar *arg = g_array_index (rule->args, gchar *, i);
+ if (arg != NULL) {
+ gint type;
+ gchar *value;
+
+ type = ibus_message_iter_get_arg_type (&iter);
+ if (type != G_TYPE_STRING && type != IBUS_TYPE_OBJECT_PATH)
+ return FALSE;
+
+ ibus_message_iter_get_basic (&iter, &value);
+
+ if (g_strcmp0 (arg, value) != 0)
+ return FALSE;
+ }
+ ibus_message_iter_next (&iter);
+ }
+ }
+ return TRUE;
+}
+
+gboolean
+bus_match_rule_is_equal (BusMatchRule *a,
+ BusMatchRule *b)
+{
+ g_assert (a != NULL);
+ g_assert (b != NULL);
+
+ if (a->flags != b->flags)
+ return FALSE;
+
+ if (a->flags & MATCH_TYPE) {
+ if (a->message_type != b->message_type)
+ return FALSE;
+ }
+
+ if (a->flags & MATCH_INTERFACE) {
+ if (g_strcmp0 (a->interface, b->interface) != 0)
+ return FALSE;
+ }
+
+ if (a->flags & MATCH_MEMBER) {
+ if (g_strcmp0 (a->member, b->member) != 0)
+ return FALSE;
+ }
+
+ if (a->flags & MATCH_SENDER) {
+ if (g_strcmp0 (a->sender, b->sender) != 0)
+ return FALSE;
+ }
+
+ if (a->flags & MATCH_DESTINATION) {
+ if (g_strcmp0 (a->destination, b->destination) != 0)
+ return FALSE;
+ }
+
+ if (a->flags & MATCH_PATH) {
+ if (g_strcmp0 (a->path, b->path) != 0)
+ return FALSE;
+ }
+
+ if (a->flags & MATCH_ARGS) {
+ if (a->args->len != b->args->len)
+ return FALSE;
+
+ gint i;
+
+ for (i = 0; i < a->args->len; i++) {
+ if (g_strcmp0 (g_array_index (a->args, gchar *, i),
+ g_array_index (b->args, gchar *, i)) != 0)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_connection_destroy_cb (BusConnection *connection,
+ BusMatchRule *rule)
+{
+ g_assert (BUS_IS_MATCH_RULE (rule));
+ g_assert (BUS_IS_CONNECTION (connection));
+
+ GList *link;
+ BusRecipient *recipient;
+
+ for (link = rule->recipients; link != NULL; link = link->next) {
+ recipient = (BusRecipient *)link->data;
+
+ if (recipient->connection == connection) {
+ rule->recipients = g_list_remove_link (rule->recipients, link);
+ g_object_unref (connection);
+ g_slice_free (BusRecipient, recipient);
+ return;
+ }
+
+ if (rule->recipients == NULL) {
+ ibus_object_destroy (IBUS_OBJECT (rule));
+ }
+ }
+ g_assert_not_reached ();
+}
+
+void
+bus_match_rule_add_recipient (BusMatchRule *rule,
+ BusConnection *connection)
+{
+ g_assert (BUS_IS_MATCH_RULE (rule));
+ g_assert (BUS_IS_CONNECTION (connection));
+
+ GList *link;
+ BusRecipient *recipient;
+
+ for (link = rule->recipients; link != NULL; link = link->next) {
+ recipient = (BusRecipient *) link->data;
+ if (connection == recipient->connection) {
+ recipient->refcount ++;
+ return;
+ }
+ }
+
+ recipient = g_slice_new (BusRecipient);
+
+ g_object_ref (connection);
+ recipient->connection = connection;
+ recipient->refcount = 1;
+
+ rule->recipients = g_list_append (rule->recipients, recipient);
+ g_signal_connect (connection,
+ "destroy",
+ G_CALLBACK (_connection_destroy_cb),
+ rule);
+}
+
+void
+bus_match_rule_remove_recipient (BusMatchRule *rule,
+ BusConnection *connection)
+{
+ g_assert (BUS_IS_MATCH_RULE (rule));
+ g_assert (BUS_IS_CONNECTION (connection));
+
+ GList *link;
+ BusRecipient *recipient;
+
+ for (link = rule->recipients; link != NULL; link = link->next) {
+ recipient = (BusRecipient *) link->data;
+ if (connection == recipient->connection) {
+ recipient->refcount --;
+ if (recipient->refcount == 0) {
+ rule->recipients = g_list_remove_link (rule->recipients, link);
+ g_slice_free (BusRecipient, recipient);
+ g_signal_handlers_disconnect_by_func (connection,
+ G_CALLBACK (_connection_destroy_cb),
+ rule);
+ g_object_unref (connection);
+ }
+
+ if (rule->recipients == NULL ) {
+ ibus_object_destroy (IBUS_OBJECT(rule));
+ }
+ return;
+ }
+ }
+
+ g_warning ("Remove recipient failed");
+}
+
+gboolean
+bus_match_rule_get_recipients (BusMatchRule *rule,
+ DBusMessage *message,
+ GList **recipients)
+{
+ g_assert (BUS_IS_MATCH_RULE (rule));
+ g_assert (message != NULL);
+ g_assert (recipients != NULL);
+
+ GList *link;
+
+ if (!bus_match_rule_match (rule, message))
+ return FALSE;
+
+ for (link = rule->recipients; link != NULL; link = link->next) {
+ BusRecipient *recipient = (BusRecipient *) link->data;
+
+ g_object_ref (recipient->connection);
+ *recipients = g_list_append (*recipients, recipient->connection);
+ }
+
+ return TRUE;
+}
+