1 /*
2  * sip-media-session.c - Source for SIPMediaSession
3  * Copyright (C) 2005 Collabora Ltd.
4  * Copyright (C) 2005,2006,2007 Nokia Corporation
5  *   @author Kai Vehmanen <first.surname@nokia.com>
6  *   @author Mikhail Zabaluev <first.surname@nokia.com>
7  *
8  * Based on telepathy-gabble implementation (gabble-media-session).
9  *   @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * version 2.1 as published by the Free Software Foundation.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  */
24 
25 #include <dbus/dbus-glib.h>
26 #include <stdlib.h>
27 #include <time.h>
28 #include <string.h>
29 
30 #include <sofia-sip/sip_status.h>
31 
32 #include <telepathy-glib/interfaces.h>
33 #include <telepathy-glib/dbus.h>
34 #include <telepathy-glib/errors.h>
35 #include <telepathy-glib/svc-media-interfaces.h>
36 
37 #include "config.h"
38 
39 #include "sip-media-session.h"
40 #include "sip-media-channel.h"
41 #include "sip-media-stream.h"
42 #include "sip-connection-helpers.h"
43 #include "telepathy-helpers.h"
44 
45 #define DEBUG_FLAG SIP_DEBUG_MEDIA
46 #include "debug.h"
47 
48 static void session_handler_iface_init (gpointer, gpointer);
49 
50 G_DEFINE_TYPE_WITH_CODE(SIPMediaSession,
51     sip_media_session,
52     G_TYPE_OBJECT,
53     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_SESSION_HANDLER,
54       session_handler_iface_init)
55     )
56 
57 #define DEFAULT_SESSION_TIMEOUT 50000
58 
59 /* signal enum */
60 enum
61 {
62   SIG_STREAM_ADDED,
63   SIG_LAST_SIGNAL
64 };
65 
66 /* properties */
67 enum
68 {
69   PROP_MEDIA_CHANNEL = 1,
70   PROP_OBJECT_PATH,
71   PROP_NUA_OP,
72   PROP_PEER,
73   PROP_LOCAL_IP_ADDRESS,
74   PROP_STATE,
75   LAST_PROPERTY
76 };
77 
78 static guint signals[SIG_LAST_SIGNAL] = {0};
79 
80 #ifdef ENABLE_DEBUG
81 
82 /**
83  * StreamEngine session states:
84  * - created, objects created, local cand/codec query ongoing
85  * - invite-sent, an INVITE with local SDP sent, awaiting response
86  * - invite-received, a remote INVITE received, response is pending
87  * - response-received, a 200 OK received, codec intersection is in progress
88  * - active, codecs and candidate pairs have been negotiated (note,
89  *   SteamEngine might still fail to verify connectivity and report
90  *   an error)
91  * - reinvite-sent, a local re-INVITE sent, response is pending
92  * - reinvite-received, a remote re-INVITE received, response is pending
93  * - ended, session has ended
94  */
95 static const char* session_states[] =
96 {
97     "created",
98     "invite-sent",
99     "invite-received",
100     "response-received",
101     "active",
102     "reinvite-sent",
103     "reinvite-received",
104     "ended"
105 };
106 
107 #endif /* ENABLE_DEBUG */
108 
109 /* private structure */
110 typedef struct _SIPMediaSessionPrivate SIPMediaSessionPrivate;
111 
112 struct _SIPMediaSessionPrivate
113 {
114   SIPMediaChannel *channel;             /** see gobj. prop. 'media-channel' */
115   gchar *object_path;                   /** see gobj. prop. 'object-path' */
116   nua_handle_t *nua_op;                 /** see gobj. prop. 'nua-handle' */
117   TpHandle peer;                        /** see gobj. prop. 'peer' */
118   gchar *local_ip_address;              /** see gobj. prop. 'local-ip-address' */
119   SIPMediaSessionState state;           /** see gobj. prop. 'state' */
120   nua_saved_event_t saved_event[1];     /** Saved incoming request event */
121   gint local_non_ready;                 /** number of streams with local information update pending */
122   guint catcher_id;
123   guint timer_id;
124   su_home_t *home;                      /** Sofia memory home for remote SDP session structure */
125   su_home_t *backup_home;               /** Sofia memory home for previous generation remote SDP session*/
126   sdp_session_t *remote_sdp;            /** last received remote session */
127   sdp_session_t *backup_remote_sdp;     /** previous remote session */
128   GPtrArray *streams;
129   gboolean accepted;                    /**< session has been locally accepted for use */
130   gboolean se_ready;                    /**< connection established with stream-engine */
131   gboolean pending_offer;               /**< local media have been changed, but a re-INVITE is pending */
132   gboolean dispose_has_run;
133 };
134 
135 #define SIP_MEDIA_SESSION_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), SIP_TYPE_MEDIA_SESSION, SIPMediaSessionPrivate))
136 
137 static void sip_media_session_get_property (GObject    *object,
138 					    guint       property_id,
139 					    GValue     *value,
140 					    GParamSpec *pspec);
141 static void sip_media_session_set_property (GObject      *object,
142 					    guint         property_id,
143 					    const GValue *value,
144 					    GParamSpec   *pspec);
145 
146 static SIPMediaStream *
147 sip_media_session_get_stream (SIPMediaSession *self,
148                               guint stream_id,
149                               GError **error);
150 
151 static void priv_session_state_changed (SIPMediaSession *session,
152                                         SIPMediaSessionState prev_state);
153 static gboolean priv_catch_remote_nonupdate (gpointer data);
154 static gboolean priv_timeout_session (gpointer data);
155 static SIPMediaStream* priv_create_media_stream (SIPMediaSession *session,
156                                                  guint media_type,
157                                                  guint pending_send_flags);
158 static void priv_request_response_step (SIPMediaSession *session);
159 static void priv_session_invite (SIPMediaSession *session, gboolean reinvite);
160 static void priv_local_media_changed (SIPMediaSession *session);
161 static gboolean priv_update_remote_media (SIPMediaSession *session,
162                                           gboolean authoritative);
163 static void priv_save_event (SIPMediaSession *self);
164 static void priv_zap_event (SIPMediaSession *self);
165 
sip_media_session_init(SIPMediaSession * obj)166 static void sip_media_session_init (SIPMediaSession *obj)
167 {
168   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (obj);
169 
170   priv->state = SIP_MEDIA_SESSION_STATE_CREATED;
171 
172   /* allocate any data required by the object here */
173   priv->streams = g_ptr_array_new ();
174 }
175 
176 static GObject *
sip_media_session_constructor(GType type,guint n_props,GObjectConstructParam * props)177 sip_media_session_constructor (GType type, guint n_props,
178 			       GObjectConstructParam *props)
179 {
180   GObject *obj;
181   SIPMediaSessionPrivate *priv;
182   DBusGConnection *bus;
183 
184   obj = G_OBJECT_CLASS (sip_media_session_parent_class)->
185            constructor (type, n_props, props);
186   priv = SIP_MEDIA_SESSION_GET_PRIVATE (SIP_MEDIA_SESSION (obj));
187 
188   if (priv->nua_op)
189     {
190       nua_hmagic_t *nua_op_magic;
191 
192       g_assert (priv->channel != NULL);
193 
194       /* migrating a NUA handle between two active media channels
195        * makes no sense either */
196       nua_op_magic = nua_handle_magic (priv->nua_op);
197       g_return_val_if_fail (nua_op_magic == NULL || nua_op_magic == priv->channel, NULL);
198 
199       /* tell the NUA that we're handling this call */
200       nua_handle_bind (priv->nua_op, priv->channel);
201     }
202 
203   bus = tp_get_bus ();
204   dbus_g_connection_register_g_object (bus, priv->object_path, obj);
205 
206   return obj;
207 }
208 
sip_media_session_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)209 static void sip_media_session_get_property (GObject    *object,
210 					    guint       property_id,
211 					    GValue     *value,
212 					    GParamSpec *pspec)
213 {
214   SIPMediaSession *session = SIP_MEDIA_SESSION (object);
215   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
216 
217   switch (property_id) {
218     case PROP_MEDIA_CHANNEL:
219       g_value_set_object (value, priv->channel);
220       break;
221     case PROP_OBJECT_PATH:
222       g_value_set_string (value, priv->object_path);
223       break;
224     case PROP_NUA_OP:
225       g_value_set_pointer (value, priv->nua_op);
226       break;
227     case PROP_PEER:
228       g_value_set_uint (value, priv->peer);
229       break;
230     case PROP_LOCAL_IP_ADDRESS:
231       g_value_set_string (value, priv->local_ip_address);
232       break;
233     case PROP_STATE:
234       g_value_set_uint (value, priv->state);
235       break;
236     default:
237       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
238       break;
239   }
240 }
241 
sip_media_session_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)242 static void sip_media_session_set_property (GObject      *object,
243 					    guint         property_id,
244 					    const GValue *value,
245 					    GParamSpec   *pspec)
246 {
247   SIPMediaSession *session = SIP_MEDIA_SESSION (object);
248   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
249 
250   switch (property_id) {
251     case PROP_MEDIA_CHANNEL:
252       priv->channel = SIP_MEDIA_CHANNEL (g_value_get_object (value));
253       break;
254     case PROP_OBJECT_PATH:
255       g_assert (priv->object_path == NULL);
256       priv->object_path = g_value_dup_string (value);
257       break;
258     case PROP_NUA_OP:
259       /* you can only set the NUA handle once - migrating a media session
260        * between two NUA handles makes no sense */
261       g_return_if_fail (priv->nua_op == NULL);
262       priv->nua_op = g_value_get_pointer (value);
263       nua_handle_ref (priv->nua_op);
264       break;
265     case PROP_PEER:
266       priv->peer = g_value_get_uint (value);
267       break;
268     case PROP_LOCAL_IP_ADDRESS:
269       g_assert (priv->local_ip_address == NULL);
270       priv->local_ip_address = g_value_dup_string (value);
271       break;
272     case PROP_STATE:
273       priv_session_state_changed (session, g_value_get_uint (value));
274       break;
275     default:
276       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
277       break;
278   }
279 }
280 
281 static void sip_media_session_dispose (GObject *object);
282 static void sip_media_session_finalize (GObject *object);
283 
284 static void
sip_media_session_class_init(SIPMediaSessionClass * sip_media_session_class)285 sip_media_session_class_init (SIPMediaSessionClass *sip_media_session_class)
286 {
287   GObjectClass *object_class = G_OBJECT_CLASS (sip_media_session_class);
288   GParamSpec *param_spec;
289 
290   g_type_class_add_private (sip_media_session_class, sizeof (SIPMediaSessionPrivate));
291 
292   object_class->constructor = sip_media_session_constructor;
293 
294   object_class->get_property = sip_media_session_get_property;
295   object_class->set_property = sip_media_session_set_property;
296 
297   object_class->dispose = sip_media_session_dispose;
298   object_class->finalize = sip_media_session_finalize;
299 
300   param_spec = g_param_spec_object ("media-channel", "SIPMediaChannel object",
301                                     "SIP media channel object that owns this "
302                                     "media session object (not reference counted).",
303                                     SIP_TYPE_MEDIA_CHANNEL,
304                                     G_PARAM_CONSTRUCT_ONLY |
305                                     G_PARAM_READWRITE |
306                                     G_PARAM_STATIC_NICK |
307                                     G_PARAM_STATIC_BLURB);
308   g_object_class_install_property (object_class, PROP_MEDIA_CHANNEL, param_spec);
309 
310   param_spec = g_param_spec_string ("object-path", "D-Bus object path",
311                                     "The D-Bus object path used for this "
312                                     "object on the bus.",
313                                     NULL,
314                                     G_PARAM_CONSTRUCT_ONLY |
315                                     G_PARAM_READWRITE |
316                                     G_PARAM_STATIC_NAME |
317                                     G_PARAM_STATIC_BLURB);
318   g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
319 
320   param_spec = g_param_spec_pointer("nua-handle", "Sofia-SIP NUA operator handle",
321                                     "NUA stack operation handle associated "
322                                     "with this media session.",
323                                     G_PARAM_CONSTRUCT_ONLY |
324                                     G_PARAM_READWRITE |
325                                     G_PARAM_STATIC_NAME |
326                                     G_PARAM_STATIC_BLURB);
327   g_object_class_install_property (object_class, PROP_NUA_OP, param_spec);
328 
329   param_spec = g_param_spec_uint ("peer", "Session peer",
330                                   "The TpHandle representing the contact "
331                                   "with whom this session communicates.",
332                                   0, G_MAXUINT32, 0,
333                                   G_PARAM_CONSTRUCT_ONLY |
334                                   G_PARAM_READWRITE |
335                                   G_PARAM_STATIC_NAME |
336                                   G_PARAM_STATIC_BLURB);
337   g_object_class_install_property (object_class, PROP_PEER, param_spec);
338 
339   param_spec = g_param_spec_string ("local-ip-address", "Local IP address",
340                                     "The local IP address preferred for "
341                                     "media streams",
342                                     NULL,
343                                     G_PARAM_CONSTRUCT_ONLY |
344                                     G_PARAM_READWRITE |
345                                     G_PARAM_STATIC_NAME |
346                                     G_PARAM_STATIC_BLURB);
347   g_object_class_install_property (object_class, PROP_LOCAL_IP_ADDRESS, param_spec);
348 
349   param_spec = g_param_spec_uint ("state", "Session state",
350                                   "The current state that the session is in.",
351                                   0, G_MAXUINT32, 0,
352                                   G_PARAM_READWRITE |
353                                   G_PARAM_STATIC_NAME |
354                                   G_PARAM_STATIC_BLURB);
355   g_object_class_install_property (object_class, PROP_STATE, param_spec);
356 
357   signals[SIG_STREAM_ADDED] =
358     g_signal_new ("stream-added",
359                   G_OBJECT_CLASS_TYPE (sip_media_session_class),
360                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
361                   0,
362                   NULL, NULL,
363                   g_cclosure_marshal_VOID__OBJECT,
364                   G_TYPE_NONE, 1, G_TYPE_OBJECT);
365 }
366 
367 static void
sip_media_session_dispose(GObject * object)368 sip_media_session_dispose (GObject *object)
369 {
370   SIPMediaSession *self = SIP_MEDIA_SESSION (object);
371   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
372 
373   if (priv->dispose_has_run)
374     return;
375 
376   DEBUG("enter");
377 
378   priv->dispose_has_run = TRUE;
379 
380   if (priv->catcher_id)
381     g_source_remove (priv->catcher_id);
382 
383   if (priv->timer_id)
384     g_source_remove (priv->timer_id);
385 
386   if (G_OBJECT_CLASS (sip_media_session_parent_class)->dispose)
387     G_OBJECT_CLASS (sip_media_session_parent_class)->dispose (object);
388 
389   DEBUG("exit");
390 }
391 
392 static void
sip_media_session_finalize(GObject * object)393 sip_media_session_finalize (GObject *object)
394 {
395   SIPMediaSession *self = SIP_MEDIA_SESSION (object);
396   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
397   guint i;
398 
399   /* terminating the session should have discarded the NUA handle */
400   g_assert (priv->nua_op == NULL);
401 
402   /* free any data held directly by the object here */
403 
404   for (i = 0; i < priv->streams->len; i++) {
405     SIPMediaStream *stream = g_ptr_array_index (priv->streams, i);
406     if (stream != NULL)
407       {
408         g_warning ("stream %u (%p) left over, reaping", i, stream);
409         g_object_unref (stream);
410       }
411   }
412   g_ptr_array_free(priv->streams, TRUE);
413 
414   priv_zap_event (self);
415 
416   if (priv->home != NULL)
417     su_home_unref (priv->home);
418   if (priv->backup_home != NULL)
419     su_home_unref (priv->backup_home);
420 
421   g_free (priv->local_ip_address);
422   g_free (priv->object_path);
423 
424   G_OBJECT_CLASS (sip_media_session_parent_class)->finalize (object);
425 
426   DEBUG("exit");
427 }
428 
429 
430 
431 /**
432  * sip_media_session_error
433  *
434  * Implements DBus method Error
435  * on interface org.freedesktop.Telepathy.Media.SessionHandler
436  */
437 static void
sip_media_session_error(TpSvcMediaSessionHandler * iface,guint errno,const gchar * message,DBusGMethodInvocation * context)438 sip_media_session_error (TpSvcMediaSessionHandler *iface,
439                          guint errno,
440                          const gchar *message,
441                          DBusGMethodInvocation *context)
442 {
443   SIPMediaSession *obj = SIP_MEDIA_SESSION (iface);
444 
445   SESSION_DEBUG(obj, "Media.SessionHandler::Error called (%s) terminating session", message);
446 
447   sip_media_session_terminate (obj);
448 
449   tp_svc_media_session_handler_return_from_error (context);
450 }
451 
priv_emit_new_stream(SIPMediaSession * self,SIPMediaStream * stream)452 static void priv_emit_new_stream (SIPMediaSession *self,
453 				  SIPMediaStream *stream)
454 {
455   gchar *object_path;
456   guint id;
457   guint media_type;
458   guint direction;
459 
460   g_object_get (stream,
461                 "object-path", &object_path,
462                 "id", &id,
463                 "media-type", &media_type,
464                 "direction", &direction,
465                 NULL);
466 
467   /* note: all of the streams are bidirectional from farsight's point of view, it's
468    * just in the signalling they change */
469 
470   tp_svc_media_session_handler_emit_new_stream_handler (
471       (TpSvcMediaSessionHandler *)self, object_path, id, media_type,
472       direction);
473 
474   g_free (object_path);
475 }
476 
477 
478 /**
479  * sip_media_session_ready
480  *
481  * Implements DBus method Ready
482  * on interface org.freedesktop.Telepathy.Media.SessionHandler
483  */
484 static void
sip_media_session_ready(TpSvcMediaSessionHandler * iface,DBusGMethodInvocation * context)485 sip_media_session_ready (TpSvcMediaSessionHandler *iface,
486                          DBusGMethodInvocation *context)
487 {
488   SIPMediaSession *obj = SIP_MEDIA_SESSION (iface);
489   SIPMediaSessionPrivate *priv;
490   guint i;
491 
492   DEBUG ("enter");
493 
494   g_assert (SIP_IS_MEDIA_SESSION (obj));
495 
496   priv = SIP_MEDIA_SESSION_GET_PRIVATE (obj);
497 
498   priv->se_ready = TRUE;
499 
500   /* note: streams are generated in priv_create_media_stream() */
501 
502   for (i = 0; i < priv->streams->len; i++) {
503     SIPMediaStream *stream = g_ptr_array_index (priv->streams, i);
504     if (stream)
505       priv_emit_new_stream (obj, stream);
506   }
507 
508   tp_svc_media_session_handler_return_from_ready (context);
509 }
510 
511 /***********************************************************************
512  * Helper functions follow (not based on generated templates)
513  ***********************************************************************/
514 
515 TpHandle
sip_media_session_get_peer(SIPMediaSession * session)516 sip_media_session_get_peer (SIPMediaSession *session)
517 {
518   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
519   return priv->peer;
520 }
521 
522 static void
priv_close_all_streams(SIPMediaSession * session)523 priv_close_all_streams (SIPMediaSession *session)
524 {
525   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
526   guint i;
527   for (i = 0; i < priv->streams->len; i++)
528     {
529       SIPMediaStream *stream;
530       stream = g_ptr_array_index (priv->streams, i);
531       if (stream != NULL)
532         sip_media_stream_close (stream);
533       g_assert (g_ptr_array_index (priv->streams, i) == NULL);
534     }
535 }
536 
537 static void
priv_clear_streams_pending_send(SIPMediaSession * session)538 priv_clear_streams_pending_send (SIPMediaSession *session)
539 {
540   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
541   SIPMediaStream *stream;
542   guint i;
543 
544   /* Clear the local pending send flags, enabling sending */
545   for (i = 0; i < priv->streams->len; i++)
546     {
547       stream = g_ptr_array_index(priv->streams, i);
548       if (stream != NULL)
549         {
550           guint direction = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL;
551           g_object_get (stream, "direction", &direction, NULL);
552           if (direction & TP_MEDIA_STREAM_DIRECTION_SEND)
553             sip_media_stream_set_direction (stream, direction, 0);
554         }
555     }
556 }
557 
558 static void
priv_session_state_changed(SIPMediaSession * session,SIPMediaSessionState new_state)559 priv_session_state_changed (SIPMediaSession *session,
560                             SIPMediaSessionState new_state)
561 {
562   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
563 
564   if (priv->state == new_state)
565     return;
566 
567   SESSION_DEBUG(session, "state changed from %s to %s",
568                 session_states[priv->state],
569                 session_states[new_state]);
570 
571   priv->state = new_state;
572 
573   switch (new_state)
574     {
575     case SIP_MEDIA_SESSION_STATE_CREATED:
576       break;
577     case SIP_MEDIA_SESSION_STATE_ENDED:
578       priv_close_all_streams (session);
579       DEBUG("freeing the NUA handle %p", priv->nua_op);
580       if (priv->nua_op != NULL)
581         {
582           nua_handle_bind (priv->nua_op, SIP_NH_EXPIRED);
583           nua_handle_unref (priv->nua_op);
584           priv->nua_op = NULL;
585         }
586       break;
587     case SIP_MEDIA_SESSION_STATE_INVITE_RECEIVED:
588     case SIP_MEDIA_SESSION_STATE_REINVITE_RECEIVED:
589       priv->catcher_id = g_idle_add (priv_catch_remote_nonupdate, session);
590       /* Fall through to the next case */
591     case SIP_MEDIA_SESSION_STATE_INVITE_SENT:
592     case SIP_MEDIA_SESSION_STATE_REINVITE_SENT:
593       if (priv->timer_id)
594         {
595           g_source_remove (priv->timer_id);
596         }
597       priv->timer_id =
598         g_timeout_add (DEFAULT_SESSION_TIMEOUT, priv_timeout_session, session);
599       break;
600     case SIP_MEDIA_SESSION_STATE_RESPONSE_RECEIVED:
601       break;
602     case SIP_MEDIA_SESSION_STATE_ACTIVE:
603       if (priv->timer_id)
604         {
605 	  g_source_remove (priv->timer_id);
606 	  priv->timer_id = 0;
607         }
608       priv_clear_streams_pending_send (session);
609       if (priv->pending_offer)
610         {
611           priv_session_invite (session, TRUE);
612         }
613       break;
614 
615     /* Don't add default because we want to be warned by the compiler
616      * about unhandled states */
617     }
618 }
619 
620 #ifdef ENABLE_DEBUG
621 void
sip_media_session_debug(SIPMediaSession * session,const gchar * format,...)622 sip_media_session_debug (SIPMediaSession *session,
623 			 const gchar *format, ...)
624 {
625   va_list list;
626   gchar buf[512];
627   SIPMediaSessionPrivate *priv;
628 
629   g_assert (SIP_IS_MEDIA_SESSION (session));
630 
631   priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
632 
633   va_start (list, format);
634 
635   g_vsnprintf (buf, sizeof (buf), format, list);
636 
637   va_end (list);
638 
639   sip_debug (DEBUG_FLAG, "SIP media session [%-17s]: %s",
640       session_states[priv->state],
641       buf);
642 }
643 #endif /* ENABLE_DEBUG */
644 
645 static gboolean
priv_catch_remote_nonupdate(gpointer data)646 priv_catch_remote_nonupdate (gpointer data)
647 {
648   SIPMediaSession *session = data;
649 
650   DEBUG("called");
651 
652   /* Accordingly to the last experimental data, non-modifying INVITEs
653    * cause the stack to emit nua_i_state nonetheless */
654   g_assert_not_reached();
655 
656   /* TODO: figure out what happens in the 3pcc scenario when we get
657    * an INVITE but no session offer */
658 
659   /* Should do the right thing if there were no remote media updates */
660   priv_request_response_step (session);
661 
662   return FALSE;
663 }
664 
priv_timeout_session(gpointer data)665 static gboolean priv_timeout_session (gpointer data)
666 {
667   SIPMediaSession *session = data;
668   SIPMediaSessionPrivate *priv;
669   TpChannelGroupChangeReason reason;
670   gboolean change = FALSE;
671   TpHandle actor;
672 
673   DEBUG("session timed out");
674 
675   priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
676 
677   if (priv->state == SIP_MEDIA_SESSION_STATE_INVITE_SENT)
678     {
679       reason = TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER;
680       actor = 0;
681       change = TRUE;
682     }
683   else if (priv->state == SIP_MEDIA_SESSION_STATE_INVITE_RECEIVED)
684     {
685       reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
686       actor = priv->peer;
687       change = TRUE;
688     }
689 
690   if (change)
691     {
692       TpIntSet *set = tp_intset_new ();
693       tp_intset_add (set, priv->peer);
694       tp_group_mixin_change_members (G_OBJECT (priv->channel), "Timed out",
695                                      NULL, set, NULL, NULL, actor, reason);
696       tp_intset_destroy (set);
697     }
698 
699   sip_media_session_terminate (session);
700 
701   return FALSE;
702 }
703 
sip_media_session_terminate(SIPMediaSession * session)704 void sip_media_session_terminate (SIPMediaSession *session)
705 {
706   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
707 
708   DEBUG ("enter");
709 
710   if (priv->state == SIP_MEDIA_SESSION_STATE_ENDED)
711     return;
712 
713   /* XXX: taken care of by the state change? */
714   priv_close_all_streams (session);
715 
716   if (priv->nua_op != NULL)
717     {
718       g_assert (nua_handle_magic (priv->nua_op) == priv->channel);
719 
720       switch (priv->state)
721         {
722         case SIP_MEDIA_SESSION_STATE_ACTIVE:
723         case SIP_MEDIA_SESSION_STATE_RESPONSE_RECEIVED:
724         case SIP_MEDIA_SESSION_STATE_REINVITE_SENT:
725           DEBUG("sending BYE");
726           nua_bye (priv->nua_op, TAG_END());
727           break;
728         case SIP_MEDIA_SESSION_STATE_INVITE_SENT:
729           DEBUG("sending CANCEL");
730           nua_cancel (priv->nua_op, TAG_END());
731           break;
732         case SIP_MEDIA_SESSION_STATE_INVITE_RECEIVED:
733           DEBUG("sending the 480 response to an incoming INVITE");
734           nua_respond (priv->nua_op, 480, "Terminated", TAG_END());
735           break;
736         case SIP_MEDIA_SESSION_STATE_REINVITE_RECEIVED:
737           DEBUG("sending the 480 response to an incoming re-INVITE");
738           {
739             msg_t *msg;
740 
741             msg = (priv->saved_event[0])
742                         ? nua_saved_event_request (priv->saved_event) : NULL;
743 
744             nua_respond (priv->nua_op, 480, "Terminated",
745                          TAG_IF(msg, NUTAG_WITH(msg)),
746                          TAG_END());
747           }
748           DEBUG("sending BYE to terminate the call itself");
749           nua_bye (priv->nua_op, TAG_END());
750           break;
751         default:
752           /* let the Sofia stack decide what do to */;
753         }
754     }
755 
756   g_object_set (session, "state", SIP_MEDIA_SESSION_STATE_ENDED, NULL);
757 }
758 
759 gboolean
sip_media_session_set_remote_media(SIPMediaSession * session,const sdp_session_t * sdp)760 sip_media_session_set_remote_media (SIPMediaSession *session,
761                                     const sdp_session_t* sdp)
762 {
763   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
764 
765   DEBUG ("enter");
766 
767   /* Remove the non-update catcher because we've got an update */
768   if (priv->catcher_id)
769     {
770       g_source_remove (priv->catcher_id);
771       priv->catcher_id = 0;
772     }
773 
774   /* Switch the state machine to processing the response */
775   if (priv->state == SIP_MEDIA_SESSION_STATE_INVITE_SENT
776       || priv->state == SIP_MEDIA_SESSION_STATE_REINVITE_SENT)
777     g_object_set (session,
778                   "state", SIP_MEDIA_SESSION_STATE_RESPONSE_RECEIVED,
779                   NULL);
780 
781   /* Handle session non-updates */
782   if (!sdp_session_cmp (priv->remote_sdp, sdp))
783     {
784       /* Should do the proper response etc. */
785       priv_request_response_step (session);
786       return TRUE;
787     }
788 
789   /* Delete a backup session structure, if any */
790   if (priv->backup_remote_sdp != NULL)
791     {
792       priv->backup_remote_sdp = NULL;
793       g_assert (priv->backup_home != NULL);
794       su_home_unref (priv->backup_home);
795       priv->backup_home = NULL;
796     }
797   /* Back up the old session.
798    * The streams still need the old media descriptions */
799   if (priv->remote_sdp != NULL)
800     {
801       g_assert (priv->home != NULL);
802       g_assert (priv->backup_home == NULL);
803       priv->backup_home = priv->home;
804       priv->backup_remote_sdp = priv->remote_sdp;
805     }
806 
807   /* Store the session description structure */
808   priv->home = su_home_create ();
809   priv->remote_sdp = sdp_session_dup (priv->home, sdp);
810   g_return_val_if_fail (priv->remote_sdp != NULL, FALSE);
811 
812   return priv_update_remote_media (
813                 session,
814                 (priv->state == SIP_MEDIA_SESSION_STATE_INVITE_RECEIVED
815                  || priv->state == SIP_MEDIA_SESSION_STATE_REINVITE_RECEIVED));
816 }
817 
DEFINE_TP_STRUCT_TYPE(sip_tp_stream_struct_type,G_TYPE_UINT,G_TYPE_UINT,G_TYPE_UINT,G_TYPE_UINT,G_TYPE_UINT,G_TYPE_UINT)818 DEFINE_TP_STRUCT_TYPE(sip_tp_stream_struct_type,
819                       G_TYPE_UINT,
820                       G_TYPE_UINT,
821                       G_TYPE_UINT,
822                       G_TYPE_UINT,
823                       G_TYPE_UINT,
824                       G_TYPE_UINT)
825 
826 DEFINE_TP_LIST_TYPE(sip_tp_stream_list_type,
827                     sip_tp_stream_struct_type ())
828 
829 
830 void
831 priv_add_stream_list_entry (GPtrArray *list,
832                             SIPMediaStream *stream,
833                             SIPMediaSession *session)
834 {
835   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
836   GValue entry = { 0 };
837   GType stream_type;
838   guint id;
839   TpMediaStreamType type = TP_MEDIA_STREAM_TYPE_AUDIO;
840   TpMediaStreamState connection_state = TP_MEDIA_STREAM_STATE_CONNECTED;
841   TpMediaStreamDirection direction = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL;
842   guint pending_send_flags = 0;
843 
844   g_assert(stream != NULL);
845 
846   g_object_get (stream,
847                 "id", &id,
848                 "media-type", &type,
849                 "state", &connection_state,
850                 "direction", &direction,
851                 "pending-send-flags", &pending_send_flags,
852                 NULL);
853 
854   stream_type = sip_tp_stream_struct_type ();
855 
856   g_value_init (&entry, stream_type);
857   g_value_take_boxed (&entry,
858                       dbus_g_type_specialized_construct (stream_type));
859 
860   dbus_g_type_struct_set (&entry,
861                           0, id,
862                           1, priv->peer,
863                           2, type,
864                           3, connection_state,
865                           4, direction,
866                           5, pending_send_flags,
867                           G_MAXUINT);
868 
869   g_ptr_array_add (list, g_value_get_boxed (&entry));
870 }
871 
sip_media_session_request_streams(SIPMediaSession * session,const GArray * media_types,GPtrArray ** ret,GError ** error)872 gboolean sip_media_session_request_streams (SIPMediaSession *session,
873 					    const GArray *media_types,
874 					    GPtrArray **ret,
875 					    GError **error)
876 {
877   guint i;
878 
879   DEBUG ("enter");
880 
881   *ret = g_ptr_array_sized_new (media_types->len);
882 
883   for (i = 0; i < media_types->len; i++) {
884     guint media_type = g_array_index (media_types, guint, i);
885     SIPMediaStream *stream;
886 
887     stream = priv_create_media_stream (session,
888                                        media_type,
889                                        TP_MEDIA_STREAM_PENDING_REMOTE_SEND);
890 
891     priv_add_stream_list_entry (*ret, stream, session);
892   }
893 
894   priv_local_media_changed (session);
895 
896   return TRUE;
897 }
898 
899 gboolean
sip_media_session_remove_streams(SIPMediaSession * self,const GArray * stream_ids,GError ** error)900 sip_media_session_remove_streams (SIPMediaSession *self,
901                                   const GArray *stream_ids,
902                                   GError **error)
903 {
904   SIPMediaStream *stream;
905   guint stream_id;
906   guint i;
907 
908   DEBUG ("enter");
909 
910   for (i = 0; i < stream_ids->len; i++)
911     {
912       stream_id = g_array_index (stream_ids, guint, i);
913       stream = sip_media_session_get_stream (self, stream_id, error);
914       if (stream == NULL)
915         return FALSE;
916       sip_media_stream_close (stream);
917     }
918 
919   priv_local_media_changed (self);
920 
921   return TRUE;
922 }
923 
924 /**
925  * Returns a list of pointers to SIPMediaStream objects
926  * associated with this session.
927  */
sip_media_session_list_streams(SIPMediaSession * session,GPtrArray ** ret)928 gboolean sip_media_session_list_streams (SIPMediaSession *session,
929 					 GPtrArray **ret)
930 {
931   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
932   SIPMediaStream *stream;
933   guint i;
934 
935   if (priv->streams == NULL || priv->streams->len == 0)
936     return FALSE;
937 
938   *ret = g_ptr_array_sized_new (priv->streams->len);
939 
940   for (i = 0; i < priv->streams->len; i++)
941     {
942       stream = g_ptr_array_index(priv->streams, i);
943       if (stream)
944 	priv_add_stream_list_entry (*ret, stream, session);
945     }
946 
947   return TRUE;
948 }
949 
950 gboolean
sip_media_session_request_stream_direction(SIPMediaSession * self,guint stream_id,guint direction,GError ** error)951 sip_media_session_request_stream_direction (SIPMediaSession *self,
952                                             guint stream_id,
953                                             guint direction,
954                                             GError **error)
955 {
956   SIPMediaStream *stream;
957   guint old_direction = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL;
958   guint pending_send_flags = 0;
959 
960   stream = sip_media_session_get_stream (self, stream_id, error);
961   if (stream == NULL)
962     return FALSE;
963 
964   g_object_get (stream,
965                 "direction", &old_direction,
966                 "pending-send-flags", &pending_send_flags,
967                 NULL);
968 
969   SESSION_DEBUG(self, "stream %u direction change requested: %u -> %u", stream_id, old_direction, direction);
970 
971   /* Set pending send flag if we're going to start sending */
972   if (direction & ~old_direction & TP_MEDIA_STREAM_DIRECTION_SEND)
973     pending_send_flags |= TP_MEDIA_STREAM_PENDING_REMOTE_SEND;
974 
975   sip_media_stream_set_direction (stream, direction, pending_send_flags);
976 
977   if (direction != old_direction)
978     priv_local_media_changed (self);
979 
980   return TRUE;
981 }
982 
983 static void
priv_save_event(SIPMediaSession * self)984 priv_save_event (SIPMediaSession *self)
985 {
986   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
987   SIPConnection *conn = NULL;
988 
989   priv_zap_event (self);
990 
991   g_object_get (priv->channel, "connection", &conn, NULL);
992 
993   g_return_if_fail (conn != NULL);
994 
995   sip_conn_save_event (conn, priv->saved_event);
996 
997   g_object_unref (conn);
998 
999 #ifdef ENABLE_DEBUG
1000   {
1001     nua_event_data_t const *ev_data = nua_event_data (priv->saved_event);
1002     g_assert (ev_data != NULL);
1003     DEBUG("saved the last event: %s %hd %s", nua_event_name (ev_data->e_event), ev_data->e_status, ev_data->e_phrase);
1004   }
1005 #endif
1006 }
1007 
1008 static void
priv_zap_event(SIPMediaSession * self)1009 priv_zap_event (SIPMediaSession *self)
1010 {
1011   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
1012 
1013   if (priv->saved_event[0])
1014     {
1015       nua_event_data_t const *ev_data = nua_event_data (priv->saved_event);
1016       g_assert (ev_data != NULL);
1017       g_warning ("zapping unhandled saved event '%s'", nua_event_name (ev_data->e_event));
1018       nua_destroy_event (priv->saved_event);
1019     }
1020 }
1021 
1022 void
sip_media_session_receive_invite(SIPMediaSession * self)1023 sip_media_session_receive_invite (SIPMediaSession *self)
1024 {
1025   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
1026 
1027   g_return_if_fail (priv->state == SIP_MEDIA_SESSION_STATE_CREATED);
1028   g_return_if_fail (priv->nua_op != NULL);
1029 
1030   nua_respond (priv->nua_op, SIP_180_RINGING, TAG_END());
1031 
1032   g_object_set (self, "state", SIP_MEDIA_SESSION_STATE_INVITE_RECEIVED, NULL);
1033 }
1034 
1035 void
sip_media_session_receive_reinvite(SIPMediaSession * self)1036 sip_media_session_receive_reinvite (SIPMediaSession *self)
1037 {
1038   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
1039   g_return_if_fail (priv->state == SIP_MEDIA_SESSION_STATE_ACTIVE
1040                     || priv->state == SIP_MEDIA_SESSION_STATE_RESPONSE_RECEIVED);
1041 
1042   priv_save_event (self);
1043 
1044   g_object_set (self, "state", SIP_MEDIA_SESSION_STATE_REINVITE_RECEIVED, NULL);
1045 }
1046 
1047 void
sip_media_session_accept(SIPMediaSession * self)1048 sip_media_session_accept (SIPMediaSession *self)
1049 {
1050   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
1051 
1052   if (priv->accepted)
1053     return;
1054 
1055   SESSION_DEBUG(self, "accepting the session");
1056 
1057   priv->accepted = TRUE;
1058 
1059   /* Will change session state to active when streams are ready
1060    * and clear the pending send flags, enabling sending */
1061   priv_request_response_step (self);
1062 }
1063 
1064 void
sip_media_session_reject(SIPMediaSession * self,gint status,const char * message)1065 sip_media_session_reject (SIPMediaSession *self,
1066                           gint status,
1067                           const char *message)
1068 {
1069   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
1070 
1071   if (message != NULL && !message[0])
1072     message = NULL;
1073 
1074   SESSION_DEBUG(self, "responding: %03d %s", status, message == NULL? "" : message);
1075 
1076   if (priv->nua_op)
1077     nua_respond (priv->nua_op, status, message, TAG_END());
1078 }
1079 
1080 static SIPMediaStream *
sip_media_session_get_stream(SIPMediaSession * self,guint stream_id,GError ** error)1081 sip_media_session_get_stream (SIPMediaSession *self,
1082                               guint stream_id,
1083                               GError **error)
1084 {
1085   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
1086   SIPMediaStream *stream;
1087 
1088   g_assert (priv->streams != NULL);
1089 
1090   if (stream_id >= priv->streams->len)
1091     {
1092       g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
1093                    "stream ID %u is invalid", stream_id);
1094       return NULL;
1095     }
1096 
1097   stream = g_ptr_array_index (priv->streams, stream_id);
1098 
1099   if (stream == NULL)
1100     {
1101       g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
1102                    "stream %u does not exist", stream_id);
1103       return NULL;
1104     }
1105 
1106   return stream;
1107 }
1108 
1109 gboolean
sip_media_session_start_telephony_event(SIPMediaSession * self,guint stream_id,guchar event,GError ** error)1110 sip_media_session_start_telephony_event (SIPMediaSession *self,
1111                                          guint stream_id,
1112                                          guchar event,
1113                                          GError **error)
1114 {
1115   SIPMediaStream *stream;
1116 
1117   stream = sip_media_session_get_stream (self, stream_id, error);
1118   if (stream == NULL)
1119     return FALSE;
1120 
1121   if (sip_media_stream_get_media_type (stream) != TP_MEDIA_STREAM_TYPE_AUDIO)
1122     {
1123       g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1124                    "non-audio stream %u does not support telephony events", stream_id);
1125       return FALSE;
1126     }
1127 
1128   DEBUG("starting telephony event %u on stream %u", (guint) event, stream_id);
1129 
1130   sip_media_stream_start_telephony_event (stream, event);
1131 
1132   return TRUE;
1133 }
1134 
1135 gboolean
sip_media_session_stop_telephony_event(SIPMediaSession * self,guint stream_id,GError ** error)1136 sip_media_session_stop_telephony_event  (SIPMediaSession *self,
1137                                          guint stream_id,
1138                                          GError **error)
1139 {
1140   SIPMediaStream *stream;
1141 
1142   stream = sip_media_session_get_stream (self, stream_id, error);
1143   if (stream == NULL)
1144     return FALSE;
1145 
1146   if (sip_media_stream_get_media_type (stream) != TP_MEDIA_STREAM_TYPE_AUDIO)
1147     {
1148       g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
1149                    "non-audio stream %u does not support telephony events; spurious use of the stop event?", stream_id);
1150       return FALSE;
1151     }
1152 
1153   DEBUG("stopping the telephony event on stream %u", stream_id);
1154 
1155   sip_media_stream_stop_telephony_event (stream);
1156 
1157   return TRUE;
1158 }
1159 
1160 gint
sip_media_session_rate_native_transport(SIPMediaSession * session,const GValue * transport)1161 sip_media_session_rate_native_transport (SIPMediaSession *session,
1162                                          const GValue *transport)
1163 {
1164   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1165   gint result = 0;
1166   gchar *address = NULL;
1167   guint proto = TP_MEDIA_STREAM_BASE_PROTO_UDP;
1168 
1169   dbus_g_type_struct_get (transport,
1170                           1, &address,
1171                           3, &proto,
1172                           G_MAXUINT);
1173 
1174   g_assert (address != NULL);
1175 
1176   if (proto != TP_MEDIA_STREAM_BASE_PROTO_UDP)
1177     result = -1;
1178   /* XXX: this will not work properly when IPv6 support comes */
1179   else if (priv->local_ip_address != NULL
1180       && strcmp (address, priv->local_ip_address) == 0)
1181     result = 1;
1182 
1183   g_free (address);
1184 
1185   return result;
1186 }
1187 
priv_session_media_state(SIPMediaSession * session,gboolean playing)1188 static void priv_session_media_state (SIPMediaSession *session, gboolean playing)
1189 {
1190   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1191   SIPMediaStream *stream;
1192   guint i;
1193 
1194   for (i = 0; i < priv->streams->len; i++)
1195     {
1196       stream = g_ptr_array_index(priv->streams, i);
1197       if (stream != NULL)
1198         sip_media_stream_set_playing (stream, playing);
1199     }
1200 }
1201 
1202 static void
priv_local_media_changed(SIPMediaSession * session)1203 priv_local_media_changed (SIPMediaSession *session)
1204 {
1205   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1206 
1207   switch (priv->state)
1208     {
1209     case SIP_MEDIA_SESSION_STATE_CREATED:
1210     case SIP_MEDIA_SESSION_STATE_INVITE_RECEIVED:
1211     case SIP_MEDIA_SESSION_STATE_REINVITE_RECEIVED:
1212       /* The changes will be sent when all streams are ready;
1213        * check if now's the time */
1214       priv_request_response_step (session);
1215       break;
1216     case SIP_MEDIA_SESSION_STATE_INVITE_SENT:
1217     case SIP_MEDIA_SESSION_STATE_REINVITE_SENT:
1218     case SIP_MEDIA_SESSION_STATE_RESPONSE_RECEIVED:
1219       /* Cannot send another offer right now */
1220       priv->pending_offer = TRUE;
1221       break;
1222     case SIP_MEDIA_SESSION_STATE_ACTIVE:
1223       if (priv->local_non_ready == 0)
1224         priv_session_invite (session, TRUE);
1225       else
1226         priv->pending_offer = TRUE;
1227       break;
1228     default:
1229       g_assert_not_reached();
1230     }
1231 }
1232 
1233 static gboolean
priv_update_remote_media(SIPMediaSession * session,gboolean authoritative)1234 priv_update_remote_media (SIPMediaSession *session, gboolean authoritative)
1235 {
1236   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1237   const sdp_media_t *media;
1238   gboolean has_supported_media = FALSE;
1239   guint i;
1240 
1241   g_return_val_if_fail (priv->remote_sdp != NULL, FALSE);
1242 
1243   media = priv->remote_sdp->sdp_media;
1244 
1245   /* note: for each session, we maintain an ordered list of
1246    *       streams (SDP m-lines) which are matched 1:1 to
1247    *       the streams of the remote SDP */
1248 
1249   for (i = 0; media; media = media->m_next, i++)
1250     {
1251       SIPMediaStream *stream = NULL;
1252       guint media_type;
1253 
1254       media_type = sip_tp_media_type (media->m_type);
1255 
1256       if (i >= priv->streams->len)
1257         stream = priv_create_media_stream (
1258                         session,
1259                         media_type,
1260                         (priv->accepted)?
1261                                 0 : TP_MEDIA_STREAM_PENDING_LOCAL_SEND);
1262       else
1263         stream = g_ptr_array_index(priv->streams, i);
1264 
1265       /* note: it is ok for the stream to be NULL (unsupported media type) */
1266       if (stream == NULL)
1267         continue;
1268 
1269       DEBUG("setting remote SDP for stream %u", i);
1270 
1271       if (media->m_rejected)
1272         {
1273           DEBUG("the stream has been rejected, closing");
1274         }
1275       else if (sip_media_stream_get_media_type (stream) != media_type)
1276         {
1277           /* XXX: close this stream and create a new one in its place? */
1278           g_warning ("The peer has changed the media type, don't know what to do");
1279         }
1280       else
1281         {
1282           if (sip_media_stream_set_remote_media (stream,
1283                                                  media,
1284                                                  authoritative))
1285             {
1286               has_supported_media = TRUE;
1287               continue;
1288             }
1289         }
1290 
1291       /* There have been problems with the stream update, kill the stream */
1292       sip_media_stream_close (stream);
1293     }
1294 
1295   g_assert(media == NULL);
1296   g_assert(i <= priv->streams->len);
1297   if (i < priv->streams->len)
1298     {
1299       do
1300         {
1301           SIPMediaStream *stream;
1302           stream = g_ptr_array_index(priv->streams, i);
1303           if (stream != NULL)
1304             {
1305               g_message ("closing a mismatched stream %u", i);
1306               sip_media_stream_close (stream);
1307             }
1308         }
1309       while (++i < priv->streams->len);
1310     }
1311 
1312   DEBUG("exit");
1313 
1314   return has_supported_media;
1315 }
1316 
1317 static void
priv_session_rollback(SIPMediaSession * session)1318 priv_session_rollback (SIPMediaSession *session)
1319 {
1320   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1321   msg_t *msg;
1322 
1323   DEBUG("enter");
1324 
1325   if (priv->remote_sdp != NULL)
1326     {
1327       priv->remote_sdp = NULL;
1328       g_assert (priv->home != NULL);
1329       su_home_unref (priv->home);
1330       priv->home = NULL;
1331     }
1332   if (priv->backup_remote_sdp == NULL)
1333     {
1334       sip_media_session_terminate (session);
1335       return;
1336     }
1337 
1338   /* restore remote SDP from the backup */
1339   priv->remote_sdp = priv->backup_remote_sdp;
1340   g_assert (priv->backup_home != NULL);
1341   priv->home = priv->backup_home;
1342   priv->backup_remote_sdp = NULL;
1343   priv->backup_home = NULL;
1344 
1345   priv_update_remote_media (session, FALSE);
1346 
1347   msg = (priv->saved_event[0])
1348         ? nua_saved_event_request (priv->saved_event) : NULL;
1349 
1350   nua_respond (priv->nua_op, 488, sip_488_Not_acceptable,
1351                TAG_IF(msg, NUTAG_WITH(msg)),
1352                TAG_END());
1353 
1354   if (priv->saved_event[0])
1355     nua_destroy_event (priv->saved_event);
1356 
1357   g_object_set (session, "state", SIP_MEDIA_SESSION_STATE_ACTIVE, NULL);
1358 }
1359 
1360 static gboolean
priv_session_local_sdp(SIPMediaSession * session,GString * user_sdp)1361 priv_session_local_sdp (SIPMediaSession *session, GString *user_sdp)
1362 {
1363   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1364   gboolean has_supported_media = FALSE;
1365   guint i;
1366 
1367   g_return_val_if_fail (priv->local_non_ready == 0, FALSE);
1368 
1369   g_string_append (user_sdp, "v=0\r\n");
1370 
1371   for (i = 0; i < priv->streams->len; i++)
1372     {
1373       SIPMediaStream *stream = g_ptr_array_index (priv->streams, i);
1374       if (stream)
1375         {
1376           user_sdp = g_string_append (user_sdp,
1377                                       sip_media_stream_local_sdp (stream));
1378           has_supported_media = TRUE;
1379         }
1380       else
1381         {
1382           user_sdp = g_string_append (user_sdp, "m=audio 0 RTP/AVP 0\r\n");
1383         }
1384     }
1385 
1386   SESSION_DEBUG(session, "generated SDP: {\n%s}", user_sdp->str);
1387 
1388   return has_supported_media;
1389 }
1390 
1391 static void
priv_session_invite(SIPMediaSession * session,gboolean reinvite)1392 priv_session_invite (SIPMediaSession *session, gboolean reinvite)
1393 {
1394   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1395   GString *user_sdp;
1396 
1397   DEBUG("enter");
1398 
1399   g_return_if_fail (priv->nua_op != NULL);
1400 
1401   user_sdp = g_string_new (NULL);
1402 
1403   if (priv_session_local_sdp (session, user_sdp))
1404     {
1405       nua_invite (priv->nua_op,
1406                   SOATAG_USER_SDP_STR(user_sdp->str),
1407                   SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE),
1408                   SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL),
1409                   NUTAG_AUTOANSWER(0),
1410                   TAG_END());
1411       priv->pending_offer = FALSE;
1412       g_object_set (session,
1413                     "state", reinvite? SIP_MEDIA_SESSION_STATE_REINVITE_SENT
1414                                      : SIP_MEDIA_SESSION_STATE_INVITE_SENT,
1415                     NULL);
1416     }
1417   else
1418     g_warning ("cannot send a valid SDP offer, are there no streams?");
1419 
1420   g_string_free (user_sdp, TRUE);
1421 }
1422 
1423 static void
priv_session_respond(SIPMediaSession * session)1424 priv_session_respond (SIPMediaSession *session)
1425 {
1426   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1427   GString *user_sdp;
1428 
1429   g_return_if_fail (priv->nua_op != NULL);
1430 
1431   user_sdp = g_string_new (NULL);
1432 
1433   if (priv_session_local_sdp (session, user_sdp))
1434     {
1435       msg_t *msg;
1436 
1437       msg = (priv->saved_event[0])
1438                 ? nua_saved_event_request (priv->saved_event) : NULL;
1439 
1440       nua_respond (priv->nua_op, 200, sip_200_OK,
1441                    TAG_IF(msg, NUTAG_WITH(msg)),
1442                    SOATAG_USER_SDP_STR (user_sdp->str),
1443                    SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE),
1444                    SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL),
1445                    NUTAG_AUTOANSWER(0),
1446                    TAG_END());
1447 
1448       if (priv->saved_event[0])
1449         nua_destroy_event (priv->saved_event);
1450 
1451       g_object_set (session, "state", SIP_MEDIA_SESSION_STATE_ACTIVE, NULL);
1452     }
1453   else
1454     {
1455       g_warning ("cannot respond with a valid SDP answer, were all streams closed?");
1456 
1457       priv_session_rollback (session);
1458     }
1459 
1460   g_string_free (user_sdp, TRUE);
1461 }
1462 
1463 static gboolean
priv_is_codec_intersect_pending(SIPMediaSession * session)1464 priv_is_codec_intersect_pending (SIPMediaSession *session)
1465 {
1466   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1467   guint i;
1468 
1469   for (i = 0; i < priv->streams->len; i++)
1470     {
1471       SIPMediaStream *stream = g_ptr_array_index (priv->streams, i);
1472       if (stream != NULL
1473           && sip_media_stream_is_codec_intersect_pending (stream))
1474         return TRUE;
1475     }
1476 
1477   return FALSE;
1478 }
1479 
1480 /**
1481  * Sends requests and responses with an outbound offer/answer
1482  * if all streams of the session are prepared.
1483  *
1484  * Following inputs are considered in decision making:
1485  *  - state of the session (is remote INVITE being handled)
1486  *  - status of local streams (set up with stream-engine)
1487  *  - whether session is locally accepted
1488  */
1489 static void
priv_request_response_step(SIPMediaSession * session)1490 priv_request_response_step (SIPMediaSession *session)
1491 {
1492   SIPMediaSessionPrivate *priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1493 
1494   DEBUG ("enter, local non ready %d", priv->local_non_ready);
1495 
1496   switch (priv->state)
1497     {
1498     case SIP_MEDIA_SESSION_STATE_CREATED:
1499       if (priv->local_non_ready == 0)
1500         {
1501           /* note:  we need to be prepared to receive media right after the
1502            *       offer is sent, so we must set state to playing */
1503           priv_session_media_state (session, TRUE);
1504 
1505           priv_session_invite (session, FALSE);
1506         }
1507       break;
1508     case SIP_MEDIA_SESSION_STATE_RESPONSE_RECEIVED:
1509       if (priv->accepted
1510           && !priv_is_codec_intersect_pending (session))
1511         {
1512           g_assert (priv->local_non_ready == 0);
1513           g_object_set (session,
1514                         "state", SIP_MEDIA_SESSION_STATE_ACTIVE,
1515                         NULL);
1516         }
1517       break;
1518     case SIP_MEDIA_SESSION_STATE_INVITE_RECEIVED:
1519       /* TODO: if the call has not yet been accepted locally
1520        * and the remote endpoint supports 100rel, send them
1521        * an early session answer in a reliable 183 response */
1522       if (priv->accepted
1523           && priv->local_non_ready == 0
1524           && !priv_is_codec_intersect_pending (session))
1525         {
1526           priv_session_respond (session);
1527 
1528           /* note: we have accepted the call, set state to playing */
1529           priv_session_media_state (session, TRUE);
1530         }
1531       break;
1532     case SIP_MEDIA_SESSION_STATE_REINVITE_RECEIVED:
1533       if (priv->local_non_ready == 0
1534           && !priv_is_codec_intersect_pending (session))
1535         {
1536           priv_session_respond (session);
1537         }
1538       break;
1539     case SIP_MEDIA_SESSION_STATE_ACTIVE:
1540       if (priv->pending_offer && priv->local_non_ready == 0)
1541         {
1542           priv_session_invite (session, TRUE);
1543         }
1544       break;
1545     default:
1546       g_assert_not_reached ();
1547     }
1548 }
1549 
1550 static void
priv_stream_close_cb(SIPMediaStream * stream,SIPMediaSession * session)1551 priv_stream_close_cb (SIPMediaStream *stream,
1552                       SIPMediaSession *session)
1553 {
1554   SIPMediaSessionPrivate *priv;
1555   guint id;
1556 
1557   DEBUG("enter");
1558 
1559   priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1560 
1561   id = sip_media_stream_get_id (stream);
1562   g_return_if_fail (g_ptr_array_index(priv->streams, id) == stream);
1563 
1564   if (!sip_media_stream_is_ready (stream))
1565     {
1566       g_assert (priv->local_non_ready > 0);
1567       --priv->local_non_ready;
1568       DEBUG("stream wasn't ready, decrement the local non ready counter to %d", priv->local_non_ready);
1569     }
1570 
1571   g_object_unref (stream);
1572 
1573   g_ptr_array_index(priv->streams, id) = NULL;
1574 
1575   tp_svc_channel_type_streamed_media_emit_stream_removed (priv->channel, id);
1576 }
1577 
priv_stream_ready_cb(SIPMediaStream * stream,SIPMediaSession * session)1578 static void priv_stream_ready_cb (SIPMediaStream *stream,
1579 				  SIPMediaSession *session)
1580 {
1581   SIPMediaSessionPrivate *priv;
1582 
1583   DEBUG ("enter");
1584 
1585   priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1586 
1587   g_assert (priv->local_non_ready > 0);
1588   --priv->local_non_ready;
1589 
1590   priv_request_response_step (session);
1591 }
1592 
priv_stream_supported_codecs_cb(SIPMediaStream * stream,guint num_codecs,SIPMediaSession * session)1593 static void priv_stream_supported_codecs_cb (SIPMediaStream *stream,
1594 					     guint num_codecs,
1595 					     SIPMediaSession *session)
1596 {
1597   SIPMediaSessionPrivate *priv;
1598 
1599   g_assert (SIP_IS_MEDIA_SESSION (session));
1600 
1601   priv = SIP_MEDIA_SESSION_GET_PRIVATE (session);
1602 
1603   g_assert (!sip_media_stream_is_codec_intersect_pending (stream));
1604 
1605   if (num_codecs == 0)
1606     {
1607       /* This remote media description got no codec intersection. */
1608       switch (priv->state)
1609         {
1610         case SIP_MEDIA_SESSION_STATE_RESPONSE_RECEIVED:
1611         case SIP_MEDIA_SESSION_STATE_INVITE_RECEIVED:
1612           DEBUG("no codec intersection, closing the stream");
1613           sip_media_stream_close (stream);
1614           break;
1615         case SIP_MEDIA_SESSION_STATE_REINVITE_RECEIVED:
1616           /* In this case, we have the stream negotiated already,
1617            * and we don't want to close it just because the remote party
1618            * offers a different set of codecs.
1619            * Roll back the whole session to the previously negotiated state. */
1620           priv_session_rollback (session);
1621           return;
1622         default:
1623           g_assert_not_reached();
1624         }
1625     }
1626 
1627   priv_request_response_step (session);
1628 }
1629 
1630 static void
priv_stream_state_changed_cb(SIPMediaStream * stream,guint state,SIPMediaChannel * channel)1631 priv_stream_state_changed_cb (SIPMediaStream *stream,
1632                               guint state,
1633                               SIPMediaChannel *channel)
1634 {
1635   g_assert (SIP_IS_MEDIA_CHANNEL (channel));
1636   tp_svc_channel_type_streamed_media_emit_stream_state_changed(
1637         channel,
1638         sip_media_stream_get_id (stream), state);
1639 }
1640 
1641 static void
priv_stream_direction_changed_cb(SIPMediaStream * stream,guint direction,guint pending_send_flags,SIPMediaChannel * channel)1642 priv_stream_direction_changed_cb (SIPMediaStream *stream,
1643                                   guint direction,
1644                                   guint pending_send_flags,
1645                                   SIPMediaChannel *channel)
1646 {
1647   g_assert (SIP_IS_MEDIA_CHANNEL (channel));
1648   tp_svc_channel_type_streamed_media_emit_stream_direction_changed (
1649         channel,
1650         sip_media_stream_get_id (stream), direction, pending_send_flags);
1651 }
1652 
1653 static SIPMediaStream*
priv_create_media_stream(SIPMediaSession * self,guint media_type,guint pending_send_flags)1654 priv_create_media_stream (SIPMediaSession *self,
1655                           guint media_type,
1656                           guint pending_send_flags)
1657 {
1658   SIPMediaSessionPrivate *priv;
1659   gchar *object_path;
1660   SIPMediaStream *stream = NULL;
1661 
1662   g_assert (SIP_IS_MEDIA_SESSION (self));
1663 
1664   DEBUG ("enter");
1665 
1666   priv = SIP_MEDIA_SESSION_GET_PRIVATE (self);
1667 
1668   if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO ||
1669       media_type == TP_MEDIA_STREAM_TYPE_VIDEO) {
1670 
1671     object_path = g_strdup_printf ("%s/MediaStream%u",
1672                                    priv->object_path,
1673                                    priv->streams->len);
1674 
1675     stream = g_object_new (SIP_TYPE_MEDIA_STREAM,
1676 			   "media-session", self,
1677 			   "media-type", media_type,
1678 			   "object-path", object_path,
1679 			   "id", priv->streams->len,
1680                            "pending-send-flags", pending_send_flags,
1681 			   NULL);
1682 
1683     g_free (object_path);
1684 
1685     g_signal_connect (stream, "close",
1686                       (GCallback) priv_stream_close_cb,
1687                       self);
1688     g_signal_connect (stream, "ready",
1689 		      (GCallback) priv_stream_ready_cb,
1690 		      self);
1691     g_signal_connect (stream, "supported-codecs",
1692 		      (GCallback) priv_stream_supported_codecs_cb,
1693 		      self);
1694     g_signal_connect (stream, "state-changed",
1695                       (GCallback) priv_stream_state_changed_cb,
1696                       priv->channel);
1697     g_signal_connect (stream, "direction-changed",
1698                       (GCallback) priv_stream_direction_changed_cb,
1699                       priv->channel);
1700 
1701     g_assert (priv->local_non_ready >= 0);
1702     ++priv->local_non_ready;
1703 
1704     if (priv->se_ready == TRUE) {
1705       priv_emit_new_stream (self, stream);
1706     }
1707 
1708     g_signal_emit (self, signals[SIG_STREAM_ADDED], 0, stream);
1709   }
1710 
1711   /* note: we add an entry even for unsupported media types */
1712   g_ptr_array_add (priv->streams, stream);
1713 
1714   DEBUG ("exit");
1715 
1716   return stream;
1717 }
1718 
1719 static void
session_handler_iface_init(gpointer g_iface,gpointer iface_data)1720 session_handler_iface_init (gpointer g_iface, gpointer iface_data)
1721 {
1722   TpSvcMediaSessionHandlerClass *klass = (TpSvcMediaSessionHandlerClass *)g_iface;
1723 
1724 #define IMPLEMENT(x) tp_svc_media_session_handler_implement_##x (\
1725     klass, (tp_svc_media_session_handler_##x##_impl) sip_media_session_##x)
1726   IMPLEMENT(error);
1727   IMPLEMENT(ready);
1728 #undef IMPLEMENT
1729 }
1730