/* tp-conn.c * * Copyright (C) 2005 Nokia Corporation. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include <string.h> #include "tp-conn.h" #include "tp-helpers.h" #include "tp-connmgr.h" #include "tp-props-iface.h" #include "internal.h" static GObjectClass *parent_class = NULL; static gboolean tp_conn_status_change_handler(DBusGProxy *proxy, guint status, guint reason, gpointer user_data); static void tp_conn_connect_req_handler(DBusGProxy *proxy, GError *error, gpointer user_data); static void _tp_conn_register_signal_marshallers() { /* Register marshaller for NewChannel signal */ dbus_g_object_register_marshaller(tp_conn_signals_marshal_VOID__OBJECT_STRING_UINT_UINT_BOOLEAN, G_TYPE_NONE, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN, G_TYPE_INVALID); /* Register marshaller for StatusChanged signal */ dbus_g_object_register_marshaller(tp_conn_signals_marshal_VOID__UINT_UINT, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID); } static void _tp_conn_register_interface_signal_marshallers() { /* Register marshaller for Aliasing interface signal AliasUpdate and ContactInfo interface signal GotContactInfo*/ dbus_g_object_register_marshaller(tp_ifaces_signals_marshal_VOID__UINT_STRING, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID); /* Register marshaller for Forwarding interface signal ForwardingChanged */ dbus_g_object_register_marshaller(tp_ifaces_signals_marshal_VOID__UINT, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_INVALID); /* Register marshaller for Presence interface signal PresenceUpdate */ dbus_g_object_register_marshaller(tp_ifaces_signals_marshal_VOID__BOXED, G_TYPE_NONE, G_TYPE_BOXED, G_TYPE_INVALID); /* Register marshaller for Privacy interface signal PrivacyModeChanged */ dbus_g_object_register_marshaller(tp_ifaces_signals_marshal_VOID__STRING, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_INVALID); /* Register marshaller for Renaming interface signal Renamed */ dbus_g_object_register_marshaller(tp_ifaces_signals_marshal_VOID__UINT_UINT, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID); } /* We initialize the list of signatures here, so that we can use * it to add them for new interface instances later. */ /* FIXME: This should be replaced by a more automatic way of doing * this. The reason for using a set of function pointers is that there is no * apparent cleaner way of doing this, unless DBusGProxy gains a non-varargs * version of dbus_g_proxy_add_signal... */ static void _tp_conn_init_interface_signal_signatures(GData **signal_sigs) { g_datalist_init(signal_sigs); /* Create and store aliasing iface signal signatures */ g_datalist_id_set_data(signal_sigs, TELEPATHY_CONN_IFACE_ALIASING_QUARK, &tp_conn_set_aliasing_signatures); /* Create and store avatars iface signal signatures */ g_datalist_id_set_data(signal_sigs, TELEPATHY_CONN_IFACE_AVATARS_QUARK, &tp_conn_set_avatars_signatures); /* Create and store capabilities iface signal signatures */ g_datalist_id_set_data(signal_sigs, TELEPATHY_CONN_IFACE_CAPABILITIES_QUARK, &tp_conn_set_capabilities_signatures); /* Create and store contactinfo iface signal signatures */ g_datalist_id_set_data(signal_sigs, TELEPATHY_CONN_IFACE_CONTACTINFO_QUARK, &tp_conn_set_contactinfo_signatures); /* Create and store forwarding iface signal signatures */ g_datalist_id_set_data(signal_sigs, TELEPATHY_CONN_IFACE_FORWARDING_QUARK, &tp_conn_set_forwarding_signatures); /* Create and store presence iface signal signatures */ g_datalist_id_set_data(signal_sigs, TELEPATHY_CONN_IFACE_PRESENCE_QUARK, &tp_conn_set_presence_signatures); /* Create and store privacy iface signal signatures */ g_datalist_id_set_data(signal_sigs, TELEPATHY_CONN_IFACE_PRIVACY_QUARK, &tp_conn_set_privacy_signatures); /* Create and store renaming iface signal signatures */ g_datalist_id_set_data(signal_sigs, TELEPATHY_CONN_IFACE_RENAMING_QUARK, &tp_conn_set_renaming_signatures); } static void tp_conn_init(GTypeInstance *instance, gpointer g_class) { TpConn *self = TELEPATHY_CONN(instance); self->first_run = TRUE; } static void tp_conn_dispose(GObject *obj) { TpConn *self = TELEPATHY_CONN(obj); if (self->first_run == TRUE) { self->first_run = FALSE; g_datalist_clear(&(self->interface_list)); } /* Call parent class dispose method */ if (G_OBJECT_CLASS(parent_class)->dispose) { G_OBJECT_CLASS(parent_class)->dispose(obj); } } static void tp_conn_finalize(GObject *obj) { if (G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(obj); } } static void tp_conn_class_init(TpConnClass *klass) { GObjectClass *obj = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj->set_property = parent_class->set_property; obj->get_property = parent_class->get_property; obj->dispose = tp_conn_dispose; obj->finalize = tp_conn_finalize; _tp_conn_register_signal_marshallers(); _tp_conn_register_interface_signal_marshallers(); _tp_conn_init_interface_signal_signatures(&(klass->iface_signal_sigs)); } GType tp_conn_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(TpConnClass), NULL, NULL, (GClassInitFunc)tp_conn_class_init, NULL, NULL, sizeof(TpConn), 0, (GInstanceInitFunc)tp_conn_init }; type = g_type_register_static(DBUS_TYPE_G_PROXY, "TpConn", &info, 0); } return type; } /* The interface name getters */ GQuark tp_get_conn_interface() { static GQuark ret = 0; if (ret == 0) { /* FIXME: The naming conventions should be unified */ ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE); } return ret; } GQuark tp_get_conn_aliasing_interface() { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE_ALIASING); } return ret; } GQuark tp_get_conn_avatars_interface() { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE_AVATARS); } return ret; } GQuark tp_get_conn_capabilities_interface() { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE_CAPABILITIES); } return ret; } GQuark tp_get_conn_contactinfo_interface() { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE_CONTACT_INFO); } return ret; } GQuark tp_get_conn_forwarding_interface() { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE_FORWARDING); } return ret; } GQuark tp_get_conn_presence_interface() { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE_PRESENCE); } return ret; } GQuark tp_get_conn_privacy_interface() { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE_PRIVACY); } return ret; } GQuark tp_get_conn_renaming_interface() { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string(TP_IFACE_CONN_INTERFACE_RENAMING); } return ret; } TpConn *tp_conn_new(DBusGConnection *connection, const gchar *bus_name, const gchar *object_path) { GError *error = NULL; gchar *unique_name; gchar **interfaces; guint conn_status = TP_CONN_STATUS_DISCONNECTED; TpConn *obj; g_return_val_if_fail(connection != NULL, NULL); g_return_val_if_fail(bus_name != NULL, NULL); g_return_val_if_fail(object_path, NULL); /* Create the proxy object for this connection. It will be used to perform the actual method calls over D-BUS. */ if (!dbus_g_proxy_call (tp_get_bus_proxy (), "GetNameOwner", &error, G_TYPE_STRING, bus_name, G_TYPE_INVALID, G_TYPE_STRING, &unique_name, G_TYPE_INVALID)) { g_warning("tp_conn_new: getting unique name failed: %s\n", error->message); g_error_free(error); return NULL; } obj = g_object_new(TELEPATHY_CONN_TYPE, "name", unique_name, "path", object_path, "interface", TP_IFACE_CONN_INTERFACE, "connection", connection, NULL); g_free(unique_name); g_datalist_init(&(obj->interface_list)); dbus_g_proxy_add_signal(DBUS_G_PROXY(obj), "NewChannel", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN, G_TYPE_INVALID); dbus_g_proxy_add_signal(DBUS_G_PROXY(obj), "StatusChanged", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID); /* Check if the connection is already connected. If yes, we can already perform GetInterfaces(). */ if (!tp_conn_get_status(DBUS_G_PROXY(obj), &conn_status, &error)) { g_warning("GetStatus() failed: %s\n", error->message); g_error_free(error); } if (conn_status == TP_CONN_STATUS_CONNECTED) { if (!tp_conn_get_interfaces(DBUS_G_PROXY(obj), &interfaces, &error)) { _tp_warn_failure ("GetInterfaces()", error); g_error_free(error); return obj; } /* Initialize the interface objects for this TpConn object */ tp_conn_local_set_interfaces(obj, interfaces); g_strfreev(interfaces); return obj; } else { /* Not connected yet, so this is really a new connection. Thus, we have to hook up to StatusChanged signal to perform the GetInterfaces eventually there and then call Connect() to initiate the connection setup */ dbus_g_proxy_connect_signal(DBUS_G_PROXY(obj), "StatusChanged", G_CALLBACK(tp_conn_status_change_handler), NULL, NULL); tp_conn_connect_async(DBUS_G_PROXY(obj), tp_conn_connect_req_handler, NULL); } return obj; } TpChan *tp_conn_new_channel(DBusGConnection *connection, TpConn *tp_conn, const gchar *bus_name, gchar *type, guint handle_type, guint handle, gboolean supress_handler) { GError *error = NULL; gchar *chan_object_path = NULL; TpChan *new_chan = NULL; g_return_val_if_fail(connection, NULL); g_return_val_if_fail(TELEPATHY_IS_CONN(tp_conn), NULL); g_return_val_if_fail(bus_name, NULL); g_return_val_if_fail(type, NULL); /* Request a new channel to be created by using the proxy object. We also retrieve the object path for it here. */ if (!tp_conn_request_channel(DBUS_G_PROXY(tp_conn), type, handle_type, handle, supress_handler, &chan_object_path, &error)) { _tp_warn_failure ("RequestChannel()", error); g_error_free(error); return NULL; } /* Create the object to represent the channel */ new_chan = tp_chan_new(connection, bus_name, chan_object_path, type, handle_type, handle); g_free(chan_object_path); return new_chan; } void tp_conn_local_set_interfaces(TpConn *self, gchar **interfaces) { gchar **temp_ifaces = NULL; const gchar *bus_name = dbus_g_proxy_get_bus_name(DBUS_G_PROXY(self)); const gchar *object_path = dbus_g_proxy_get_path(DBUS_G_PROXY(self)); DBusGConnection *connection = tp_get_bus (); if (interfaces == NULL || connection == NULL) { return; } /* Create and store the proxy objects for the connection interfaces. */ for (temp_ifaces = interfaces; *temp_ifaces; temp_ifaces++) { DBusGProxy *if_proxy; GQuark key = g_quark_from_string(*temp_ifaces); if (key == TELEPATHY_PROPS_IFACE_QUARK) { if_proxy = DBUS_G_PROXY (tp_props_iface_new (connection, bus_name, object_path)); } else { if_proxy = dbus_g_proxy_new_for_name(connection, bus_name, object_path, *temp_ifaces); if (if_proxy != NULL) { GData *sig_list = TELEPATHY_CONN_GET_CLASS(self)->iface_signal_sigs; void (*signature_setter_func)(); /* Does the interface have signals? If yes, add their signatures for the interface instance */ signature_setter_func = g_datalist_id_get_data(&sig_list, key); if (signature_setter_func != NULL) { signature_setter_func(if_proxy); } } } if (if_proxy != NULL) { g_datalist_id_set_data_full(&(self->interface_list), key, if_proxy, g_object_unref); } } } DBusGProxy *tp_conn_get_interface(TpConn *self, GQuark iface_quark) { DBusGProxy *iface_proxy = NULL; iface_proxy = (DBusGProxy *)g_datalist_id_get_data(&(self->interface_list), iface_quark); return iface_proxy; } static void tp_conn_connect_req_handler(DBusGProxy *proxy, GError *error, gpointer user_data) { /* The interfaces for the TpConn are set on the StatusChanged handler when we get connected. Just print errors (if any) here. */ if (error) { g_warning("Could not perform Connect() for the connection, because: %s", error->message); g_error_free(error); return; } } static gboolean tp_conn_status_change_handler(DBusGProxy *proxy, guint status, guint reason, gpointer user_data) { gchar **interfaces = NULL; GError *error = NULL; TpConn *tp_conn = (TpConn *)proxy; /* If the connection is up, we can get the list of interfaces */ /* FIXME: At some point, we should switch to doing this asynchronously */ if (status == TP_CONN_STATUS_CONNECTED) { if (!tp_conn_get_interfaces(DBUS_G_PROXY(proxy), &interfaces, &error)) { g_warning("GetInterfaces failed: %s\n", error->message); g_error_free(error); return TRUE; } /* Initialize the interface objects for this TpConn object */ tp_conn_local_set_interfaces(tp_conn, interfaces); g_strfreev(interfaces); dbus_g_proxy_disconnect_signal(proxy, "StatusChanged", G_CALLBACK(tp_conn_status_change_handler), NULL); return TRUE; } return TRUE; }