diff options
Diffstat (limited to 'bus/dbusimpl.c')
-rw-r--r-- | bus/dbusimpl.c | 1174 |
1 files changed, 1174 insertions, 0 deletions
diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c new file mode 100644 index 0000000..6a70f89 --- /dev/null +++ b/bus/dbusimpl.c @@ -0,0 +1,1174 @@ +/* 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 <ibusinternal.h> +#include <ibusmarshalers.h> +#include "dbusimpl.h" +#include "connection.h" +#include "matchrule.h" + +#define BUS_DBUS_IMPL_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), BUS_TYPE_DBUS_IMPL, BusDBusImplPrivate)) + +enum { + NAME_ACQUIRED, + NAME_LOST, + NAME_OWNER_CHANGED, + LAST_SIGNAL, +}; + +enum { + PROP_0, +}; + + +/* IBusDBusImplPriv */ +struct _BusDBusImplPrivate { + GHashTable *unique_names; + GHashTable *names; + GHashTable *objects; + GList *connections; + GList *rules; + gint id; +}; + +typedef struct _BusDBusImplPrivate BusDBusImplPrivate; + +static guint dbus_signals[LAST_SIGNAL] = { 0 }; + +/* functions prototype */ +static void bus_dbus_impl_class_init (BusDBusImplClass *klass); +static void bus_dbus_impl_init (BusDBusImpl *dbus); +static void bus_dbus_impl_dispose (BusDBusImpl *dbus); +static gboolean bus_dbus_impl_ibus_message (BusDBusImpl *dbus, + BusConnection *connection, + IBusMessage *message); +static void bus_dbus_impl_name_owner_changed + (BusDBusImpl *dbus, + gchar *name, + gchar *old_name, + gchar *new_name); + +static IBusServiceClass *parent_class = NULL; + +GType +bus_dbus_impl_get_type (void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof (BusDBusImplClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) bus_dbus_impl_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (BusDBusImpl), + 0, + (GInstanceInitFunc) bus_dbus_impl_init, + }; + + if (type == 0) { + type = g_type_register_static (IBUS_TYPE_SERVICE, + "BusDBusImpl", + &type_info, + (GTypeFlags) 0); + } + return type; +} + +BusDBusImpl * +bus_dbus_impl_get_default (void) +{ + // BusDBusImplPrivate *priv; + static BusDBusImpl *dbus = NULL; + + if (dbus == NULL) { + dbus = (BusDBusImpl *) g_object_new (BUS_TYPE_DBUS_IMPL, + "path", DBUS_PATH_DBUS, + NULL); + } + + return dbus; +} + +static void +bus_dbus_impl_class_init (BusDBusImplClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + IBusServiceClass *service_class = IBUS_SERVICE_CLASS (klass); + + parent_class = (IBusServiceClass *) g_type_class_peek_parent (klass); + + g_type_class_add_private (klass, sizeof (BusDBusImplPrivate)); + + gobject_class->dispose = (GObjectFinalizeFunc) bus_dbus_impl_dispose; + + service_class->ibus_message = (ServiceIBusMessageFunc) bus_dbus_impl_ibus_message; + + klass->name_owner_changed = bus_dbus_impl_name_owner_changed; + + /* install signals */ + dbus_signals[NAME_OWNER_CHANGED] = + g_signal_new (I_("name-owner-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (BusDBusImplClass, name_owner_changed), + NULL, NULL, + ibus_marshal_VOID__STRING_STRING_STRING, + G_TYPE_NONE, + 3, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + +} + +static void +bus_dbus_impl_init (BusDBusImpl *dbus) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + priv->unique_names = g_hash_table_new (g_str_hash, g_str_equal); + priv->names = g_hash_table_new (g_str_hash, g_str_equal); + priv->objects = g_hash_table_new (g_str_hash, g_str_equal); + priv->connections = NULL; + priv->rules = NULL; + priv->id = 1; + + g_object_ref (dbus); + g_hash_table_insert (priv->objects, DBUS_PATH_DBUS, dbus); +} + +static void +bus_dbus_impl_dispose (BusDBusImpl *dbus) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + G_OBJECT_CLASS(parent_class)->dispose (G_OBJECT (dbus)); +} + + +/* introspectable interface */ +static IBusMessage * +_dbus_introspect (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + static const gchar *introspect = + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "<node>\n" + " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" + " <method name=\"Introspect\">\n" + " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n" + " </method>\n" + " </interface>\n" + " <interface name=\"org.freedesktop.DBus\">\n" + " <method name=\"Hello\">\n" + " <arg direction=\"out\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"RequestName\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"in\" type=\"u\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"ReleaseName\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"StartServiceByName\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"in\" type=\"u\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"UpdateActivationEnvironment\">\n" + " <arg direction=\"in\" type=\"a{ss}\"/>\n" + " </method>\n" + " <method name=\"NameHasOwner\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"ListNames\">\n" + " <arg direction=\"out\" type=\"as\"/>\n" + " </method>\n" + " <method name=\"ListActivatableNames\">\n" + " <arg direction=\"out\" type=\"as\"/>\n" + " </method>\n" + " <method name=\"AddMatch\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"RemoveMatch\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"GetNameOwner\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"s\"/>\n" + " </method>\n" + " <method name=\"ListQueuedOwners\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"as\"/>\n" + " </method>\n" + " <method name=\"GetConnectionUnixUser\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"GetConnectionUnixProcessID\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"u\"/>\n" + " </method>\n" + " <method name=\"GetAdtAuditSessionData\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"ay\"/>\n" + " </method>\n" + " <method name=\"GetConnectionSELinuxSecurityContext\">\n" + " <arg direction=\"in\" type=\"s\"/>\n" + " <arg direction=\"out\" type=\"ay\"/>\n" + " </method>\n" + " <method name=\"ReloadConfig\">\n" + " </method>\n" + " <method name=\"GetId\">\n" + " <arg direction=\"out\" type=\"s\"/>\n" + " </method>\n" + " <signal name=\"NameOwnerChanged\">\n" + " <arg type=\"s\"/>\n" + " <arg type=\"s\"/>\n" + " <arg type=\"s\"/>\n" + " </signal>\n" + " <signal name=\"NameLost\">\n" + " <arg type=\"s\"/>\n" + " </signal>\n" + " <signal name=\"NameAcquired\">\n" + " <arg type=\"s\"/>\n" + " </signal>\n" + " </interface>\n" + "</node>\n"; + + IBusMessage *reply_message; + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_STRING, &introspect, + G_TYPE_INVALID); + + return reply_message; +} + + +/* dbus interface */ +static IBusMessage * +_dbus_no_implement (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + IBusMessage *reply_message; + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_UNKNOWN_METHOD, + "IBus does not support %s.", + ibus_message_get_member (message)); + return reply_message; +} + + +static IBusMessage * +_dbus_hello (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + + if (bus_connection_get_unique_name (connection) != NULL) { + reply_message = ibus_message_new_error (message, + DBUS_ERROR_FAILED, + "Already handled an Hello message"); + } + else { + gchar *name; + + name = g_strdup_printf (":1.%d", priv->id ++); + bus_connection_set_unique_name (connection, name); + g_free (name); + + name = (gchar *) bus_connection_get_unique_name (connection); + g_hash_table_insert (priv->unique_names, name, connection); + + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_STRING, &name, + G_TYPE_INVALID); + + ibus_connection_send ((IBusConnection *) connection, reply_message); + ibus_message_unref (reply_message); + ibus_connection_flush ((IBusConnection *) connection); + reply_message = NULL; + + g_signal_emit (dbus, + dbus_signals[NAME_OWNER_CHANGED], + 0, + name, + "", + name); + + } + + return reply_message; +} + +static IBusMessage * +_dbus_list_names (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusMessageIter iter, sub_iter; + GList *name, *names; + + reply_message = ibus_message_new_method_return (message); + + ibus_message_iter_init_append (message, &iter); + ibus_message_iter_open_container (&iter, IBUS_TYPE_ARRAY, "s", &sub_iter); + + // append unique names + names = g_hash_table_get_keys (priv->unique_names); + + names = g_list_sort (names, (GCompareFunc) g_strcmp0); + for (name = names; name != NULL; name = name->next) { + ibus_message_iter_append (&sub_iter, G_TYPE_STRING, &(name->data)); + } + g_list_free (names); + + // append well-known names + names = g_hash_table_get_keys (priv->names); + names = g_list_sort (names, (GCompareFunc) g_strcmp0); + for (name = names; name != NULL; name = name->next) { + ibus_message_iter_append (&sub_iter, G_TYPE_STRING, &(name->data)); + } + g_list_free (names); + + ibus_message_iter_close_container (&iter, &sub_iter); + + return reply_message; +} + +static IBusMessage * +_dbus_name_has_owner (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + gchar *name; + gboolean retval; + gboolean has_owner; + IBusMessage *reply_message; + IBusError *error; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &name, + G_TYPE_INVALID); + + if (! retval) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + if (name[0] == ':') { + has_owner = g_hash_table_lookup (priv->unique_names, name) != NULL; + } + else { + has_owner = g_hash_table_lookup (priv->names, name) != NULL; + } + + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_BOOLEAN, &has_owner, + G_TYPE_INVALID); + + return reply_message; +} + + +static IBusMessage * +_dbus_get_name_owner (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + gchar *name; + BusConnection *owner; + gboolean retval; + const gchar *owner_name = NULL; + IBusMessage *reply_message; + IBusError *error; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &name, + G_TYPE_INVALID); + + if (! retval) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + if (g_strcmp0 (name, DBUS_SERVICE_DBUS) == 0 || + g_strcmp0 (name, IBUS_SERVICE_IBUS) == 0) { + owner_name = name; + } + else { + owner = bus_dbus_impl_get_connection_by_name (dbus, name); + if (owner != NULL) { + owner_name = bus_connection_get_unique_name (owner); + } + } + + if (owner_name != NULL) { + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_STRING, &owner_name, + G_TYPE_INVALID); + } + else { + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_NAME_HAS_NO_OWNER, + "Name '%s' does have owner", + name); + } + + return reply_message; +} + +static IBusMessage * +_dbus_get_id (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + const gchar *name; + + name = bus_connection_get_unique_name (connection); + + if (name == NULL) { + reply_message = ibus_message_new_error (message, + DBUS_ERROR_FAILED, + "Can not GetId before Hello"); + return reply_message; + } + + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_STRING, &name, + G_TYPE_INVALID); + return reply_message; +} + +static void +_rule_destroy_cb (BusMatchRule *rule, + BusDBusImpl *dbus) +{ + g_assert (BUS_IS_MATCH_RULE (rule)); + g_assert (BUS_IS_DBUS_IMPL (dbus)); + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + priv->rules = g_list_remove (priv->rules, rule); + g_object_unref (rule); +} + +static IBusMessage * +_dbus_add_match (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusError *error; + gboolean retval; + gchar *rule_text; + BusMatchRule *rule; + GList *link; + + retval = ibus_message_get_args (message, + &error, + G_TYPE_STRING, &rule_text, + G_TYPE_INVALID); + + if (!retval) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + rule = bus_match_rule_new (rule_text); + + if (rule == NULL) { + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_MATCH_RULE_INVALID, + "Parse rule [%s] failed", + rule_text); + return reply_message; + } + + for (link = priv->rules; link != NULL; link = link->next) { + if (bus_match_rule_is_equal (rule, BUS_MATCH_RULE (link->data))) { + bus_match_rule_add_recipient (BUS_MATCH_RULE (link->data), connection); + g_object_unref (rule); + rule = NULL; + break; + } + } + + if (rule) { + bus_match_rule_add_recipient (rule, connection); + priv->rules = g_list_append (priv->rules, rule); + g_signal_connect (rule, "destroy", G_CALLBACK (_rule_destroy_cb), dbus); + } + + reply_message = ibus_message_new_method_return (message); + return reply_message; +} + +static IBusMessage * +_dbus_remove_match (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusError *error; + gchar *rule_text; + BusMatchRule *rule; + GList *link; + + if (!ibus_message_get_args (message, + &error, + G_TYPE_STRING, &rule_text, + G_TYPE_INVALID)) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + rule = bus_match_rule_new (rule_text); + + if (rule == NULL ) { + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_MATCH_RULE_INVALID, + "Parse rule [%s] failed", + rule_text); + return reply_message; + } + + for (link = priv->rules; link != NULL; link = link->next) { + if (bus_match_rule_is_equal (rule, BUS_MATCH_RULE (link->data))) { + bus_match_rule_remove_recipient (BUS_MATCH_RULE (link->data), connection); + break; + } + } + + g_object_unref (rule); + + reply_message = ibus_message_new_method_return (message); + return reply_message; +} + +static IBusMessage * +_dbus_request_name (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusError *error; + gchar *name; + guint flags; + guint retval; + + if (!ibus_message_get_args (message, + &error, + G_TYPE_STRING, &name, + G_TYPE_UINT, &flags, + G_TYPE_INVALID)) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + if (g_hash_table_lookup (priv->names, name) != NULL) { + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_FAILED, + "Name %s has owner", + name); + return reply_message; + } + + retval = 1; + g_hash_table_insert (priv->names, + (gpointer )bus_connection_add_name (connection, name), + connection); + reply_message = ibus_message_new_method_return (message); + ibus_message_append_args (reply_message, + G_TYPE_UINT, &retval, + G_TYPE_INVALID); + + ibus_connection_send ((IBusConnection *) connection, reply_message); + ibus_message_unref (reply_message); + ibus_connection_flush ((IBusConnection *) connection); + + g_signal_emit (dbus, + dbus_signals[NAME_OWNER_CHANGED], + 0, + name, + "", + bus_connection_get_unique_name (connection)); + + return NULL; +} + +static IBusMessage * +_dbus_release_name (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *connection) +{ + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + IBusMessage *reply_message; + IBusError *error; + gchar *name; + guint retval; + + if (!ibus_message_get_args (message, + &error, + G_TYPE_STRING, &name, + G_TYPE_INVALID)) { + reply_message = ibus_message_new_error (message, + error->name, + error->message); + ibus_error_free (error); + return reply_message; + } + + reply_message = ibus_message_new_method_return (message); + if (bus_connection_remove_name (connection, name)) { + retval = 1; + } + else { + retval = 2; + } + + ibus_message_append_args (message, + G_TYPE_UINT, &retval, + G_TYPE_INVALID); + + return reply_message; +} + + +static gboolean +bus_dbus_impl_ibus_message (BusDBusImpl *dbus, + BusConnection *connection, + IBusMessage *message) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (message != NULL); + + gint i; + IBusMessage *reply_message = NULL; + + static const struct { + const gchar *interface; + const gchar *name; + IBusMessage *(* handler) (BusDBusImpl *, IBusMessage *, BusConnection *); + } handlers[] = { + /* Introspectable interface */ + { DBUS_INTERFACE_INTROSPECTABLE, + "Introspect", _dbus_introspect }, + /* DBus interface */ + { DBUS_INTERFACE_DBUS, "Hello", _dbus_hello }, + { DBUS_INTERFACE_DBUS, "ListNames", _dbus_list_names }, + { DBUS_INTERFACE_DBUS, "ListActivatableNames", + _dbus_no_implement }, + { DBUS_INTERFACE_DBUS, "NameHasOwner", + _dbus_name_has_owner }, + { DBUS_INTERFACE_DBUS, "StartServiceByName", + _dbus_no_implement }, + { DBUS_INTERFACE_DBUS, "GetNameOwner", + _dbus_get_name_owner }, + { DBUS_INTERFACE_DBUS, "GetConnectionUnixUser", + _dbus_no_implement }, + { DBUS_INTERFACE_DBUS, "AddMatch", _dbus_add_match }, + { DBUS_INTERFACE_DBUS, "RemoveMatch", + _dbus_remove_match }, + { DBUS_INTERFACE_DBUS, "GetId", _dbus_get_id }, + { DBUS_INTERFACE_DBUS, "RequestName", _dbus_request_name }, + { DBUS_INTERFACE_DBUS, "ReleaseName", _dbus_release_name }, + { NULL, NULL, NULL } + }; + + ibus_message_set_destination (message, DBUS_SERVICE_DBUS); + + for (i = 0; handlers[i].interface != NULL; i++) { + if (ibus_message_is_method_call (message, + handlers[i].interface, + handlers[i].name)) { + + reply_message = handlers[i].handler (dbus, message, connection); + if (reply_message) { + + ibus_message_set_sender (reply_message, DBUS_SERVICE_DBUS); + ibus_message_set_destination (reply_message, + bus_connection_get_unique_name (connection)); + ibus_message_set_no_reply (reply_message, TRUE); + + ibus_connection_send (IBUS_CONNECTION (connection), reply_message); + ibus_message_unref (reply_message); + } + + g_signal_stop_emission_by_name (dbus, "ibus-message"); + return TRUE; + } + } + + return parent_class->ibus_message ((IBusService *) dbus, + (IBusConnection *) connection, + message); +} + +static void +bus_dbus_impl_name_owner_changed (BusDBusImpl *dbus, + gchar *name, + gchar *old_name, + gchar *new_name) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (name != NULL); + g_assert (old_name != NULL); + g_assert (new_name != NULL); + + IBusMessage *message; + + message = ibus_message_new_signal (DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged"); + ibus_message_append_args (message, + G_TYPE_STRING, &name, + G_TYPE_STRING, &old_name, + G_TYPE_STRING, &new_name, + G_TYPE_INVALID); + ibus_message_set_sender (message, DBUS_SERVICE_DBUS); + + bus_dbus_impl_dispatch_message_by_rule (dbus, message, NULL); + + ibus_message_unref (message); + +} + +static gboolean +_connection_ibus_message_cb (BusConnection *connection, + IBusMessage *message, + BusDBusImpl *dbus) +{ + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (message != NULL); + g_assert (BUS_IS_DBUS_IMPL (dbus)); + + const gchar *dest; + + if (ibus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) { + /* ignore signal from local interface */ + return FALSE; + } + + ibus_message_set_sender (message, bus_connection_get_unique_name (connection)); + + switch (ibus_message_get_type (message)) { + case DBUS_MESSAGE_TYPE_ERROR: + g_debug ("From :%s to %s, Error: %s : %s", + ibus_message_get_sender (message), + ibus_message_get_destination (message), + ibus_message_get_error_name (message), + ibus_message_get_error_message (message)); + break; +#if 0 + case DBUS_MESSAGE_TYPE_METHOD_CALL: + g_debug("From %s to %s, Method %s on %s", + ibus_message_get_sender (message), + ibus_message_get_destination (message), + ibus_message_get_path (message), + ibus_message_get_member (message)); + break; +#endif + } + + dest = ibus_message_get_destination (message); + + if (dest == NULL || + strcmp ((gchar *)dest, IBUS_SERVICE_IBUS) == 0 || + strcmp ((gchar *)dest, DBUS_SERVICE_DBUS) == 0) { + /* this message is sent to ibus-daemon */ + + switch (ibus_message_get_type (message)) { + case DBUS_MESSAGE_TYPE_SIGNAL: + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + case DBUS_MESSAGE_TYPE_ERROR: + bus_dbus_impl_dispatch_message_by_rule (dbus, message, NULL); + return FALSE; + case DBUS_MESSAGE_TYPE_METHOD_CALL: + { + const gchar *path; + IBusService *object; + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + path = ibus_message_get_path (message); + + object = g_hash_table_lookup (priv->objects, path); + + if (object == NULL || + ibus_service_handle_message (object, + (IBusConnection *) connection, + message) == FALSE) { + IBusMessage *error; + error = ibus_message_new_error_printf (message, + DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s on %s", + ibus_message_get_member (message), + path); + ibus_connection_send ((IBusConnection *) connection, error); + ibus_message_unref (error); + } + + /* dispatch message */ + bus_dbus_impl_dispatch_message_by_rule (dbus, message, NULL); + } + break; + default: + g_assert (FALSE); + } + } + else { + /* If the destination is not IBus or DBus, the message will be forwanded. */ + bus_dbus_impl_dispatch_message (dbus, message); + } + + g_signal_stop_emission_by_name (connection, "ibus-message"); + return TRUE; +} + +static void +_connection_ibus_message_sent_cb (BusConnection *connection, + IBusMessage *message, + BusDBusImpl *dbus) +{ + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (message != NULL); + g_assert (BUS_IS_DBUS_IMPL (dbus)); + + bus_dbus_impl_dispatch_message_by_rule (dbus, message, connection); +} + +static void +_connection_destroy_cb (BusConnection *connection, + BusDBusImpl *dbus) +{ + g_assert (BUS_IS_CONNECTION (connection)); + g_assert (BUS_IS_DBUS_IMPL (dbus)); + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + /* + ibus_service_remove_from_connection ( + IBUS_SERVICE (dbus), + IBUS_CONNECTION (connection)); + */ + + const gchar *unique_name = bus_connection_get_unique_name (connection); + if (unique_name != NULL) { + g_hash_table_remove (priv->unique_names, unique_name); + g_signal_emit (dbus, + dbus_signals[NAME_OWNER_CHANGED], + 0, + unique_name, + unique_name, + ""); + } + + const GList *name = bus_connection_get_names (connection); + + while (name != NULL) { + g_hash_table_remove (priv->names, name->data); + g_signal_emit (dbus, + dbus_signals[NAME_OWNER_CHANGED], + 0, + name->data, + unique_name, + ""); + name = name->next; + } + + priv->connections = g_list_remove (priv->connections, connection); + g_object_unref (connection); +} + + +gboolean +bus_dbus_impl_new_connection (BusDBusImpl *dbus, + BusConnection *connection) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (BUS_IS_CONNECTION (connection)); + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + g_assert (g_list_find (priv->connections, connection) == NULL); + + g_object_ref (connection); + priv->connections = g_list_append (priv->connections, connection); + + g_signal_connect (connection, + "ibus-message", + G_CALLBACK (_connection_ibus_message_cb), + dbus); + + g_signal_connect (connection, + "ibus-message-sent", + G_CALLBACK (_connection_ibus_message_sent_cb), + dbus); + + + g_signal_connect (connection, + "destroy", + G_CALLBACK (_connection_destroy_cb), + dbus); + return TRUE; +} + + +BusConnection * +bus_dbus_impl_get_connection_by_name (BusDBusImpl *dbus, + const gchar *name) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (name != NULL); + + BusConnection *connection = NULL; + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + if (name[0] == ':') { + connection = BUS_CONNECTION (g_hash_table_lookup ( + priv->unique_names, + name)); + } + else { + connection = BUS_CONNECTION (g_hash_table_lookup ( + priv->names, + name)); + } + + return connection; +} + + +void +bus_dbus_impl_dispatch_message (BusDBusImpl *dbus, + IBusMessage *message) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (message != NULL); + + const gchar *destination; + BusConnection *dest_connection = NULL; + + destination = ibus_message_get_destination (message); + + if (destination != NULL) { + dest_connection = bus_dbus_impl_get_connection_by_name (dbus, destination); + + if (dest_connection != NULL) { + ibus_connection_send (IBUS_CONNECTION (dest_connection), message); + } + else { + IBusMessage *reply_message; + reply_message = ibus_message_new_error_printf (message, + DBUS_ERROR_SERVICE_UNKNOWN, + "Can not find service %s", + destination); + bus_dbus_impl_dispatch_message (dbus, reply_message); + ibus_message_unref (reply_message); + } + } + + bus_dbus_impl_dispatch_message_by_rule (dbus, message, dest_connection); +} + +void +bus_dbus_impl_dispatch_message_by_rule (BusDBusImpl *dbus, + IBusMessage *message, + BusConnection *skip_connection) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (message != NULL); + g_assert (BUS_IS_CONNECTION (skip_connection) || skip_connection == NULL); + + GList *recipients = NULL; + GList *link = NULL; + + static gint32 data_slot = -1; + + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + if (data_slot == -1) { + dbus_message_allocate_data_slot (&data_slot); + } + + /* If this message has been dispatched by rule, it will be ignored. */ + if (dbus_message_get_data (message, data_slot) != NULL) + return; + + dbus_message_set_data (message, data_slot, (gpointer) TRUE, NULL); + +#if 0 + if (g_strcmp0 (ibus_message_get_member (message), "ValueChanged") == 0) { + g_debug ("Dispatch ValueChanged"); + } +#endif + + for (link = priv->rules; link != NULL; link = link->next) { + if (bus_match_rule_get_recipients (BUS_MATCH_RULE (link->data), + message, + &recipients)) { + break; + } + } + + for (link = recipients; link != NULL; link = link->next) { + BusConnection *connection = BUS_CONNECTION (link->data); + if (connection != skip_connection) { + ibus_connection_send (IBUS_CONNECTION (connection), message); + } + g_object_unref (link->data); + } + g_list_free (recipients); +} + + +static void +_object_destroy_cb (IBusService *object, + BusDBusImpl *dbus) +{ + gboolean retval; + + retval = bus_dbus_impl_unregister_object (dbus, object); + + g_assert (retval); +} + +gboolean +bus_dbus_impl_register_object (BusDBusImpl *dbus, + IBusService *object) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (IBUS_IS_SERVICE (object)); + + const gchar *path; + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + path = ibus_service_get_path (object); + + g_return_val_if_fail (path, FALSE); + + g_return_val_if_fail (g_hash_table_lookup (priv->objects, path) == NULL, FALSE); + + g_object_ref (object); + g_hash_table_insert (priv->objects, (gpointer)path, object); + + g_signal_connect (object, "destroy", G_CALLBACK (_object_destroy_cb), dbus); + + return TRUE; +} + +gboolean +bus_dbus_impl_unregister_object (BusDBusImpl *dbus, + IBusService *object) +{ + g_assert (BUS_IS_DBUS_IMPL (dbus)); + g_assert (IBUS_IS_SERVICE (object)); + + const gchar *path; + BusDBusImplPrivate *priv; + priv = BUS_DBUS_IMPL_GET_PRIVATE (dbus); + + path = ibus_service_get_path (object); + g_return_val_if_fail (path, FALSE); + + g_return_val_if_fail (g_hash_table_lookup (priv->objects, path) == object, FALSE); + + g_signal_handlers_disconnect_by_func (object, G_CALLBACK (_object_destroy_cb), dbus); + + g_hash_table_remove (priv->objects, path); + g_object_unref (object); + + return TRUE; +} + |