1 /*
2  * text-factory.c - Text channel factory for SIP connection manager
3  * Copyright (C) 2007 Collabora Ltd.
4  * Copyright (C) 2007 Nokia Corporation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * version 2.1 as published by the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 
21 #include <telepathy-glib/svc-connection.h>
22 #include <telepathy-glib/interfaces.h>
23 #include <string.h>
24 #include "text-factory.h"
25 
26 #define DEBUG_FLAG SIP_DEBUG_IM
27 #include "debug.h"
28 
29 
30 static void factory_iface_init (gpointer, gpointer);
31 
32 G_DEFINE_TYPE_WITH_CODE (SIPTextFactory, sip_text_factory,
33     G_TYPE_OBJECT,
34     G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE,
35       factory_iface_init))
36 
37 enum
38 {
39   PROP_CONNECTION = 1,
40   LAST_PROPERTY
41 };
42 
43 typedef struct _SIPTextFactoryPrivate SIPTextFactoryPrivate;
44 struct _SIPTextFactoryPrivate
45 {
46   SIPConnection *conn;
47   /* guint handle => SIPTextChannel *channel */
48   GHashTable *channels;
49 
50   gboolean dispose_has_run;
51 };
52 
53 #define SIP_TEXT_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SIP_TYPE_TEXT_FACTORY, SIPTextFactoryPrivate))
54 
55 static void
sip_text_factory_init(SIPTextFactory * fac)56 sip_text_factory_init (SIPTextFactory *fac)
57 {
58   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
59 
60   priv->conn = NULL;
61   priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
62       NULL, g_object_unref);
63 
64   priv->dispose_has_run = FALSE;
65 }
66 
67 static void
sip_text_factory_dispose(GObject * object)68 sip_text_factory_dispose (GObject *object)
69 {
70   SIPTextFactory *fac = SIP_TEXT_FACTORY (object);
71   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
72 
73   if (priv->dispose_has_run)
74     return;
75 
76   priv->dispose_has_run = TRUE;
77 
78   tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object));
79 
80   g_assert (priv->channels == NULL);
81 
82   if (G_OBJECT_CLASS (sip_text_factory_parent_class)->dispose)
83     G_OBJECT_CLASS (sip_text_factory_parent_class)->dispose (object);
84 }
85 
86 static void
sip_text_factory_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)87 sip_text_factory_get_property (GObject *object,
88                                guint property_id,
89                                GValue *value,
90                                GParamSpec *pspec)
91 {
92   SIPTextFactory *fac = SIP_TEXT_FACTORY (object);
93   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
94 
95   switch (property_id) {
96     case PROP_CONNECTION:
97       g_value_set_object (value, priv->conn);
98       break;
99     default:
100       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
101       break;
102   }
103 }
104 
105 static void
sip_text_factory_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)106 sip_text_factory_set_property (GObject *object,
107                                guint property_id,
108                                const GValue *value,
109                                GParamSpec *pspec)
110 {
111   SIPTextFactory *fac = SIP_TEXT_FACTORY (object);
112   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
113 
114   switch (property_id) {
115     case PROP_CONNECTION:
116       priv->conn = g_value_get_object (value);
117       break;
118     default:
119       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
120       break;
121   }
122 }
123 
124 static void
sip_text_factory_class_init(SIPTextFactoryClass * klass)125 sip_text_factory_class_init (SIPTextFactoryClass *klass)
126 {
127   GObjectClass *object_class = G_OBJECT_CLASS (klass);
128   GParamSpec *param_spec;
129 
130   g_type_class_add_private (klass, sizeof (SIPTextFactoryPrivate));
131 
132   object_class->get_property = sip_text_factory_get_property;
133   object_class->set_property = sip_text_factory_set_property;
134   object_class->dispose = sip_text_factory_dispose;
135 
136   param_spec = g_param_spec_object ("connection", "SIPConnection object",
137                                     "SIP connection that owns this text "
138                                     "channel factory",
139                                     SIP_TYPE_CONNECTION,
140                                     G_PARAM_CONSTRUCT_ONLY |
141                                     G_PARAM_READWRITE |
142                                     G_PARAM_STATIC_NICK |
143                                     G_PARAM_STATIC_BLURB);
144   g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
145 }
146 
147 static gboolean
sip_text_factory_close_one(gpointer key,gpointer data,gpointer user_data)148 sip_text_factory_close_one (gpointer key,
149                             gpointer data,
150                             gpointer user_data)
151 {
152   sip_text_channel_close (SIP_TEXT_CHANNEL(data));
153   return TRUE;
154 }
155 
156 static void
sip_text_factory_close_all(TpChannelFactoryIface * iface)157 sip_text_factory_close_all (TpChannelFactoryIface *iface)
158 {
159   SIPTextFactory *fac = SIP_TEXT_FACTORY (iface);
160   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
161   GHashTable *channels;
162 
163   if (!priv->channels)
164     return;
165 
166   channels = priv->channels;
167   priv->channels = NULL;
168 
169   g_hash_table_foreach_remove (channels, sip_text_factory_close_one, NULL);
170 }
171 
172 static void
sip_text_factory_connecting(TpChannelFactoryIface * iface)173 sip_text_factory_connecting (TpChannelFactoryIface *iface)
174 {
175 }
176 
177 static void
sip_text_factory_connected(TpChannelFactoryIface * iface)178 sip_text_factory_connected (TpChannelFactoryIface *iface)
179 {
180 }
181 
182 static void
sip_text_factory_disconnected(TpChannelFactoryIface * iface)183 sip_text_factory_disconnected (TpChannelFactoryIface *iface)
184 {
185 }
186 
187 struct _ForeachData
188 {
189   TpChannelFunc foreach;
190   gpointer user_data;
191 };
192 
193 static void
_foreach_slave(gpointer key,gpointer value,gpointer user_data)194 _foreach_slave (gpointer key, gpointer value, gpointer user_data)
195 {
196   struct _ForeachData *data = (struct _ForeachData *)user_data;
197   TpChannelIface *chan = TP_CHANNEL_IFACE (value);
198 
199   data->foreach (chan, data->user_data);
200 }
201 
202 static void
sip_text_factory_foreach(TpChannelFactoryIface * iface,TpChannelFunc foreach,gpointer user_data)203 sip_text_factory_foreach (TpChannelFactoryIface *iface,
204                           TpChannelFunc foreach,
205                           gpointer user_data)
206 {
207   struct _ForeachData data = { foreach, user_data };
208   SIPTextFactory *fac = SIP_TEXT_FACTORY (iface);
209   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
210 
211   g_hash_table_foreach (priv->channels, _foreach_slave, &data);
212 }
213 
214 /**
215  * text_channel_closed_cb:
216  *
217  * Signal callback for when a text channel is closed. Removes the references
218  * that #SIPChannelFactory holds to them.
219  */
220 static void
channel_closed(SIPTextChannel * chan,gpointer user_data)221 channel_closed (SIPTextChannel *chan, gpointer user_data)
222 {
223   SIPTextFactory *fac = SIP_TEXT_FACTORY (user_data);
224   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
225   TpHandle contact_handle;
226 
227   g_object_get (chan, "handle", &contact_handle, NULL);
228   DEBUG("removing text channel with handle %u", contact_handle);
229 
230   if (priv->channels)
231     g_hash_table_remove (priv->channels, GINT_TO_POINTER (contact_handle));
232 }
233 
234 /**
235  * new_text_channel
236  *
237  * Creates a new empty SIPTextChannel.
238  */
239 SIPTextChannel *
sip_text_factory_new_channel(TpChannelFactoryIface * iface,TpHandle handle,gpointer request)240 sip_text_factory_new_channel (TpChannelFactoryIface *iface,
241                               TpHandle handle,
242                               gpointer request)
243 {
244   SIPTextFactory *fac = SIP_TEXT_FACTORY (iface);
245   SIPTextFactoryPrivate *priv;
246   SIPTextChannel *chan;
247   gchar *object_path;
248   TpBaseConnection *conn;
249 
250   g_assert (SIP_IS_TEXT_FACTORY (fac));
251 
252   priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
253   conn = (TpBaseConnection *)(priv->conn);
254 
255   object_path = g_strdup_printf ("%s/TextChannel%u",
256       conn->object_path, handle);
257 
258   g_debug ("%s: object path %s", G_STRFUNC, object_path);
259 
260   chan = g_object_new (SIP_TYPE_TEXT_CHANNEL,
261                        "connection", priv->conn,
262                        "object-path", object_path,
263                        "handle", handle,
264                        NULL);
265 
266   g_free (object_path);
267 
268   g_signal_connect (chan, "closed", (GCallback) channel_closed, fac);
269 
270   g_hash_table_insert (priv->channels, GUINT_TO_POINTER (handle), chan);
271 
272   tp_channel_factory_iface_emit_new_channel (fac, (TpChannelIface *)chan,
273       request);
274 
275   return chan;
276 }
277 
278 SIPTextChannel *
sip_text_factory_lookup_channel(TpChannelFactoryIface * iface,guint handle)279 sip_text_factory_lookup_channel (TpChannelFactoryIface *iface,
280                                  guint handle)
281 {
282   SIPTextFactory *fac = SIP_TEXT_FACTORY (iface);
283   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
284 
285   return (SIPTextChannel *)g_hash_table_lookup (priv->channels,
286       GUINT_TO_POINTER(handle));
287 }
288 
289 static TpChannelFactoryRequestStatus
sip_text_factory_request(TpChannelFactoryIface * iface,const gchar * chan_type,TpHandleType handle_type,guint handle,gpointer request,TpChannelIface ** ret,GError ** error)290 sip_text_factory_request (TpChannelFactoryIface *iface,
291                           const gchar *chan_type,
292                           TpHandleType handle_type,
293                           guint handle,
294                           gpointer request,
295                           TpChannelIface **ret,
296                           GError **error)
297 {
298   SIPTextFactory *fac = SIP_TEXT_FACTORY (iface);
299   SIPTextFactoryPrivate *priv = SIP_TEXT_FACTORY_GET_PRIVATE (fac);
300   TpChannelIface *chan;
301   TpChannelFactoryRequestStatus status;
302 
303   if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT))
304     {
305       return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED;
306     }
307 
308   if (handle_type != TP_HANDLE_TYPE_CONTACT)
309     {
310       return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE;
311     }
312 
313   status = TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING;
314   chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle));
315   if (!chan)
316     {
317       chan = (TpChannelIface *)sip_text_factory_new_channel (iface, handle,
318           request);
319       status = TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED;
320     }
321   *ret = chan;
322   return status;
323 }
324 
325 static void
factory_iface_init(gpointer g_iface,gpointer iface_data)326 factory_iface_init (gpointer g_iface, gpointer iface_data)
327 {
328   TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface;
329 
330 #define IMPLEMENT(x) klass->x = sip_text_factory_##x
331   IMPLEMENT(close_all);
332   IMPLEMENT(foreach);
333   IMPLEMENT(request);
334   IMPLEMENT(connecting);
335   IMPLEMENT(connected);
336   IMPLEMENT(disconnected);
337 #undef IMPLEMENT
338 }
339