|
1 /* |
|
2 * im-factory.c - Source for GabbleImFactory |
|
3 * Copyright (C) 2006 Collabora Ltd. |
|
4 * |
|
5 * This library is free software; you can redistribute it and/or |
|
6 * modify it under the terms of the GNU Lesser General Public |
|
7 * License as published by the Free Software Foundation; either |
|
8 * version 2.1 of the License, or (at your option) any later version. |
|
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 Free Software |
|
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 */ |
|
19 |
|
20 |
|
21 #include <stdlib.h> |
|
22 #include <string.h> |
|
23 #include <time.h> |
|
24 |
|
25 #include <glib.h> |
|
26 |
|
27 #include <dbus/dbus-glib.h> |
|
28 #include <dbus/dbus-glib-lowlevel.h> |
|
29 |
|
30 #include "loudmouth/loudmouth.h" |
|
31 |
|
32 #include "debug.h" |
|
33 #include "disco.h" |
|
34 #include "gabble-connection.h" |
|
35 #include "gabble-im-channel.h" |
|
36 #include "handles.h" |
|
37 #include "telepathy-interfaces.h" |
|
38 #include "text-mixin.h" |
|
39 #include "tp-channel-factory-iface.h" |
|
40 |
|
41 #include "im-factory.h" |
|
42 |
|
43 static void gabble_im_factory_iface_init (gpointer g_iface, gpointer iface_data); |
|
44 |
|
45 #ifndef EMULATOR |
|
46 G_DEFINE_TYPE_WITH_CODE (GabbleImFactory, gabble_im_factory, G_TYPE_OBJECT, |
|
47 G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, gabble_im_factory_iface_init)); |
|
48 #endif |
|
49 |
|
50 #ifdef EMULATOR |
|
51 #include "libgabble_wsd_solution.h" |
|
52 |
|
53 GET_STATIC_VAR_FROM_TLS(gabble_im_factory_parent_class,im_factory,gpointer) |
|
54 #define gabble_im_factory_parent_class (*GET_WSD_VAR_NAME(gabble_im_factory_parent_class,im_factory,s)()) |
|
55 |
|
56 GET_STATIC_VAR_FROM_TLS(g_define_type_id,im_factory,GType) |
|
57 #define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,im_factory,s)()) |
|
58 |
|
59 static void gabble_im_factory_init (GabbleImFactory *self); |
|
60 static void gabble_im_factory_class_init (GabbleImFactoryClass *klass); |
|
61 static void gabble_im_factory_class_intern_init (gpointer klass) |
|
62 { |
|
63 gabble_im_factory_parent_class = g_type_class_peek_parent (klass); |
|
64 gabble_im_factory_class_init ((GabbleImFactoryClass*) klass); |
|
65 } |
|
66 EXPORT_C GType gabble_im_factory_get_type (void) |
|
67 { |
|
68 if ((g_define_type_id == 0)) { static const GTypeInfo g_define_type_info = { sizeof (GabbleImFactoryClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_im_factory_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleImFactory), 0, (GInstanceInitFunc) gabble_im_factory_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleImFactory"), &g_define_type_info, (GTypeFlags) 0); { { static const GInterfaceInfo g_implement_interface_info = { (GInterfaceInitFunc) gabble_im_factory_iface_init }; g_type_add_interface_static (g_define_type_id, tp_channel_factory_iface_get_type(), &g_implement_interface_info); } ; } } return g_define_type_id; } ; |
|
69 |
|
70 #endif |
|
71 |
|
72 #define DBUS_API_SUBJECT_TO_CHANGE |
|
73 #define DEBUG_FLAG GABBLE_DEBUG_IM |
|
74 |
|
75 #ifdef DEBUG_FLAG |
|
76 //#define DEBUG(format, ...) |
|
77 #define DEBUGGING 0 |
|
78 #define NODE_DEBUG(n, s) |
|
79 #endif /* DEBUG_FLAG */ |
|
80 |
|
81 /* properties */ |
|
82 enum |
|
83 { |
|
84 PROP_CONNECTION = 1, |
|
85 LAST_PROPERTY |
|
86 }; |
|
87 |
|
88 typedef struct _GabbleImFactoryPrivate GabbleImFactoryPrivate; |
|
89 struct _GabbleImFactoryPrivate |
|
90 { |
|
91 GabbleConnection *conn; |
|
92 LmMessageHandler *message_cb; |
|
93 GHashTable *channels; |
|
94 |
|
95 gboolean dispose_has_run; |
|
96 }; |
|
97 |
|
98 #define GABBLE_IM_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_IM_FACTORY, GabbleImFactoryPrivate)) |
|
99 |
|
100 static GObject *gabble_im_factory_constructor (GType type, guint n_props, GObjectConstructParam *props); |
|
101 |
|
102 static void |
|
103 gabble_im_factory_init (GabbleImFactory *fac) |
|
104 { |
|
105 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
106 |
|
107 priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, |
|
108 NULL, g_object_unref); |
|
109 |
|
110 priv->message_cb = NULL; |
|
111 |
|
112 priv->conn = NULL; |
|
113 priv->dispose_has_run = FALSE; |
|
114 } |
|
115 |
|
116 static GObject * |
|
117 gabble_im_factory_constructor (GType type, guint n_props, |
|
118 GObjectConstructParam *props) |
|
119 { |
|
120 GObject *obj; |
|
121 /* GabbleImFactoryPrivate *priv; */ |
|
122 |
|
123 obj = G_OBJECT_CLASS (gabble_im_factory_parent_class)-> |
|
124 constructor (type, n_props, props); |
|
125 /* priv = GABBLE_IM_FACTORY_GET_PRIVATE (obj); */ |
|
126 |
|
127 return obj; |
|
128 } |
|
129 |
|
130 |
|
131 static void |
|
132 gabble_im_factory_dispose (GObject *object) |
|
133 { |
|
134 GabbleImFactory *fac = GABBLE_IM_FACTORY (object); |
|
135 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
136 |
|
137 if (priv->dispose_has_run) |
|
138 return; |
|
139 |
|
140 gabble_debug (DEBUG_FLAG, "dispose called"); |
|
141 priv->dispose_has_run = TRUE; |
|
142 |
|
143 tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object)); |
|
144 g_assert (priv->channels == NULL); |
|
145 |
|
146 if (G_OBJECT_CLASS (gabble_im_factory_parent_class)->dispose) |
|
147 G_OBJECT_CLASS (gabble_im_factory_parent_class)->dispose (object); |
|
148 } |
|
149 |
|
150 static void |
|
151 gabble_im_factory_get_property (GObject *object, |
|
152 guint property_id, |
|
153 GValue *value, |
|
154 GParamSpec *pspec) |
|
155 { |
|
156 GabbleImFactory *fac = GABBLE_IM_FACTORY (object); |
|
157 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
158 |
|
159 switch (property_id) { |
|
160 case PROP_CONNECTION: |
|
161 g_value_set_object (value, priv->conn); |
|
162 break; |
|
163 default: |
|
164 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
165 break; |
|
166 } |
|
167 } |
|
168 |
|
169 static void |
|
170 gabble_im_factory_set_property (GObject *object, |
|
171 guint property_id, |
|
172 const GValue *value, |
|
173 GParamSpec *pspec) |
|
174 { |
|
175 GabbleImFactory *fac = GABBLE_IM_FACTORY (object); |
|
176 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
177 |
|
178 switch (property_id) { |
|
179 case PROP_CONNECTION: |
|
180 priv->conn = g_value_get_object (value); |
|
181 break; |
|
182 default: |
|
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
184 break; |
|
185 } |
|
186 } |
|
187 |
|
188 static void |
|
189 gabble_im_factory_class_init (GabbleImFactoryClass *gabble_im_factory_class) |
|
190 { |
|
191 GObjectClass *object_class = G_OBJECT_CLASS (gabble_im_factory_class); |
|
192 GParamSpec *param_spec; |
|
193 |
|
194 g_type_class_add_private (gabble_im_factory_class, sizeof (GabbleImFactoryPrivate)); |
|
195 |
|
196 object_class->constructor = gabble_im_factory_constructor; |
|
197 object_class->dispose = gabble_im_factory_dispose; |
|
198 |
|
199 object_class->get_property = gabble_im_factory_get_property; |
|
200 object_class->set_property = gabble_im_factory_set_property; |
|
201 |
|
202 param_spec = g_param_spec_object ("connection", "GabbleConnection object", |
|
203 "Gabble connection object that owns this " |
|
204 "IM channel factory object.", |
|
205 GABBLE_TYPE_CONNECTION, |
|
206 G_PARAM_CONSTRUCT_ONLY | |
|
207 G_PARAM_READWRITE | |
|
208 G_PARAM_STATIC_NICK | |
|
209 G_PARAM_STATIC_BLURB); |
|
210 g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); |
|
211 |
|
212 } |
|
213 |
|
214 static GabbleIMChannel *new_im_channel (GabbleImFactory *fac, GabbleHandle handle); |
|
215 |
|
216 static void im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data); |
|
217 |
|
218 |
|
219 /** |
|
220 * im_factory_message_cb: |
|
221 * |
|
222 * Called by loudmouth when we get an incoming <message>. |
|
223 */ |
|
224 static LmHandlerResult |
|
225 im_factory_message_cb (LmMessageHandler *handler, |
|
226 LmConnection *lmconn, |
|
227 LmMessage *message, |
|
228 gpointer user_data) |
|
229 { |
|
230 GabbleImFactory *fac = GABBLE_IM_FACTORY (user_data); |
|
231 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
232 |
|
233 const gchar *from, *body, *body_offset; |
|
234 time_t stamp; |
|
235 TpChannelTextMessageType msgtype; |
|
236 GabbleHandle handle; |
|
237 GabbleIMChannel *chan; |
|
238 GabbleTextMixinSendError send_error; |
|
239 |
|
240 if (!gabble_text_mixin_parse_incoming_message (message, &from, &stamp, &msgtype, &body, &body_offset, &send_error)) |
|
241 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
242 |
|
243 if (body == NULL) |
|
244 { |
|
245 NODE_DEBUG (message->node, "got a message without a body field, ignoring"); |
|
246 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
247 } |
|
248 |
|
249 handle = gabble_handle_for_contact (priv->conn->handles, from, FALSE); |
|
250 if (handle == 0) |
|
251 { |
|
252 NODE_DEBUG (message->node, "ignoring message node from malformed jid"); |
|
253 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
254 } |
|
255 |
|
256 gabble_debug (DEBUG_FLAG, "message from %s (handle %u), msgtype %d, body:\n%s", |
|
257 from, handle, msgtype, body_offset); |
|
258 |
|
259 chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)); |
|
260 |
|
261 if (chan == NULL) |
|
262 { |
|
263 if (send_error != CHANNEL_TEXT_SEND_NO_ERROR) |
|
264 { |
|
265 gabble_debug (DEBUG_FLAG, "ignoring message error; no sending channel"); |
|
266 return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
267 } |
|
268 |
|
269 gabble_debug (DEBUG_FLAG, "found no IM channel, creating one"); |
|
270 |
|
271 chan = new_im_channel (fac, handle); |
|
272 } |
|
273 |
|
274 if (send_error != CHANNEL_TEXT_SEND_NO_ERROR) |
|
275 { |
|
276 _gabble_text_mixin_send_error_signal (G_OBJECT (chan), send_error, stamp, |
|
277 msgtype, body_offset); |
|
278 return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
279 } |
|
280 |
|
281 if (_gabble_im_channel_receive (chan, msgtype, handle, from, |
|
282 stamp, body_offset)) |
|
283 return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
284 |
|
285 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
286 } |
|
287 |
|
288 /** |
|
289 * im_channel_closed_cb: |
|
290 * |
|
291 * Signal callback for when an IM channel is closed. Removes the references |
|
292 * that #GabbleConnection holds to them. |
|
293 */ |
|
294 static void |
|
295 im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data) |
|
296 { |
|
297 GabbleImFactory *conn = GABBLE_IM_FACTORY (user_data); |
|
298 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (conn); |
|
299 GabbleHandle contact_handle; |
|
300 |
|
301 if (priv->channels) |
|
302 { |
|
303 g_object_get (chan, "handle", &contact_handle, NULL); |
|
304 |
|
305 gabble_debug (DEBUG_FLAG, "removing channel with handle %d", contact_handle); |
|
306 |
|
307 g_hash_table_remove (priv->channels, GINT_TO_POINTER (contact_handle)); |
|
308 } |
|
309 } |
|
310 |
|
311 /** |
|
312 * new_im_channel |
|
313 */ |
|
314 static GabbleIMChannel * |
|
315 new_im_channel (GabbleImFactory *fac, GabbleHandle handle) |
|
316 { |
|
317 GabbleImFactoryPrivate *priv; |
|
318 GabbleIMChannel *chan; |
|
319 char *object_path; |
|
320 |
|
321 g_assert (GABBLE_IS_IM_FACTORY (fac)); |
|
322 |
|
323 priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
324 |
|
325 object_path = g_strdup_printf ("%s/ImChannel%u", priv->conn->object_path, handle); |
|
326 |
|
327 chan = g_object_new (GABBLE_TYPE_IM_CHANNEL, |
|
328 "connection", priv->conn, |
|
329 "object-path", object_path, |
|
330 "handle", handle, |
|
331 NULL); |
|
332 |
|
333 gabble_debug (DEBUG_FLAG, "object path %s", object_path); |
|
334 |
|
335 g_signal_connect (chan, "closed", (GCallback) im_channel_closed_cb, fac); |
|
336 |
|
337 g_hash_table_insert (priv->channels, GINT_TO_POINTER (handle), chan); |
|
338 |
|
339 g_signal_emit_by_name (fac, "new-channel", chan); |
|
340 |
|
341 g_free (object_path); |
|
342 |
|
343 return chan; |
|
344 } |
|
345 |
|
346 static void |
|
347 gabble_im_factory_iface_close_all (TpChannelFactoryIface *iface) |
|
348 { |
|
349 GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); |
|
350 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
351 |
|
352 gabble_debug (DEBUG_FLAG, "closing channels"); |
|
353 |
|
354 if (priv->channels) |
|
355 { |
|
356 GHashTable *tmp = priv->channels; |
|
357 priv->channels = NULL; |
|
358 g_hash_table_destroy (tmp); |
|
359 } |
|
360 } |
|
361 |
|
362 static void |
|
363 gabble_im_factory_iface_connecting (TpChannelFactoryIface *iface) |
|
364 { |
|
365 GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); |
|
366 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
367 |
|
368 gabble_debug (DEBUG_FLAG, "adding callbacks"); |
|
369 |
|
370 g_assert (priv->message_cb == NULL); |
|
371 |
|
372 priv->message_cb = lm_message_handler_new (im_factory_message_cb, fac, NULL); |
|
373 lm_connection_register_message_handler (priv->conn->lmconn, priv->message_cb, |
|
374 LM_MESSAGE_TYPE_MESSAGE, |
|
375 LM_HANDLER_PRIORITY_LAST); |
|
376 } |
|
377 |
|
378 |
|
379 |
|
380 static void |
|
381 gabble_im_factory_iface_connected (TpChannelFactoryIface *iface) |
|
382 { |
|
383 /* nothing to do */ |
|
384 } |
|
385 |
|
386 static void |
|
387 gabble_im_factory_iface_disconnected (TpChannelFactoryIface *iface) |
|
388 { |
|
389 GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); |
|
390 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
391 |
|
392 gabble_debug (DEBUG_FLAG, "removing callbacks"); |
|
393 |
|
394 g_assert (priv->message_cb != NULL); |
|
395 |
|
396 lm_connection_unregister_message_handler (priv->conn->lmconn, priv->message_cb, |
|
397 LM_MESSAGE_TYPE_MESSAGE); |
|
398 lm_message_handler_unref (priv->message_cb); |
|
399 priv->message_cb = NULL; |
|
400 } |
|
401 |
|
402 struct _ForeachData |
|
403 { |
|
404 TpChannelFunc foreach; |
|
405 gpointer user_data; |
|
406 }; |
|
407 |
|
408 static void |
|
409 _foreach_slave (gpointer key, gpointer value, gpointer user_data) |
|
410 { |
|
411 struct _ForeachData *data = (struct _ForeachData *) user_data; |
|
412 TpChannelIface *chan = TP_CHANNEL_IFACE (value); |
|
413 |
|
414 data->foreach (chan, data->user_data); |
|
415 } |
|
416 |
|
417 static void |
|
418 gabble_im_factory_iface_foreach (TpChannelFactoryIface *iface, TpChannelFunc foreach, gpointer user_data) |
|
419 { |
|
420 GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); |
|
421 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
422 struct _ForeachData data; |
|
423 |
|
424 data.user_data = user_data; |
|
425 data.foreach = foreach; |
|
426 |
|
427 g_hash_table_foreach (priv->channels, _foreach_slave, &data); |
|
428 } |
|
429 |
|
430 static TpChannelFactoryRequestStatus |
|
431 gabble_im_factory_iface_request (TpChannelFactoryIface *iface, |
|
432 const gchar *chan_type, |
|
433 TpHandleType handle_type, |
|
434 guint handle, |
|
435 TpChannelIface **ret, |
|
436 GError **error) |
|
437 { |
|
438 GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); |
|
439 GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); |
|
440 GabbleIMChannel *chan; |
|
441 |
|
442 if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT)) |
|
443 return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED; |
|
444 |
|
445 if (handle_type != TP_HANDLE_TYPE_CONTACT) |
|
446 return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE; |
|
447 |
|
448 if (!gabble_handle_is_valid (priv->conn->handles, handle_type, handle, error)) |
|
449 return TP_CHANNEL_FACTORY_REQUEST_STATUS_ERROR; |
|
450 |
|
451 chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)); |
|
452 |
|
453 if (!chan) |
|
454 chan = new_im_channel (fac, handle); |
|
455 |
|
456 g_assert (chan); |
|
457 *ret = TP_CHANNEL_IFACE (chan); |
|
458 return TP_CHANNEL_FACTORY_REQUEST_STATUS_DONE; |
|
459 } |
|
460 |
|
461 static void |
|
462 gabble_im_factory_iface_init (gpointer g_iface, |
|
463 gpointer iface_data) |
|
464 { |
|
465 TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface; |
|
466 |
|
467 klass->close_all = gabble_im_factory_iface_close_all; |
|
468 klass->connecting = gabble_im_factory_iface_connecting; |
|
469 klass->connected = gabble_im_factory_iface_connected; |
|
470 klass->disconnected = gabble_im_factory_iface_disconnected; |
|
471 klass->foreach = gabble_im_factory_iface_foreach; |
|
472 klass->request = gabble_im_factory_iface_request; |
|
473 } |
|
474 |