1 /*
2 * sip-media-channel.c - Source for SIPMediaChannel
3 * Copyright (C) 2005-2007 Collabora Ltd.
4 * Copyright (C) 2005-2007 Nokia Corporation
5 * @author Kai Vehmanen <first.surname@nokia.com>
6 *
7 * Based on telepathy-gabble implementation (gabble-media-channel).
8 * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * version 2.1 as published by the Free Software Foundation.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #define DBUS_API_SUBJECT_TO_CHANGE 1
25 #include <dbus/dbus-glib.h>
26 #include <stdlib.h>
27
28 #include <telepathy-glib/channel-iface.h>
29 #include <telepathy-glib/dbus.h>
30 #include <telepathy-glib/errors.h>
31 #include <telepathy-glib/interfaces.h>
32 #include <telepathy-glib/intset.h>
33 #include <telepathy-glib/svc-channel.h>
34
35 #include "sip-media-channel.h"
36 #include "media-factory.h"
37 #include "sip-connection.h"
38 #include "sip-media-session.h"
39 #include "sip-media-stream.h"
40
41 #include "telepathy-helpers.h"
42 #include "sip-connection-helpers.h"
43
44 #define DEBUG_FLAG SIP_DEBUG_MEDIA
45 #include "debug.h"
46
47 static void channel_iface_init (gpointer, gpointer);
48 static void media_signalling_iface_init (gpointer, gpointer);
49 static void streamed_media_iface_init (gpointer, gpointer);
50 static void dtmf_iface_init (gpointer, gpointer);
51 static void priv_group_mixin_iface_init (gpointer, gpointer);
52
53 G_DEFINE_TYPE_WITH_CODE (SIPMediaChannel, sip_media_channel,
54 G_TYPE_OBJECT,
55 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
56 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
57 priv_group_mixin_iface_init);
58 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
59 media_signalling_iface_init);
60 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DTMF,
61 dtmf_iface_init);
62 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA,
63 streamed_media_iface_init);
64 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_PROPERTIES_INTERFACE,
65 tp_properties_mixin_iface_init);
66 G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL));
67
68 /* properties */
69 enum
70 {
71 PROP_CONNECTION = 1,
72 PROP_FACTORY,
73 PROP_OBJECT_PATH,
74 PROP_CHANNEL_TYPE,
75 PROP_HANDLE_TYPE,
76 PROP_HANDLE,
77 /* Telepathy properties (see below too) */
78 PROP_NAT_TRAVERSAL,
79 PROP_STUN_SERVER,
80 PROP_STUN_PORT,
81 LAST_PROPERTY
82 };
83
84 /* TP channel properties */
85 enum
86 {
87 TP_PROP_NAT_TRAVERSAL = 0,
88 TP_PROP_STUN_SERVER,
89 TP_PROP_STUN_PORT,
90 NUM_TP_PROPS
91 };
92
93 const TpPropertySignature media_channel_property_signatures[NUM_TP_PROPS] =
94 {
95 { "nat-traversal", G_TYPE_STRING },
96 { "stun-server", G_TYPE_STRING },
97 { "stun-port", G_TYPE_UINT },
98 };
99
100 /* private structure */
101 typedef struct _SIPMediaChannelPrivate SIPMediaChannelPrivate;
102
103 struct _SIPMediaChannelPrivate
104 {
105 gboolean dispose_has_run;
106 gboolean closed;
107 SIPConnection *conn;
108 SIPMediaFactory *factory;
109 SIPMediaSession *session;
110 gchar *object_path;
111 };
112
113 #define SIP_MEDIA_CHANNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SIP_TYPE_MEDIA_CHANNEL, SIPMediaChannelPrivate))
114
DEFINE_TP_STRUCT_TYPE(sip_session_handler_type,DBUS_TYPE_G_OBJECT_PATH,G_TYPE_STRING)115 DEFINE_TP_STRUCT_TYPE(sip_session_handler_type,
116 DBUS_TYPE_G_OBJECT_PATH,
117 G_TYPE_STRING)
118
119
120 /***********************************************************************
121 * Set: Gobject interface
122 ***********************************************************************/
123
124 static void
125 sip_media_channel_init (SIPMediaChannel *obj)
126 {
127 /* allocate any data required by the object here */
128
129 /* initialise the properties mixin *before* GObject
130 * sets the construct-time properties */
131 tp_properties_mixin_init (G_OBJECT (obj),
132 G_STRUCT_OFFSET (SIPMediaChannel, properties));
133 }
134
135 static GObject *
sip_media_channel_constructor(GType type,guint n_props,GObjectConstructParam * props)136 sip_media_channel_constructor (GType type, guint n_props,
137 GObjectConstructParam *props)
138 {
139 GObject *obj;
140 SIPMediaChannelPrivate *priv;
141 DBusGConnection *bus;
142 TpBaseConnection *conn;
143 TpHandleRepoIface *contact_repo;
144
145 DEBUG("enter");
146
147 obj = G_OBJECT_CLASS (sip_media_channel_parent_class)->
148 constructor (type, n_props, props);
149
150 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (SIP_MEDIA_CHANNEL (obj));
151 conn = (TpBaseConnection *)(priv->conn);
152 contact_repo = tp_base_connection_get_handles (conn,
153 TP_HANDLE_TYPE_CONTACT);
154
155 /* register object on the bus */
156 bus = tp_get_bus ();
157
158 DEBUG("registering object to dbus path=%s", priv->object_path);
159 dbus_g_connection_register_g_object (bus, priv->object_path, obj);
160
161 tp_group_mixin_init (obj,
162 G_STRUCT_OFFSET (SIPMediaChannel, group),
163 contact_repo,
164 conn->self_handle);
165
166 /* allow member adding */
167 tp_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD, 0);
168
169 return obj;
170 }
171
172 static void sip_media_channel_dispose (GObject *object);
173 static void sip_media_channel_finalize (GObject *object);
174 static void sip_media_channel_get_property (GObject *object,
175 guint property_id,
176 GValue *value,
177 GParamSpec *pspec);
178 static void sip_media_channel_set_property (GObject *object,
179 guint property_id,
180 const GValue *value,
181 GParamSpec *pspec);
182
183 static void priv_create_session (SIPMediaChannel *channel,
184 nua_handle_t *nh,
185 TpHandle peer,
186 gboolean remote_initiated);
187 static void priv_destroy_session(SIPMediaChannel *channel);
188 static gboolean sip_media_channel_add_member (GObject *iface,
189 TpHandle handle,
190 const gchar *message,
191 GError **error);
192 static gboolean sip_media_channel_remove_with_reason (
193 GObject *iface,
194 TpHandle handle,
195 const gchar *message,
196 guint reason,
197 GError **error);
198
199 static void
sip_media_channel_class_init(SIPMediaChannelClass * sip_media_channel_class)200 sip_media_channel_class_init (SIPMediaChannelClass *sip_media_channel_class)
201 {
202 GObjectClass *object_class = G_OBJECT_CLASS (sip_media_channel_class);
203 GParamSpec *param_spec;
204
205 DEBUG("enter");
206
207 g_type_class_add_private (sip_media_channel_class, sizeof (SIPMediaChannelPrivate));
208
209 object_class->constructor = sip_media_channel_constructor;
210
211 object_class->get_property = sip_media_channel_get_property;
212 object_class->set_property = sip_media_channel_set_property;
213
214 object_class->dispose = sip_media_channel_dispose;
215 object_class->finalize = sip_media_channel_finalize;
216
217 tp_group_mixin_class_init (object_class,
218 G_STRUCT_OFFSET (SIPMediaChannelClass, group_class),
219 sip_media_channel_add_member,
220 NULL);
221 tp_group_mixin_class_set_remove_with_reason_func(object_class,
222 sip_media_channel_remove_with_reason);
223
224 g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
225 "handle-type");
226 g_object_class_override_property (object_class, PROP_HANDLE, "handle");
227
228 tp_properties_mixin_class_init (object_class,
229 G_STRUCT_OFFSET (SIPMediaChannelClass, properties_class),
230 media_channel_property_signatures, NUM_TP_PROPS, NULL);
231
232 param_spec = g_param_spec_object ("connection", "SIPConnection object",
233 "SIP connection object that owns this "
234 "SIP media channel object.",
235 SIP_TYPE_CONNECTION,
236 G_PARAM_CONSTRUCT_ONLY |
237 G_PARAM_READWRITE |
238 G_PARAM_STATIC_NICK |
239 G_PARAM_STATIC_BLURB);
240 g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
241
242 param_spec = g_param_spec_object ("factory", "SIPMediaFactory object",
243 "Channel factory object that owns this "
244 "SIP media channel object.",
245 SIP_TYPE_MEDIA_FACTORY,
246 G_PARAM_CONSTRUCT_ONLY |
247 G_PARAM_READWRITE |
248 G_PARAM_STATIC_NICK |
249 G_PARAM_STATIC_BLURB);
250 g_object_class_install_property (object_class, PROP_FACTORY, param_spec);
251
252 param_spec = g_param_spec_string ("object-path", "D-Bus object path",
253 "The D-Bus object path used for this "
254 "object on the bus.",
255 NULL,
256 G_PARAM_CONSTRUCT_ONLY |
257 G_PARAM_READWRITE |
258 G_PARAM_STATIC_NAME |
259 G_PARAM_STATIC_BLURB);
260 g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
261
262 param_spec = g_param_spec_string ("channel-type", "Telepathy channel type",
263 "The D-Bus interface representing the "
264 "type of this channel.",
265 NULL,
266 G_PARAM_READABLE |
267 G_PARAM_STATIC_NAME |
268 G_PARAM_STATIC_BLURB);
269 g_object_class_install_property (object_class, PROP_CHANNEL_TYPE, param_spec);
270
271 param_spec = g_param_spec_string ("nat-traversal", "NAT traversal mechanism",
272 "A string representing the type of NAT "
273 "traversal that should be performed for "
274 "streams on this channel.",
275 "none",
276 G_PARAM_CONSTRUCT_ONLY |
277 G_PARAM_READWRITE |
278 G_PARAM_STATIC_NAME |
279 G_PARAM_STATIC_BLURB);
280 g_object_class_install_property (object_class, PROP_NAT_TRAVERSAL, param_spec);
281
282 param_spec = g_param_spec_string ("stun-server",
283 "STUN server",
284 "IP or address of STUN server.",
285 NULL,
286 G_PARAM_READWRITE |
287 G_PARAM_STATIC_NAME |
288 G_PARAM_STATIC_BLURB);
289 g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec);
290
291 param_spec = g_param_spec_uint ("stun-port",
292 "STUN port",
293 "UDP port of STUN server.",
294 0, G_MAXUINT16, 0,
295 G_PARAM_READWRITE |
296 G_PARAM_STATIC_NAME |
297 G_PARAM_STATIC_BLURB);
298 g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec);
299 }
300
301 static void
sip_media_channel_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)302 sip_media_channel_get_property (GObject *object,
303 guint property_id,
304 GValue *value,
305 GParamSpec *pspec)
306 {
307 SIPMediaChannel *chan = SIP_MEDIA_CHANNEL (object);
308 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (chan);
309
310 switch (property_id) {
311 case PROP_CONNECTION:
312 g_value_set_object (value, priv->conn);
313 break;
314 case PROP_FACTORY:
315 g_value_set_object (value, priv->factory);
316 break;
317 case PROP_OBJECT_PATH:
318 g_value_set_string (value, priv->object_path);
319 break;
320 case PROP_CHANNEL_TYPE:
321 g_value_set_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
322 break;
323 case PROP_HANDLE:
324 g_value_set_uint (value, 0);
325 break;
326 case PROP_HANDLE_TYPE:
327 g_value_set_uint (value, TP_HANDLE_TYPE_NONE);
328 break;
329 default:
330 /* the NAT_TRAVERSAL property lives in the mixin */
331 {
332 const gchar *param_name = g_param_spec_get_name (pspec);
333 guint tp_property_id;
334
335 if (tp_properties_mixin_has_property (object, param_name,
336 &tp_property_id))
337 {
338 GValue *tp_property_value =
339 chan->properties.properties[tp_property_id].value;
340
341 if (tp_property_value)
342 {
343 g_value_copy (tp_property_value, value);
344 return;
345 }
346 }
347 }
348
349 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
350 break;
351 }
352 }
353
354 static void
sip_media_channel_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)355 sip_media_channel_set_property (GObject *object,
356 guint property_id,
357 const GValue *value,
358 GParamSpec *pspec)
359 {
360 SIPMediaChannel *chan = SIP_MEDIA_CHANNEL (object);
361 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (chan);
362
363 switch (property_id) {
364 case PROP_HANDLE:
365 /* this property is writable in the interface, but not actually
366 * meaningfully changable on this channel, so we do nothing */
367 break;
368 case PROP_HANDLE_TYPE:
369 /* this property is writable in the interface, but not actually
370 * meaningfully changable on this channel, so we do nothing */
371 break;
372 case PROP_CONNECTION:
373 priv->conn = SIP_CONNECTION (g_value_dup_object (value));
374 break;
375 case PROP_FACTORY:
376 priv->factory = SIP_MEDIA_FACTORY (g_value_dup_object (value));
377 break;
378 case PROP_OBJECT_PATH:
379 g_free (priv->object_path);
380 priv->object_path = g_value_dup_string (value);
381 break;
382 default:
383 /* the NAT_TRAVERSAL property lives in the mixin */
384 {
385 const gchar *param_name = g_param_spec_get_name (pspec);
386 guint tp_property_id;
387
388 if (tp_properties_mixin_has_property (object, param_name,
389 &tp_property_id))
390 {
391 tp_properties_mixin_change_value (object, tp_property_id,
392 value, NULL);
393 tp_properties_mixin_change_flags (object, tp_property_id,
394 TP_PROPERTY_FLAG_READ, 0, NULL);
395 return;
396 }
397 }
398
399 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
400 break;
401 }
402 }
403
404 void
sip_media_channel_dispose(GObject * object)405 sip_media_channel_dispose (GObject *object)
406 {
407 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (object);
408 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
409
410 if (priv->dispose_has_run)
411 return;
412
413 DEBUG("enter");
414
415 priv->dispose_has_run = TRUE;
416
417 priv_destroy_session(self);
418
419 if (!priv->closed)
420 sip_media_channel_close (self);
421
422 if (priv->factory)
423 g_object_unref (priv->factory);
424
425 if (priv->conn)
426 g_object_unref (priv->conn);
427
428 if (G_OBJECT_CLASS (sip_media_channel_parent_class)->dispose)
429 G_OBJECT_CLASS (sip_media_channel_parent_class)->dispose (object);
430
431 DEBUG("exit");
432 }
433
434 void
sip_media_channel_finalize(GObject * object)435 sip_media_channel_finalize (GObject *object)
436 {
437 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (object);
438 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
439
440 g_free (priv->object_path);
441
442 tp_group_mixin_finalize (object);
443
444 tp_properties_mixin_finalize (object);
445
446 G_OBJECT_CLASS (sip_media_channel_parent_class)->finalize (object);
447
448 DEBUG("exit");
449 }
450
451 /***********************************************************************
452 * Set: Channel interface implementation (same for 0.12/0.13)
453 ***********************************************************************/
454
455 /**
456 * sip_media_channel_close_async
457 *
458 * Implements DBus method Close
459 * on interface org.freedesktop.Telepathy.Channel
460 */
461 static void
sip_media_channel_close_async(TpSvcChannel * iface,DBusGMethodInvocation * context)462 sip_media_channel_close_async (TpSvcChannel *iface,
463 DBusGMethodInvocation *context)
464 {
465 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
466
467 sip_media_channel_close (self);
468 tp_svc_channel_return_from_close (context);
469 }
470
471 void
sip_media_channel_close(SIPMediaChannel * obj)472 sip_media_channel_close (SIPMediaChannel *obj)
473 {
474 SIPMediaChannelPrivate *priv;
475
476 DEBUG("enter");
477
478 g_assert (SIP_IS_MEDIA_CHANNEL (obj));
479 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (obj);
480
481 if (priv->closed)
482 return;
483
484 priv->closed = TRUE;
485
486 if (priv->session) {
487 sip_media_session_terminate (priv->session);
488 g_assert (priv->session == NULL);
489 }
490
491 tp_svc_channel_emit_closed ((TpSvcChannel *)obj);
492
493 return;
494 }
495
496 void
sip_media_channel_terminated(SIPMediaChannel * self)497 sip_media_channel_terminated (SIPMediaChannel *self)
498 {
499 SIPMediaChannelPrivate *priv;
500
501 DEBUG("enter");
502
503 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
504
505 if (priv->session)
506 {
507 g_object_set (priv->session,
508 "state", SIP_MEDIA_SESSION_STATE_ENDED,
509 NULL);
510 }
511 }
512
513 /**
514 * sip_media_channel_get_channel_type
515 *
516 * Implements DBus method GetChannelType
517 * on interface org.freedesktop.Telepathy.Channel
518 */
519 static void
sip_media_channel_get_channel_type(TpSvcChannel * obj,DBusGMethodInvocation * context)520 sip_media_channel_get_channel_type (TpSvcChannel *obj,
521 DBusGMethodInvocation *context)
522 {
523 tp_svc_channel_return_from_get_channel_type (context,
524 TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
525 }
526
527
528 /**
529 * sip_media_channel_get_handle
530 *
531 * Implements DBus method GetHandle
532 * on interface org.freedesktop.Telepathy.Channel
533 */
534 static void
sip_media_channel_get_handle(TpSvcChannel * iface,DBusGMethodInvocation * context)535 sip_media_channel_get_handle (TpSvcChannel *iface,
536 DBusGMethodInvocation *context)
537 {
538 tp_svc_channel_return_from_get_handle (context, 0, 0);
539 }
540
541 /**
542 * sip_media_channel_get_interfaces
543 *
544 * Implements DBus method GetInterfaces
545 * on interface org.freedesktop.Telepathy.Channel
546 */
547 static void
sip_media_channel_get_interfaces(TpSvcChannel * iface,DBusGMethodInvocation * context)548 sip_media_channel_get_interfaces (TpSvcChannel *iface,
549 DBusGMethodInvocation *context)
550 {
551 const gchar *interfaces[] = {
552 TP_IFACE_CHANNEL_INTERFACE_GROUP,
553 TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
554 TP_IFACE_CHANNEL_INTERFACE_DTMF,
555 TP_IFACE_PROPERTIES_INTERFACE,
556 NULL
557 };
558
559 tp_svc_channel_return_from_get_interfaces (context, interfaces);
560 }
561
562 /***********************************************************************
563 * Set: Channel.Interface.MediaSignalling Telepathy-0.13 interface
564 ***********************************************************************/
565
566 /**
567 * sip_media_channel_get_session_handlers
568 *
569 * Implements DBus method GetSessionHandlers
570 * on interface org.freedesktop.Telepathy.Channel.Interface.MediaSignalling
571 *
572 * @error: Used to return a pointer to a GError detailing any error
573 * that occured, DBus will throw the error only if this
574 * function returns false.
575 *
576 * Returns: TRUE if successful, FALSE if an error was thrown.
577 */
578 static void
sip_media_channel_get_session_handlers(TpSvcChannelInterfaceMediaSignalling * iface,DBusGMethodInvocation * context)579 sip_media_channel_get_session_handlers (TpSvcChannelInterfaceMediaSignalling *iface,
580 DBusGMethodInvocation *context)
581 {
582 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
583 SIPMediaChannelPrivate *priv;
584 GPtrArray *ret;
585 GValue handler = { 0 };
586
587 DEBUG("enter");
588
589 g_assert (SIP_IS_MEDIA_CHANNEL (self));
590
591 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
592
593 ret = g_ptr_array_new ();
594
595 if (priv->session)
596 {
597 GType handler_type;
598 gchar *path;
599
600 g_object_get (priv->session,
601 "object-path", &path,
602 NULL);
603
604 handler_type = sip_session_handler_type ();
605
606 g_value_init (&handler, handler_type);
607 g_value_take_boxed (&handler,
608 dbus_g_type_specialized_construct (handler_type));
609
610 dbus_g_type_struct_set (&handler,
611 0, path,
612 1, "rtp",
613 G_MAXUINT);
614
615 g_free (path);
616
617 g_ptr_array_add (ret, g_value_get_boxed (&handler));
618 }
619
620 tp_svc_channel_interface_media_signalling_return_from_get_session_handlers (
621 context, ret);
622
623 if (G_IS_VALUE(&handler))
624 g_value_unset (&handler);
625
626 g_ptr_array_free (ret, TRUE);
627 }
628
629
630 /***********************************************************************
631 * Set: Channel.Type.StreamedMedia Telepathy-0.13 interface
632 ***********************************************************************/
633
634 /**
635 * sip_media_channel_list_streams
636 *
637 * Implements D-Bus method ListStreams
638 * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
639 */
640 static void
sip_media_channel_list_streams(TpSvcChannelTypeStreamedMedia * iface,DBusGMethodInvocation * context)641 sip_media_channel_list_streams (TpSvcChannelTypeStreamedMedia *iface,
642 DBusGMethodInvocation *context)
643 {
644 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
645 SIPMediaChannelPrivate *priv;
646 GPtrArray *ret = NULL;
647
648 g_assert (SIP_IS_MEDIA_CHANNEL (self));
649 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
650
651 if (!sip_media_session_list_streams (priv->session, &ret))
652 ret = g_ptr_array_new ();
653
654 tp_svc_channel_type_streamed_media_return_from_list_streams (context, ret);
655
656 g_boxed_free (SIP_TP_STREAM_LIST_TYPE, ret);
657 }
658
659 /**
660 * sip_media_channel_remove_streams
661 *
662 * Implements D-Bus method RemoveStreams
663 * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
664 */
665 static void
sip_media_channel_remove_streams(TpSvcChannelTypeStreamedMedia * iface,const GArray * streams,DBusGMethodInvocation * context)666 sip_media_channel_remove_streams (TpSvcChannelTypeStreamedMedia *iface,
667 const GArray *streams,
668 DBusGMethodInvocation *context)
669 {
670 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
671 SIPMediaChannelPrivate *priv;
672 GError *error = NULL;
673
674 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
675
676 if (priv->session != NULL)
677 {
678 sip_media_session_remove_streams(priv->session,
679 streams,
680 &error);
681 }
682 else
683 {
684 error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
685 "No session is available");
686 }
687
688 if (error != NULL)
689 {
690 dbus_g_method_return_error (context, error);
691 g_error_free (error);
692 return;
693 }
694
695 tp_svc_channel_type_streamed_media_return_from_remove_streams (context);
696 }
697
698 /**
699 * sip_media_channel_request_stream_direction
700 *
701 * Implements D-Bus method RequestStreamDirection
702 * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
703 */
704 static void
sip_media_channel_request_stream_direction(TpSvcChannelTypeStreamedMedia * iface,guint stream_id,guint stream_direction,DBusGMethodInvocation * context)705 sip_media_channel_request_stream_direction (TpSvcChannelTypeStreamedMedia *iface,
706 guint stream_id,
707 guint stream_direction,
708 DBusGMethodInvocation *context)
709 {
710 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
711 SIPMediaChannelPrivate *priv;
712 GError *error = NULL;
713
714 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
715
716 if (priv->session != NULL)
717 {
718 sip_media_session_request_stream_direction (priv->session,
719 stream_id,
720 stream_direction,
721 &error);
722 }
723 else
724 {
725 error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
726 "No session is available");
727 }
728
729 if (error != NULL)
730 {
731 dbus_g_method_return_error (context, error);
732 g_error_free (error);
733 return;
734 }
735 tp_svc_channel_type_streamed_media_return_from_request_stream_direction (context);
736 }
737
738
739 /**
740 * sip_media_channel_request_streams
741 *
742 * Implements D-Bus method RequestStreams
743 * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
744 */
745 static void
sip_media_channel_request_streams(TpSvcChannelTypeStreamedMedia * iface,guint contact_handle,const GArray * types,DBusGMethodInvocation * context)746 sip_media_channel_request_streams (TpSvcChannelTypeStreamedMedia *iface,
747 guint contact_handle,
748 const GArray *types,
749 DBusGMethodInvocation *context)
750 {
751 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
752 GError *error = NULL;
753 GPtrArray *ret = NULL;
754 SIPMediaChannelPrivate *priv;
755 TpHandleRepoIface *contact_repo;
756
757 DEBUG("enter");
758
759 g_assert (SIP_IS_MEDIA_CHANNEL (self));
760
761 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
762
763 contact_repo = tp_base_connection_get_handles (
764 (TpBaseConnection *)(priv->conn), TP_HANDLE_TYPE_CONTACT);
765
766 if (!tp_handle_is_valid (contact_repo, contact_handle, &error))
767 {
768 dbus_g_method_return_error (context, error);
769 g_error_free (error);
770 return;
771 }
772
773 if (!tp_handle_set_is_member (self->group.members, contact_handle) &&
774 !tp_handle_set_is_member (self->group.remote_pending, contact_handle))
775 {
776 error = g_error_new (TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "given handle "
777 "%u is not a member of the channel", contact_handle);
778 dbus_g_method_return_error (context, error);
779 g_error_free (error);
780 return;
781 }
782
783 /* if the person is a channel member, we should have a session */
784 g_assert (priv->session != NULL);
785
786 if (!sip_media_session_request_streams (priv->session, types, &ret, &error))
787 {
788 dbus_g_method_return_error (context, error);
789 g_error_free (error);
790 return;
791 }
792
793 g_assert (types->len == ret->len);
794
795 tp_svc_channel_type_streamed_media_return_from_request_streams (context, ret);
796
797 g_boxed_free (SIP_TP_STREAM_LIST_TYPE, ret);
798
799 DEBUG ("exit");
800 }
801
802 /***********************************************************************
803 * Set: sip-media-channel API towards sip-connection
804 ***********************************************************************/
805
806 /**
807 * Handle an incoming INVITE, normally called just after the channel
808 * has been created.
809 */
810 void
sip_media_channel_receive_invite(SIPMediaChannel * self,nua_handle_t * nh,TpHandle handle)811 sip_media_channel_receive_invite (SIPMediaChannel *self,
812 nua_handle_t *nh,
813 TpHandle handle)
814 {
815 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
816 TpGroupMixin *mixin = TP_GROUP_MIXIN (self);
817 GObject *obj = G_OBJECT (self);
818 TpHandleRepoIface *contact_repo;
819 TpIntSet *member_set, *pending_set;
820
821 contact_repo = tp_base_connection_get_handles (
822 (TpBaseConnection *)(priv->conn), TP_HANDLE_TYPE_CONTACT);
823
824 if (priv->session == NULL)
825 {
826 priv_create_session (self, nh, handle, TRUE);
827
828 /* note: start the local stream-engine; once the local
829 * candidate are ready, reply with nua_respond()
830 *
831 * with the tp-0.13 API, the streams need to be created
832 * based on remote SDP (see sip_media_session_set_remote_media()) */
833 }
834 else
835 g_warning ("session already exists");
836
837 /* XXX: should be attached more data than just the handle?
838 * - yes, we need to be able to access all the <op,handle> pairs */
839
840 DEBUG("adding handle %d (%s)",
841 handle,
842 tp_handle_inspect (contact_repo, handle));
843
844 /* add the peer to channel members and self_handle to local pending */
845
846 member_set = tp_intset_new ();
847 tp_intset_add (member_set, handle);
848
849 pending_set = tp_intset_new ();
850 tp_intset_add (pending_set, mixin->self_handle);
851
852 tp_group_mixin_change_members (obj, "INVITE received",
853 member_set, /* add */
854 NULL, /* remove */
855 pending_set, /* local pending */
856 NULL, /* remote pending */
857 0, 0); /* irrelevant */
858
859 tp_intset_destroy (member_set);
860 tp_intset_destroy (pending_set);
861 }
862
863 /**
864 * Handle an incoming re-INVITE request.
865 */
866 void
sip_media_channel_receive_reinvite(SIPMediaChannel * self)867 sip_media_channel_receive_reinvite (SIPMediaChannel *self)
868 {
869 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
870
871 g_return_if_fail (priv->session != NULL);
872
873 sip_media_session_receive_reinvite (priv->session);
874 }
875
876 gboolean
sip_media_channel_set_remote_media(SIPMediaChannel * chan,const sdp_session_t * r_sdp)877 sip_media_channel_set_remote_media (SIPMediaChannel *chan,
878 const sdp_session_t* r_sdp)
879 {
880 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (chan);
881
882 g_return_val_if_fail (priv->session != NULL, FALSE);
883
884 return sip_media_session_set_remote_media (priv->session, r_sdp);
885 }
886
887 void
sip_media_channel_ready(SIPMediaChannel * self)888 sip_media_channel_ready (SIPMediaChannel *self)
889 {
890 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
891 g_return_if_fail (priv->session != NULL);
892 sip_media_session_accept (priv->session);
893 }
894
895 void
sip_media_channel_peer_error(SIPMediaChannel * self,guint status,const char * message)896 sip_media_channel_peer_error (SIPMediaChannel *self,
897 guint status,
898 const char* message)
899 {
900 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
901 TpIntSet *set;
902 TpHandle peer;
903 guint reason = TP_CHANNEL_GROUP_CHANGE_REASON_ERROR;
904
905 DEBUG("peer responded with %u %s", status, message);
906
907 g_return_if_fail (priv->session != NULL);
908
909 g_assert (status >= 300);
910
911 switch (status)
912 {
913 case 410:
914 case 604:
915 reason = TP_CHANNEL_GROUP_CHANGE_REASON_INVALID_CONTACT;
916 break;
917 case 486:
918 case 600:
919 reason = TP_CHANNEL_GROUP_CHANGE_REASON_BUSY;
920 break;
921 case 408:
922 reason = TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER;
923 break;
924 case 404:
925 case 480:
926 reason = TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE;
927 break;
928 case 603:
929 /* No reason means roughly "rejected" */
930 reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
931 break;
932 case 403:
933 case 401:
934 case 407:
935 reason = TP_CHANNEL_GROUP_CHANGE_REASON_PERMISSION_DENIED;
936 break;
937 }
938
939 peer = sip_media_session_get_peer (priv->session);
940
941 if (message == NULL || !g_utf8_validate (message, -1, NULL))
942 message = "";
943
944 set = tp_intset_new ();
945 tp_intset_add (set, peer);
946 tp_group_mixin_change_members ((GObject *)self, message,
947 NULL, set, NULL, NULL, peer, reason);
948 tp_intset_destroy (set);
949 }
950
951 void
sip_media_channel_peer_cancel(SIPMediaChannel * self,guint cause,const gchar * message)952 sip_media_channel_peer_cancel (SIPMediaChannel *self,
953 guint cause,
954 const gchar *message)
955 {
956 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
957 TpGroupMixin *mixin = TP_GROUP_MIXIN (self);
958 TpIntSet *set;
959 TpHandle actor = 0;
960 TpHandle peer;
961
962 g_return_if_fail (priv->session != NULL);
963
964 peer = sip_media_session_get_peer (priv->session);
965
966 switch (cause)
967 {
968 case 200:
969 case 603:
970 /* The user must have acted on another branch of the forked call */
971 actor = mixin->self_handle;
972 break;
973 default:
974 actor = peer;
975 }
976
977 if (message == NULL || !g_utf8_validate (message, -1, NULL))
978 message = "Cancelled";
979
980 set = tp_intset_new ();
981 tp_intset_add (set, peer);
982 tp_intset_add (set, mixin->self_handle);
983
984 tp_group_mixin_change_members ((GObject *) self,
985 message,
986 NULL, /* add */
987 set, /* remove */
988 NULL,
989 NULL,
990 actor,
991 TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
992
993 tp_intset_destroy (set);
994 }
995
996 /***********************************************************************
997 * Set: Helper functions follow (not based on generated templates)
998 ***********************************************************************/
999
priv_session_state_changed_cb(SIPMediaSession * session,GParamSpec * arg1,SIPMediaChannel * channel)1000 static void priv_session_state_changed_cb (SIPMediaSession *session,
1001 GParamSpec *arg1,
1002 SIPMediaChannel *channel)
1003 {
1004 TpGroupMixin *mixin = TP_GROUP_MIXIN (channel);
1005 SIPMediaSessionState state;
1006 TpHandle peer;
1007 TpIntSet *set;
1008
1009 DEBUG("enter");
1010
1011 g_object_get (session,
1012 "state", &state,
1013 "peer", &peer,
1014 NULL);
1015
1016 if (state == SIP_MEDIA_SESSION_STATE_ACTIVE) {
1017 set = tp_intset_new ();
1018
1019 /* add the peer to the member list */
1020 tp_intset_add (set, peer);
1021
1022 tp_group_mixin_change_members ((GObject *)channel,
1023 "", set, NULL, NULL, NULL, 0, 0);
1024
1025 tp_intset_destroy (set);
1026
1027 /* update flags accordingly -- allow removal, deny adding and rescinding */
1028 tp_group_mixin_change_flags ((GObject *)channel,
1029 TP_CHANNEL_GROUP_FLAG_CAN_REMOVE,
1030 TP_CHANNEL_GROUP_FLAG_CAN_ADD |
1031 TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
1032 }
1033 else if (state == SIP_MEDIA_SESSION_STATE_ENDED) {
1034 set = tp_intset_new ();
1035
1036 /* remove us and the peer from the member list */
1037 tp_intset_add (set, mixin->self_handle);
1038 tp_intset_add (set, peer);
1039 tp_group_mixin_change_members ((GObject *)channel,
1040 "", NULL, set, NULL, NULL, 0, 0);
1041
1042 tp_intset_destroy (set);
1043
1044 #if 0
1045 /* update flags accordingly -- allow adding, deny removal */
1046 tp_group_mixin_change_flags ((GObject *)channel,
1047 TP_CHANNEL_GROUP_FLAG_CAN_ADD, TP_CHANNEL_GROUP_FLAG_CAN_REMOVE);
1048 #endif
1049
1050 priv_destroy_session (channel);
1051 sip_media_channel_close (channel);
1052 }
1053 }
1054
priv_session_stream_added_cb(SIPMediaSession * session,SIPMediaStream * stream,SIPMediaChannel * chan)1055 static void priv_session_stream_added_cb (SIPMediaSession *session,
1056 SIPMediaStream *stream,
1057 SIPMediaChannel *chan)
1058 {
1059 guint id, handle, type;
1060
1061 DEBUG("enter");
1062
1063 /* emit StreamAdded */
1064 handle = sip_media_session_get_peer (session);
1065 g_object_get (stream, "id", &id, "media-type", &type, NULL);
1066
1067 tp_svc_channel_type_streamed_media_emit_stream_added (
1068 (TpSvcChannelTypeStreamedMedia *)chan, id, handle, type);
1069 }
1070
1071 /**
1072 * priv_create_session:
1073 *
1074 * Creates a SIPMediaSession object for given peer.
1075 **/
1076 static void
priv_create_session(SIPMediaChannel * channel,nua_handle_t * nh,TpHandle peer,gboolean remote_initiated)1077 priv_create_session (SIPMediaChannel *channel,
1078 nua_handle_t *nh,
1079 TpHandle peer,
1080 gboolean remote_initiated)
1081 {
1082 SIPMediaChannelPrivate *priv;
1083 SIPMediaSession *session;
1084 TpBaseConnection *conn;
1085 TpHandleRepoIface *contact_repo;
1086 gchar *object_path;
1087 gchar *local_ip_address = NULL;
1088
1089 DEBUG("enter");
1090
1091 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (channel);
1092 g_assert (priv->session == NULL);
1093 conn = (TpBaseConnection *)(priv->conn);
1094 contact_repo = tp_base_connection_get_handles (conn,
1095 TP_HANDLE_TYPE_CONTACT);
1096
1097 object_path = g_strdup_printf ("%s/MediaSession%u", priv->object_path, peer);
1098
1099 DEBUG("allocating session, peer=%u", peer);
1100
1101 /* The channel manages references to the peer handle for the session */
1102 tp_handle_ref (contact_repo, peer);
1103
1104 g_object_get (priv->conn,
1105 "local-ip-address", &local_ip_address,
1106 NULL);
1107
1108 session = g_object_new (SIP_TYPE_MEDIA_SESSION,
1109 "media-channel", channel,
1110 "object-path", object_path,
1111 "nua-handle", nh,
1112 "peer", peer,
1113 "local-ip-address", local_ip_address,
1114 NULL);
1115
1116 g_free (local_ip_address);
1117
1118 g_signal_connect (session, "notify::state",
1119 (GCallback) priv_session_state_changed_cb, channel);
1120 g_signal_connect (session, "stream-added",
1121 (GCallback) priv_session_stream_added_cb, channel);
1122
1123 priv->session = session;
1124
1125 if (remote_initiated)
1126 sip_media_session_receive_invite (session);
1127
1128 tp_svc_channel_interface_media_signalling_emit_new_session_handler (
1129 (TpSvcChannelInterfaceMediaSignalling *)channel, object_path, "rtp");
1130
1131 g_free (object_path);
1132
1133 DEBUG ("exit");
1134 }
1135
1136 static void
priv_destroy_session(SIPMediaChannel * channel)1137 priv_destroy_session(SIPMediaChannel *channel)
1138 {
1139 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (channel);
1140 SIPMediaSession *session;
1141 TpBaseConnection *conn;
1142 TpHandleRepoIface *contact_repo;
1143
1144 session = priv->session;
1145 if (session == NULL)
1146 return;
1147
1148 DEBUG("enter");
1149
1150 /* Release the peer handle */
1151 conn = (TpBaseConnection *)(priv->conn);
1152 contact_repo = tp_base_connection_get_handles (conn,
1153 TP_HANDLE_TYPE_CONTACT);
1154 tp_handle_unref (contact_repo, sip_media_session_get_peer (session));
1155
1156 priv->session = NULL;
1157 g_object_unref (session);
1158
1159 DEBUG("exit");
1160 }
1161
1162 #if 0
1163 /* Check that self_handle is not already in the members. If it is,
1164 * we're trying to call ourselves. */
1165 static void
1166 priv_add_members (TpSvcChannelInterfaceGroup *obj,
1167 const GArray *contacts,
1168 const gchar *message,
1169 DBusGMethodInvocation *context)
1170 {
1171 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
1172 guint i;
1173 TpHandle handle;
1174 GError *error = NULL;
1175
1176 for (i = 0; i < contacts->len; i++)
1177 {
1178 handle = g_array_index (contacts, TpHandle, i);
1179
1180 if (handle == mixin->self_handle &&
1181 tp_handle_set_is_member (mixin->members, handle))
1182 {
1183 DEBUG ("attempted to add self_handle into the mixin again");
1184 g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_HANDLE,
1185 "you cannot call yourself");
1186 }
1187 }
1188
1189 if (error == NULL)
1190 tp_group_mixin_add_members ((GObject *) obj, contacts, message, &error);
1191
1192 if (error == NULL)
1193 {
1194 tp_svc_channel_interface_group_return_from_add_members (context);
1195 }
1196 else
1197 {
1198 dbus_g_method_return_error (context, error);
1199 g_error_free (error);
1200 }
1201 }
1202 #endif
1203
1204 static gboolean
sip_media_channel_add_member(GObject * iface,TpHandle handle,const gchar * message,GError ** error)1205 sip_media_channel_add_member (GObject *iface,
1206 TpHandle handle,
1207 const gchar *message,
1208 GError **error)
1209 {
1210 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
1211 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
1212 TpGroupMixin *mixin = TP_GROUP_MIXIN (iface);
1213
1214 DEBUG("mixin->self_handle=%d, handle=%d", mixin->self_handle, handle);
1215
1216 /* case a: outgoing call (we are the initiator, a new handle added) */
1217 if (mixin->self_handle != handle)
1218 {
1219 TpGroupMixin *mixin = TP_GROUP_MIXIN (self);
1220 TpIntSet *lset, *rset;
1221 nua_handle_t *nh;
1222
1223 DEBUG("making outbound call - setting peer handle to %u", handle);
1224
1225 nh = sip_conn_create_request_handle (priv->conn, handle);
1226 priv_create_session (self, nh, handle, FALSE);
1227 nua_handle_unref (nh);
1228
1229 /* make remote pending */
1230 rset = tp_intset_new ();
1231 lset = tp_intset_new ();
1232 tp_intset_add (lset, mixin->self_handle);
1233 tp_intset_add (rset, handle);
1234 tp_group_mixin_change_members (iface, "Sending INVITE",
1235 lset, /* add */
1236 NULL, /* remove */
1237 NULL, /* local pending */
1238 rset, /* remote pending */
1239 0, 0);
1240 tp_intset_destroy (lset);
1241 tp_intset_destroy (rset);
1242
1243 /* and update flags accordingly */
1244 tp_group_mixin_change_flags (iface,
1245 TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_REMOVE |
1246 TP_CHANNEL_GROUP_FLAG_CAN_RESCIND,
1247 0);
1248
1249 return TRUE;
1250 }
1251 /* case b: an incoming invite */
1252 if (tp_handle_set_is_member (mixin->local_pending, handle))
1253 {
1254 TpIntSet *set;
1255
1256 DEBUG("accepting an incoming invite");
1257
1258 g_return_val_if_fail (priv->session != NULL, FALSE);
1259
1260 set = tp_intset_new ();
1261 tp_intset_add (set, handle);
1262 tp_group_mixin_change_members (iface, "Incoming call accepted",
1263 set, /* add */
1264 NULL, /* remove */
1265 NULL, /* local pending */
1266 NULL, /* remote pending */
1267 0, 0);
1268 tp_intset_destroy (set);
1269
1270 sip_media_session_accept (priv->session);
1271
1272 return TRUE;
1273 }
1274
1275 /* This can only legitimately happen if the user is trying to call themselves */
1276 g_message ("unsupported member change requested for a media channel");
1277
1278 g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
1279 "Can't call yourself");
1280 return FALSE;
1281 }
1282
1283 static gint
sip_status_from_tp_reason(TpChannelGroupChangeReason reason)1284 sip_status_from_tp_reason (TpChannelGroupChangeReason reason)
1285 {
1286 switch (reason)
1287 {
1288 case TP_CHANNEL_GROUP_CHANGE_REASON_NONE:
1289 return 603; /* Decline */
1290 case TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER:
1291 case TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE:
1292 return 480; /* Temporarily Unavailable */
1293 case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY:
1294 return 486; /* Busy Here */
1295 case TP_CHANNEL_GROUP_CHANGE_REASON_PERMISSION_DENIED:
1296 case TP_CHANNEL_GROUP_CHANGE_REASON_BANNED:
1297 return 403; /* Forbidden */
1298 case TP_CHANNEL_GROUP_CHANGE_REASON_INVALID_CONTACT:
1299 return 404; /* Not Found */
1300 default:
1301 return 500; /* Server Internal Error */
1302 }
1303 }
1304
1305 static gboolean
sip_media_channel_remove_with_reason(GObject * obj,TpHandle handle,const gchar * message,guint reason,GError ** error)1306 sip_media_channel_remove_with_reason (GObject *obj,
1307 TpHandle handle,
1308 const gchar *message,
1309 guint reason,
1310 GError **error)
1311 {
1312 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (obj);
1313 SIPMediaChannelPrivate *priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
1314 TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
1315
1316 /* We handle only one case: removal of the self handle from local pending
1317 * due to the user rejecting the call */
1318 if (priv->session &&
1319 handle == mixin->self_handle &&
1320 tp_handle_set_is_member (mixin->local_pending, handle))
1321 {
1322 TpIntSet *set;
1323 gint status;
1324
1325 status = sip_status_from_tp_reason (reason);
1326
1327 /* XXX: raise NotAvailable if it's the wrong state? */
1328 sip_media_session_reject (priv->session, status, message);
1329
1330 set = tp_intset_new ();
1331 tp_intset_add (set, handle);
1332 tp_group_mixin_change_members (obj,
1333 message,
1334 NULL, /* add */
1335 set, /* remove */
1336 NULL, /* add local pending */
1337 NULL, /* add remote pending */
1338 0, /* actor */
1339 reason);
1340 tp_intset_destroy (set);
1341
1342 /* no more adding to this channel */
1343 tp_group_mixin_change_flags (obj,
1344 0,
1345 TP_CHANNEL_GROUP_FLAG_CAN_ADD);
1346
1347 return TRUE;
1348 }
1349
1350 g_assert_not_reached();
1351
1352 g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
1353 "Can't map this member change to protocol behavior");
1354 return FALSE;
1355 }
1356
1357 static void
sip_media_channel_start_tone(TpSvcChannelInterfaceDTMF * iface,guint stream_id,guchar event,DBusGMethodInvocation * context)1358 sip_media_channel_start_tone (TpSvcChannelInterfaceDTMF *iface,
1359 guint stream_id,
1360 guchar event,
1361 DBusGMethodInvocation *context)
1362 {
1363 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
1364 SIPMediaChannelPrivate *priv;
1365 GError *error = NULL;
1366
1367 DEBUG("enter");
1368
1369 g_assert (SIP_IS_MEDIA_CHANNEL (self));
1370
1371 if (event >= NUM_TP_DTMF_EVENTS)
1372 {
1373 g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
1374 "event %u is not a known DTMF event", event);
1375 dbus_g_method_return_error (context, error);
1376 g_error_free (error);
1377 return;
1378 }
1379
1380 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
1381
1382 if (!sip_media_session_start_telephony_event (priv->session,
1383 stream_id,
1384 event,
1385 &error))
1386 {
1387 dbus_g_method_return_error (context, error);
1388 g_error_free (error);
1389 return;
1390 }
1391
1392 tp_svc_channel_interface_dtmf_return_from_start_tone (context);
1393 }
1394
1395 static void
sip_media_channel_stop_tone(TpSvcChannelInterfaceDTMF * iface,guint stream_id,DBusGMethodInvocation * context)1396 sip_media_channel_stop_tone (TpSvcChannelInterfaceDTMF *iface,
1397 guint stream_id,
1398 DBusGMethodInvocation *context)
1399 {
1400 SIPMediaChannel *self = SIP_MEDIA_CHANNEL (iface);
1401 SIPMediaChannelPrivate *priv;
1402 GError *error = NULL;
1403
1404 DEBUG("enter");
1405
1406 g_assert (SIP_IS_MEDIA_CHANNEL (self));
1407
1408 priv = SIP_MEDIA_CHANNEL_GET_PRIVATE (self);
1409
1410 if (!sip_media_session_stop_telephony_event (priv->session,
1411 stream_id,
1412 &error))
1413 {
1414 dbus_g_method_return_error (context, error);
1415 g_error_free (error);
1416 return;
1417 }
1418
1419 tp_svc_channel_interface_dtmf_return_from_stop_tone (context);
1420 }
1421
1422 static void
channel_iface_init(gpointer g_iface,gpointer iface_data)1423 channel_iface_init(gpointer g_iface, gpointer iface_data)
1424 {
1425 TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface;
1426
1427 #define IMPLEMENT(x, suffix) tp_svc_channel_implement_##x (\
1428 klass, sip_media_channel_##x##suffix)
1429 IMPLEMENT(close,_async);
1430 IMPLEMENT(get_channel_type,);
1431 IMPLEMENT(get_handle,);
1432 IMPLEMENT(get_interfaces,);
1433 #undef IMPLEMENT
1434 }
1435
1436 static void
streamed_media_iface_init(gpointer g_iface,gpointer iface_data)1437 streamed_media_iface_init(gpointer g_iface, gpointer iface_data)
1438 {
1439 TpSvcChannelTypeStreamedMediaClass *klass = (TpSvcChannelTypeStreamedMediaClass *)g_iface;
1440
1441 #define IMPLEMENT(x) tp_svc_channel_type_streamed_media_implement_##x (\
1442 klass, sip_media_channel_##x)
1443 IMPLEMENT(list_streams);
1444 IMPLEMENT(remove_streams);
1445 IMPLEMENT(request_stream_direction);
1446 IMPLEMENT(request_streams);
1447 #undef IMPLEMENT
1448 }
1449
1450 static void
media_signalling_iface_init(gpointer g_iface,gpointer iface_data)1451 media_signalling_iface_init(gpointer g_iface, gpointer iface_data)
1452 {
1453 TpSvcChannelInterfaceMediaSignallingClass *klass = (TpSvcChannelInterfaceMediaSignallingClass *)g_iface;
1454
1455 #define IMPLEMENT(x) tp_svc_channel_interface_media_signalling_implement_##x (\
1456 klass, sip_media_channel_##x)
1457 IMPLEMENT(get_session_handlers);
1458 #undef IMPLEMENT
1459 }
1460
1461 static void
dtmf_iface_init(gpointer g_iface,gpointer iface_data)1462 dtmf_iface_init (gpointer g_iface, gpointer iface_data)
1463 {
1464 TpSvcChannelInterfaceDTMFClass *klass = (TpSvcChannelInterfaceDTMFClass *)g_iface;
1465
1466 #define IMPLEMENT(x) tp_svc_channel_interface_dtmf_implement_##x (\
1467 klass, sip_media_channel_##x)
1468 IMPLEMENT(start_tone);
1469 IMPLEMENT(stop_tone);
1470 #undef IMPLEMENT
1471 }
1472
1473 static void
priv_group_mixin_iface_init(gpointer g_iface,gpointer iface_data)1474 priv_group_mixin_iface_init (gpointer g_iface, gpointer iface_data)
1475 {
1476 #if 0
1477 TpSvcChannelInterfaceGroupClass *klass =
1478 (TpSvcChannelInterfaceGroupClass *)g_iface;
1479 #endif
1480
1481 tp_group_mixin_iface_init (g_iface, iface_data);
1482
1483 #if 0
1484 tp_svc_channel_interface_group_implement_add_members (klass,
1485 priv_add_members);
1486 #endif
1487 }
1488
1489