telepathygabble/src/gabble-media-session.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * gabble-media-session.c - Source for GabbleMediaSession
       
     3  * Copyright (C) 2006 Collabora Ltd.
       
     4  * 
       
     5  *   @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
       
     6  *
       
     7  * This library is free software; you can redistribute it and/or
       
     8  * modify it under the terms of the GNU Lesser General Public
       
     9  * License as published by the Free Software Foundation; either
       
    10  * version 2.1 of the License, or (at your option) any later version.
       
    11  *
       
    12  * This library is distributed in the hope that it will be useful,
       
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    15  * Lesser General Public License for more details.
       
    16  *
       
    17  * You should have received a copy of the GNU Lesser General Public
       
    18  * License along with this library; if not, write to the Free Software
       
    19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    20  */
       
    21 
       
    22 #include <dbus/dbus-glib.h>
       
    23 #include <stdio.h>
       
    24 #include <stdlib.h>
       
    25 #include <string.h>
       
    26 #include <time.h>
       
    27 
       
    28 #include "debug.h"
       
    29 #include "ansi.h"
       
    30 #include "handles.h"
       
    31 #include "namespaces.h"
       
    32 #include "util.h"
       
    33 
       
    34 #include "telepathy-errors.h"
       
    35 #include "telepathy-helpers.h"
       
    36 
       
    37 #include "gabble-connection.h"
       
    38 #include "gabble-media-channel.h"
       
    39 #include "gabble-media-stream.h"
       
    40 #include "gabble-presence-cache.h"
       
    41 #include "gabble-presence.h"
       
    42 
       
    43 #include "gabble-media-session.h"
       
    44 #include "gabble-media-session-signals-marshal.h"
       
    45 #include "gabble-media-session-glue.h"
       
    46 
       
    47 #include "gabble_enums.h"
       
    48 
       
    49 #ifndef EMULATOR
       
    50 G_DEFINE_TYPE(GabbleMediaSession, gabble_media_session, G_TYPE_OBJECT)
       
    51 #endif
       
    52 
       
    53 
       
    54 #define DEBUG_FLAG GABBLE_DEBUG_MEDIA
       
    55 //vinod
       
    56 #ifdef DEBUG_FLAG
       
    57 //#define DEBUG(format, ...)
       
    58 #define DEBUGGING 0
       
    59 //#define NODE_DEBUG(n, s);
       
    60 #endif /* DEBUG_FLAG */
       
    61 
       
    62 #define DEFAULT_SESSION_TIMEOUT 50000
       
    63 
       
    64 #define GTALK_STREAM_NAME "gtalk"
       
    65 
       
    66 /* 99 streams gives us a max name length of 8 (videoXX\0 or audioXX\0) */
       
    67 #define MAX_STREAMS 99
       
    68 
       
    69 #ifndef EMULATOR
       
    70 #define MAX_STREAM_NAME_LEN 8
       
    71 #endif
       
    72 
       
    73 //#define DEBUGGING 0
       
    74 
       
    75 /* signal enum */
       
    76 enum
       
    77 {
       
    78     NEW_STREAM_HANDLER,
       
    79     STREAM_ADDED,
       
    80     TERMINATED,
       
    81     LAST_SIGNAL
       
    82 };
       
    83 
       
    84 #ifdef EMULATOR
       
    85 #include "libgabble_wsd_solution.h"
       
    86 
       
    87 	GET_STATIC_ARRAY_FROM_TLS(signals,gabble_med_sess,guint)
       
    88 	#define signals (GET_WSD_VAR_NAME(signals,gabble_med_sess, s)())	
       
    89 	
       
    90 	
       
    91 	GET_STATIC_VAR_FROM_TLS(google_audio_caps,gabble_med_sess,GabblePresenceCapabilities)
       
    92 	#define google_audio_caps (*GET_WSD_VAR_NAME(google_audio_caps,gabble_med_sess, s)())	
       
    93 	
       
    94 	GET_STATIC_VAR_FROM_TLS(jingle_audio_caps,gabble_med_sess,GabblePresenceCapabilities)
       
    95 	#define jingle_audio_caps (*GET_WSD_VAR_NAME(jingle_audio_caps,gabble_med_sess, s)())	
       
    96 	
       
    97 	GET_STATIC_VAR_FROM_TLS(jingle_video_caps,gabble_med_sess,GabblePresenceCapabilities)
       
    98 	#define jingle_video_caps (*GET_WSD_VAR_NAME(jingle_video_caps,gabble_med_sess, s)())	
       
    99 	
       
   100 	GET_STATIC_ARRAY_FROM_TLS(ret_sess,gabble_med_sess,gchar)
       
   101 	#define ret_sess (GET_WSD_VAR_NAME(ret_sess,gabble_med_sess, s)())	
       
   102 	
       
   103 	GET_STATIC_VAR_FROM_TLS(gabble_media_session_parent_class,gabble_med_sess,gpointer)
       
   104 	#define gabble_media_session_parent_class (*GET_WSD_VAR_NAME(gabble_media_session_parent_class,gabble_med_sess,s)())
       
   105 	
       
   106 	GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_med_sess,GType)
       
   107 	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_med_sess,s)())	
       
   108 	
       
   109 	
       
   110 	static void gabble_media_session_init (GabbleMediaSession *self); 
       
   111 	static void gabble_media_session_class_init (GabbleMediaSessionClass *klass); 
       
   112 	static void gabble_media_session_class_intern_init (gpointer klass) 
       
   113 	{ 
       
   114 	gabble_media_session_parent_class = g_type_class_peek_parent (klass); 
       
   115 	gabble_media_session_class_init ((GabbleMediaSessionClass*) klass); }
       
   116 	
       
   117 	 EXPORT_C GType gabble_media_session_get_type (void) 
       
   118 	 { 
       
   119 	 if ((g_define_type_id == 0)) { static const GTypeInfo g_define_type_info = { sizeof (GabbleMediaSessionClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_media_session_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleMediaSession), 0, (GInstanceInitFunc) gabble_media_session_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleMediaSession"), &g_define_type_info, (GTypeFlags) 0); { {} ; } } return g_define_type_id; 
       
   120 	 }
       
   121 
       
   122 	
       
   123 		
       
   124 #else
       
   125 
       
   126 	static guint signals[LAST_SIGNAL] = {0};
       
   127 
       
   128 #endif
       
   129 
       
   130 /* properties */
       
   131 enum
       
   132 {
       
   133   PROP_CONNECTION = 1,
       
   134   PROP_MEDIA_CHANNEL,
       
   135   PROP_OBJECT_PATH,
       
   136   PROP_SESSION_ID,
       
   137   PROP_INITIATOR,
       
   138   PROP_PEER,
       
   139   PROP_PEER_RESOURCE,
       
   140   PROP_STATE,
       
   141   LAST_PROPERTY
       
   142 };
       
   143 
       
   144 /* private structure */
       
   145 typedef struct _GabbleMediaSessionPrivate GabbleMediaSessionPrivate;
       
   146 
       
   147 struct _GabbleMediaSessionPrivate
       
   148 {
       
   149   GabbleConnection *conn;
       
   150   GabbleMediaChannel *channel;
       
   151   GabbleMediaSessionMode mode;
       
   152   gchar *object_path;
       
   153 
       
   154   GPtrArray *streams;
       
   155   GPtrArray *remove_requests;
       
   156 
       
   157   gchar *id;
       
   158   GabbleHandle peer;
       
   159   gchar *peer_resource;
       
   160 
       
   161   JingleSessionState state;
       
   162   gboolean ready;
       
   163   gboolean locally_accepted;
       
   164   gboolean terminated;
       
   165 
       
   166   guint timer_id;
       
   167 
       
   168   gboolean dispose_has_run;
       
   169 };
       
   170 
       
   171 #define GABBLE_MEDIA_SESSION_GET_PRIVATE(obj) \
       
   172     ((GabbleMediaSessionPrivate *)obj->priv)
       
   173 
       
   174 typedef struct {
       
   175     gchar *name;
       
   176     gchar *attributes;
       
   177 } SessionStateDescription;
       
   178 
       
   179 static const SessionStateDescription session_states[] =
       
   180 {
       
   181     { "JS_STATE_PENDING_CREATED",       ANSI_BOLD_ON ANSI_FG_BLACK ANSI_BG_WHITE   },
       
   182     { "JS_STATE_PENDING_INITIATE_SENT", ANSI_BOLD_ON               ANSI_BG_CYAN    },
       
   183     { "JS_STATE_PENDING_INITIATED",     ANSI_BOLD_ON               ANSI_BG_MAGENTA },
       
   184     { "JS_STATE_PENDING_ACCEPT_SENT",   ANSI_BOLD_ON               ANSI_BG_CYAN    },
       
   185     { "JS_STATE_ACTIVE",                ANSI_BOLD_ON               ANSI_BG_BLUE    },
       
   186     { "JS_STATE_ENDED",                                            ANSI_BG_RED     }
       
   187 };
       
   188 
       
   189 static void
       
   190 gabble_media_session_init (GabbleMediaSession *self)
       
   191 {
       
   192   GabbleMediaSessionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
       
   193       GABBLE_TYPE_MEDIA_SESSION, GabbleMediaSessionPrivate);
       
   194 
       
   195   self->priv = priv;
       
   196 
       
   197   priv->mode = MODE_JINGLE;
       
   198   priv->state = JS_STATE_PENDING_CREATED;
       
   199   priv->streams = g_ptr_array_new ();
       
   200   priv->remove_requests = g_ptr_array_new ();
       
   201 }
       
   202 
       
   203 static void stream_connection_state_changed_cb (GabbleMediaStream *stream,
       
   204                                                 GParamSpec *param,
       
   205                                                 GabbleMediaSession *session);
       
   206 static void stream_got_local_codecs_changed_cb (GabbleMediaStream *stream,
       
   207                                                 GParamSpec *param,
       
   208                                                 GabbleMediaSession *session);
       
   209 
       
   210 static void
       
   211 _emit_new_stream (GabbleMediaSession *session,
       
   212                   GabbleMediaStream *stream)
       
   213 {
       
   214   gchar *object_path;
       
   215   guint id, media_type;
       
   216 
       
   217   g_object_get (stream,
       
   218                 "object-path", &object_path,
       
   219                 "id", &id,
       
   220                 "media-type", &media_type,
       
   221                 NULL);
       
   222 
       
   223   /* all of the streams are bidirectional from farsight's point of view, it's
       
   224    * just in the signalling they change */
       
   225   g_signal_emit (session, signals[NEW_STREAM_HANDLER], 0, object_path, id,
       
   226       media_type, TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL);
       
   227 
       
   228   g_free (object_path);
       
   229 }
       
   230 
       
   231 
       
   232 static GabbleMediaStream *
       
   233 _lookup_stream_by_name_and_initiator (GabbleMediaSession *session,
       
   234                                       const gchar *stream_name,
       
   235                                       JingleInitiator stream_initiator)
       
   236 {
       
   237   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
   238   guint i;
       
   239 
       
   240   for (i = 0; i < priv->streams->len; i++)
       
   241     {
       
   242       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
   243 
       
   244       if (g_strdiff (stream->name, stream_name))
       
   245         continue;
       
   246 
       
   247       if (stream_initiator != INITIATOR_INVALID &&
       
   248           stream_initiator != stream->initiator)
       
   249         continue;
       
   250 
       
   251       return stream;
       
   252     }
       
   253 
       
   254   return NULL;
       
   255 }
       
   256 
       
   257 
       
   258 static GabbleMediaStream *
       
   259 create_media_stream (GabbleMediaSession *session,
       
   260                      const gchar *name,
       
   261                      JingleInitiator initiator,
       
   262                      guint media_type)
       
   263 {
       
   264   GabbleMediaSessionPrivate *priv;
       
   265   gchar *object_path;
       
   266   GabbleMediaStream *stream;
       
   267   guint id;
       
   268 
       
   269   g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
   270   g_assert (name != NULL);
       
   271 
       
   272   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
   273 
       
   274   /* assert that if we're in google mode:
       
   275    *  - we only try to make one stream
       
   276    *  - it's an audio stream
       
   277    *  - it's called GTALK_STREAM_NAME */
       
   278   if (priv->mode == MODE_GOOGLE)
       
   279     {
       
   280       g_assert (priv->streams->len == 0);
       
   281       g_assert (media_type == TP_MEDIA_STREAM_TYPE_AUDIO);
       
   282       g_assert (!g_strdiff (name, GTALK_STREAM_NAME));
       
   283     }
       
   284 
       
   285   g_assert (priv->streams->len < MAX_STREAMS);
       
   286   g_assert (_lookup_stream_by_name_and_initiator (session, name, initiator) ==
       
   287       NULL);
       
   288 
       
   289   id = _gabble_media_channel_get_stream_id (priv->channel);
       
   290 
       
   291   _gabble_media_session_debug (session, DEBUG_MSG_INFO,
       
   292       "creating new %s %s stream called \"%s\" with id %u",
       
   293       priv->mode == MODE_GOOGLE ? "google" : "jingle",
       
   294       media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video",
       
   295       name, id);
       
   296 
       
   297   object_path = g_strdup_printf ("%s/MediaStream%u", priv->object_path, id);
       
   298 
       
   299   stream = g_object_new (GABBLE_TYPE_MEDIA_STREAM,
       
   300                          "connection", priv->conn,
       
   301                          "media-session", session,
       
   302                          "object-path", object_path,
       
   303                          "mode", priv->mode,
       
   304                          "name", name,
       
   305                          "id", id,
       
   306                          "initiator", initiator,
       
   307                          "media-type", media_type,
       
   308                          NULL);
       
   309 
       
   310   g_signal_connect (stream, "notify::connection-state",
       
   311                     (GCallback) stream_connection_state_changed_cb,
       
   312                     session);
       
   313   g_signal_connect (stream, "notify::got-local-codecs",
       
   314                     (GCallback) stream_got_local_codecs_changed_cb,
       
   315                     session);
       
   316 
       
   317   g_ptr_array_add (priv->streams, stream);
       
   318 
       
   319   g_free (object_path);
       
   320 
       
   321   if (priv->ready)
       
   322     _emit_new_stream (session, stream);
       
   323 
       
   324   g_signal_emit (session, signals[STREAM_ADDED], 0, stream);
       
   325 
       
   326   return stream;
       
   327 }
       
   328 
       
   329 static void
       
   330 destroy_media_stream (GabbleMediaSession *session,
       
   331                       GabbleMediaStream *stream)
       
   332 {
       
   333   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
   334 
       
   335   _gabble_media_stream_close (stream);
       
   336   g_ptr_array_remove_fast (priv->streams, stream);
       
   337   g_object_unref (stream);
       
   338 }
       
   339 
       
   340 static GObject *
       
   341 gabble_media_session_constructor (GType type, guint n_props,
       
   342                                   GObjectConstructParam *props)
       
   343 {
       
   344   GObject *obj;
       
   345   GabbleMediaSessionPrivate *priv;
       
   346   DBusGConnection *bus;
       
   347 
       
   348   obj = G_OBJECT_CLASS (gabble_media_session_parent_class)->
       
   349            constructor (type, n_props, props);
       
   350   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (GABBLE_MEDIA_SESSION (obj));
       
   351 
       
   352   bus = tp_get_bus ();
       
   353   dbus_g_connection_register_g_object (bus, priv->object_path, obj);
       
   354 
       
   355   return obj;
       
   356 }
       
   357 
       
   358 static void
       
   359 gabble_media_session_get_property (GObject    *object,
       
   360                                    guint       property_id,
       
   361                                    GValue     *value,
       
   362                                    GParamSpec *pspec)
       
   363 {
       
   364   GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object);
       
   365   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
   366 
       
   367   switch (property_id) {
       
   368     case PROP_CONNECTION:
       
   369       g_value_set_object (value, priv->conn);
       
   370       break;
       
   371     case PROP_MEDIA_CHANNEL:
       
   372       g_value_set_object (value, priv->channel);
       
   373       break;
       
   374     case PROP_OBJECT_PATH:
       
   375       g_value_set_string (value, priv->object_path);
       
   376       break;
       
   377     case PROP_SESSION_ID:
       
   378       g_value_set_string (value, priv->id);
       
   379       break;
       
   380     case PROP_INITIATOR:
       
   381       g_value_set_uint (value, session->initiator);
       
   382       break;
       
   383     case PROP_PEER:
       
   384       g_value_set_uint (value, priv->peer);
       
   385       break;
       
   386     case PROP_PEER_RESOURCE:
       
   387       g_value_set_string (value, priv->peer_resource);
       
   388       break;
       
   389     case PROP_STATE:
       
   390       g_value_set_uint (value, priv->state);
       
   391       break;
       
   392     default:
       
   393       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   394       break;
       
   395   }
       
   396 }
       
   397 
       
   398 static void session_state_changed (GabbleMediaSession *session,
       
   399                                    JingleSessionState prev_state,
       
   400                                    JingleSessionState new_state);
       
   401 
       
   402 static void
       
   403 gabble_media_session_set_property (GObject      *object,
       
   404                                    guint         property_id,
       
   405                                    const GValue *value,
       
   406                                    GParamSpec   *pspec)
       
   407 {
       
   408   GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object);
       
   409   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
   410   JingleSessionState prev_state;
       
   411 
       
   412   switch (property_id) {
       
   413     case PROP_CONNECTION:
       
   414       priv->conn = g_value_get_object (value);
       
   415       break;
       
   416     case PROP_MEDIA_CHANNEL:
       
   417       priv->channel = g_value_get_object (value);
       
   418       break;
       
   419     case PROP_OBJECT_PATH:
       
   420       g_free (priv->object_path);
       
   421       priv->object_path = g_value_dup_string (value);
       
   422       break;
       
   423     case PROP_SESSION_ID:
       
   424       g_free (priv->id);
       
   425       priv->id = g_value_dup_string (value);
       
   426       break;
       
   427     case PROP_INITIATOR:
       
   428       session->initiator = g_value_get_uint (value);
       
   429       break;
       
   430     case PROP_PEER:
       
   431       priv->peer = g_value_get_uint (value);
       
   432       break;
       
   433     case PROP_PEER_RESOURCE:
       
   434       g_free (priv->peer_resource);
       
   435       priv->peer_resource = g_value_dup_string (value);
       
   436       break;
       
   437     case PROP_STATE:
       
   438       prev_state = priv->state;
       
   439       priv->state = g_value_get_uint (value);
       
   440 
       
   441       if (priv->state == JS_STATE_ENDED)
       
   442         g_assert (priv->terminated);
       
   443 
       
   444       if (priv->state != prev_state)
       
   445         session_state_changed (session, prev_state, priv->state);
       
   446 
       
   447       break;
       
   448     default:
       
   449       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   450       break;
       
   451   }
       
   452 }
       
   453 
       
   454 static void gabble_media_session_dispose (GObject *object);
       
   455 static void gabble_media_session_finalize (GObject *object);
       
   456 
       
   457 static void
       
   458 gabble_media_session_class_init (GabbleMediaSessionClass *gabble_media_session_class)
       
   459 {
       
   460   GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_session_class);
       
   461   GParamSpec *param_spec;
       
   462 
       
   463   g_type_class_add_private (gabble_media_session_class, sizeof (GabbleMediaSessionPrivate));
       
   464 
       
   465   object_class->constructor = gabble_media_session_constructor;
       
   466 
       
   467   object_class->get_property = gabble_media_session_get_property;
       
   468   object_class->set_property = gabble_media_session_set_property;
       
   469 
       
   470   object_class->dispose = gabble_media_session_dispose;
       
   471   object_class->finalize = gabble_media_session_finalize;
       
   472 
       
   473   param_spec = g_param_spec_object ("connection", "GabbleConnection object",
       
   474                                     "Gabble connection object that owns this "
       
   475                                     "media session's channel.",
       
   476                                     GABBLE_TYPE_CONNECTION,
       
   477                                     G_PARAM_CONSTRUCT_ONLY |
       
   478                                     G_PARAM_READWRITE |
       
   479                                     G_PARAM_STATIC_NICK |
       
   480                                     G_PARAM_STATIC_BLURB);
       
   481   g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
       
   482 
       
   483   param_spec = g_param_spec_object ("media-channel", "GabbleMediaChannel object",
       
   484                                     "Gabble media channel object that owns this "
       
   485                                     "media session object.",
       
   486                                     GABBLE_TYPE_MEDIA_CHANNEL,
       
   487                                     G_PARAM_CONSTRUCT_ONLY |
       
   488                                     G_PARAM_READWRITE |
       
   489                                     G_PARAM_STATIC_NICK |
       
   490                                     G_PARAM_STATIC_BLURB);
       
   491   g_object_class_install_property (object_class, PROP_MEDIA_CHANNEL, param_spec);
       
   492 
       
   493   param_spec = g_param_spec_string ("object-path", "D-Bus object path",
       
   494                                     "The D-Bus object path used for this "
       
   495                                     "object on the bus.",
       
   496                                     NULL,
       
   497                                     G_PARAM_CONSTRUCT_ONLY |
       
   498                                     G_PARAM_READWRITE |
       
   499                                     G_PARAM_STATIC_NAME |
       
   500                                     G_PARAM_STATIC_BLURB);
       
   501   g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
       
   502 
       
   503   param_spec = g_param_spec_string ("session-id", "Session ID",
       
   504                                     "A unique session identifier used "
       
   505                                     "throughout all communication.",
       
   506                                     NULL,
       
   507                                     G_PARAM_CONSTRUCT_ONLY |
       
   508                                     G_PARAM_READWRITE |
       
   509                                     G_PARAM_STATIC_NAME |
       
   510                                     G_PARAM_STATIC_BLURB);
       
   511   g_object_class_install_property (object_class, PROP_SESSION_ID, param_spec);
       
   512 
       
   513   param_spec = g_param_spec_uint ("initiator", "Session initiator",
       
   514                                   "An enum signifying which end initiated "
       
   515                                   "the session.",
       
   516                                   INITIATOR_LOCAL,
       
   517                                   INITIATOR_REMOTE,
       
   518                                   INITIATOR_LOCAL,
       
   519                                   G_PARAM_CONSTRUCT_ONLY |
       
   520                                   G_PARAM_READWRITE |
       
   521                                   G_PARAM_STATIC_NAME |
       
   522                                   G_PARAM_STATIC_BLURB);
       
   523   g_object_class_install_property (object_class, PROP_INITIATOR, param_spec);
       
   524 
       
   525   param_spec = g_param_spec_uint ("peer", "Session peer",
       
   526                                   "The GabbleHandle representing the contact "
       
   527                                   "with whom this session communicates.",
       
   528                                   0, G_MAXUINT32, 0,
       
   529                                   G_PARAM_CONSTRUCT_ONLY |
       
   530                                   G_PARAM_READWRITE |
       
   531                                   G_PARAM_STATIC_NAME |
       
   532                                   G_PARAM_STATIC_BLURB);
       
   533   g_object_class_install_property (object_class, PROP_PEER, param_spec);
       
   534 
       
   535   param_spec = g_param_spec_string ("peer-resource",
       
   536                                     "Session peer's resource",
       
   537                                     "The resource of the contact "
       
   538                                     "with whom this session communicates, "
       
   539                                     "if applicable",
       
   540                                     NULL,
       
   541                                     G_PARAM_CONSTRUCT_ONLY |
       
   542                                     G_PARAM_WRITABLE |
       
   543                                     G_PARAM_STATIC_NAME |
       
   544                                     G_PARAM_STATIC_BLURB);
       
   545   g_object_class_install_property (object_class, PROP_PEER_RESOURCE,
       
   546                                    param_spec);
       
   547 
       
   548   param_spec = g_param_spec_uint ("state", "Session state",
       
   549                                   "The current state that the session is in.",
       
   550                                   0, G_MAXUINT32, 0,
       
   551                                   G_PARAM_READWRITE |
       
   552                                   G_PARAM_STATIC_NAME |
       
   553                                   G_PARAM_STATIC_BLURB);
       
   554   g_object_class_install_property (object_class, PROP_STATE, param_spec);
       
   555 
       
   556   signals[NEW_STREAM_HANDLER] =
       
   557     g_signal_new ("new-stream-handler",
       
   558                   G_OBJECT_CLASS_TYPE (gabble_media_session_class),
       
   559                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   560                   0,
       
   561                   NULL, NULL,
       
   562                   gabble_media_session_marshal_VOID__STRING_UINT_UINT_UINT,
       
   563                   G_TYPE_NONE, 4, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
       
   564 
       
   565   signals[STREAM_ADDED] =
       
   566     g_signal_new ("stream-added",
       
   567                   G_OBJECT_CLASS_TYPE (gabble_media_session_class),
       
   568                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   569                   0,
       
   570                   NULL, NULL,
       
   571                   g_cclosure_marshal_VOID__OBJECT,
       
   572                   G_TYPE_NONE, 1, G_TYPE_OBJECT);
       
   573 
       
   574   signals[TERMINATED] =
       
   575     g_signal_new ("terminated",
       
   576                   G_OBJECT_CLASS_TYPE (gabble_media_session_class),
       
   577                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   578                   0,
       
   579                   NULL, NULL,
       
   580                   gabble_media_session_marshal_VOID__UINT_UINT,
       
   581                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
       
   582 
       
   583   dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (gabble_media_session_class), &dbus_glib_gabble_media_session_object_info);
       
   584 }
       
   585 
       
   586 static void
       
   587 gabble_media_session_dispose (GObject *object)
       
   588 {
       
   589   GabbleMediaSession *self = GABBLE_MEDIA_SESSION (object);
       
   590   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (self);
       
   591   guint i;
       
   592 
       
   593   gabble_debug (DEBUG_FLAG, "called");
       
   594 
       
   595   if (priv->dispose_has_run)
       
   596     return;
       
   597 
       
   598   priv->dispose_has_run = TRUE;
       
   599 
       
   600   _gabble_media_session_terminate (self, INITIATOR_LOCAL,
       
   601       TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
       
   602 
       
   603   if (priv->timer_id != 0)
       
   604     g_source_remove (priv->timer_id);
       
   605 
       
   606   if (priv->streams != NULL)
       
   607     {
       
   608       for (i = 0; i < priv->streams->len; i++)
       
   609         g_object_unref (g_ptr_array_index (priv->streams, i));
       
   610       g_ptr_array_free (priv->streams, TRUE);
       
   611       priv->streams = NULL;
       
   612     }
       
   613 
       
   614   for (i = 0; i < priv->remove_requests->len; i++)
       
   615     g_ptr_array_free (g_ptr_array_index (priv->remove_requests, i), TRUE);
       
   616   g_ptr_array_free (priv->remove_requests, TRUE);
       
   617   priv->remove_requests = NULL;
       
   618 
       
   619   if (G_OBJECT_CLASS (gabble_media_session_parent_class)->dispose)
       
   620     G_OBJECT_CLASS (gabble_media_session_parent_class)->dispose (object);
       
   621 }
       
   622 
       
   623 static void
       
   624 gabble_media_session_finalize (GObject *object)
       
   625 {
       
   626   GabbleMediaSession *self = GABBLE_MEDIA_SESSION (object);
       
   627   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (self);
       
   628 
       
   629   g_free (priv->id);
       
   630   g_free (priv->object_path);
       
   631   g_free (priv->peer_resource);
       
   632   G_OBJECT_CLASS (gabble_media_session_parent_class)->finalize (object);
       
   633 }
       
   634 
       
   635 
       
   636 /**
       
   637  * gabble_media_session_error
       
   638  *
       
   639  * Implements D-Bus method Error
       
   640  * on interface org.freedesktop.Telepathy.Media.SessionHandler
       
   641  *
       
   642  * @error: Used to return a pointer to a GError detailing any error
       
   643  *         that occurred, D-Bus will throw the error only if this
       
   644  *         function returns FALSE.
       
   645  *
       
   646  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   647  */
       
   648 gboolean
       
   649 gabble_media_session_error (GabbleMediaSession *self,
       
   650                             guint errno,
       
   651                             const gchar *message,
       
   652                             GError **error)
       
   653 {
       
   654   GabbleMediaSessionPrivate *priv;
       
   655   GPtrArray *tmp;
       
   656   guint i;
       
   657 
       
   658   g_assert (GABBLE_IS_MEDIA_SESSION (self));
       
   659 
       
   660   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (self);
       
   661 
       
   662   _gabble_media_session_debug (self, DEBUG_MSG_INFO, "Media.SessionHandler::Error called, error %u (%s) -- "
       
   663       "emitting error on each stream", errno, message);
       
   664 
       
   665   if (priv->state == JS_STATE_ENDED)
       
   666     {
       
   667       return TRUE;
       
   668     }
       
   669   else if (priv->state == JS_STATE_PENDING_CREATED)
       
   670     {
       
   671       /* shortcut to prevent sending remove actions if we haven't sent an
       
   672        * initiate yet */
       
   673       g_object_set (self, "state", JS_STATE_ENDED, NULL);
       
   674       return TRUE;
       
   675     }
       
   676 
       
   677   g_assert (priv->streams != NULL);
       
   678 
       
   679   tmp = priv->streams;
       
   680   priv->streams = NULL;
       
   681 
       
   682   for (i = 0; i < tmp->len; i++)
       
   683     {
       
   684       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
   685       //FIXME: Temporary fix to comment "errno" to resolve the mecevo integration build break, fix me when this method is being used.
       
   686       gabble_media_stream_error (stream, /*errno*/0, message, NULL);
       
   687     }
       
   688 
       
   689   g_ptr_array_free (tmp, TRUE);
       
   690 
       
   691   return TRUE;
       
   692 }
       
   693 
       
   694 
       
   695 /**
       
   696  * gabble_media_session_ready
       
   697  *
       
   698  * Implements D-Bus method Ready
       
   699  * on interface org.freedesktop.Telepathy.Media.SessionHandler
       
   700  *
       
   701  * @error: Used to return a pointer to a GError detailing any error
       
   702  *         that occurred, D-Bus will throw the error only if this
       
   703  *         function returns FALSE.
       
   704  *
       
   705  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   706  */
       
   707 gboolean
       
   708 gabble_media_session_ready (GabbleMediaSession *self,
       
   709                             GError **error)
       
   710 {
       
   711   GabbleMediaSessionPrivate *priv;
       
   712   guint i;
       
   713 
       
   714   g_assert (GABBLE_IS_MEDIA_SESSION (self));
       
   715 
       
   716   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (self);
       
   717 
       
   718   priv->ready = TRUE;
       
   719 
       
   720   for (i = 0; i < priv->streams->len; i++)
       
   721     _emit_new_stream (self, g_ptr_array_index (priv->streams, i));
       
   722 
       
   723   return TRUE;
       
   724 }
       
   725 
       
   726 
       
   727 static gboolean
       
   728 _handle_create (GabbleMediaSession *session,
       
   729                 LmMessage *message,
       
   730                 LmMessageNode *content_node,
       
   731                 const gchar *stream_name,
       
   732                 GabbleMediaStream *stream,
       
   733                 LmMessageNode *desc_node,
       
   734                 LmMessageNode *trans_node,
       
   735                 GError **error)
       
   736 {
       
   737   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
   738   GabbleMediaSessionMode session_mode;
       
   739   TpMediaStreamType stream_type;
       
   740   gboolean override_existing = FALSE;
       
   741 
       
   742   if ((priv->state == JS_STATE_PENDING_CREATED) &&
       
   743       (session->initiator == INITIATOR_LOCAL))
       
   744     {
       
   745       gabble_debug (DEBUG_FLAG, "we're trying to call ourselves, rejecting with busy");
       
   746       _gabble_media_session_terminate (session, INITIATOR_REMOTE,
       
   747           TP_CHANNEL_GROUP_CHANGE_REASON_BUSY);
       
   748           return FALSE;
       
   749     }
       
   750 
       
   751 
       
   752   if (stream != NULL)
       
   753     {
       
   754       /* streams added by the session initiator may replace similarly-named
       
   755        * streams which we are trying to add (but havn't had acknowledged) */
       
   756       if (stream->signalling_state < STREAM_SIG_STATE_ACKNOWLEDGED)
       
   757         {
       
   758           if (session->initiator == INITIATOR_REMOTE)
       
   759             {
       
   760               override_existing = TRUE;
       
   761             }
       
   762           else
       
   763             {
       
   764               g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_CONFLICT,
       
   765                   "session initiator is creating a stream named \"%s\" already",
       
   766                   stream_name);
       
   767               return FALSE;
       
   768             }
       
   769         }
       
   770       else
       
   771         {
       
   772           g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_CONFLICT,
       
   773               "can't create new stream called \"%s\", it already exists, "
       
   774               "rejecting", stream_name);
       
   775           return FALSE;
       
   776         }
       
   777     }
       
   778 
       
   779   if (desc_node == NULL)
       
   780     {
       
   781       g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
   782           "unable to create stream without a content description");
       
   783       return FALSE;
       
   784     }
       
   785 
       
   786   if (lm_message_node_has_namespace (desc_node,
       
   787         NS_GOOGLE_SESSION_PHONE, NULL))
       
   788     {
       
   789       session_mode = MODE_GOOGLE;
       
   790       stream_type = TP_MEDIA_STREAM_TYPE_AUDIO;
       
   791     }
       
   792   else if (lm_message_node_has_namespace (desc_node,
       
   793         NS_JINGLE_DESCRIPTION_AUDIO, NULL))
       
   794     {
       
   795       session_mode = MODE_JINGLE;
       
   796       stream_type = TP_MEDIA_STREAM_TYPE_AUDIO;
       
   797     }
       
   798   else if (lm_message_node_has_namespace (desc_node,
       
   799         NS_JINGLE_DESCRIPTION_VIDEO, NULL))
       
   800     {
       
   801       session_mode = MODE_JINGLE;
       
   802       stream_type = TP_MEDIA_STREAM_TYPE_VIDEO;
       
   803     }
       
   804   else
       
   805     {
       
   806       g_set_error (error, GABBLE_XMPP_ERROR,
       
   807           XMPP_ERROR_JINGLE_UNSUPPORTED_CONTENT,
       
   808           "refusing to create stream for unsupported content description");
       
   809       return FALSE;
       
   810     }
       
   811 
       
   812   /* MODE_GOOGLE is allowed to have a null transport node */
       
   813   if (session_mode == MODE_JINGLE && trans_node == NULL)
       
   814     {
       
   815       g_set_error (error, GABBLE_XMPP_ERROR,
       
   816           XMPP_ERROR_JINGLE_UNSUPPORTED_TRANSPORT,
       
   817           "refusing to create stream for unsupported transport");
       
   818       return FALSE;
       
   819     }
       
   820 
       
   821   if (session_mode != priv->mode)
       
   822     {
       
   823       if (priv->streams->len > 0)
       
   824         {
       
   825           g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_UNEXPECTED_REQUEST,
       
   826               "refusing to change mode because streams already exist");
       
   827           return FALSE;
       
   828         }
       
   829       else
       
   830         {
       
   831           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "setting session mode to %s",
       
   832               session_mode == MODE_GOOGLE ? "google" : "jingle");
       
   833           priv->mode = session_mode;
       
   834         }
       
   835     }
       
   836 
       
   837   if (override_existing)
       
   838     {
       
   839       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "removing our unacknowledged stream \"%s\" "
       
   840           "in favour of the session initiator's", stream_name);
       
   841 
       
   842       /* disappear this stream */
       
   843       destroy_media_stream (session, stream);
       
   844 
       
   845       stream = NULL;
       
   846     }
       
   847 
       
   848   if (priv->streams->len == MAX_STREAMS)
       
   849     {
       
   850       g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_RESOURCE_CONSTRAINT,
       
   851           "refusing to create more than " G_STRINGIFY (MAX_STREAMS)
       
   852           " streams");
       
   853       return FALSE;
       
   854     }
       
   855 
       
   856   stream = create_media_stream (session, stream_name, INITIATOR_REMOTE,
       
   857       stream_type);
       
   858 
       
   859   /* set the signalling state to ACKNOWLEDGED */
       
   860   g_object_set (stream,
       
   861       "signalling-state", STREAM_SIG_STATE_ACKNOWLEDGED,
       
   862       NULL);
       
   863 
       
   864   /* for jingle streams, set the direction to none, so that the
       
   865    * direction handler adds the right flags */
       
   866   if (priv->mode == MODE_JINGLE)
       
   867     g_object_set (stream,
       
   868         "combined-direction", TP_MEDIA_STREAM_DIRECTION_NONE,
       
   869         NULL);
       
   870 
       
   871   return TRUE;
       
   872 }
       
   873 
       
   874 
       
   875 static TpMediaStreamDirection
       
   876 _senders_to_direction (GabbleMediaSession *session,
       
   877                        const gchar *senders)
       
   878 {
       
   879   TpMediaStreamDirection ret = TP_MEDIA_STREAM_DIRECTION_NONE;
       
   880 
       
   881   if (!g_strdiff (senders, "initiator"))
       
   882     {
       
   883       if (session->initiator == INITIATOR_LOCAL)
       
   884         ret = TP_MEDIA_STREAM_DIRECTION_SEND;
       
   885       else
       
   886         ret = TP_MEDIA_STREAM_DIRECTION_RECEIVE;
       
   887     }
       
   888   else if (!g_strdiff (senders, "responder"))
       
   889     {
       
   890       if (session->initiator == INITIATOR_REMOTE)
       
   891         ret = TP_MEDIA_STREAM_DIRECTION_SEND;
       
   892       else
       
   893         ret = TP_MEDIA_STREAM_DIRECTION_RECEIVE;
       
   894     }
       
   895   else if (!g_strdiff (senders, "both"))
       
   896     {
       
   897       ret = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL;
       
   898     }
       
   899 
       
   900   return ret;
       
   901 }
       
   902 
       
   903 static gboolean
       
   904 _handle_direction (GabbleMediaSession *session,
       
   905                    LmMessage *message,
       
   906                    LmMessageNode *content_node,
       
   907                    const gchar *stream_name,
       
   908                    GabbleMediaStream *stream,
       
   909                    LmMessageNode *desc_node,
       
   910                    LmMessageNode *trans_node,
       
   911                    GError **error)
       
   912 {
       
   913   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
   914   const gchar *senders;
       
   915   CombinedStreamDirection new_combined_dir;
       
   916   TpMediaStreamDirection requested_dir, current_dir;
       
   917   TpMediaStreamPendingSend pending_send;
       
   918 
       
   919   if (priv->mode == MODE_GOOGLE)
       
   920     return TRUE;
       
   921 
       
   922   requested_dir = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL;
       
   923 
       
   924   senders = lm_message_node_get_attribute (content_node, "senders");
       
   925   if (senders != NULL)
       
   926     requested_dir = _senders_to_direction (session, senders);
       
   927 
       
   928   if (requested_dir == TP_MEDIA_STREAM_DIRECTION_NONE)
       
   929     {
       
   930       g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
   931           "received invalid content senders value \"%s\" on stream \"%s\"; "
       
   932           "rejecting", senders, stream_name);
       
   933       return FALSE;
       
   934     }
       
   935 
       
   936   current_dir = COMBINED_DIRECTION_GET_DIRECTION (stream->combined_direction);
       
   937   pending_send = COMBINED_DIRECTION_GET_PENDING_SEND
       
   938     (stream->combined_direction);
       
   939 
       
   940   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "received request for senders \"%s\" on stream "
       
   941       "\"%s\"", senders, stream_name);
       
   942 
       
   943   /* if local sending has been added, remove it,
       
   944    * and set the pending local send flag */
       
   945   if (((current_dir & TP_MEDIA_STREAM_DIRECTION_SEND) == 0) &&
       
   946     ((requested_dir & TP_MEDIA_STREAM_DIRECTION_SEND) != 0))
       
   947     {
       
   948       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "setting pending local send flag");
       
   949       requested_dir &= ~TP_MEDIA_STREAM_DIRECTION_SEND;
       
   950       pending_send |= TP_MEDIA_STREAM_PENDING_LOCAL_SEND;
       
   951     }
       
   952 
       
   953 #if 0
       
   954   /* clear any pending remote send */
       
   955   if ((pending_send & TP_MEDIA_STREAM_PENDING_REMOTE_SEND) != 0)
       
   956     {
       
   957       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "setting pending local send flag");
       
   958       pending_send &= ~TP_MEDIA_STREAM_PENDING_REMOTE_SEND;
       
   959     }
       
   960 #endif
       
   961 
       
   962   /* make any necessary changes */
       
   963   new_combined_dir = MAKE_COMBINED_DIRECTION (requested_dir, pending_send);
       
   964   if (new_combined_dir != stream->combined_direction)
       
   965     {
       
   966       g_object_set (stream, "combined-direction", new_combined_dir, NULL);
       
   967       _gabble_media_stream_update_sending (stream, FALSE);
       
   968     }
       
   969 
       
   970   return TRUE;
       
   971 }
       
   972 
       
   973 
       
   974 static gboolean
       
   975 _handle_accept (GabbleMediaSession *session,
       
   976                 LmMessage *message,
       
   977                 LmMessageNode *content_node,
       
   978                 const gchar *stream_name,
       
   979                 GabbleMediaStream *stream,
       
   980                 LmMessageNode *desc_node,
       
   981                 LmMessageNode *trans_node,
       
   982                 GError **error)
       
   983 {
       
   984   g_object_set (stream, "playing", TRUE, NULL);
       
   985 
       
   986   _gabble_media_stream_update_sending (stream, TRUE);
       
   987 
       
   988   return TRUE;
       
   989 }
       
   990 
       
   991 
       
   992 static gboolean
       
   993 _handle_codecs (GabbleMediaSession *session,
       
   994                 LmMessage *message,
       
   995                 LmMessageNode *content_node,
       
   996                 const gchar *stream_name,
       
   997                 GabbleMediaStream *stream,
       
   998                 LmMessageNode *desc_node,
       
   999                 LmMessageNode *trans_node,
       
  1000                 GError **error)
       
  1001 {
       
  1002   if (desc_node == NULL)
       
  1003     {
       
  1004       g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
  1005           "unable to handle codecs without a content description node");
       
  1006       return FALSE;
       
  1007     }
       
  1008 
       
  1009   if (!_gabble_media_stream_post_remote_codecs (stream, message, desc_node,
       
  1010         error))
       
  1011     return FALSE;
       
  1012 
       
  1013   return TRUE;
       
  1014 }
       
  1015 
       
  1016 
       
  1017 static gboolean
       
  1018 _handle_candidates (GabbleMediaSession *session,
       
  1019                     LmMessage *message,
       
  1020                     LmMessageNode *content_node,
       
  1021                     const gchar *stream_name,
       
  1022                     GabbleMediaStream *stream,
       
  1023                     LmMessageNode *desc_node,
       
  1024                     LmMessageNode *trans_node,
       
  1025                     GError **error)
       
  1026 {
       
  1027   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1028 
       
  1029   if (trans_node == NULL)
       
  1030     {
       
  1031       if (priv->mode == MODE_GOOGLE)
       
  1032         {
       
  1033           trans_node = content_node;
       
  1034         }
       
  1035       else
       
  1036         {
       
  1037           g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
  1038               "unable to handle candidates without a transport node");
       
  1039           return FALSE;
       
  1040         }
       
  1041     }
       
  1042 
       
  1043   if (!_gabble_media_stream_post_remote_candidates (stream, message,
       
  1044         trans_node, error))
       
  1045     return FALSE;
       
  1046 
       
  1047   return TRUE;
       
  1048 }
       
  1049 
       
  1050 static guint
       
  1051 _count_non_removing_streams (GabbleMediaSession *session)
       
  1052 {
       
  1053   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1054   guint i, ret = 0;
       
  1055 
       
  1056   for (i = 0; i < priv->streams->len; i++)
       
  1057     {
       
  1058       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1059 
       
  1060       if (stream->signalling_state < STREAM_SIG_STATE_REMOVING)
       
  1061         ret++;
       
  1062     }
       
  1063 
       
  1064   return ret;
       
  1065 }
       
  1066 
       
  1067 static gboolean
       
  1068 _handle_remove (GabbleMediaSession *session,
       
  1069                 LmMessage *message,
       
  1070                 LmMessageNode *content_node,
       
  1071                 const gchar *stream_name,
       
  1072                 GabbleMediaStream *stream,
       
  1073                 LmMessageNode *desc_node,
       
  1074                 LmMessageNode *trans_node,
       
  1075                 GError **error)
       
  1076 {
       
  1077   /* reducing a session to contain 0 streams is invalid; instead the peer
       
  1078    * should terminate the session. I guess we'll do it for them... */
       
  1079   if (_count_non_removing_streams (session) == 1)
       
  1080     {
       
  1081       g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
  1082           "unable to remove the last stream in a Jingle call");
       
  1083       return FALSE;
       
  1084     }
       
  1085 
       
  1086   /* close the stream */
       
  1087   destroy_media_stream (session, stream);
       
  1088 
       
  1089   return TRUE;
       
  1090 }
       
  1091 
       
  1092 
       
  1093 static gboolean
       
  1094 _handle_terminate (GabbleMediaSession *session,
       
  1095                    LmMessage *message,
       
  1096                    LmMessageNode *content_node,
       
  1097                    const gchar *stream_name,
       
  1098                    GabbleMediaStream *stream,
       
  1099                    LmMessageNode *desc_node,
       
  1100                    LmMessageNode *trans_node,
       
  1101                    GError **error)
       
  1102 {
       
  1103   gabble_debug (DEBUG_FLAG, "called for %s", stream_name);
       
  1104 
       
  1105   _gabble_media_session_terminate (session, INITIATOR_REMOTE,
       
  1106       TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
       
  1107 
       
  1108   return TRUE;
       
  1109 }
       
  1110 
       
  1111 
       
  1112 #ifndef EMULATOR
       
  1113 
       
  1114 typedef gboolean (*StreamHandlerFunc)(GabbleMediaSession *session,
       
  1115                                       LmMessage *message,
       
  1116                                       LmMessageNode *content_node,
       
  1117                                       const gchar *stream_name,
       
  1118                                       GabbleMediaStream *stream,
       
  1119                                       LmMessageNode *desc_node,
       
  1120                                       LmMessageNode *trans_node,
       
  1121                                       GError **error);
       
  1122                                       
       
  1123 typedef struct _Handler Handler;
       
  1124 
       
  1125 struct _Handler {
       
  1126   const gchar *actions[3];
       
  1127   JingleSessionState min_allowed_state;
       
  1128   JingleSessionState max_allowed_state;
       
  1129   StreamHandlerFunc stream_handlers[4];
       
  1130   JingleSessionState new_state;
       
  1131 };
       
  1132 
       
  1133 static Handler handlers[] = {
       
  1134   {
       
  1135     { "initiate", "session-initiate", NULL },
       
  1136     JS_STATE_PENDING_CREATED,
       
  1137     JS_STATE_PENDING_CREATED,
       
  1138     { _handle_create, _handle_direction, _handle_codecs, NULL },
       
  1139     JS_STATE_PENDING_INITIATED
       
  1140   },
       
  1141   {
       
  1142     { "accept", "session-accept", NULL },
       
  1143     JS_STATE_PENDING_INITIATED,
       
  1144     JS_STATE_PENDING_INITIATED,
       
  1145     { _handle_direction, _handle_codecs, _handle_accept, NULL },
       
  1146     JS_STATE_ACTIVE
       
  1147   },
       
  1148   {
       
  1149     { "reject", NULL },
       
  1150     JS_STATE_PENDING_INITIATE_SENT,
       
  1151     JS_STATE_PENDING_INITIATED,
       
  1152     { _handle_terminate, NULL },
       
  1153     JS_STATE_INVALID
       
  1154   },
       
  1155   {
       
  1156     { "terminate", "session-terminate", NULL },
       
  1157     JS_STATE_PENDING_INITIATED,
       
  1158     JS_STATE_ENDED,
       
  1159     { _handle_terminate, NULL },
       
  1160     JS_STATE_INVALID
       
  1161   },
       
  1162   {
       
  1163     { "candidates", "transport-info", NULL },
       
  1164     JS_STATE_PENDING_INITIATED,
       
  1165     JS_STATE_ACTIVE,
       
  1166     { _handle_candidates, NULL },
       
  1167     JS_STATE_INVALID
       
  1168   },
       
  1169   {
       
  1170     { "content-add", NULL },
       
  1171     JS_STATE_ACTIVE,
       
  1172     JS_STATE_ACTIVE,
       
  1173     { _handle_create, _handle_direction, _handle_codecs, NULL },
       
  1174     JS_STATE_INVALID,
       
  1175   },
       
  1176   {
       
  1177     { "content-modify", NULL },
       
  1178     JS_STATE_PENDING_INITIATED,
       
  1179     JS_STATE_ACTIVE,
       
  1180     { _handle_direction, NULL },
       
  1181     JS_STATE_INVALID
       
  1182   },
       
  1183   {
       
  1184     { "content-accept", NULL },
       
  1185     JS_STATE_PENDING_INITIATED,
       
  1186     JS_STATE_ACTIVE,
       
  1187     { _handle_direction, _handle_codecs, _handle_accept, NULL },
       
  1188     JS_STATE_INVALID
       
  1189   },
       
  1190   {
       
  1191     { "content-remove", "content-decline", NULL },
       
  1192     JS_STATE_PENDING_INITIATED,
       
  1193     JS_STATE_ACTIVE,
       
  1194     { _handle_remove, NULL },
       
  1195     JS_STATE_INVALID
       
  1196   },
       
  1197   {
       
  1198     { NULL },
       
  1199     JS_STATE_INVALID,
       
  1200     JS_STATE_INVALID,
       
  1201     { NULL },
       
  1202     JS_STATE_INVALID
       
  1203   }
       
  1204 };
       
  1205 #else
       
  1206 
       
  1207 Handler *_s_gabble_med_sess_handlers() 
       
  1208 	{ 
       
  1209 	Handler* handler = libgabble_ImpurePtr()->_s_gabble_med_sess_handlers;
       
  1210 	handler[0].stream_handlers[0] = _handle_create;
       
  1211 	handler[0].stream_handlers[1] = _handle_direction;
       
  1212 	handler[0].stream_handlers[2] = _handle_codecs;
       
  1213 	
       
  1214 	handler[1].stream_handlers[0] = _handle_direction;
       
  1215 	handler[1].stream_handlers[1] = _handle_codecs;
       
  1216 	handler[1].stream_handlers[2] = _handle_accept;
       
  1217 	
       
  1218 	handler[2].stream_handlers[0] = _handle_terminate;
       
  1219 	
       
  1220 	handler[3].stream_handlers[0] = _handle_terminate;
       
  1221 	
       
  1222 	handler[4].stream_handlers[0] = _handle_candidates;
       
  1223 	
       
  1224 	handler[5].stream_handlers[0] = _handle_create;
       
  1225 	handler[5].stream_handlers[0] = _handle_direction;
       
  1226 	handler[5].stream_handlers[0] = _handle_codecs;
       
  1227 	
       
  1228 	handler[6].stream_handlers[0] = _handle_direction;
       
  1229 	
       
  1230 	handler[7].stream_handlers[0] = _handle_direction;
       
  1231 	handler[7].stream_handlers[2] = _handle_codecs;
       
  1232 	handler[7].stream_handlers[3] = _handle_accept;
       
  1233 	
       
  1234 	handler[8].stream_handlers[0] = _handle_remove;
       
  1235 	
       
  1236 
       
  1237 	return handler;
       
  1238 	
       
  1239 	
       
  1240 
       
  1241 	
       
  1242 	}
       
  1243 	#define handlers (GET_WSD_VAR_NAME(handlers,gabble_med_sess, s)())	
       
  1244 	
       
  1245 
       
  1246 
       
  1247 #endif
       
  1248 
       
  1249 
       
  1250 static gboolean
       
  1251 _call_handlers_on_stream (GabbleMediaSession *session,
       
  1252                           LmMessage *message,
       
  1253                           LmMessageNode *content_node,
       
  1254                           const gchar *stream_name,
       
  1255                           JingleInitiator stream_creator,
       
  1256                           StreamHandlerFunc *func,
       
  1257                           GError **error)
       
  1258 {
       
  1259   GabbleMediaStream *stream = NULL;
       
  1260   LmMessageNode *desc_node = NULL, *trans_node = NULL;
       
  1261   StreamHandlerFunc *tmp;
       
  1262   gboolean stream_created = FALSE;
       
  1263 
       
  1264   if (content_node != NULL)
       
  1265     {
       
  1266       desc_node = lm_message_node_get_child (content_node, "description");
       
  1267 
       
  1268       trans_node = lm_message_node_get_child_with_namespace (content_node,
       
  1269           "transport", NS_GOOGLE_TRANSPORT_P2P);
       
  1270     }
       
  1271 
       
  1272   for (tmp = func; *tmp != NULL; tmp++)
       
  1273     {
       
  1274       /* handlers may create the stream */
       
  1275       if (stream == NULL && stream_name != NULL)
       
  1276         stream = _lookup_stream_by_name_and_initiator (session, stream_name,
       
  1277             stream_creator);
       
  1278 
       
  1279       /* the create handler is able to check whether or not the stream
       
  1280        * exists, and act accordingly (sometimes it will replace an existing
       
  1281        * stream, sometimes it will reject). the termination handler
       
  1282        * also requires no stream to do it's job. */
       
  1283       if (*tmp != _handle_create && *tmp != _handle_terminate)
       
  1284         {
       
  1285           /* all other handlers require the stream to exist */
       
  1286           if (stream == NULL)
       
  1287             {
       
  1288               const gchar *created = "";
       
  1289 
       
  1290               if (stream_creator == INITIATOR_LOCAL)
       
  1291                 created = "locally-created ";
       
  1292               else if (stream_creator == INITIATOR_REMOTE)
       
  1293                 created = "remotely-created ";
       
  1294 
       
  1295               g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_ITEM_NOT_FOUND,
       
  1296                   "unable to handle action for unknown %sstream \"%s\" ",
       
  1297                   created, stream_name);
       
  1298 
       
  1299               return FALSE;
       
  1300             }
       
  1301           else
       
  1302             {
       
  1303               /* don't do anything with actions on streams which have not been
       
  1304                * acknowledged, or that we're trying to remove, to deal with
       
  1305                * adding/removing race conditions (actions sent by the other end
       
  1306                * before they're aware that we've added or removed a stream) */
       
  1307               if (stream->signalling_state != STREAM_SIG_STATE_ACKNOWLEDGED)
       
  1308                 {
       
  1309                   _gabble_media_session_debug (session, DEBUG_MSG_WARNING, "ignoring action because stream "
       
  1310                       "%s is in state %d, not ACKNOWLEDGED", stream->name,
       
  1311                       stream->signalling_state);
       
  1312                   return TRUE;
       
  1313                 }
       
  1314             }
       
  1315         }
       
  1316 
       
  1317       if (!(*tmp) (session, message, content_node, stream_name, stream,
       
  1318             desc_node, trans_node, error))
       
  1319         {
       
  1320           /* if we successfully created the stream but failed to do something
       
  1321            * with it later, remove it */
       
  1322           if (stream_created)
       
  1323             destroy_media_stream (session, stream);
       
  1324 
       
  1325           return FALSE;
       
  1326         }
       
  1327 
       
  1328       if (*tmp == _handle_create)
       
  1329         {
       
  1330           stream_created = TRUE;
       
  1331           /* force a stream lookup after the create handler, even if we
       
  1332            * already had one (it has replacement semantics in certain
       
  1333            * situations) */
       
  1334           stream = NULL;
       
  1335         }
       
  1336     }
       
  1337 
       
  1338   return TRUE;
       
  1339 }
       
  1340 
       
  1341 
       
  1342 static JingleInitiator
       
  1343 _creator_to_initiator (GabbleMediaSession *session, const gchar *creator)
       
  1344 {
       
  1345   if (!g_strdiff (creator, "initiator"))
       
  1346     {
       
  1347       if (session->initiator == INITIATOR_LOCAL)
       
  1348         return INITIATOR_LOCAL;
       
  1349       else
       
  1350         return INITIATOR_REMOTE;
       
  1351     }
       
  1352   else if (!g_strdiff (creator, "responder"))
       
  1353     {
       
  1354       if (session->initiator == INITIATOR_LOCAL)
       
  1355         return INITIATOR_REMOTE;
       
  1356       else
       
  1357         return INITIATOR_LOCAL;
       
  1358     }
       
  1359   else
       
  1360     return INITIATOR_INVALID;
       
  1361 }
       
  1362 
       
  1363 
       
  1364 static gboolean
       
  1365 _call_handlers_on_streams (GabbleMediaSession *session,
       
  1366                            LmMessage *message,
       
  1367                            LmMessageNode *session_node,
       
  1368                            StreamHandlerFunc *func,
       
  1369                            GError **error)
       
  1370 {
       
  1371   LmMessageNode *content_node;
       
  1372 
       
  1373   if (lm_message_node_has_namespace (session_node, NS_GOOGLE_SESSION, NULL))
       
  1374     return _call_handlers_on_stream (session, message, session_node,
       
  1375         GTALK_STREAM_NAME, INITIATOR_INVALID, func, error);
       
  1376 
       
  1377   if (session_node->children == NULL)
       
  1378     return _call_handlers_on_stream (session, message, NULL, NULL,
       
  1379         INITIATOR_INVALID, func, error);
       
  1380 
       
  1381   for (content_node = session_node->children;
       
  1382        NULL != content_node;
       
  1383        content_node = content_node->next)
       
  1384     {
       
  1385       const gchar *stream_name, *stream_creator;
       
  1386       JingleInitiator stream_initiator;
       
  1387 
       
  1388       if (g_strdiff (content_node->name, "content"))
       
  1389         continue;
       
  1390 
       
  1391       stream_name = lm_message_node_get_attribute (content_node, "name");
       
  1392 
       
  1393       if (stream_name == NULL)
       
  1394         {
       
  1395           g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
  1396               "rejecting content node with no name");
       
  1397           return FALSE;
       
  1398         }
       
  1399 
       
  1400       stream_creator = lm_message_node_get_attribute (content_node, "creator");
       
  1401       stream_initiator = _creator_to_initiator (session, stream_creator);
       
  1402 
       
  1403       /* we allow NULL creator to mean INITIATOR_INVALID for backwards
       
  1404        * compatibility with clients that don't put a creator attribute in */
       
  1405       if (stream_creator != NULL && stream_initiator == INITIATOR_INVALID)
       
  1406         {
       
  1407           g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
  1408               "rejecting content node with invalid creators value");
       
  1409           return FALSE;
       
  1410         }
       
  1411 
       
  1412       if (!_call_handlers_on_stream (session, message, content_node,
       
  1413             stream_name, stream_initiator, func, error))
       
  1414         return FALSE;
       
  1415     }
       
  1416 
       
  1417   return TRUE;
       
  1418 }
       
  1419 
       
  1420 
       
  1421 gboolean
       
  1422 _gabble_media_session_handle_action (GabbleMediaSession *session,
       
  1423                                      LmMessage *message,
       
  1424                                      LmMessageNode *session_node,
       
  1425                                      const gchar *action,
       
  1426                                      GError **error)
       
  1427 {
       
  1428   GabbleMediaSessionPrivate *priv;
       
  1429   StreamHandlerFunc *funcs = NULL;
       
  1430   JingleSessionState new_state = JS_STATE_INVALID;
       
  1431   Handler *i;
       
  1432 
       
  1433 #ifdef EMULATOR
       
  1434 	gchar **tmp;
       
  1435 #else  
       
  1436   const gchar **tmp;
       
  1437 #endif
       
  1438 
       
  1439   g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
  1440 
       
  1441   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1442 
       
  1443   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "got jingle session action \"%s\" from peer",
       
  1444       action);
       
  1445 
       
  1446   /* do the state machine dance */
       
  1447 
       
  1448   /* search the table of handlers for the action */
       
  1449   for (i = handlers; NULL != i->actions[0]; i++)
       
  1450     {
       
  1451       for (tmp = (char**)i->actions; NULL != *tmp; tmp++)
       
  1452         if (0 == strcmp (*tmp, action))
       
  1453           break;
       
  1454 
       
  1455       if (NULL == *tmp)
       
  1456         continue;
       
  1457 
       
  1458       /* if we're outside the allowable states for this action, return an error
       
  1459        * immediately */
       
  1460       if (priv->state < i->min_allowed_state ||
       
  1461           priv->state > i->max_allowed_state)
       
  1462         {
       
  1463           g_set_error (error, GABBLE_XMPP_ERROR,
       
  1464               XMPP_ERROR_JINGLE_OUT_OF_ORDER,
       
  1465               "action \"%s\" not allowed in current state", action);
       
  1466           goto ERROR;
       
  1467         }
       
  1468 
       
  1469       funcs = i->stream_handlers;
       
  1470       new_state = i->new_state;
       
  1471 
       
  1472       break;
       
  1473     }
       
  1474 
       
  1475   /* pointer is not NULL if we found a matching action */
       
  1476   if (NULL == funcs)
       
  1477     {
       
  1478       g_set_error (error, GABBLE_XMPP_ERROR,
       
  1479           XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, "action \"%s\" not implemented",
       
  1480           action);
       
  1481       goto ERROR;
       
  1482     }
       
  1483 
       
  1484   /* call handlers if there are any (NULL-terminated array) */
       
  1485   if (NULL != *funcs)
       
  1486     {
       
  1487       if (!_call_handlers_on_streams (session, message, session_node, funcs,
       
  1488             error))
       
  1489         {
       
  1490           if (*error == NULL)
       
  1491             g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
  1492                 "unknown error encountered with action \"%s\"",
       
  1493                 action);
       
  1494 
       
  1495           goto ERROR;
       
  1496         }
       
  1497     }
       
  1498 
       
  1499   /* acknowledge the IQ before changing the state because the new state
       
  1500    * could perform some actions which the other end will only accept
       
  1501    * if this action has been acknowledged */
       
  1502   _gabble_connection_acknowledge_set_iq (priv->conn, message);
       
  1503 
       
  1504   /* if the action specified a new state to go to, set it */
       
  1505   if (JS_STATE_INVALID != new_state)
       
  1506     g_object_set (session, "state", new_state, NULL);
       
  1507 
       
  1508   return TRUE;
       
  1509 
       
  1510 ERROR:
       
  1511   g_assert (error != NULL);
       
  1512   _gabble_media_session_debug (session, DEBUG_MSG_ERROR, (*error)->message);
       
  1513   return FALSE;
       
  1514 }
       
  1515 
       
  1516 static gboolean
       
  1517 timeout_session (gpointer data)
       
  1518 {
       
  1519   GabbleMediaSession *session = data;
       
  1520 
       
  1521   gabble_debug (DEBUG_FLAG, "session timed out");
       
  1522 
       
  1523   _gabble_media_session_terminate (session, INITIATOR_LOCAL,
       
  1524       TP_CHANNEL_GROUP_CHANGE_REASON_ERROR);
       
  1525 
       
  1526   return FALSE;
       
  1527 }
       
  1528 
       
  1529 static void do_content_add (GabbleMediaSession *, GabbleMediaStream *);
       
  1530 
       
  1531 void
       
  1532 _add_ready_new_streams (GabbleMediaSession *session)
       
  1533 {
       
  1534   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1535   guint i;
       
  1536 
       
  1537   for (i = 0; i < priv->streams->len; i++)
       
  1538     {
       
  1539       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1540 
       
  1541       _gabble_media_session_debug (session, DEBUG_MSG_DUMP, "pondering accept-time add for stream: %s, got "
       
  1542           "local codecs: %s, initiator: %s, signalling state: %d", stream->name,
       
  1543           stream->got_local_codecs ? "true" : "false",
       
  1544           stream->initiator == INITIATOR_LOCAL ? "local" : "remote",
       
  1545           stream->signalling_state);
       
  1546 
       
  1547       if (stream->got_local_codecs == FALSE)
       
  1548         continue;
       
  1549 
       
  1550       if (stream->initiator == INITIATOR_REMOTE)
       
  1551         continue;
       
  1552 
       
  1553       if (stream->signalling_state > STREAM_SIG_STATE_NEW)
       
  1554         continue;
       
  1555 
       
  1556       do_content_add (session, stream);
       
  1557     }
       
  1558 }
       
  1559 
       
  1560 static void
       
  1561 session_state_changed (GabbleMediaSession *session,
       
  1562                        JingleSessionState prev_state,
       
  1563                        JingleSessionState new_state)
       
  1564 {
       
  1565   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1566 
       
  1567   _gabble_media_session_debug (session, DEBUG_MSG_EVENT, "state changed from %s to %s",
       
  1568                    session_states[prev_state].name,
       
  1569                    session_states[new_state].name);
       
  1570 
       
  1571   /*
       
  1572    * If the state goes from CREATED to INITIATED (which means the remote
       
  1573    * end initiated), set the timer. If, OTOH, we're the end which just sent an
       
  1574    * initiate, set the timer.
       
  1575    */
       
  1576   if ((prev_state == JS_STATE_PENDING_CREATED &&
       
  1577        new_state == JS_STATE_PENDING_INITIATED) ||
       
  1578       (new_state == JS_STATE_PENDING_INITIATE_SENT))
       
  1579     {
       
  1580       priv->timer_id =
       
  1581         g_timeout_add (DEFAULT_SESSION_TIMEOUT, timeout_session, session);
       
  1582     }
       
  1583   else if (new_state == JS_STATE_ACTIVE)
       
  1584     {
       
  1585       g_source_remove (priv->timer_id);
       
  1586       priv->timer_id = 0;
       
  1587 
       
  1588       /* signal any streams to the remote end which were added locally & became
       
  1589        * ready before the session was accepted, so haven't been mentioned yet */
       
  1590       _add_ready_new_streams (session);
       
  1591     }
       
  1592 }
       
  1593 
       
  1594 static void
       
  1595 _mark_local_streams_sent (GabbleMediaSession *session)
       
  1596 {
       
  1597   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1598   guint i;
       
  1599 
       
  1600   for (i = 0; i < priv->streams->len; i++)
       
  1601     {
       
  1602       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1603 
       
  1604       if (stream->initiator == INITIATOR_REMOTE)
       
  1605         continue;
       
  1606 
       
  1607       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "marking local stream %s as signalled", stream->name);
       
  1608 
       
  1609       g_object_set (stream, "signalling-state", STREAM_SIG_STATE_SENT, NULL);
       
  1610     }
       
  1611 }
       
  1612 
       
  1613 static void
       
  1614 _mark_local_streams_acked (GabbleMediaSession *session)
       
  1615 {
       
  1616   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1617   guint i;
       
  1618 
       
  1619   for (i = 0; i < priv->streams->len; i++)
       
  1620     {
       
  1621       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1622 
       
  1623       if (stream->initiator == INITIATOR_REMOTE)
       
  1624         continue;
       
  1625 
       
  1626       if (stream->signalling_state != STREAM_SIG_STATE_SENT)
       
  1627         continue;
       
  1628 
       
  1629       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "marking local stream %s as acknowledged", stream->name);
       
  1630 
       
  1631       g_object_set (stream,
       
  1632           "signalling-state", STREAM_SIG_STATE_ACKNOWLEDGED,
       
  1633           NULL);
       
  1634     }
       
  1635 }
       
  1636 
       
  1637 static void
       
  1638 _set_remote_streams_playing (GabbleMediaSession *session)
       
  1639 {
       
  1640   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1641   guint i;
       
  1642 
       
  1643   for (i = 0; i < priv->streams->len; i++)
       
  1644     {
       
  1645       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1646 
       
  1647       if (stream->initiator == INITIATOR_LOCAL)
       
  1648         continue;
       
  1649 
       
  1650       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "setting remote stream %s as playing", stream->name);
       
  1651 
       
  1652       g_object_set (stream, "playing", TRUE, NULL);
       
  1653     }
       
  1654 }
       
  1655 
       
  1656 static const gchar *_direction_to_senders (GabbleMediaSession *,
       
  1657     TpMediaStreamDirection);
       
  1658 
       
  1659 static void
       
  1660 _add_content_descriptions_one (GabbleMediaSession *session,
       
  1661                                GabbleMediaStream *stream,
       
  1662                                LmMessageNode *session_node)
       
  1663 {
       
  1664   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1665   LmMessageNode *content_node;
       
  1666 
       
  1667   if (priv->mode == MODE_GOOGLE)
       
  1668     {
       
  1669       content_node = session_node;
       
  1670     }
       
  1671   else
       
  1672     {
       
  1673       TpMediaStreamDirection direction;
       
  1674       TpMediaStreamPendingSend pending_send;
       
  1675 
       
  1676       content_node = _gabble_media_stream_add_content_node (stream,
       
  1677           session_node);
       
  1678 
       
  1679       direction = COMBINED_DIRECTION_GET_DIRECTION (stream->combined_direction);
       
  1680       pending_send = COMBINED_DIRECTION_GET_PENDING_SEND
       
  1681         (stream->combined_direction);
       
  1682 
       
  1683       /* if we have a pending local send flag set, the signalled (ie understood
       
  1684        * by both ends) direction of the stream is assuming that we are actually
       
  1685        * sending, so we should OR that into the direction before deciding what
       
  1686        * to signal the stream with. we don't need to consider pending remote
       
  1687        * send because it doesn't happen in Jingle */
       
  1688 
       
  1689       if ((pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0)
       
  1690         direction |= TP_MEDIA_STREAM_DIRECTION_SEND;
       
  1691 
       
  1692       if (direction != TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)
       
  1693         {
       
  1694           const gchar *senders;
       
  1695           senders = _direction_to_senders (session, direction);
       
  1696           lm_message_node_set_attribute (content_node, "senders", senders);
       
  1697         }
       
  1698     }
       
  1699 
       
  1700   _gabble_media_stream_content_node_add_description (stream, content_node);
       
  1701 
       
  1702   _gabble_media_stream_content_node_add_transport (stream, content_node);
       
  1703 }
       
  1704 
       
  1705 static void
       
  1706 _add_content_descriptions (GabbleMediaSession *session,
       
  1707                            LmMessageNode *session_node,
       
  1708                            JingleInitiator stream_initiator)
       
  1709 {
       
  1710   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1711   guint i;
       
  1712 
       
  1713   for (i = 0; i < priv->streams->len; i++)
       
  1714     {
       
  1715       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1716 
       
  1717       if (stream->initiator != stream_initiator)
       
  1718         {
       
  1719           _gabble_media_session_debug (session, DEBUG_MSG_INFO,
       
  1720               "not adding content description for %s stream %s",
       
  1721               stream->initiator == INITIATOR_LOCAL ? "local" : "remote",
       
  1722               stream->name);
       
  1723           continue;
       
  1724         }
       
  1725 
       
  1726       _add_content_descriptions_one (session, stream, session_node);
       
  1727     }
       
  1728 }
       
  1729 
       
  1730 static LmHandlerResult
       
  1731 accept_msg_reply_cb (GabbleConnection *conn,
       
  1732                      LmMessage *sent_msg,
       
  1733                      LmMessage *reply_msg,
       
  1734                      GObject *object,
       
  1735                      gpointer user_data)
       
  1736 {
       
  1737   GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object);
       
  1738   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1739   guint i;
       
  1740 
       
  1741   MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (session, "accept failed");
       
  1742 
       
  1743   for (i = 0; i < priv->streams->len; i++)
       
  1744     {
       
  1745       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1746 
       
  1747       if (stream->initiator == INITIATOR_LOCAL)
       
  1748         continue;
       
  1749 
       
  1750       _gabble_media_stream_update_sending (stream, TRUE);
       
  1751     }
       
  1752 
       
  1753   g_object_set (session, "state", JS_STATE_ACTIVE, NULL);
       
  1754 
       
  1755   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1756 }
       
  1757 
       
  1758 static gboolean
       
  1759 _stream_not_ready_for_accept (GabbleMediaSession *session,
       
  1760                               GabbleMediaStream *stream)
       
  1761 {
       
  1762   /* locally initiated streams shouldn't delay acceptance */
       
  1763   if (stream->initiator == INITIATOR_LOCAL)
       
  1764     return FALSE;
       
  1765 
       
  1766   if (!stream->got_local_codecs)
       
  1767     {
       
  1768       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "stream %s does not yet have local codecs",
       
  1769           stream->name);
       
  1770 
       
  1771       return TRUE;
       
  1772     }
       
  1773 
       
  1774   if (stream->connection_state != TP_MEDIA_STREAM_STATE_CONNECTED)
       
  1775     {
       
  1776       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "stream %s is not yet connected", stream->name);
       
  1777 
       
  1778       return TRUE;
       
  1779     }
       
  1780 
       
  1781   return FALSE;
       
  1782 }
       
  1783 
       
  1784 static void
       
  1785 try_session_accept (GabbleMediaSession *session)
       
  1786 {
       
  1787   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1788   LmMessage *msg;
       
  1789   LmMessageNode *session_node;
       
  1790   const gchar *action;
       
  1791   guint i;
       
  1792 
       
  1793   if (priv->state < JS_STATE_ACTIVE && !priv->locally_accepted)
       
  1794     {
       
  1795       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "not sending accept yet, waiting for local "
       
  1796           "user to accept call");
       
  1797       return;
       
  1798     }
       
  1799 
       
  1800   for (i = 0; i < priv->streams->len; i++)
       
  1801     {
       
  1802       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1803 
       
  1804       if (_stream_not_ready_for_accept (session, stream))
       
  1805         {
       
  1806           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "not sending accept yet, found a stream "
       
  1807               "which was not yet connected or was missing local codecs");
       
  1808           return;
       
  1809         }
       
  1810     }
       
  1811 
       
  1812   if (priv->mode == MODE_GOOGLE)
       
  1813     action = "accept";
       
  1814   else
       
  1815     action = "session-accept";
       
  1816 
       
  1817   /* construct a session acceptance message */
       
  1818   msg = _gabble_media_session_message_new (session, action, &session_node);
       
  1819 
       
  1820   /* only accept REMOTE streams; any LOCAL streams were added by the local
       
  1821    * user before accepting and should be signalled after the accept */
       
  1822   _add_content_descriptions (session, session_node, INITIATOR_REMOTE);
       
  1823 
       
  1824   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "sending jingle session action \"%s\" to peer",
       
  1825       action);
       
  1826 
       
  1827   /* send the final acceptance message */
       
  1828   _gabble_connection_send_with_reply (priv->conn, msg, accept_msg_reply_cb,
       
  1829                                       G_OBJECT (session), NULL, NULL);
       
  1830 
       
  1831   lm_message_unref (msg);
       
  1832 
       
  1833   /* set remote streams playing */
       
  1834   _set_remote_streams_playing (session);
       
  1835 
       
  1836   g_object_set (session, "state", JS_STATE_PENDING_ACCEPT_SENT, NULL);
       
  1837 }
       
  1838 
       
  1839 static LmHandlerResult
       
  1840 content_accept_msg_reply_cb (GabbleConnection *conn,
       
  1841                              LmMessage *sent_msg,
       
  1842                              LmMessage *reply_msg,
       
  1843                              GObject *object,
       
  1844                              gpointer user_data)
       
  1845 {
       
  1846   GabbleMediaSession *session = GABBLE_MEDIA_SESSION (user_data);
       
  1847   GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object);
       
  1848 
       
  1849   if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
       
  1850     {
       
  1851       _gabble_media_session_debug (session, DEBUG_MSG_ERROR, "content-accept failed; removing stream");
       
  1852       NODE_DEBUG (sent_msg->node, "message sent");
       
  1853       NODE_DEBUG (reply_msg->node, "message reply");
       
  1854 
       
  1855       _gabble_media_session_remove_streams (session, &stream, 1);
       
  1856 
       
  1857       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1858     }
       
  1859 
       
  1860   _gabble_media_stream_update_sending (stream, TRUE);
       
  1861 
       
  1862   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1863 }
       
  1864 
       
  1865 static void
       
  1866 try_content_accept (GabbleMediaSession *session,
       
  1867                     GabbleMediaStream *stream)
       
  1868 {
       
  1869   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1870   LmMessage *msg;
       
  1871   LmMessageNode *session_node;
       
  1872 
       
  1873   g_assert (priv->state == JS_STATE_ACTIVE);
       
  1874   g_assert (priv->mode == MODE_JINGLE);
       
  1875 
       
  1876   if (_stream_not_ready_for_accept (session, stream))
       
  1877     {
       
  1878       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "not sending content-accept yet, stream %s "
       
  1879           "is disconnected or missing local codecs", stream->name);
       
  1880       return;
       
  1881     }
       
  1882 
       
  1883   /* send a content acceptance message */
       
  1884   msg = _gabble_media_session_message_new (session, "content-accept",
       
  1885       &session_node);
       
  1886 
       
  1887   _add_content_descriptions_one (session, stream, session_node);
       
  1888 
       
  1889   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "sending jingle session action \"content-accept\" "
       
  1890       "to peer for stream %s", stream->name);
       
  1891 
       
  1892   _gabble_connection_send_with_reply (priv->conn, msg,
       
  1893       content_accept_msg_reply_cb, G_OBJECT (stream), session, NULL);
       
  1894 
       
  1895   lm_message_unref (msg);
       
  1896 
       
  1897   /* set stream playing */
       
  1898   g_object_set (stream, "playing", TRUE, NULL);
       
  1899 }
       
  1900 
       
  1901 static LmHandlerResult
       
  1902 initiate_msg_reply_cb (GabbleConnection *conn,
       
  1903                        LmMessage *sent_msg,
       
  1904                        LmMessage *reply_msg,
       
  1905                        GObject *object,
       
  1906                        gpointer user_data)
       
  1907 {
       
  1908   GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object);
       
  1909 
       
  1910   MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (session, "initiate failed");
       
  1911 
       
  1912   g_object_set (session, "state", JS_STATE_PENDING_INITIATED, NULL);
       
  1913 
       
  1914   /* mark all of the streams that we sent in the initiate as acknowledged */
       
  1915   _mark_local_streams_acked (session);
       
  1916 
       
  1917   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1918 }
       
  1919 
       
  1920 static gboolean
       
  1921 _stream_not_ready_for_initiate (GabbleMediaSession *session,
       
  1922                                 GabbleMediaStream *stream)
       
  1923 {
       
  1924   if (!stream->got_local_codecs)
       
  1925     {
       
  1926       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "stream %s does not yet have local codecs",
       
  1927           stream->name);
       
  1928 
       
  1929       return TRUE;
       
  1930     }
       
  1931 
       
  1932   return FALSE;
       
  1933 }
       
  1934 
       
  1935 static void
       
  1936 try_session_initiate (GabbleMediaSession *session)
       
  1937 {
       
  1938   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  1939   LmMessage *msg;
       
  1940   LmMessageNode *session_node;
       
  1941   const gchar *action;
       
  1942   guint i;
       
  1943 
       
  1944   for (i = 0; i < priv->streams->len; i++)
       
  1945     {
       
  1946       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1947 
       
  1948       if (_stream_not_ready_for_initiate (session, stream))
       
  1949         {
       
  1950           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "not sending initiate yet, found a stream "
       
  1951             "which was missing local codecs");
       
  1952           return;
       
  1953         }
       
  1954     }
       
  1955 
       
  1956   if (priv->mode == MODE_GOOGLE)
       
  1957       action = "initiate";
       
  1958   else
       
  1959       action = "session-initiate";
       
  1960 
       
  1961   msg = _gabble_media_session_message_new (session, action, &session_node);
       
  1962 
       
  1963   _add_content_descriptions (session, session_node, INITIATOR_LOCAL);
       
  1964 
       
  1965   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "sending jingle action \"%s\" to peer", action);
       
  1966 
       
  1967   _gabble_connection_send_with_reply (priv->conn, msg, initiate_msg_reply_cb,
       
  1968                                       G_OBJECT (session), NULL, NULL);
       
  1969 
       
  1970   lm_message_unref (msg);
       
  1971 
       
  1972   /* mark local streams as sent (so that eg candidates will be sent) */
       
  1973   _mark_local_streams_sent (session);
       
  1974 
       
  1975   g_object_set (session, "state", JS_STATE_PENDING_INITIATE_SENT, NULL);
       
  1976 }
       
  1977 
       
  1978 static LmHandlerResult
       
  1979 content_add_msg_reply_cb (GabbleConnection *conn,
       
  1980                           LmMessage *sent_msg,
       
  1981                           LmMessage *reply_msg,
       
  1982                           GObject *object,
       
  1983                           gpointer user_data)
       
  1984 {
       
  1985   GabbleMediaSession *session = GABBLE_MEDIA_SESSION (user_data);
       
  1986   GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object);
       
  1987 
       
  1988   if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
       
  1989     {
       
  1990       if (session->initiator == INITIATOR_REMOTE &&
       
  1991           stream->signalling_state == STREAM_SIG_STATE_ACKNOWLEDGED)
       
  1992         {
       
  1993           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "ignoring content-add failure, stream has "
       
  1994               "been successfully created by the session initiator");
       
  1995         }
       
  1996       else
       
  1997         {
       
  1998           _gabble_media_session_debug (session, DEBUG_MSG_ERROR, "content-add failed; removing stream");
       
  1999           NODE_DEBUG (sent_msg->node, "message sent");
       
  2000           NODE_DEBUG (reply_msg->node, "message reply");
       
  2001 
       
  2002           _gabble_media_stream_close (stream);
       
  2003         }
       
  2004     }
       
  2005   else
       
  2006     {
       
  2007       if (stream->signalling_state == STREAM_SIG_STATE_SENT)
       
  2008         {
       
  2009           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "content-add succeeded, marking stream as "
       
  2010               "ACKNOWLEDGED");
       
  2011 
       
  2012           g_object_set (stream,
       
  2013               "signalling-state", STREAM_SIG_STATE_ACKNOWLEDGED,
       
  2014               NULL);
       
  2015         }
       
  2016       else
       
  2017         {
       
  2018           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "content-add succeeded, but not marking"
       
  2019               "stream as ACKNOWLEDGED, it's in state %d",
       
  2020               stream->signalling_state);
       
  2021         }
       
  2022     }
       
  2023 
       
  2024   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  2025 }
       
  2026 
       
  2027 static void
       
  2028 do_content_add (GabbleMediaSession *session,
       
  2029                 GabbleMediaStream *stream)
       
  2030 {
       
  2031   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2032   LmMessage *msg;
       
  2033   LmMessageNode *session_node;
       
  2034 
       
  2035   g_assert (priv->state == JS_STATE_ACTIVE);
       
  2036   g_assert (priv->mode == MODE_JINGLE);
       
  2037 
       
  2038   if (_stream_not_ready_for_initiate (session, stream))
       
  2039     {
       
  2040       _gabble_media_session_debug (session, DEBUG_MSG_ERROR, "trying to send content-add for stream %s "
       
  2041           "but we have no local codecs. what?!", stream->name);
       
  2042       g_assert_not_reached ();
       
  2043       return;
       
  2044     }
       
  2045 
       
  2046   msg = _gabble_media_session_message_new (session, "content-add",
       
  2047       &session_node);
       
  2048 
       
  2049   _add_content_descriptions_one (session, stream, session_node);
       
  2050 
       
  2051   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "sending jingle action \"content-add\" to peer for "
       
  2052       "stream %s", stream->name);
       
  2053 
       
  2054   _gabble_connection_send_with_reply (priv->conn, msg,
       
  2055       content_add_msg_reply_cb, G_OBJECT (stream), session, NULL);
       
  2056 
       
  2057   lm_message_unref (msg);
       
  2058 
       
  2059   /* mark stream as sent */
       
  2060   g_object_set (stream, "signalling-state", STREAM_SIG_STATE_SENT, NULL);
       
  2061 }
       
  2062 
       
  2063 static void
       
  2064 stream_connection_state_changed_cb (GabbleMediaStream *stream,
       
  2065                                     GParamSpec *param,
       
  2066                                     GabbleMediaSession *session)
       
  2067 {
       
  2068   GabbleMediaSessionPrivate *priv;
       
  2069 
       
  2070   g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
  2071 
       
  2072   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2073 
       
  2074   if (stream->connection_state != TP_MEDIA_STREAM_STATE_CONNECTED)
       
  2075     return;
       
  2076 
       
  2077   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "stream %s has gone connected", stream->name);
       
  2078 
       
  2079   if (stream->playing)
       
  2080     {
       
  2081       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "doing nothing, stream is already playing");
       
  2082       return;
       
  2083     }
       
  2084 
       
  2085   /* after session is active, we do things per-stream with content-* actions */
       
  2086   if (priv->state < JS_STATE_ACTIVE)
       
  2087     {
       
  2088       /* send a session accept if the session was initiated by the peer */
       
  2089       if (session->initiator == INITIATOR_REMOTE)
       
  2090         {
       
  2091           try_session_accept (session);
       
  2092         }
       
  2093       else
       
  2094         {
       
  2095           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "session initiated by us, so we're not "
       
  2096               "going to consider sending an accept");
       
  2097         }
       
  2098     }
       
  2099   else
       
  2100     {
       
  2101       /* send a content accept if the stream was added by the peer */
       
  2102       if (stream->initiator == INITIATOR_REMOTE)
       
  2103         {
       
  2104           try_content_accept (session, stream);
       
  2105         }
       
  2106       else
       
  2107         {
       
  2108           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "stream added by us, so we're not going "
       
  2109               "to send an accept");
       
  2110         }
       
  2111     }
       
  2112 }
       
  2113 
       
  2114 static void
       
  2115 stream_got_local_codecs_changed_cb (GabbleMediaStream *stream,
       
  2116                                     GParamSpec *param,
       
  2117                                     GabbleMediaSession *session)
       
  2118 {
       
  2119   GabbleMediaSessionPrivate *priv;
       
  2120 
       
  2121   g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
  2122 
       
  2123   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2124 
       
  2125   if (!stream->got_local_codecs)
       
  2126     return;
       
  2127 
       
  2128   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "stream %s has got local codecs", stream->name);
       
  2129 
       
  2130   if (stream->playing)
       
  2131     {
       
  2132       _gabble_media_session_debug (session, DEBUG_MSG_ERROR, "stream was already playing and we got local "
       
  2133           "codecs. what?!");
       
  2134       g_assert_not_reached ();
       
  2135       return;
       
  2136     }
       
  2137 
       
  2138   /* after session is active, we do things per-stream with content-* actions */
       
  2139   if (priv->state < JS_STATE_ACTIVE)
       
  2140     {
       
  2141       if (session->initiator == INITIATOR_REMOTE)
       
  2142         {
       
  2143           if (priv->state < JS_STATE_PENDING_ACCEPT_SENT)
       
  2144             {
       
  2145               try_session_accept (session);
       
  2146             }
       
  2147           else
       
  2148             {
       
  2149               _gabble_media_session_debug (session, DEBUG_MSG_INFO, "stream added after sending accept; "
       
  2150                   "not doing content-add until remote end acknowledges");
       
  2151             }
       
  2152         }
       
  2153       else
       
  2154         {
       
  2155           if (priv->state < JS_STATE_PENDING_INITIATE_SENT)
       
  2156             {
       
  2157               try_session_initiate (session);
       
  2158             }
       
  2159           else
       
  2160             {
       
  2161               _gabble_media_session_debug (session, DEBUG_MSG_INFO, "stream added after sending initiate; "
       
  2162                   "not doing content-add until remote end accepts");
       
  2163             }
       
  2164         }
       
  2165     }
       
  2166   else
       
  2167     {
       
  2168       if (stream->initiator == INITIATOR_REMOTE)
       
  2169         {
       
  2170           try_content_accept (session, stream);
       
  2171         }
       
  2172       else
       
  2173         {
       
  2174           do_content_add (session, stream);
       
  2175         }
       
  2176     }
       
  2177 }
       
  2178 
       
  2179 static gchar *
       
  2180 get_jid_for_contact (GabbleMediaSession *session,
       
  2181                      GabbleHandle handle)
       
  2182 {
       
  2183   GabbleMediaSessionPrivate *priv;
       
  2184   const gchar *base_jid;
       
  2185   GabbleHandle self;
       
  2186 
       
  2187   g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
  2188 
       
  2189   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2190   self = priv->conn->self_handle;
       
  2191 
       
  2192   base_jid = gabble_handle_inspect (priv->conn->handles,
       
  2193       TP_HANDLE_TYPE_CONTACT, handle);
       
  2194   g_assert (base_jid != NULL);
       
  2195 
       
  2196   if (handle == self)
       
  2197     {
       
  2198       gchar *resource, *ret;
       
  2199       g_object_get (priv->conn, "resource", &resource, NULL);
       
  2200       g_assert (resource != NULL);
       
  2201       ret = g_strdup_printf ("%s/%s", base_jid, resource);
       
  2202       g_free (resource);
       
  2203       return ret;
       
  2204     }
       
  2205   else
       
  2206     {
       
  2207       g_assert (priv->peer_resource != NULL);
       
  2208       return g_strdup_printf ("%s/%s", base_jid, priv->peer_resource);
       
  2209     }
       
  2210 }
       
  2211 
       
  2212 LmMessage *
       
  2213 _gabble_media_session_message_new (GabbleMediaSession *session,
       
  2214                                    const gchar *action,
       
  2215                                    LmMessageNode **session_node)
       
  2216 {
       
  2217   GabbleMediaSessionPrivate *priv;
       
  2218   LmMessage *msg;
       
  2219   LmMessageNode *iq_node, *node;
       
  2220   gchar *peer_jid, *initiator_jid;
       
  2221   GabbleHandle initiator_handle;
       
  2222   const gchar *element, *xmlns;
       
  2223 
       
  2224   g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
  2225 
       
  2226   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2227 
       
  2228   peer_jid = get_jid_for_contact (session, priv->peer);
       
  2229 
       
  2230   msg = lm_message_new_with_sub_type (
       
  2231       peer_jid,
       
  2232       LM_MESSAGE_TYPE_IQ,
       
  2233       LM_MESSAGE_SUB_TYPE_SET);
       
  2234 
       
  2235   g_free (peer_jid);
       
  2236 
       
  2237   iq_node = lm_message_get_node (msg);
       
  2238 
       
  2239   if (priv->mode == MODE_GOOGLE)
       
  2240     element = "session";
       
  2241   else
       
  2242     element = "jingle";
       
  2243 
       
  2244   if (session->initiator == INITIATOR_LOCAL)
       
  2245     initiator_handle = priv->conn->self_handle;
       
  2246   else
       
  2247     initiator_handle = priv->peer;
       
  2248 
       
  2249   node = lm_message_node_add_child (iq_node, element, NULL);
       
  2250   initiator_jid = get_jid_for_contact (session, initiator_handle);
       
  2251 
       
  2252   lm_message_node_set_attributes (node,
       
  2253       (priv->mode == MODE_GOOGLE) ? "id" : "sid", priv->id,
       
  2254       (priv->mode == MODE_GOOGLE) ? "type" : "action", action,
       
  2255       "initiator", initiator_jid,
       
  2256       NULL);
       
  2257 
       
  2258   if (priv->mode == MODE_GOOGLE)
       
  2259     xmlns = NS_GOOGLE_SESSION;
       
  2260   else
       
  2261     xmlns = NS_JINGLE;
       
  2262 
       
  2263   lm_message_node_set_attribute (node, "xmlns", xmlns);
       
  2264   g_free (initiator_jid);
       
  2265 
       
  2266   if (session_node)
       
  2267     *session_node = node;
       
  2268 
       
  2269   return msg;
       
  2270 }
       
  2271 
       
  2272 void
       
  2273 _gabble_media_session_accept (GabbleMediaSession *session)
       
  2274 {
       
  2275   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2276   guint i;
       
  2277 
       
  2278   priv->locally_accepted = TRUE;
       
  2279 
       
  2280   /* accept any local pending sends */
       
  2281   for (i = 0; i < priv->streams->len; i++)
       
  2282     {
       
  2283       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  2284       CombinedStreamDirection combined_dir = stream->combined_direction;
       
  2285       TpMediaStreamDirection current_dir;
       
  2286       TpMediaStreamPendingSend pending_send;
       
  2287 
       
  2288       current_dir = COMBINED_DIRECTION_GET_DIRECTION (combined_dir);
       
  2289       pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined_dir);
       
  2290 
       
  2291       if ((pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0)
       
  2292         {
       
  2293           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "accepting pending local send on stream %s",
       
  2294               stream->name);
       
  2295 
       
  2296           current_dir |= TP_MEDIA_STREAM_DIRECTION_SEND;
       
  2297           pending_send &= ~TP_MEDIA_STREAM_PENDING_LOCAL_SEND;
       
  2298           combined_dir = MAKE_COMBINED_DIRECTION (current_dir, pending_send);
       
  2299           g_object_set (stream, "combined-direction", combined_dir, NULL);
       
  2300           _gabble_media_stream_update_sending (stream, FALSE);
       
  2301         }
       
  2302     }
       
  2303 
       
  2304   try_session_accept (session);
       
  2305 }
       
  2306 
       
  2307 static LmHandlerResult
       
  2308 content_remove_msg_reply_cb (GabbleConnection *conn,
       
  2309                              LmMessage *sent_msg,
       
  2310                              LmMessage *reply_msg,
       
  2311                              GObject *object,
       
  2312                              gpointer user_data)
       
  2313 {
       
  2314   GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object);
       
  2315   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2316   GPtrArray *removing = (GPtrArray *) user_data;
       
  2317   guint i;
       
  2318 
       
  2319   MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (session, "stream removal failed");
       
  2320 
       
  2321   for (i = 0; i < removing->len; i++)
       
  2322     destroy_media_stream (session,
       
  2323         GABBLE_MEDIA_STREAM (g_ptr_array_index (removing, i)));
       
  2324 
       
  2325   g_ptr_array_remove_fast (priv->remove_requests, removing);
       
  2326   g_ptr_array_free (removing, TRUE);
       
  2327 
       
  2328   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  2329 }
       
  2330 
       
  2331 void
       
  2332 _gabble_media_session_remove_streams (GabbleMediaSession *session,
       
  2333                                       GabbleMediaStream **streams,
       
  2334                                       guint len)
       
  2335 {
       
  2336   GabbleMediaSessionPrivate *priv;
       
  2337   LmMessage *msg = NULL;
       
  2338   LmMessageNode *session_node;
       
  2339   GPtrArray *removing = NULL;
       
  2340   guint i;
       
  2341 
       
  2342   g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
  2343 
       
  2344   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2345 
       
  2346   /* end the session if there'd be no streams left after reducing it */
       
  2347   if (_count_non_removing_streams (session) == len)
       
  2348     {
       
  2349       _gabble_media_session_terminate (session, INITIATOR_LOCAL,
       
  2350           TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
       
  2351       return;
       
  2352     }
       
  2353 
       
  2354   /* construct a remove message if we're in a state greater than CREATED (ie
       
  2355    * something has been sent/received about this session) */
       
  2356   if (priv->state > JS_STATE_PENDING_CREATED)
       
  2357     {
       
  2358       msg = _gabble_media_session_message_new (session, "content-remove",
       
  2359           &session_node);
       
  2360       removing = g_ptr_array_sized_new (len);
       
  2361     }
       
  2362 
       
  2363   /* right, remove them */
       
  2364   for (i = 0; i < len; i++)
       
  2365     {
       
  2366       GabbleMediaStream *stream = streams[i];
       
  2367 
       
  2368       switch (stream->signalling_state)
       
  2369         {
       
  2370         case STREAM_SIG_STATE_NEW:
       
  2371           destroy_media_stream (session, stream);
       
  2372           break;
       
  2373         case STREAM_SIG_STATE_SENT:
       
  2374         case STREAM_SIG_STATE_ACKNOWLEDGED:
       
  2375           {
       
  2376             LmMessageNode *content_node;
       
  2377 
       
  2378             g_assert (msg != NULL);
       
  2379             g_assert (removing != NULL);
       
  2380 
       
  2381             content_node = _gabble_media_stream_add_content_node (stream,
       
  2382                 session_node);
       
  2383 
       
  2384             g_object_set (stream,
       
  2385                 "playing", FALSE,
       
  2386                 "signalling-state", STREAM_SIG_STATE_REMOVING,
       
  2387                 NULL);
       
  2388 
       
  2389             /* close the stream now, but don't forget about it until the
       
  2390              * removal message is acknowledged, since we need to be able to
       
  2391              * detect content-remove cross-talk */
       
  2392             _gabble_media_stream_close (stream);
       
  2393             g_ptr_array_add (removing, stream);
       
  2394           }
       
  2395           break;
       
  2396         case STREAM_SIG_STATE_REMOVING:
       
  2397           break;
       
  2398         }
       
  2399     }
       
  2400 
       
  2401   /* send the remove message if necessary */
       
  2402   if (msg != NULL)
       
  2403     {
       
  2404       if (removing->len > 0)
       
  2405         {
       
  2406           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "sending jingle session action "
       
  2407               "\"content-remove\" to peer");
       
  2408 
       
  2409           _gabble_connection_send_with_reply (priv->conn, msg,
       
  2410               content_remove_msg_reply_cb, G_OBJECT (session), removing, NULL);
       
  2411 
       
  2412           g_ptr_array_add (priv->remove_requests, removing);
       
  2413         }
       
  2414       else
       
  2415         {
       
  2416           g_ptr_array_free (removing, TRUE);
       
  2417         }
       
  2418 
       
  2419       lm_message_unref (msg);
       
  2420     }
       
  2421   else
       
  2422     {
       
  2423       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "not sending jingle session action "
       
  2424           "\"content-remove\" to peer, no initiates or adds sent for "
       
  2425           "these streams");
       
  2426     }
       
  2427 }
       
  2428 
       
  2429 /* for when you want the reply to be removed from
       
  2430  * the handler chain, but don't care what it is */
       
  2431 static LmHandlerResult
       
  2432 ignore_reply_cb (GabbleConnection *conn,
       
  2433                  LmMessage *sent_msg,
       
  2434                  LmMessage *reply_msg,
       
  2435                  GObject *object,
       
  2436                  gpointer user_data)
       
  2437 {
       
  2438   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  2439 }
       
  2440 
       
  2441 static void
       
  2442 send_reject_message (GabbleMediaSession *session)
       
  2443 {
       
  2444   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2445   LmMessage *msg;
       
  2446   LmMessageNode *session_node;
       
  2447 
       
  2448   /* this should only happen in google mode, and we should only arrive in that
       
  2449    * mode when we've ended up talking to a resource that doesn't support
       
  2450    * jingle */
       
  2451   g_assert (priv->mode == MODE_GOOGLE);
       
  2452   g_assert (priv->peer_resource != NULL);
       
  2453 
       
  2454   /* construct a session terminate message */
       
  2455   msg = _gabble_media_session_message_new (session, "reject", &session_node);
       
  2456 
       
  2457   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "sending jingle session action \"reject\" to peer");
       
  2458 
       
  2459   /* send it */
       
  2460   _gabble_connection_send_with_reply (priv->conn, msg, ignore_reply_cb,
       
  2461                                       G_OBJECT (session), NULL, NULL);
       
  2462 
       
  2463   lm_message_unref (msg);
       
  2464 }
       
  2465 
       
  2466 static void
       
  2467 send_terminate_message (GabbleMediaSession *session)
       
  2468 {
       
  2469   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2470   const gchar *action;
       
  2471   LmMessage *msg;
       
  2472   LmMessageNode *session_node;
       
  2473 
       
  2474   /* construct a session terminate message */
       
  2475   if (priv->mode == MODE_GOOGLE)
       
  2476     action = "terminate";
       
  2477   else
       
  2478     action = "session-terminate";
       
  2479 
       
  2480   msg = _gabble_media_session_message_new (session, action, &session_node);
       
  2481 
       
  2482   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "sending jingle session action \"%s\" to peer",
       
  2483       action);
       
  2484 
       
  2485   /* send it */
       
  2486   _gabble_connection_send_with_reply (priv->conn, msg, ignore_reply_cb,
       
  2487                                       G_OBJECT (session), NULL, NULL);
       
  2488 
       
  2489   lm_message_unref (msg);
       
  2490 }
       
  2491 
       
  2492 void
       
  2493 _gabble_media_session_terminate (GabbleMediaSession *session,
       
  2494                                  JingleInitiator who,
       
  2495                                  TpChannelGroupChangeReason why)
       
  2496 {
       
  2497   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2498   GabbleHandle actor;
       
  2499 
       
  2500   if (priv->state == JS_STATE_ENDED)
       
  2501     return;
       
  2502 
       
  2503   if (who == INITIATOR_REMOTE)
       
  2504     {
       
  2505       actor = priv->peer;
       
  2506     }
       
  2507   else
       
  2508     {
       
  2509       actor = priv->conn->self_handle;
       
  2510 
       
  2511       /* Need to tell them that it's all over. */
       
  2512 
       
  2513       /* Jingle doesn't have a "reject" action; a termination before an
       
  2514        * acceptance indicates that the call has been declined */
       
  2515 
       
  2516       if (session->initiator == INITIATOR_REMOTE &&
       
  2517           priv->state == JS_STATE_PENDING_INITIATED &&
       
  2518           priv->mode == MODE_GOOGLE)
       
  2519         {
       
  2520           send_reject_message (session);
       
  2521         }
       
  2522 
       
  2523       /* if we're still in CREATED, then we've not sent or received any
       
  2524        * messages about this session yet, so no terminate is necessary */
       
  2525       else if (priv->state > JS_STATE_PENDING_CREATED)
       
  2526         {
       
  2527           send_terminate_message (session);
       
  2528         }
       
  2529 
       
  2530       while (priv->streams->len > 0)
       
  2531         destroy_media_stream (session, g_ptr_array_index (priv->streams, 0));
       
  2532     }
       
  2533 
       
  2534   priv->terminated = TRUE;
       
  2535   g_object_set (session, "state", JS_STATE_ENDED, NULL);
       
  2536   g_signal_emit (session, signals[TERMINATED], 0, actor, why);
       
  2537 }
       
  2538 
       
  2539 #if _GMS_DEBUG_LEVEL
       
  2540 void
       
  2541 _gabble_media_session_debug (GabbleMediaSession *session,
       
  2542                              DebugMessageType type,
       
  2543                              const gchar *format, ...)
       
  2544 {
       
  2545   if (DEBUGGING)
       
  2546     {
       
  2547       va_list list;
       
  2548       gchar buf[512];
       
  2549       GabbleMediaSessionPrivate *priv;
       
  2550       time_t curtime;
       
  2551       struct tm *loctime;
       
  2552       gchar stamp[10];
       
  2553       const gchar *type_str;
       
  2554 
       
  2555       g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
  2556 
       
  2557       priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2558 
       
  2559       curtime = time (NULL);
       
  2560       loctime = localtime (&curtime);
       
  2561 
       
  2562       strftime (stamp, sizeof (stamp), "%T", loctime);
       
  2563 
       
  2564       va_start (list, format);
       
  2565 
       
  2566       vsnprintf (buf, sizeof (buf), format, list);
       
  2567 
       
  2568       va_end (list);
       
  2569 
       
  2570       switch (type) {
       
  2571         case DEBUG_MSG_INFO:
       
  2572           type_str = ANSI_BOLD_ON ANSI_FG_WHITE;
       
  2573           break;
       
  2574         case DEBUG_MSG_DUMP:
       
  2575           type_str = ANSI_BOLD_ON ANSI_FG_GREEN;
       
  2576           break;
       
  2577         case DEBUG_MSG_WARNING:
       
  2578           type_str = ANSI_BOLD_ON ANSI_FG_YELLOW;
       
  2579           break;
       
  2580         case DEBUG_MSG_ERROR:
       
  2581           type_str = ANSI_BOLD_ON ANSI_FG_WHITE ANSI_BG_RED;
       
  2582           break;
       
  2583         case DEBUG_MSG_EVENT:
       
  2584           type_str = ANSI_BOLD_ON ANSI_FG_CYAN;
       
  2585           break;
       
  2586         default:
       
  2587           g_assert_not_reached ();
       
  2588           return;
       
  2589       }
       
  2590 
       
  2591       g_message ("[%s%s%s] %s%-26s%s %s%s%s\n",
       
  2592           ANSI_BOLD_ON ANSI_FG_WHITE,
       
  2593           stamp,
       
  2594           ANSI_RESET,
       
  2595           session_states[priv->state].attributes,
       
  2596           session_states[priv->state].name,
       
  2597           ANSI_RESET,
       
  2598           type_str,
       
  2599           buf,
       
  2600           ANSI_RESET);
       
  2601 
       
  2602       fflush (stdout);
       
  2603     }
       
  2604 }
       
  2605 
       
  2606 #endif /* _GMS_DEBUG_LEVEL */
       
  2607 
       
  2608 static const gchar *
       
  2609 _name_stream (GabbleMediaSession *session,
       
  2610               TpMediaStreamType media_type)
       
  2611 {
       
  2612   GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2613   
       
  2614 #ifndef EMULATOR  
       
  2615   static gchar ret_sess[MAX_STREAM_NAME_LEN] = GTALK_STREAM_NAME;
       
  2616 #endif
       
  2617 
       
  2618   if (priv->mode != MODE_GOOGLE)
       
  2619     {
       
  2620       guint i = 1;
       
  2621 
       
  2622       do {
       
  2623           g_snprintf (ret_sess, MAX_STREAM_NAME_LEN, "%s%u",
       
  2624               media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video",
       
  2625               i++);
       
  2626 
       
  2627           /* even though we now have seperate namespaces for local and remote,
       
  2628            * actually check in both so that we can still support clients which
       
  2629            * have 1 namespace (such as our older selves :D) */
       
  2630           if (_lookup_stream_by_name_and_initiator (session, ret_sess,
       
  2631                 INITIATOR_INVALID) != NULL)
       
  2632             {
       
  2633               ret_sess[0] = '\0';
       
  2634             }
       
  2635       } while (ret_sess[0] == '\0');
       
  2636     }
       
  2637 
       
  2638   return ret_sess;
       
  2639 }
       
  2640 
       
  2641 
       
  2642 gboolean
       
  2643 _gabble_media_session_request_streams (GabbleMediaSession *session,
       
  2644                                        const GArray *media_types,
       
  2645                                        GPtrArray **ret,
       
  2646                                        GError **error)
       
  2647 {
       
  2648 
       
  2649 #ifndef EMULATOR
       
  2650   static GabblePresenceCapabilities google_audio_caps =
       
  2651     PRESENCE_CAP_GOOGLE_VOICE;
       
  2652   static GabblePresenceCapabilities jingle_audio_caps =
       
  2653     PRESENCE_CAP_JINGLE | PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO |
       
  2654     PRESENCE_CAP_GOOGLE_TRANSPORT_P2P;
       
  2655   static GabblePresenceCapabilities jingle_video_caps =
       
  2656     PRESENCE_CAP_JINGLE | PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO |
       
  2657     PRESENCE_CAP_GOOGLE_TRANSPORT_P2P;
       
  2658 #endif
       
  2659 
       
  2660   GabbleMediaSessionPrivate *priv;
       
  2661   GabblePresence *presence;
       
  2662   gboolean want_audio, want_video;
       
  2663   GabblePresenceCapabilities jingle_desired_caps;
       
  2664   guint idx;
       
  2665   gchar *dump;
       
  2666 
       
  2667   g_assert (GABBLE_IS_MEDIA_SESSION (session));
       
  2668 
       
  2669   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2670 
       
  2671   presence = gabble_presence_cache_get (priv->conn->presence_cache,
       
  2672       priv->peer);
       
  2673 
       
  2674   if (presence == NULL)
       
  2675     {
       
  2676       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2677           "member has no audio/video capabilities");
       
  2678 
       
  2679       return FALSE;
       
  2680     }
       
  2681 
       
  2682   dump = gabble_presence_dump (presence);
       
  2683   _gabble_media_session_debug (session, DEBUG_MSG_DUMP, "presence for peer %d:\n%s", priv->peer, dump);
       
  2684   g_free (dump);
       
  2685 
       
  2686   want_audio = want_video = FALSE;
       
  2687 
       
  2688   for (idx = 0; idx < media_types->len; idx++)
       
  2689     {
       
  2690       guint media_type = g_array_index (media_types, guint, idx);
       
  2691 
       
  2692       if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
       
  2693         {
       
  2694           want_audio = TRUE;
       
  2695         }
       
  2696       else if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
       
  2697         {
       
  2698           want_video = TRUE;
       
  2699         }
       
  2700       else
       
  2701         {
       
  2702           g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
  2703               "given media type %u is invalid", media_type);
       
  2704           return FALSE;
       
  2705         }
       
  2706     }
       
  2707 
       
  2708   /* work out what we'd need to do these streams with jingle */
       
  2709   jingle_desired_caps = 0;
       
  2710 
       
  2711   if (want_audio)
       
  2712     jingle_desired_caps |= jingle_audio_caps;
       
  2713 
       
  2714   if (want_video)
       
  2715     jingle_desired_caps |= jingle_video_caps;
       
  2716 
       
  2717   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "want audio: %s; want video: %s",
       
  2718     want_audio ? "yes" : "no", want_video ? "yes" : "no");
       
  2719 
       
  2720   /* existing call; the recipient and the mode has already been decided */
       
  2721   if (priv->peer_resource)
       
  2722     {
       
  2723       /* is a google call... we have no other option */
       
  2724       if (priv->mode == MODE_GOOGLE)
       
  2725         {
       
  2726           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "already in Google mode; can't add new "
       
  2727               "stream");
       
  2728 
       
  2729           g_assert (priv->streams->len == 1);
       
  2730 
       
  2731           g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2732               "Google Talk calls may only contain one stream");
       
  2733 
       
  2734           return FALSE;
       
  2735         }
       
  2736 
       
  2737       if (!gabble_presence_resource_has_caps (presence, priv->peer_resource,
       
  2738             jingle_desired_caps))
       
  2739         {
       
  2740           _gabble_media_session_debug (session, DEBUG_MSG_INFO,
       
  2741             "in Jingle mode but have insufficient caps for requested streams");
       
  2742 
       
  2743           g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2744               "existing call member doesn't support all requested media"
       
  2745               " types");
       
  2746 
       
  2747           return FALSE;
       
  2748         }
       
  2749 
       
  2750       _gabble_media_session_debug (session, DEBUG_MSG_INFO,
       
  2751         "in Jingle mode, and have necessary caps");
       
  2752     }
       
  2753 
       
  2754   /* no existing call; we should choose a recipient and a mode */
       
  2755   else
       
  2756     {
       
  2757       const gchar *resource;
       
  2758 
       
  2759       g_assert (priv->streams->len == 0);
       
  2760 
       
  2761       /* see if we have a fully-capable jingle resource; regardless of the
       
  2762        * desired media type it's best if we can add/remove the others later */
       
  2763       resource = gabble_presence_pick_resource_by_caps (presence,
       
  2764           jingle_audio_caps | jingle_video_caps);
       
  2765 
       
  2766       if (resource == NULL)
       
  2767         {
       
  2768           _gabble_media_session_debug (session, DEBUG_MSG_INFO, "contact is not fully jingle-capable");
       
  2769 
       
  2770           /* ok, no problem. see if we can do just what's wanted with jingle */
       
  2771           resource = gabble_presence_pick_resource_by_caps (presence,
       
  2772               jingle_desired_caps);
       
  2773 
       
  2774           if (resource == NULL && want_audio && !want_video)
       
  2775             {
       
  2776               _gabble_media_session_debug (session, DEBUG_MSG_INFO,
       
  2777                 "contact doesn't have desired Jingle capabilities");
       
  2778 
       
  2779               /* last ditch... if we want only audio and not video, we can make
       
  2780                * do with google talk */
       
  2781               resource = gabble_presence_pick_resource_by_caps (presence,
       
  2782                   google_audio_caps);
       
  2783 
       
  2784               if (resource != NULL)
       
  2785                 {
       
  2786                   /* only one stream possible with google */
       
  2787                   if (media_types->len == 1)
       
  2788                     {
       
  2789                       _gabble_media_session_debug (session, DEBUG_MSG_INFO,
       
  2790                         "contact has no Jingle capabilities; "
       
  2791                         "falling back to Google audio call");
       
  2792                       priv->mode = MODE_GOOGLE;
       
  2793                     }
       
  2794                   else
       
  2795                     {
       
  2796                       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2797                           "Google Talk calls may only contain one stream");
       
  2798 
       
  2799                       return FALSE;
       
  2800                     }
       
  2801                 }
       
  2802               else
       
  2803                 {
       
  2804                   _gabble_media_session_debug (session, DEBUG_MSG_INFO,
       
  2805                     "contact doesn't have desired Google capabilities");
       
  2806                 }
       
  2807             }
       
  2808         }
       
  2809 
       
  2810       if (resource == NULL)
       
  2811         {
       
  2812           _gabble_media_session_debug (session, DEBUG_MSG_INFO,
       
  2813             "contact doesn't have a resource with suitable capabilities");
       
  2814 
       
  2815           g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2816               "member does not have the desired audio/video capabilities");
       
  2817 
       
  2818           return FALSE;
       
  2819         }
       
  2820 
       
  2821       priv->peer_resource = g_strdup (resource);
       
  2822     }
       
  2823 
       
  2824   /* check it's not a ridiculous number of streams */
       
  2825   if ((priv->streams->len + media_types->len) > MAX_STREAMS)
       
  2826     {
       
  2827       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2828           "I think that's quite enough streams already");
       
  2829       return FALSE;
       
  2830     }
       
  2831 
       
  2832   /* if we've got here, we're good to make the streams */
       
  2833 
       
  2834   *ret = g_ptr_array_sized_new (media_types->len);
       
  2835 
       
  2836   for (idx = 0; idx < media_types->len; idx++)
       
  2837     {
       
  2838       guint media_type = g_array_index (media_types, guint, idx);
       
  2839       GabbleMediaStream *stream;
       
  2840       const gchar *stream_name;
       
  2841 
       
  2842       if (priv->mode == MODE_GOOGLE)
       
  2843         stream_name = GTALK_STREAM_NAME;
       
  2844       else
       
  2845         stream_name = _name_stream (session, media_type);
       
  2846 
       
  2847       stream = create_media_stream (session, stream_name, INITIATOR_LOCAL,
       
  2848                                     media_type);
       
  2849 
       
  2850       g_ptr_array_add (*ret, stream);
       
  2851     }
       
  2852 
       
  2853   return TRUE;
       
  2854 }
       
  2855 
       
  2856 static const gchar *
       
  2857 _direction_to_senders (GabbleMediaSession *session,
       
  2858                        TpMediaStreamDirection dir)
       
  2859 {
       
  2860   const gchar *ret = NULL;
       
  2861 
       
  2862   switch (dir)
       
  2863     {
       
  2864       case TP_MEDIA_STREAM_DIRECTION_NONE:
       
  2865         g_assert_not_reached ();
       
  2866         break;
       
  2867       case TP_MEDIA_STREAM_DIRECTION_SEND:
       
  2868         if (session->initiator == INITIATOR_LOCAL)
       
  2869           ret = "initiator";
       
  2870         else
       
  2871           ret = "responder";
       
  2872         break;
       
  2873       case TP_MEDIA_STREAM_DIRECTION_RECEIVE:
       
  2874         if (session->initiator == INITIATOR_REMOTE)
       
  2875           ret = "initiator";
       
  2876         else
       
  2877           ret = "responder";
       
  2878         break;
       
  2879       case TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL:
       
  2880         ret = "both";
       
  2881         break;
       
  2882     }
       
  2883 
       
  2884   g_assert (ret != NULL);
       
  2885 
       
  2886   return ret;
       
  2887 }
       
  2888 
       
  2889 static LmHandlerResult
       
  2890 direction_msg_reply_cb (GabbleConnection *conn,
       
  2891                         LmMessage *sent_msg,
       
  2892                         LmMessage *reply_msg,
       
  2893                         GObject *object,
       
  2894                         gpointer user_data)
       
  2895 {
       
  2896   GabbleMediaSession *session = GABBLE_MEDIA_SESSION (user_data);
       
  2897   GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object);
       
  2898 
       
  2899   MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (session, "direction change failed");
       
  2900 
       
  2901   if (stream->playing)
       
  2902     {
       
  2903       _gabble_media_stream_update_sending (stream, TRUE);
       
  2904     }
       
  2905 
       
  2906   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  2907 }
       
  2908 
       
  2909 static gboolean
       
  2910 send_direction_change (GabbleMediaSession *session,
       
  2911                        GabbleMediaStream *stream,
       
  2912                        TpMediaStreamDirection dir,
       
  2913                        GError **error)
       
  2914 {
       
  2915   GabbleMediaSessionPrivate *priv;
       
  2916   const gchar *senders;
       
  2917   LmMessage *msg;
       
  2918   LmMessageNode *session_node, *content_node;
       
  2919   gboolean ret;
       
  2920 
       
  2921   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2922   senders = _direction_to_senders (session, dir);
       
  2923 
       
  2924   if (stream->signalling_state == STREAM_SIG_STATE_NEW ||
       
  2925       stream->signalling_state == STREAM_SIG_STATE_REMOVING)
       
  2926     {
       
  2927       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "not sending content-modify for %s stream %s",
       
  2928           stream->signalling_state == STREAM_SIG_STATE_NEW ? "new" : "removing",
       
  2929           stream->name);
       
  2930       return TRUE;
       
  2931     }
       
  2932 
       
  2933   _gabble_media_session_debug (session, DEBUG_MSG_INFO, "sending jingle session action \"content-modify\" "
       
  2934       "to peer for stream %s (senders=%s)", stream->name, senders);
       
  2935 
       
  2936   msg = _gabble_media_session_message_new (session, "content-modify",
       
  2937       &session_node);
       
  2938   content_node = _gabble_media_stream_add_content_node (stream, session_node);
       
  2939 
       
  2940   lm_message_node_set_attribute (content_node, "senders", senders);
       
  2941 
       
  2942   ret = _gabble_connection_send_with_reply (priv->conn, msg,
       
  2943       direction_msg_reply_cb, G_OBJECT (stream), session, error);
       
  2944 
       
  2945   lm_message_unref (msg);
       
  2946 
       
  2947   return ret;
       
  2948 }
       
  2949 
       
  2950 gboolean
       
  2951 _gabble_media_session_request_stream_direction (GabbleMediaSession *session,
       
  2952                                                 GabbleMediaStream *stream,
       
  2953                                                 TpMediaStreamDirection requested_dir,
       
  2954                                                 GError **error)
       
  2955 {
       
  2956   GabbleMediaSessionPrivate *priv;
       
  2957   CombinedStreamDirection new_combined_dir;
       
  2958   TpMediaStreamDirection current_dir; //, new_dir;
       
  2959   TpMediaStreamPendingSend pending_send;
       
  2960 
       
  2961   priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session);
       
  2962 
       
  2963   current_dir = COMBINED_DIRECTION_GET_DIRECTION (stream->combined_direction);
       
  2964   pending_send = COMBINED_DIRECTION_GET_PENDING_SEND
       
  2965     (stream->combined_direction);
       
  2966 
       
  2967   if (priv->mode == MODE_GOOGLE)
       
  2968     {
       
  2969       g_assert (current_dir == TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL);
       
  2970 
       
  2971       if (requested_dir == TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)
       
  2972         return TRUE;
       
  2973 
       
  2974       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2975           "Google Talk calls can only be bi-directional");
       
  2976       return FALSE;
       
  2977     }
       
  2978 
       
  2979   if (requested_dir == TP_MEDIA_STREAM_DIRECTION_NONE)
       
  2980     {
       
  2981       _gabble_media_session_debug (session, DEBUG_MSG_INFO, "request for NONE direction; removing stream");
       
  2982 
       
  2983       _gabble_media_session_remove_streams (session, &stream, 1);
       
  2984 
       
  2985       return TRUE;
       
  2986     }
       
  2987 
       
  2988   /* if we're awaiting a local decision on sending... */
       
  2989   if ((pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0)
       
  2990     {
       
  2991       /* clear the flag */
       
  2992       pending_send &= ~TP_MEDIA_STREAM_PENDING_LOCAL_SEND;
       
  2993 
       
  2994       /* make our current_dir match what other end thinks (he thinks we're
       
  2995        * bidirectional) so that we send the correct transitions */
       
  2996       current_dir ^= TP_MEDIA_STREAM_DIRECTION_SEND;
       
  2997     }
       
  2998 
       
  2999 #if 0
       
  3000   /* if we're asking the remote end to start sending, set the pending flag and
       
  3001    * don't change our directionality just yet */
       
  3002   new_dir = requested_dir;
       
  3003   if (((current_dir & TP_MEDIA_STREAM_DIRECTION_RECEIVE) == 0) &&
       
  3004       ((new_dir & TP_MEDIA_STREAM_DIRECTION_RECEIVE) != 0))
       
  3005     {
       
  3006       pending_send ^= TP_MEDIA_STREAM_PENDING_REMOTE_SEND;
       
  3007       new_dir &= ~TP_MEDIA_STREAM_DIRECTION_RECEIVE;
       
  3008     }
       
  3009 #endif
       
  3010 
       
  3011   /* make any necessary changes */
       
  3012   new_combined_dir = MAKE_COMBINED_DIRECTION (requested_dir, pending_send);
       
  3013   if (new_combined_dir != stream->combined_direction)
       
  3014     {
       
  3015       g_object_set (stream, "combined-direction", new_combined_dir, NULL);
       
  3016       _gabble_media_stream_update_sending (stream, FALSE);
       
  3017     }
       
  3018 
       
  3019   /* short-circuit sending a request if we're not asking for anything new */
       
  3020   if (current_dir == requested_dir)
       
  3021     return TRUE;
       
  3022 
       
  3023   /* send request */
       
  3024   return send_direction_change (session, stream, requested_dir, error);
       
  3025 }
       
  3026