/* * jingle-mint.c - creates and configures a WockyJingleFactory * Copyright ©2012 Collabora Ltd. * * 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 * * * * "Mint" is intended in the manufacturing sense: a mint is a factory which * produces coins. . It was chosen * in favour of "factory" because this is a "factory factory"; and in favour of * "foundry" to make JingleFactory and this class have different initials. */ #include "config.h" #include "jingle-mint.h" #define DEBUG_FLAG GABBLE_DEBUG_MEDIA #include "debug.h" #include "connection.h" #include "conn-presence.h" #include "jingle-share.h" #include "presence-cache.h" struct _GabbleJingleMintPrivate { GabbleConnection *conn; WockyJingleFactory *factory; }; enum { INCOMING_SESSION = 0, N_SIGNALS }; static guint signals[N_SIGNALS]; enum { PROP_CONNECTION = 1, }; static void connection_status_changed_cb ( GabbleConnection *conn, guint status, guint reason, gpointer user_data); static void connection_porter_available_cb ( GabbleConnection *conn, WockyPorter *porter, gpointer user_data); static void factory_new_session_cb ( WockyJingleFactory *factory, WockyJingleSession *session, gboolean initiated_locally, gpointer user_data); static gboolean factory_query_cap_cb ( WockyJingleFactory *factory, WockyContact *contact, const gchar *cap_or_quirk, gpointer user_data); G_DEFINE_TYPE (GabbleJingleMint, gabble_jingle_mint, G_TYPE_OBJECT) static void gabble_jingle_mint_init (GabbleJingleMint *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_JINGLE_MINT, GabbleJingleMintPrivate); } static void gabble_jingle_mint_get_property ( GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (object); GabbleJingleMintPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: g_value_set_object (value, priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void gabble_jingle_mint_set_property ( GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (object); GabbleJingleMintPrivate *priv = self->priv; switch (property_id) { case PROP_CONNECTION: priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void gabble_jingle_mint_constructed (GObject *object) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (object); GabbleJingleMintPrivate *priv = self->priv; GObjectClass *parent_class = gabble_jingle_mint_parent_class; if (parent_class->constructed != NULL) parent_class->constructed (object); tp_g_signal_connect_object (priv->conn, "status-changed", (GCallback) connection_status_changed_cb, self, 0); tp_g_signal_connect_object (priv->conn, "porter-available", (GCallback) connection_porter_available_cb, self, 0); } static void gabble_jingle_mint_dispose (GObject *object) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (object); GabbleJingleMintPrivate *priv = self->priv; GObjectClass *parent_class = gabble_jingle_mint_parent_class; g_clear_object (&priv->factory); if (parent_class->dispose != NULL) parent_class->dispose (object); } static void gabble_jingle_mint_class_init (GabbleJingleMintClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; object_class->get_property = gabble_jingle_mint_get_property; object_class->set_property = gabble_jingle_mint_set_property; object_class->constructed = gabble_jingle_mint_constructed; object_class->dispose = gabble_jingle_mint_dispose; g_type_class_add_private (klass, sizeof (GabbleJingleMintPrivate)); param_spec = g_param_spec_object ("connection", "GabbleConnection object", "Gabble connection object that uses this JingleMint object", GABBLE_TYPE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); signals[INCOMING_SESSION] = g_signal_new ("incoming-session", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, WOCKY_TYPE_JINGLE_SESSION); } GabbleJingleMint * gabble_jingle_mint_new ( GabbleConnection *connection) { return g_object_new (GABBLE_TYPE_JINGLE_MINT, "connection", connection, NULL); } static void connection_status_changed_cb ( GabbleConnection *conn, guint status, guint reason, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); GabbleJingleMintPrivate *priv = self->priv; switch (status) { case TP_CONNECTION_STATUS_CONNECTING: g_assert (priv->conn != NULL); break; case TP_CONNECTION_STATUS_CONNECTED: { WockyJingleInfo *info = gabble_jingle_mint_get_info (self); gchar *stun_server = NULL; guint stun_port = 0; g_object_get (priv->conn, "stun-server", &stun_server, "stun-port", &stun_port, NULL); if (stun_server != NULL) wocky_jingle_info_take_stun_server (info, stun_server, stun_port, FALSE); g_object_get (priv->conn, "fallback-stun-server", &stun_server, "fallback-stun-port", &stun_port, NULL); if (stun_server != NULL) wocky_jingle_info_take_stun_server (info, stun_server, stun_port, TRUE); wocky_jingle_info_send_request (info, /* FIXME: one day Wocky will know about caps and then we won't * have to pass in a flag here. */ !!(priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO)); } break; case TP_CONNECTION_STATUS_DISCONNECTED: if (priv->factory != NULL) wocky_jingle_factory_stop (priv->factory); break; } } static void connection_porter_available_cb ( GabbleConnection *conn, WockyPorter *porter, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); GabbleJingleMintPrivate *priv = self->priv; /* If we have a WockyPorter, we should definitely have a WockySession */ g_assert (conn->session != NULL); g_assert (priv->factory == NULL); priv->factory = wocky_jingle_factory_new (conn->session); jingle_share_register (priv->factory); tp_g_signal_connect_object (priv->factory, "new-session", (GCallback) factory_new_session_cb, self, 0); tp_g_signal_connect_object (priv->factory, "query-cap", (GCallback) factory_query_cap_cb, self, 0); } static void session_about_to_initiate_cb ( WockyJingleSession *session, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); GabbleJingleMintPrivate *priv = self->priv; const gchar *peer_jid = wocky_jingle_session_get_peer_jid (session); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); TpHandle peer = tp_handle_ensure (contact_repo, peer_jid, NULL, NULL); /* send directed presence (including our own caps, avatar etc.) to * the peer, if we aren't already visible to them */ if (!conn_presence_visible_to (priv->conn, peer)) conn_presence_signal_own_presence (priv->conn, peer_jid, NULL); } static void factory_new_session_cb ( WockyJingleFactory *factory, WockyJingleSession *session, gboolean initiated_locally, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); if (initiated_locally) tp_g_signal_connect_object (session, "about-to-initiate", (GCallback) session_about_to_initiate_cb, self, 0); /* Proxy the signal outwards if this is a new incoming session. */ if (!initiated_locally) g_signal_emit (self, signals[INCOMING_SESSION], 0, session); } static gboolean factory_query_cap_cb ( WockyJingleFactory *factory, WockyContact *contact, const gchar *cap_or_quirk, gpointer user_data) { GabbleJingleMint *self = GABBLE_JINGLE_MINT (user_data); GabbleJingleMintPrivate *priv = self->priv; GabblePresence *presence = gabble_presence_cache_get_for_contact ( priv->conn->presence_cache, contact); if (presence == NULL) return FALSE; if (WOCKY_IS_RESOURCE_CONTACT (contact)) { const gchar *peer_resource = wocky_resource_contact_get_resource ( WOCKY_RESOURCE_CONTACT (contact)); return gabble_presence_resource_has_caps (presence, peer_resource, gabble_capability_set_predicate_has, cap_or_quirk); } else { return gabble_presence_has_cap (presence, cap_or_quirk); } } WockyJingleFactory * gabble_jingle_mint_get_factory ( GabbleJingleMint *self) { return self->priv->factory; } WockyJingleInfo * gabble_jingle_mint_get_info ( GabbleJingleMint *self) { return wocky_jingle_factory_get_jingle_info (self->priv->factory); }