/* * empathy-tube-dispatch.c - Source for EmpathyTubeDispatch * Copyright (C) 2008 Collabora Ltd. * @author Sjoerd Simons * * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "empathy-tube-dispatch.h" #include "empathy-tube-dispatch-enumtypes.h" #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER #include G_DEFINE_TYPE(EmpathyTubeDispatch, empathy_tube_dispatch, G_TYPE_OBJECT) static void empathy_tube_dispatch_set_ability ( EmpathyTubeDispatch *tube_dispatch, EmpathyTubeDispatchAbility dispatchability); /* private structure */ typedef struct _EmpathyTubeDispatchPriv EmpathyTubeDispatchPriv; /* properties */ enum { PROP_OPERATION = 1, PROP_DISPATCHABILITY }; struct _EmpathyTubeDispatchPriv { gboolean dispose_has_run; EmpathyDispatchOperation *operation; EmpathyTubeDispatchAbility dispatchability; gchar *service; gchar *bus_name; gchar *object_path; TpDBusDaemon *dbus; }; #define GET_PRIV(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ EMPATHY_TYPE_TUBE_DISPATCH, EmpathyTubeDispatchPriv)) static void empathy_tube_dispatch_init (EmpathyTubeDispatch *obj) { EmpathyTubeDispatchPriv *priv = GET_PRIV (obj); priv->dispatchability = EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN; } static void empathy_tube_dispatch_dispose (GObject *object); static void empathy_tube_dispatch_finalize (GObject *object); static void empathy_tube_dispatch_list_activatable_names_cb (TpDBusDaemon *proxy, const gchar **names, const GError *error, gpointer user_data, GObject *object) { EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object); EmpathyTubeDispatchPriv *priv = GET_PRIV (self); gchar **name; for (name = (gchar **) names; *name != NULL; name++) { if (!tp_strdiff (*name, priv->bus_name)) { DEBUG ("Found tube handler. Can dispatch it"); empathy_tube_dispatch_set_ability (self, EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE); return; } } DEBUG ("Didn't find tube handler. Can't dispatch it"); empathy_tube_dispatch_set_ability (self, EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE); } static void empathy_tube_dispatch_name_has_owner_cb (TpDBusDaemon *proxy, gboolean has_owner, const GError *error, gpointer user_data, GObject *object) { EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object); EmpathyTubeDispatchPriv *priv = GET_PRIV (self); if (error != NULL) { DEBUG ("NameHasOwner failed. Can't dispatch tube"); empathy_tube_dispatch_set_ability (self, EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE); return; } if (has_owner) { DEBUG ("Tube handler is running. Can dispatch it"); empathy_tube_dispatch_set_ability (self, EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE); } else { DEBUG ("Tube handler is not running. Calling ListActivatableNames"); tp_cli_dbus_daemon_call_list_activatable_names (priv->dbus, -1, empathy_tube_dispatch_list_activatable_names_cb, NULL, NULL, G_OBJECT (self)); } } static void empathy_tube_dispatch_constructed (GObject *object) { EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object); EmpathyTubeDispatchPriv *priv = GET_PRIV (self); TpChannel *channel; GHashTable *properties; const gchar *service; const gchar *channel_type; TpTubeType type; priv->dbus = tp_dbus_daemon_new (tp_get_bus ()); channel = empathy_dispatch_operation_get_channel (priv->operation); properties = tp_channel_borrow_immutable_properties (channel); channel_type = tp_asv_get_string (properties, TP_IFACE_CHANNEL ".ChannelType"); if (channel_type == NULL) goto failed; if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) { type = TP_TUBE_TYPE_STREAM; service = tp_asv_get_string (properties, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"); } else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) { GError *error = NULL; type = TP_TUBE_TYPE_DBUS; service = tp_asv_get_string (properties, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName"); if (!tp_dbus_check_valid_bus_name (service, TP_DBUS_NAME_TYPE_WELL_KNOWN, &error)) { DEBUG ("Can't dispatch tube; invalid ServiceName %s: %s", service, error->message); g_error_free (error); goto failed; } } else { goto failed; } if (service == NULL) goto failed; priv->bus_name = empathy_tube_handler_build_bus_name (type, service); priv->object_path = empathy_tube_handler_build_object_path (type, service); priv->service = g_strdup (service); DEBUG ("Look for tube handler %s\n", priv->bus_name); tp_cli_dbus_daemon_call_name_has_owner (priv->dbus, -1, priv->bus_name, empathy_tube_dispatch_name_has_owner_cb, NULL, NULL, G_OBJECT (self)); return; failed: empathy_tube_dispatch_set_ability (self, EMPATHY_TUBE_DISPATCHABILITY_IMPOSSIBLE); } static void empathy_tube_dispatch_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { EmpathyTubeDispatch *tube_dispatch = EMPATHY_TUBE_DISPATCH (object); EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch); switch (property_id) { case PROP_OPERATION: priv->operation = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void empathy_tube_dispatch_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { EmpathyTubeDispatch *tube_dispatch = EMPATHY_TUBE_DISPATCH (object); EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch); switch (property_id) { case PROP_OPERATION: g_value_set_object (value, priv->operation); break; case PROP_DISPATCHABILITY: g_value_set_enum (value, priv->dispatchability); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void empathy_tube_dispatch_class_init ( EmpathyTubeDispatchClass *empathy_tube_dispatch_class) { GObjectClass *object_class = G_OBJECT_CLASS (empathy_tube_dispatch_class); GParamSpec *param_spec; g_type_class_add_private (empathy_tube_dispatch_class, sizeof (EmpathyTubeDispatchPriv)); object_class->set_property = empathy_tube_dispatch_set_property; object_class->get_property = empathy_tube_dispatch_get_property; object_class->constructed = empathy_tube_dispatch_constructed; object_class->dispose = empathy_tube_dispatch_dispose; object_class->finalize = empathy_tube_dispatch_finalize; param_spec = g_param_spec_object ("operation", "operation", "The telepathy connection", EMPATHY_TYPE_DISPATCH_OPERATION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_OPERATION, param_spec); param_spec = g_param_spec_enum ("dispatchability", "dispatchability", "Whether or not there is a handler to dispatch the operation to", EMPATHY_TYPE_TUBE_DISPATCH_ABILITY, EMPATHY_TUBE_DISPATCHABILITY_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DISPATCHABILITY, param_spec); } void empathy_tube_dispatch_dispose (GObject *object) { EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object); EmpathyTubeDispatchPriv *priv = GET_PRIV (self); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; /* release any references held by the object here */ if (priv->operation != NULL) g_object_unref (priv->operation); priv->operation = NULL; if (priv->dbus != NULL) g_object_unref (priv->dbus); priv->dbus = NULL; if (G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->dispose) G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->dispose (object); } void empathy_tube_dispatch_finalize (GObject *object) { EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object); EmpathyTubeDispatchPriv *priv = GET_PRIV (self); g_free (priv->bus_name); g_free (priv->object_path); g_free (priv->service); /* free any data held directly by the object here */ G_OBJECT_CLASS (empathy_tube_dispatch_parent_class)->finalize (object); } EmpathyTubeDispatch * empathy_tube_dispatch_new (EmpathyDispatchOperation *operation) { return EMPATHY_TUBE_DISPATCH (g_object_new (EMPATHY_TYPE_TUBE_DISPATCH, "operation", operation, NULL)); } EmpathyTubeDispatchAbility empathy_tube_dispatch_is_dispatchable (EmpathyTubeDispatch *tube_dispatch) { EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch); return priv->dispatchability; } static void empathy_tube_dispatch_set_ability (EmpathyTubeDispatch *tube_dispatch, EmpathyTubeDispatchAbility dispatchability) { EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch); if (priv->dispatchability == dispatchability) return; priv->dispatchability = dispatchability; g_object_notify (G_OBJECT (tube_dispatch), "dispatchability"); } static void empathy_tube_dispatch_show_error (EmpathyTubeDispatch *self, gchar *message) { GtkWidget *dialog; dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "%s", message); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static void empathy_tube_dispatch_handle_tube_cb (TpProxy *proxy, const GError *error, gpointer user_data, GObject *object) { EmpathyTubeDispatch *self = EMPATHY_TUBE_DISPATCH (object); EmpathyTubeDispatchPriv *priv = GET_PRIV (self); if (error != NULL) { gchar *msg = g_strdup_printf ( _("Unable to start application for service %s: %s"), priv->service, error->message); empathy_tube_dispatch_show_error (self, msg); g_free (msg); } /* Remove the ref we were holding because of the dispatching */ g_object_unref (object); } static void empathy_tube_do_dispatch (EmpathyTubeDispatch *self) { EmpathyTubeDispatchPriv *priv = GET_PRIV (self); TpChannel *channel; TpProxy *connection; TpProxy *thandler; gchar *object_path; guint handle_type; guint handle; channel = empathy_dispatch_operation_get_channel (priv->operation); /* Create the proxy for the tube handler */ thandler = g_object_new (TP_TYPE_PROXY, "dbus-connection", tp_get_bus (), "bus-name", priv->bus_name, "object-path", priv->object_path, NULL); tp_proxy_add_interface_by_id (thandler, EMP_IFACE_QUARK_TUBE_HANDLER); /* Give the tube to the handler */ g_object_get (channel, "connection", &connection, "object-path", &object_path, "handle_type", &handle_type, "handle", &handle, NULL); emp_cli_tube_handler_call_handle_tube (thandler, -1, connection->bus_name, connection->object_path, object_path, handle_type, handle, empathy_tube_dispatch_handle_tube_cb, NULL, NULL, G_OBJECT (self)); g_object_unref (thandler); g_object_unref (connection); g_free (object_path); } void empathy_tube_dispatch_handle (EmpathyTubeDispatch *tube_dispatch) { EmpathyTubeDispatchPriv *priv = GET_PRIV (tube_dispatch); /* Keep ourselves alive untill the dispatching is finished */ g_object_ref (tube_dispatch); /* If we can't claim it, don't do anything */ if (!empathy_dispatch_operation_claim (priv->operation)) goto done; if (priv->dispatchability != EMPATHY_TUBE_DISPATCHABILITY_POSSIBLE) { gchar *msg; TpChannel *channel; channel = empathy_dispatch_operation_get_channel (priv->operation); msg = g_strdup_printf ( _("An invitation was offered for service %s, but you don't have the " "needed application to handle it"), priv->service); empathy_tube_dispatch_show_error (tube_dispatch, msg); g_free (msg); tp_cli_channel_call_close (channel, -1, NULL, NULL, NULL, NULL); goto done; } else { empathy_tube_do_dispatch (tube_dispatch); } return; done: g_object_unref (tube_dispatch); }