1 /* |
|
2 * media-factory.c - Source for GabbleMediaFactory |
|
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 |
|
33 #include "debug.h" |
|
34 #include "gabble-connection.h" |
|
35 #include "gabble-media-channel.h" |
|
36 #include "handles.h" |
|
37 #include "media-factory.h" |
|
38 #include "namespaces.h" |
|
39 #include "telepathy-interfaces.h" |
|
40 #include "text-mixin.h" |
|
41 #include "tp-channel-factory-iface.h" |
|
42 #include "util.h" |
|
43 |
|
44 static void gabble_media_factory_iface_init (gpointer g_iface, gpointer iface_data); |
|
45 static LmHandlerResult media_factory_jingle_cb (LmMessageHandler*, LmConnection*, LmMessage*, gpointer); |
|
46 #ifndef EMULATOR |
|
47 G_DEFINE_TYPE_WITH_CODE (GabbleMediaFactory, gabble_media_factory, G_TYPE_OBJECT, |
|
48 G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, gabble_media_factory_iface_init)); |
|
49 |
|
50 #endif |
|
51 |
|
52 #ifdef EMULATOR |
|
53 #include "libgabble_wsd_solution.h" |
|
54 |
|
55 GET_STATIC_VAR_FROM_TLS(gabble_media_factory_parent_class,media_factory,gpointer) |
|
56 #define gabble_media_factory_parent_class (*GET_WSD_VAR_NAME(gabble_media_factory_parent_class,media_factory,s)()) |
|
57 |
|
58 GET_STATIC_VAR_FROM_TLS(g_define_type_id,media_factory,GType) |
|
59 #define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,media_factory,s)()) |
|
60 |
|
61 static void gabble_media_factory_init (GabbleMediaFactory *self); |
|
62 static void gabble_media_factory_class_init (GabbleMediaFactoryClass *klass); |
|
63 static void gabble_media_factory_class_intern_init (gpointer klass) |
|
64 { |
|
65 gabble_media_factory_parent_class = g_type_class_peek_parent (klass); |
|
66 gabble_media_factory_class_init ((GabbleMediaFactoryClass*) klass); |
|
67 } |
|
68 EXPORT_C GType gabble_media_factory_get_type (void) |
|
69 { |
|
70 if ((g_define_type_id == 0)) { static const GTypeInfo g_define_type_info = { sizeof (GabbleMediaFactoryClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_media_factory_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleMediaFactory), 0, (GInstanceInitFunc) gabble_media_factory_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleMediaFactory"), &g_define_type_info, (GTypeFlags) 0); { { static const GInterfaceInfo g_implement_interface_info = { (GInterfaceInitFunc) gabble_media_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; } ; |
|
71 #endif |
|
72 |
|
73 #define DBUS_API_SUBJECT_TO_CHANGE |
|
74 #define DEBUG_FLAG GABBLE_DEBUG_MEDIA |
|
75 |
|
76 #ifdef DEBUG_FLAG |
|
77 //#define DEBUG(format, ...) |
|
78 #define DEBUGGING 0 |
|
79 #define NODE_DEBUG(n, s) |
|
80 #endif /* DEBUG_FLAG */ |
|
81 |
|
82 /* properties */ |
|
83 enum |
|
84 { |
|
85 PROP_CONNECTION = 1, |
|
86 LAST_PROPERTY |
|
87 }; |
|
88 |
|
89 typedef struct _GabbleMediaFactoryPrivate GabbleMediaFactoryPrivate; |
|
90 struct _GabbleMediaFactoryPrivate |
|
91 { |
|
92 GabbleConnection *conn; |
|
93 LmMessageHandler *jingle_cb; |
|
94 |
|
95 GPtrArray *channels; |
|
96 guint channel_index; |
|
97 |
|
98 GHashTable *session_chans; |
|
99 |
|
100 gboolean dispose_has_run; |
|
101 }; |
|
102 |
|
103 #define GABBLE_MEDIA_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MEDIA_FACTORY, GabbleMediaFactoryPrivate)) |
|
104 |
|
105 static GObject *gabble_media_factory_constructor (GType type, guint n_props, GObjectConstructParam *props); |
|
106 |
|
107 static void |
|
108 gabble_media_factory_init (GabbleMediaFactory *fac) |
|
109 { |
|
110 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
111 |
|
112 priv->channels = g_ptr_array_sized_new (1); |
|
113 priv->channel_index = 0; |
|
114 |
|
115 priv->jingle_cb = NULL; |
|
116 |
|
117 priv->conn = NULL; |
|
118 priv->dispose_has_run = FALSE; |
|
119 |
|
120 priv->session_chans = g_hash_table_new_full (g_str_hash, g_str_equal, |
|
121 g_free, NULL); |
|
122 } |
|
123 |
|
124 static GObject * |
|
125 gabble_media_factory_constructor (GType type, guint n_props, |
|
126 GObjectConstructParam *props) |
|
127 { |
|
128 GObject *obj; |
|
129 GabbleMediaFactoryPrivate *priv; |
|
130 |
|
131 obj = G_OBJECT_CLASS (gabble_media_factory_parent_class)-> |
|
132 constructor (type, n_props, props); |
|
133 priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (obj); |
|
134 |
|
135 return obj; |
|
136 } |
|
137 |
|
138 |
|
139 static void |
|
140 gabble_media_factory_dispose (GObject *object) |
|
141 { |
|
142 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); |
|
143 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
144 |
|
145 if (priv->dispose_has_run) |
|
146 return; |
|
147 |
|
148 gabble_debug (DEBUG_FLAG, "dispose called"); |
|
149 priv->dispose_has_run = TRUE; |
|
150 |
|
151 tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object)); |
|
152 g_assert (priv->channels == NULL); |
|
153 |
|
154 if (priv->session_chans) |
|
155 { |
|
156 g_assert (g_hash_table_size (priv->session_chans) == 0); |
|
157 g_hash_table_destroy (priv->session_chans); |
|
158 priv->session_chans = NULL; |
|
159 } |
|
160 |
|
161 if (G_OBJECT_CLASS (gabble_media_factory_parent_class)->dispose) |
|
162 G_OBJECT_CLASS (gabble_media_factory_parent_class)->dispose (object); |
|
163 } |
|
164 |
|
165 static void |
|
166 gabble_media_factory_get_property (GObject *object, |
|
167 guint property_id, |
|
168 GValue *value, |
|
169 GParamSpec *pspec) |
|
170 { |
|
171 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); |
|
172 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
173 |
|
174 switch (property_id) { |
|
175 case PROP_CONNECTION: |
|
176 g_value_set_object (value, priv->conn); |
|
177 break; |
|
178 default: |
|
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
180 break; |
|
181 } |
|
182 } |
|
183 |
|
184 static void |
|
185 gabble_media_factory_set_property (GObject *object, |
|
186 guint property_id, |
|
187 const GValue *value, |
|
188 GParamSpec *pspec) |
|
189 { |
|
190 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); |
|
191 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
192 |
|
193 switch (property_id) { |
|
194 case PROP_CONNECTION: |
|
195 priv->conn = g_value_get_object (value); |
|
196 break; |
|
197 default: |
|
198 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
199 break; |
|
200 } |
|
201 } |
|
202 |
|
203 static void |
|
204 gabble_media_factory_class_init (GabbleMediaFactoryClass *gabble_media_factory_class) |
|
205 { |
|
206 GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_factory_class); |
|
207 GParamSpec *param_spec; |
|
208 |
|
209 g_type_class_add_private (gabble_media_factory_class, sizeof (GabbleMediaFactoryPrivate)); |
|
210 |
|
211 object_class->constructor = gabble_media_factory_constructor; |
|
212 object_class->dispose = gabble_media_factory_dispose; |
|
213 |
|
214 object_class->get_property = gabble_media_factory_get_property; |
|
215 object_class->set_property = gabble_media_factory_set_property; |
|
216 |
|
217 param_spec = g_param_spec_object ("connection", "GabbleConnection object", |
|
218 "Gabble connection object that owns this " |
|
219 "media channel factory object.", |
|
220 GABBLE_TYPE_CONNECTION, |
|
221 G_PARAM_CONSTRUCT_ONLY | |
|
222 G_PARAM_READWRITE | |
|
223 G_PARAM_STATIC_NICK | |
|
224 G_PARAM_STATIC_BLURB); |
|
225 g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); |
|
226 |
|
227 } |
|
228 |
|
229 static gboolean _gabble_media_factory_sid_in_use (GabbleMediaFactory *fac, const gchar *sid); |
|
230 static GabbleMediaChannel *new_media_channel (GabbleMediaFactory *fac, GabbleHandle handle); |
|
231 static void media_channel_closed_cb (GabbleMediaChannel *chan, gpointer user_data); |
|
232 |
|
233 /** |
|
234 * media_factory_jingle_cb |
|
235 * |
|
236 * Called by loudmouth when we get an incoming <iq>. This handler |
|
237 * is concerned only with jingle session queries, and allows other |
|
238 * handlers to be called for other queries. |
|
239 */ |
|
240 static LmHandlerResult |
|
241 media_factory_jingle_cb (LmMessageHandler *handler, |
|
242 LmConnection *lmconn, |
|
243 LmMessage *message, |
|
244 gpointer user_data) |
|
245 { |
|
246 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (user_data); |
|
247 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
248 LmMessageNode *iq_node, *session_node; |
|
249 const gchar *from, *id, *action, *sid; |
|
250 gchar *resource; |
|
251 GabbleHandle handle; |
|
252 GabbleMediaChannel *chan = NULL; |
|
253 gboolean chan_is_new = FALSE; |
|
254 GError *error = NULL; |
|
255 |
|
256 g_assert (lmconn == priv->conn->lmconn); |
|
257 |
|
258 /* all jingle actions are sets */ |
|
259 if (LM_MESSAGE_SUB_TYPE_SET != lm_message_get_sub_type (message)) |
|
260 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
261 |
|
262 /* is it for us? */ |
|
263 iq_node = lm_message_get_node (message); |
|
264 session_node = lm_message_node_get_child_with_namespace (message->node, |
|
265 "jingle", NS_JINGLE); |
|
266 |
|
267 if (session_node != NULL) |
|
268 { |
|
269 action = lm_message_node_get_attribute (session_node, "action"); |
|
270 } |
|
271 else |
|
272 { |
|
273 session_node = lm_message_node_get_child_with_namespace (iq_node, |
|
274 "session", NS_GOOGLE_SESSION); |
|
275 |
|
276 if (session_node == NULL) |
|
277 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
278 |
|
279 action = lm_message_node_get_attribute (session_node, "type"); |
|
280 } |
|
281 |
|
282 if (action == NULL) |
|
283 { |
|
284 NODE_DEBUG (iq_node, "session action not found"); |
|
285 goto BAD_REQUEST; |
|
286 } |
|
287 |
|
288 from = lm_message_node_get_attribute (iq_node, "from"); |
|
289 if (from == NULL) |
|
290 { |
|
291 NODE_DEBUG (iq_node, "'from' attribute not found"); |
|
292 goto BAD_REQUEST; |
|
293 } |
|
294 |
|
295 handle = gabble_handle_for_contact (priv->conn->handles, from, FALSE); |
|
296 if (handle == 0) |
|
297 { |
|
298 NODE_DEBUG (iq_node, "unable to get handle for sender"); |
|
299 goto BAD_REQUEST; |
|
300 } |
|
301 |
|
302 id = lm_message_node_get_attribute (iq_node, "id"); |
|
303 if (id == NULL) |
|
304 { |
|
305 NODE_DEBUG (iq_node, "'id' attribute not found"); |
|
306 goto BAD_REQUEST; |
|
307 } |
|
308 |
|
309 /* does the session exist? */ |
|
310 sid = lm_message_node_get_attribute (session_node, "sid"); |
|
311 if (sid == NULL) |
|
312 sid = lm_message_node_get_attribute (session_node, "id"); |
|
313 |
|
314 if (sid == NULL) |
|
315 { |
|
316 NODE_DEBUG (iq_node, "unable to get session id"); |
|
317 goto BAD_REQUEST; |
|
318 } |
|
319 |
|
320 if (_gabble_media_factory_sid_in_use (fac, sid)) |
|
321 { |
|
322 /* if it's media session, we should have it in here */ |
|
323 chan = g_hash_table_lookup (priv->session_chans, sid); |
|
324 } |
|
325 |
|
326 /* it's a new session */ |
|
327 if (chan == NULL) |
|
328 { |
|
329 /* if the session is unknown, the only allowed action is "initiate" */ |
|
330 if (g_strdiff (action, "initiate") && |
|
331 g_strdiff (action, "session-initiate")) |
|
332 { |
|
333 NODE_DEBUG (iq_node, |
|
334 "action is not \"initiate\" or \"session-initiate\", rejecting"); |
|
335 goto BAD_REQUEST; |
|
336 } |
|
337 |
|
338 gabble_debug (DEBUG_FLAG, "creating media channel"); |
|
339 |
|
340 chan = new_media_channel (fac, handle); |
|
341 chan_is_new = TRUE; |
|
342 } |
|
343 |
|
344 g_assert (chan != NULL); |
|
345 |
|
346 gabble_debug (DEBUG_FLAG, "dispatching to session %s", sid); |
|
347 g_object_ref (chan); |
|
348 gabble_decode_jid (from, NULL, NULL, &resource); |
|
349 |
|
350 if (_gabble_media_channel_dispatch_session_action (chan, handle, resource, |
|
351 sid, message, session_node, action, &error)) |
|
352 { |
|
353 if (chan_is_new) |
|
354 g_signal_emit_by_name (fac, "new-channel", chan); |
|
355 } |
|
356 else |
|
357 { |
|
358 if (chan_is_new) |
|
359 gabble_media_channel_close (chan, NULL); |
|
360 |
|
361 g_assert (error != NULL); |
|
362 _gabble_connection_send_iq_error (priv->conn, message, error->code, |
|
363 error->message); |
|
364 } |
|
365 |
|
366 g_object_unref (chan); |
|
367 g_free (resource); |
|
368 |
|
369 return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
370 |
|
371 BAD_REQUEST: |
|
372 _gabble_connection_send_iq_error ( |
|
373 priv->conn, message, XMPP_ERROR_BAD_REQUEST, NULL); |
|
374 |
|
375 return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
376 } |
|
377 |
|
378 static const gchar * |
|
379 _gabble_media_factory_get_unique_sid (GabbleMediaFactory *fac) |
|
380 { |
|
381 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
382 guint32 val; |
|
383 gchar *sid = NULL; |
|
384 gboolean unique = FALSE; |
|
385 |
|
386 while (!unique) |
|
387 { |
|
388 val = g_random_int_range (1000000, G_MAXINT); |
|
389 |
|
390 g_free (sid); |
|
391 sid = g_strdup_printf ("%u", val); |
|
392 |
|
393 unique = !_gabble_media_factory_sid_in_use (fac, sid); |
|
394 } |
|
395 |
|
396 g_hash_table_insert (priv->session_chans, sid, NULL); |
|
397 |
|
398 return sid; |
|
399 } |
|
400 |
|
401 static gboolean |
|
402 _gabble_media_factory_sid_in_use (GabbleMediaFactory *fac, const gchar *sid) |
|
403 { |
|
404 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
405 gpointer key, value; |
|
406 |
|
407 return g_hash_table_lookup_extended (priv->session_chans, sid, &key, &value); |
|
408 } |
|
409 |
|
410 const gchar * |
|
411 _gabble_media_factory_allocate_sid (GabbleMediaFactory *fac, GabbleMediaChannel *chan) |
|
412 { |
|
413 const gchar *sid = _gabble_media_factory_get_unique_sid (fac); |
|
414 |
|
415 g_return_val_if_fail (sid, NULL); |
|
416 |
|
417 return _gabble_media_factory_register_sid (fac, sid, chan); |
|
418 } |
|
419 |
|
420 const gchar * |
|
421 _gabble_media_factory_register_sid (GabbleMediaFactory *fac, |
|
422 const gchar *sid, |
|
423 GabbleMediaChannel *chan) |
|
424 { |
|
425 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
426 gchar *sid_copy = g_strdup (sid); |
|
427 |
|
428 g_hash_table_replace (priv->session_chans, sid_copy, chan); |
|
429 |
|
430 return sid_copy; |
|
431 } |
|
432 |
|
433 void |
|
434 _gabble_media_factory_free_sid (GabbleMediaFactory *fac, const gchar *sid) |
|
435 { |
|
436 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
437 if (g_hash_table_lookup (priv->session_chans, sid)) |
|
438 { |
|
439 g_hash_table_remove (priv->session_chans, sid); |
|
440 } |
|
441 } |
|
442 |
|
443 static gboolean |
|
444 _remove_sid_mapping (gpointer key, gpointer value, gpointer user_data) |
|
445 { |
|
446 GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (value); |
|
447 GabbleMediaChannel *target_chan = GABBLE_MEDIA_CHANNEL (user_data); |
|
448 |
|
449 if (chan == target_chan) return TRUE; |
|
450 return FALSE; |
|
451 } |
|
452 |
|
453 /** |
|
454 * media_channel_closed_cb: |
|
455 * |
|
456 * Signal callback for when a media channel is closed. Removes the references |
|
457 * that #GabbleMediaFactory holds to them. Also removes all the sessions for |
|
458 * the closed channel. |
|
459 */ |
|
460 static void |
|
461 media_channel_closed_cb (GabbleMediaChannel *chan, gpointer user_data) |
|
462 { |
|
463 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (user_data); |
|
464 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
465 |
|
466 if (priv->channels) |
|
467 { |
|
468 gabble_debug (DEBUG_FLAG, "removing media channel %p with ref count %d", |
|
469 chan, G_OBJECT (chan)->ref_count); |
|
470 |
|
471 g_ptr_array_remove (priv->channels, chan); |
|
472 g_object_unref (chan); |
|
473 } |
|
474 |
|
475 if (priv->session_chans) |
|
476 { |
|
477 g_hash_table_foreach_remove (priv->session_chans, _remove_sid_mapping, chan); |
|
478 } |
|
479 } |
|
480 |
|
481 /** |
|
482 * new_media_channel |
|
483 * |
|
484 * Creates a new empty GabbleMediaChannel. |
|
485 */ |
|
486 static GabbleMediaChannel * |
|
487 new_media_channel (GabbleMediaFactory *fac, GabbleHandle creator) |
|
488 { |
|
489 GabbleMediaFactoryPrivate *priv; |
|
490 GabbleMediaChannel *chan; |
|
491 gchar *object_path; |
|
492 |
|
493 g_assert (GABBLE_IS_MEDIA_FACTORY (fac)); |
|
494 |
|
495 priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
496 |
|
497 object_path = g_strdup_printf ("%s/MediaChannel%u", priv->conn->object_path, |
|
498 priv->channel_index); |
|
499 priv->channel_index += 1; |
|
500 |
|
501 chan = g_object_new (GABBLE_TYPE_MEDIA_CHANNEL, |
|
502 "connection", priv->conn, |
|
503 "factory", fac, |
|
504 "object-path", object_path, |
|
505 "creator", creator, |
|
506 NULL); |
|
507 |
|
508 gabble_debug (DEBUG_FLAG, "object path %s", object_path); |
|
509 |
|
510 g_signal_connect (chan, "closed", (GCallback) media_channel_closed_cb, fac); |
|
511 |
|
512 g_ptr_array_add (priv->channels, chan); |
|
513 |
|
514 g_free (object_path); |
|
515 |
|
516 return chan; |
|
517 } |
|
518 |
|
519 |
|
520 static void |
|
521 gabble_media_factory_iface_close_all (TpChannelFactoryIface *iface) |
|
522 { |
|
523 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); |
|
524 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
525 guint i; |
|
526 |
|
527 gabble_debug (DEBUG_FLAG, "closing channels"); |
|
528 |
|
529 if (priv->channels) |
|
530 { |
|
531 GPtrArray *tmp = priv->channels; |
|
532 priv->channels = NULL; |
|
533 |
|
534 |
|
535 for (i = 0; i < tmp->len; i++) |
|
536 { |
|
537 GabbleMediaChannel *chan = g_ptr_array_index (tmp, i); |
|
538 |
|
539 gabble_debug (DEBUG_FLAG, "about to unref channel with ref_count %d", |
|
540 G_OBJECT (chan)->ref_count); |
|
541 |
|
542 g_object_unref (chan); |
|
543 } |
|
544 |
|
545 g_ptr_array_free (tmp, TRUE); |
|
546 } |
|
547 |
|
548 if (priv->session_chans) |
|
549 { |
|
550 g_hash_table_destroy (priv->session_chans); |
|
551 priv->session_chans = NULL; |
|
552 } |
|
553 } |
|
554 |
|
555 static void |
|
556 gabble_media_factory_iface_connecting (TpChannelFactoryIface *iface) |
|
557 { |
|
558 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); |
|
559 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
560 |
|
561 g_assert(priv->conn != NULL); |
|
562 g_assert(priv->conn->lmconn != NULL); |
|
563 |
|
564 gabble_debug (DEBUG_FLAG, "adding callbacks"); |
|
565 |
|
566 priv->jingle_cb = lm_message_handler_new (media_factory_jingle_cb, fac, NULL); |
|
567 lm_connection_register_message_handler (priv->conn->lmconn, priv->jingle_cb, |
|
568 LM_MESSAGE_TYPE_IQ, |
|
569 LM_HANDLER_PRIORITY_NORMAL); |
|
570 } |
|
571 |
|
572 static void |
|
573 gabble_media_factory_iface_connected (TpChannelFactoryIface *iface) |
|
574 { |
|
575 /* nothing to do */ |
|
576 } |
|
577 |
|
578 static void |
|
579 gabble_media_factory_iface_disconnected (TpChannelFactoryIface *iface) |
|
580 { |
|
581 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); |
|
582 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
583 |
|
584 g_assert (priv->jingle_cb != NULL); |
|
585 |
|
586 gabble_debug (DEBUG_FLAG, "removing callbacks"); |
|
587 |
|
588 lm_connection_unregister_message_handler (priv->conn->lmconn, priv->jingle_cb, |
|
589 LM_MESSAGE_TYPE_IQ); |
|
590 lm_message_handler_unref (priv->jingle_cb); |
|
591 priv->jingle_cb = NULL; |
|
592 } |
|
593 |
|
594 static void |
|
595 gabble_media_factory_iface_foreach (TpChannelFactoryIface *iface, TpChannelFunc foreach, gpointer user_data) |
|
596 { |
|
597 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); |
|
598 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
599 guint i; |
|
600 |
|
601 for (i = 0; i < priv->channels->len; i++) |
|
602 { |
|
603 foreach (TP_CHANNEL_IFACE (g_ptr_array_index (priv->channels, i)), user_data); |
|
604 } |
|
605 } |
|
606 |
|
607 static TpChannelFactoryRequestStatus |
|
608 gabble_media_factory_iface_request (TpChannelFactoryIface *iface, |
|
609 const gchar *chan_type, |
|
610 TpHandleType handle_type, |
|
611 guint handle, |
|
612 TpChannelIface **ret, |
|
613 GError **error) |
|
614 { |
|
615 GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); |
|
616 GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); |
|
617 GabbleMediaChannel *chan = NULL; |
|
618 |
|
619 if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) |
|
620 return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED; |
|
621 |
|
622 if (handle_type == 0) |
|
623 { |
|
624 /* create an empty channel */ |
|
625 chan = new_media_channel (fac, priv->conn->self_handle); |
|
626 } |
|
627 else if (handle_type == TP_HANDLE_TYPE_CONTACT) |
|
628 { |
|
629 chan = new_media_channel (fac, priv->conn->self_handle); |
|
630 |
|
631 if (!_gabble_media_channel_add_member (G_OBJECT (chan), handle, "", error)) |
|
632 { |
|
633 gboolean close_ret; |
|
634 |
|
635 close_ret = gabble_media_channel_close (chan, NULL); |
|
636 g_assert (close_ret); |
|
637 |
|
638 return TP_CHANNEL_FACTORY_REQUEST_STATUS_ERROR; |
|
639 } |
|
640 } |
|
641 else |
|
642 { |
|
643 return TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE; |
|
644 } |
|
645 |
|
646 g_assert (chan != NULL); |
|
647 g_signal_emit_by_name (fac, "new-channel", chan); |
|
648 |
|
649 *ret = TP_CHANNEL_IFACE (chan); |
|
650 return TP_CHANNEL_FACTORY_REQUEST_STATUS_DONE; |
|
651 } |
|
652 |
|
653 static void |
|
654 gabble_media_factory_iface_init (gpointer g_iface, |
|
655 gpointer iface_data) |
|
656 { |
|
657 TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface; |
|
658 |
|
659 klass->close_all = gabble_media_factory_iface_close_all; |
|
660 klass->connected = gabble_media_factory_iface_connected; |
|
661 klass->connecting = gabble_media_factory_iface_connecting; |
|
662 klass->disconnected = gabble_media_factory_iface_disconnected; |
|
663 klass->foreach = gabble_media_factory_iface_foreach; |
|
664 klass->request = gabble_media_factory_iface_request; |
|
665 } |
|
666 |
|