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