telepathygabble/src/gabble-media-channel.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * gabble-media-channel.c - Source for GabbleMediaChannel
       
     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 
       
    26 
       
    27 #include "ansi.h"
       
    28 #include "debug.h"
       
    29 #include "gabble-connection.h"
       
    30 #include "gabble-media-session.h"
       
    31 #include "gabble-presence.h"
       
    32 #include "gabble-presence-cache.h"
       
    33 
       
    34 #include "telepathy-errors.h"
       
    35 #include "telepathy-helpers.h"
       
    36 #include "telepathy-interfaces.h"
       
    37 #include "tp-channel-iface.h"
       
    38 
       
    39 #include "gabble-media-channel.h"
       
    40 #include "gabble-media-channel-signals-marshal.h"
       
    41 #include "gabble-media-channel-glue.h"
       
    42 
       
    43 #include "gabble-media-session.h"
       
    44 #include "gabble-media-stream.h"
       
    45 
       
    46 #include "media-factory.h"
       
    47 
       
    48 #include "gabble_enums.h"
       
    49 
       
    50 #define DEBUG_FLAG GABBLE_DEBUG_MEDIA
       
    51 
       
    52 #define TP_SESSION_HANDLER_SET_TYPE (dbus_g_type_get_struct ("GValueArray", \
       
    53       DBUS_TYPE_G_OBJECT_PATH, \
       
    54       G_TYPE_STRING, \
       
    55       G_TYPE_INVALID))
       
    56 
       
    57 #define TP_CHANNEL_STREAM_TYPE (dbus_g_type_get_struct ("GValueArray", \
       
    58       G_TYPE_UINT, \
       
    59       G_TYPE_UINT, \
       
    60       G_TYPE_UINT, \
       
    61       G_TYPE_UINT, \
       
    62       G_TYPE_UINT, \
       
    63       G_TYPE_UINT, \
       
    64       G_TYPE_INVALID))
       
    65 
       
    66 #ifndef EMULATOR
       
    67 G_DEFINE_TYPE_WITH_CODE (GabbleMediaChannel, gabble_media_channel,
       
    68     G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL));
       
    69 #endif
       
    70 
       
    71 /* signal enum */
       
    72 enum
       
    73 {
       
    74     CLOSED,
       
    75     NEW_SESSION_HANDLER,
       
    76     STREAM_ADDED,
       
    77     STREAM_DIRECTION_CHANGED,
       
    78     STREAM_ERROR,
       
    79     STREAM_REMOVED,
       
    80     STREAM_STATE_CHANGED,
       
    81     LAST_SIGNAL 
       
    82 #ifdef EMULATOR    
       
    83     = LAST_SIGNAL_MED_CHANNEL
       
    84 #endif
       
    85     
       
    86 };
       
    87 
       
    88 
       
    89 #ifdef EMULATOR
       
    90 #include "libgabble_wsd_solution.h"
       
    91 
       
    92 	GET_STATIC_ARRAY_FROM_TLS(signals,gabble_med_chnl,guint)
       
    93 	#define signals (GET_WSD_VAR_NAME(signals,gabble_med_chnl, s)())
       
    94 	
       
    95 	GET_STATIC_VAR_FROM_TLS(gabble_media_channel_parent_class,gabble_med_chnl,gpointer)
       
    96 	#define gabble_media_channel_parent_class (*GET_WSD_VAR_NAME(gabble_media_channel_parent_class,gabble_med_chnl,s)())
       
    97 	
       
    98 	GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_med_chnl,GType)
       
    99 	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_med_chnl,s)())
       
   100 
       
   101 static void gabble_media_channel_init (GabbleMediaChannel *self); 
       
   102 static void gabble_media_channel_class_init (GabbleMediaChannelClass *klass);
       
   103 static void gabble_media_channel_class_intern_init (gpointer klass)
       
   104  { 
       
   105  gabble_media_channel_parent_class = g_type_class_peek_parent (klass); 
       
   106  gabble_media_channel_class_init ((GabbleMediaChannelClass*) klass); 
       
   107  } 
       
   108  EXPORT_C GType gabble_media_channel_get_type (void)
       
   109   { 
       
   110   if ((g_define_type_id == 0))
       
   111    { static const GTypeInfo g_define_type_info = { sizeof (GabbleMediaChannelClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_media_channel_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleMediaChannel), 0, (GInstanceInitFunc) gabble_media_channel_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleMediaChannel"), &g_define_type_info, (GTypeFlags) 0); { { static const GInterfaceInfo g_implement_interface_info = { (GInterfaceInitFunc) ((void *)0) }; g_type_add_interface_static (g_define_type_id, tp_channel_iface_get_type(), &g_implement_interface_info); } ; } } return g_define_type_id;
       
   112    };
       
   113 		
       
   114 #else
       
   115 
       
   116 	static guint signals[LAST_SIGNAL] = {0};
       
   117 
       
   118 #endif
       
   119 
       
   120 
       
   121 /* properties */
       
   122 enum
       
   123 {
       
   124   PROP_OBJECT_PATH = 1,
       
   125   PROP_CHANNEL_TYPE,
       
   126   PROP_HANDLE_TYPE,
       
   127   PROP_HANDLE,
       
   128   PROP_CONNECTION,
       
   129   PROP_CREATOR,
       
   130   PROP_FACTORY,
       
   131   LAST_PROPERTY
       
   132 };
       
   133 
       
   134 /* private structure */
       
   135 typedef struct _GabbleMediaChannelPrivate GabbleMediaChannelPrivate;
       
   136 
       
   137 struct _GabbleMediaChannelPrivate
       
   138 {
       
   139   GabbleConnection *conn;
       
   140   gchar *object_path;
       
   141   GabbleHandle creator;
       
   142 
       
   143   GabbleMediaFactory *factory;
       
   144   GabbleMediaSession *session;
       
   145   GPtrArray *streams;
       
   146 
       
   147   guint next_stream_id;
       
   148 
       
   149   gboolean closed;
       
   150   gboolean dispose_has_run;
       
   151 };
       
   152 
       
   153 #define GABBLE_MEDIA_CHANNEL_GET_PRIVATE(obj) \
       
   154     ((GabbleMediaChannelPrivate *)obj->priv)
       
   155 
       
   156 static void
       
   157 gabble_media_channel_init (GabbleMediaChannel *self)
       
   158 {
       
   159   GabbleMediaChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
       
   160       GABBLE_TYPE_MEDIA_CHANNEL, GabbleMediaChannelPrivate);
       
   161 
       
   162   self->priv = priv;
       
   163 
       
   164   priv->next_stream_id = 1;
       
   165 }
       
   166 
       
   167 static GObject *
       
   168 gabble_media_channel_constructor (GType type, guint n_props,
       
   169                                   GObjectConstructParam *props)
       
   170 {
       
   171   GObject *obj;
       
   172   GabbleMediaChannelPrivate *priv;
       
   173   DBusGConnection *bus;
       
   174   GIntSet *set;
       
   175 
       
   176   obj = G_OBJECT_CLASS (gabble_media_channel_parent_class)->
       
   177            constructor (type, n_props, props);
       
   178 
       
   179   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (GABBLE_MEDIA_CHANNEL (obj));
       
   180 
       
   181   /* register object on the bus */
       
   182   bus = tp_get_bus ();
       
   183   dbus_g_connection_register_g_object (bus, priv->object_path, obj);
       
   184 
       
   185   gabble_group_mixin_init (obj, G_STRUCT_OFFSET (GabbleMediaChannel, group),
       
   186                            priv->conn->handles, priv->conn->self_handle);
       
   187 
       
   188   /* automatically add creator to channel */
       
   189   set = g_intset_new ();
       
   190   g_intset_add (set, priv->creator);
       
   191 
       
   192   gabble_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0, 0);
       
   193 
       
   194   g_intset_destroy (set);
       
   195 
       
   196   /* allow member adding */
       
   197   gabble_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD, 0);
       
   198 
       
   199   return obj;
       
   200 }
       
   201 
       
   202 static void session_state_changed_cb (GabbleMediaSession *session, GParamSpec *arg1, GabbleMediaChannel *channel);
       
   203 static void session_stream_added_cb (GabbleMediaSession *session, GabbleMediaStream  *stream, GabbleMediaChannel *chan);
       
   204 static void session_terminated_cb (GabbleMediaSession *session, guint terminator, guint reason, gpointer user_data);
       
   205 
       
   206 /**
       
   207  * create_session
       
   208  *
       
   209  * Creates a GabbleMediaSession object for given peer.
       
   210  *
       
   211  * If sid is set to NULL a unique sid is generated and
       
   212  * the "initiator" property of the newly created
       
   213  * GabbleMediaSession is set to our own handle.
       
   214  */
       
   215 static GabbleMediaSession*
       
   216 create_session (GabbleMediaChannel *channel,
       
   217                 GabbleHandle peer,
       
   218                 const gchar *peer_resource,
       
   219                 const gchar *sid)
       
   220 {
       
   221   GabbleMediaChannelPrivate *priv;
       
   222   GabbleMediaSession *session;
       
   223   gchar *object_path;
       
   224   JingleInitiator initiator;
       
   225 
       
   226   g_assert (GABBLE_IS_MEDIA_CHANNEL (channel));
       
   227 
       
   228   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
       
   229 
       
   230   g_assert (priv->session == NULL);
       
   231 
       
   232   object_path = g_strdup_printf ("%s/MediaSession%u", priv->object_path, peer);
       
   233 
       
   234   if (sid == NULL)
       
   235     {
       
   236       initiator = INITIATOR_LOCAL;
       
   237       sid = _gabble_media_factory_allocate_sid (priv->factory, channel);
       
   238     }
       
   239   else
       
   240     {
       
   241       initiator = INITIATOR_REMOTE;
       
   242       _gabble_media_factory_register_sid (priv->factory, sid, channel);
       
   243     }
       
   244 
       
   245   session = g_object_new (GABBLE_TYPE_MEDIA_SESSION,
       
   246                           "connection", priv->conn,
       
   247                           "media-channel", channel,
       
   248                           "object-path", object_path,
       
   249                           "session-id", sid,
       
   250                           "initiator", initiator,
       
   251                           "peer", peer,
       
   252                           "peer-resource", peer_resource,
       
   253                           NULL);
       
   254 
       
   255   g_signal_connect (session, "notify::state",
       
   256                     (GCallback) session_state_changed_cb, channel);
       
   257   g_signal_connect (session, "stream-added",
       
   258                     (GCallback) session_stream_added_cb, channel);
       
   259   g_signal_connect (session, "terminated",
       
   260                     (GCallback) session_terminated_cb, channel);
       
   261 
       
   262   priv->session = session;
       
   263 
       
   264   priv->streams = g_ptr_array_sized_new (1);
       
   265 
       
   266   g_signal_emit (channel, signals[NEW_SESSION_HANDLER], 0,
       
   267                  object_path, "rtp");
       
   268 
       
   269   g_free (object_path);
       
   270 
       
   271   return session;
       
   272 }
       
   273 
       
   274 gboolean
       
   275 _gabble_media_channel_dispatch_session_action (GabbleMediaChannel *chan,
       
   276                                                GabbleHandle peer,
       
   277                                                const gchar *peer_resource,
       
   278                                                const gchar *sid,
       
   279                                                LmMessage *message,
       
   280                                                LmMessageNode *session_node,
       
   281                                                const gchar *action,
       
   282                                                GError **error)
       
   283 {
       
   284   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
   285   GabbleMediaSession *session = priv->session;
       
   286   gboolean session_is_new = FALSE;
       
   287 
       
   288   if (session == NULL)
       
   289     {
       
   290       GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (chan);
       
   291       GIntSet *set;
       
   292 
       
   293       session = create_session (chan, peer, peer_resource, sid);
       
   294       session_is_new = TRUE;
       
   295 
       
   296       /* make us local pending */
       
   297       set = g_intset_new ();
       
   298       g_intset_add (set, mixin->self_handle);
       
   299 
       
   300       gabble_group_mixin_change_members (G_OBJECT (chan), "", NULL, NULL, set,
       
   301           NULL, 0, 0);
       
   302 
       
   303       g_intset_destroy (set);
       
   304 
       
   305       /* and update flags accordingly */
       
   306       gabble_group_mixin_change_flags (G_OBJECT (chan),
       
   307                                        TP_CHANNEL_GROUP_FLAG_CAN_ADD |
       
   308                                        TP_CHANNEL_GROUP_FLAG_CAN_REMOVE,
       
   309                                        0);
       
   310     }
       
   311 
       
   312   g_object_ref (session);
       
   313 
       
   314   if (_gabble_media_session_handle_action (session, message, session_node,
       
   315         action, error))
       
   316     {
       
   317       g_object_unref (session);
       
   318       return TRUE;
       
   319     }
       
   320   else
       
   321     {
       
   322       if (session_is_new)
       
   323         _gabble_media_session_terminate (session, INITIATOR_LOCAL,
       
   324             TP_CHANNEL_GROUP_CHANGE_REASON_ERROR);
       
   325 
       
   326       g_object_unref (session);
       
   327       return FALSE;
       
   328     }
       
   329 }
       
   330 
       
   331 static void
       
   332 gabble_media_channel_get_property (GObject    *object,
       
   333                                    guint       property_id,
       
   334                                    GValue     *value,
       
   335                                    GParamSpec *pspec)
       
   336 {
       
   337   GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object);
       
   338   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
   339 
       
   340   switch (property_id) {
       
   341     case PROP_OBJECT_PATH:
       
   342       g_value_set_string (value, priv->object_path);
       
   343       break;
       
   344     case PROP_CHANNEL_TYPE:
       
   345       g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
       
   346       break;
       
   347     case PROP_HANDLE_TYPE:
       
   348       g_value_set_uint (value, TP_HANDLE_TYPE_NONE);
       
   349       break;
       
   350     case PROP_HANDLE:
       
   351       g_value_set_uint (value, 0);
       
   352       break;
       
   353     case PROP_CONNECTION:
       
   354       g_value_set_object (value, priv->conn);
       
   355       break;
       
   356     case PROP_CREATOR:
       
   357       g_value_set_uint (value, priv->creator);
       
   358       break;
       
   359     case PROP_FACTORY:
       
   360       g_value_set_object (value, priv->factory);
       
   361       break;
       
   362     default:
       
   363       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   364       break;
       
   365   }
       
   366 }
       
   367 
       
   368 static void
       
   369 gabble_media_channel_set_property (GObject     *object,
       
   370                                    guint        property_id,
       
   371                                    const GValue *value,
       
   372                                    GParamSpec   *pspec)
       
   373 {
       
   374   GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object);
       
   375   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
   376 
       
   377   switch (property_id) {
       
   378     case PROP_OBJECT_PATH:
       
   379       g_free (priv->object_path);
       
   380       priv->object_path = g_value_dup_string (value);
       
   381       break;
       
   382     case PROP_HANDLE:
       
   383       /* this property is writable in the interface, but not actually
       
   384        * meaningfully changable on this channel, so we do nothing */
       
   385       break;
       
   386     case PROP_CONNECTION:
       
   387       priv->conn = g_value_get_object (value);
       
   388       break;
       
   389     case PROP_CREATOR:
       
   390       priv->creator = g_value_get_uint (value);
       
   391       break;
       
   392     case PROP_FACTORY:
       
   393       priv->factory = g_value_get_object (value);
       
   394       break;
       
   395     default:
       
   396       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   397       break;
       
   398   }
       
   399 }
       
   400 
       
   401 static void gabble_media_channel_dispose (GObject *object);
       
   402 static void gabble_media_channel_finalize (GObject *object);
       
   403 static gboolean gabble_media_channel_remove_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error);
       
   404 
       
   405 static void
       
   406 gabble_media_channel_class_init (GabbleMediaChannelClass *gabble_media_channel_class)
       
   407 {
       
   408   GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_channel_class);
       
   409   GParamSpec *param_spec;
       
   410 
       
   411   g_type_class_add_private (gabble_media_channel_class, sizeof (GabbleMediaChannelPrivate));
       
   412 
       
   413   object_class->constructor = gabble_media_channel_constructor;
       
   414 
       
   415   object_class->get_property = gabble_media_channel_get_property;
       
   416   object_class->set_property = gabble_media_channel_set_property;
       
   417 
       
   418   object_class->dispose = gabble_media_channel_dispose;
       
   419   object_class->finalize = gabble_media_channel_finalize;
       
   420 
       
   421   gabble_group_mixin_class_init (object_class,
       
   422                                  G_STRUCT_OFFSET (GabbleMediaChannelClass, group_class),
       
   423                                  _gabble_media_channel_add_member,
       
   424                                  gabble_media_channel_remove_member);
       
   425 
       
   426   g_object_class_override_property (object_class, PROP_OBJECT_PATH, "object-path");
       
   427   g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, "channel-type");
       
   428   g_object_class_override_property (object_class, PROP_HANDLE_TYPE, "handle-type");
       
   429   g_object_class_override_property (object_class, PROP_HANDLE, "handle");
       
   430 
       
   431   param_spec = g_param_spec_object ("connection", "GabbleConnection object",
       
   432                                     "Gabble connection object that owns this "
       
   433                                     "media channel object.",
       
   434                                     GABBLE_TYPE_CONNECTION,
       
   435                                     G_PARAM_CONSTRUCT_ONLY |
       
   436                                     G_PARAM_READWRITE |
       
   437                                     G_PARAM_STATIC_NICK |
       
   438                                     G_PARAM_STATIC_BLURB);
       
   439   g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
       
   440 
       
   441   param_spec = g_param_spec_uint ("creator", "Channel creator",
       
   442                                   "The GabbleHandle representing the contact "
       
   443                                   "who created the channel.",
       
   444                                   0, G_MAXUINT32, 0,
       
   445                                   G_PARAM_CONSTRUCT_ONLY |
       
   446                                   G_PARAM_READWRITE |
       
   447                                   G_PARAM_STATIC_NAME |
       
   448                                   G_PARAM_STATIC_BLURB);
       
   449   g_object_class_install_property (object_class, PROP_CREATOR, param_spec);
       
   450 
       
   451   param_spec = g_param_spec_object ("factory", "GabbleMediaFactory object",
       
   452                                     "The factory that created this object.",
       
   453                                     GABBLE_TYPE_MEDIA_FACTORY,
       
   454                                     G_PARAM_CONSTRUCT_ONLY |
       
   455                                     G_PARAM_READWRITE |
       
   456                                     G_PARAM_STATIC_NICK |
       
   457                                     G_PARAM_STATIC_BLURB);
       
   458   g_object_class_install_property (object_class, PROP_FACTORY, param_spec);
       
   459 
       
   460   signals[CLOSED] =
       
   461     g_signal_new ("closed",
       
   462                   G_OBJECT_CLASS_TYPE (gabble_media_channel_class),
       
   463                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   464                   0,
       
   465                   NULL, NULL,
       
   466                   g_cclosure_marshal_VOID__VOID,
       
   467                   G_TYPE_NONE, 0);
       
   468 
       
   469   signals[NEW_SESSION_HANDLER] =
       
   470     g_signal_new ("new-session-handler",
       
   471                   G_OBJECT_CLASS_TYPE (gabble_media_channel_class),
       
   472                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   473                   0,
       
   474                   NULL, NULL,
       
   475                   gabble_media_channel_marshal_VOID__STRING_STRING,
       
   476                   G_TYPE_NONE, 2, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING);
       
   477 
       
   478   signals[STREAM_ADDED] =
       
   479     g_signal_new ("stream-added",
       
   480                   G_OBJECT_CLASS_TYPE (gabble_media_channel_class),
       
   481                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   482                   0,
       
   483                   NULL, NULL,
       
   484                   gabble_media_channel_marshal_VOID__UINT_UINT_UINT,
       
   485                   G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
       
   486 
       
   487   signals[STREAM_DIRECTION_CHANGED] =
       
   488     g_signal_new ("stream-direction-changed",
       
   489                   G_OBJECT_CLASS_TYPE (gabble_media_channel_class),
       
   490                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   491                   0,
       
   492                   NULL, NULL,
       
   493                   gabble_media_channel_marshal_VOID__UINT_UINT_UINT,
       
   494                   G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
       
   495 
       
   496   signals[STREAM_ERROR] =
       
   497     g_signal_new ("stream-error",
       
   498                   G_OBJECT_CLASS_TYPE (gabble_media_channel_class),
       
   499                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   500                   0,
       
   501                   NULL, NULL,
       
   502                   gabble_media_channel_marshal_VOID__UINT_UINT_STRING,
       
   503                   G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING);
       
   504 
       
   505   signals[STREAM_REMOVED] =
       
   506     g_signal_new ("stream-removed",
       
   507                   G_OBJECT_CLASS_TYPE (gabble_media_channel_class),
       
   508                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   509                   0,
       
   510                   NULL, NULL,
       
   511                   g_cclosure_marshal_VOID__UINT,
       
   512                   G_TYPE_NONE, 1, G_TYPE_UINT);
       
   513 
       
   514   signals[STREAM_STATE_CHANGED] =
       
   515     g_signal_new ("stream-state-changed",
       
   516                   G_OBJECT_CLASS_TYPE (gabble_media_channel_class),
       
   517                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   518                   0,
       
   519                   NULL, NULL,
       
   520                   gabble_media_channel_marshal_VOID__UINT_UINT,
       
   521                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
       
   522 
       
   523   dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (gabble_media_channel_class), &dbus_glib_gabble_media_channel_object_info);
       
   524 }
       
   525 
       
   526 void
       
   527 gabble_media_channel_dispose (GObject *object)
       
   528 {
       
   529   GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object);
       
   530   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
   531 
       
   532   if (priv->dispose_has_run)
       
   533     return;
       
   534 
       
   535   priv->dispose_has_run = TRUE;
       
   536 
       
   537   /** In this we set the state to ENDED, then the callback unrefs
       
   538    * the session
       
   539    */
       
   540 
       
   541   if (!priv->closed)
       
   542     gabble_media_channel_close (self, NULL);
       
   543 
       
   544   g_assert (priv->closed);
       
   545   g_assert (priv->session == NULL);
       
   546   g_assert (priv->streams == NULL);
       
   547 
       
   548   if (G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose)
       
   549     G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose (object);
       
   550 }
       
   551 
       
   552 void
       
   553 gabble_media_channel_finalize (GObject *object)
       
   554 {
       
   555   GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object);
       
   556   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
   557 
       
   558   g_free (priv->object_path);
       
   559 
       
   560   gabble_group_mixin_finalize (object);
       
   561 
       
   562   G_OBJECT_CLASS (gabble_media_channel_parent_class)->finalize (object);
       
   563 }
       
   564 
       
   565 
       
   566 
       
   567 /**
       
   568  * gabble_media_channel_add_members
       
   569  *
       
   570  * Implements D-Bus method AddMembers
       
   571  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
   572  *
       
   573  * @error: Used to return a pointer to a GError detailing any error
       
   574  *         that occurred, D-Bus will throw the error only if this
       
   575  *         function returns FALSE.
       
   576  *
       
   577  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   578  */
       
   579 gboolean
       
   580 gabble_media_channel_add_members (GabbleMediaChannel *self,
       
   581                                   const GArray *contacts,
       
   582                                   const gchar *message,
       
   583                                   GError **error)
       
   584 {
       
   585   return gabble_group_mixin_add_members (G_OBJECT (self), contacts, message,
       
   586       error);
       
   587 }
       
   588 
       
   589 
       
   590 /**
       
   591  * gabble_media_channel_close
       
   592  *
       
   593  * Implements D-Bus method Close
       
   594  * on interface org.freedesktop.Telepathy.Channel
       
   595  *
       
   596  * @error: Used to return a pointer to a GError detailing any error
       
   597  *         that occurred, D-Bus will throw the error only if this
       
   598  *         function returns FALSE.
       
   599  *
       
   600  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   601  */
       
   602 gboolean
       
   603 gabble_media_channel_close (GabbleMediaChannel *self,
       
   604                             GError **error)
       
   605 {
       
   606   GabbleMediaChannelPrivate *priv;
       
   607 
       
   608   gabble_debug (DEBUG_FLAG, "called on %p", self);
       
   609 
       
   610   g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
       
   611 
       
   612   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
   613 
       
   614   if (priv->closed)
       
   615     return TRUE;
       
   616 
       
   617   priv->closed = TRUE;
       
   618 
       
   619   if (priv->session)
       
   620     {
       
   621       _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
       
   622     }
       
   623 
       
   624   g_signal_emit (self, signals[CLOSED], 0);
       
   625 
       
   626   return TRUE;
       
   627 }
       
   628 
       
   629 
       
   630 /**
       
   631  * gabble_media_channel_get_all_members
       
   632  *
       
   633  * Implements D-Bus method GetAllMembers
       
   634  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
   635  *
       
   636  * @error: Used to return a pointer to a GError detailing any error
       
   637  *         that occurred, D-Bus will throw the error only if this
       
   638  *         function returns FALSE.
       
   639  *
       
   640  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   641  */
       
   642 gboolean
       
   643 gabble_media_channel_get_all_members (GabbleMediaChannel *self,
       
   644                                       GArray **ret,
       
   645                                       GArray **ret1,
       
   646                                       GArray **ret2,
       
   647                                       GError **error)
       
   648 {
       
   649   return gabble_group_mixin_get_all_members (G_OBJECT (self), ret, ret1, ret2,
       
   650       error);
       
   651 }
       
   652 
       
   653 
       
   654 /**
       
   655  * gabble_media_channel_get_channel_type
       
   656  *
       
   657  * Implements D-Bus method GetChannelType
       
   658  * on interface org.freedesktop.Telepathy.Channel
       
   659  *
       
   660  * @error: Used to return a pointer to a GError detailing any error
       
   661  *         that occurred, D-Bus will throw the error only if this
       
   662  *         function returns FALSE.
       
   663  *
       
   664  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   665  */
       
   666 gboolean
       
   667 gabble_media_channel_get_channel_type (GabbleMediaChannel *self,
       
   668                                        gchar **ret,
       
   669                                        GError **error)
       
   670 {
       
   671   *ret = g_strdup (TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
       
   672 
       
   673   return TRUE;
       
   674 }
       
   675 
       
   676 
       
   677 /**
       
   678  * gabble_media_channel_get_group_flags
       
   679  *
       
   680  * Implements D-Bus method GetGroupFlags
       
   681  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
   682  *
       
   683  * @error: Used to return a pointer to a GError detailing any error
       
   684  *         that occurred, D-Bus will throw the error only if this
       
   685  *         function returns FALSE.
       
   686  *
       
   687  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   688  */
       
   689 gboolean
       
   690 gabble_media_channel_get_group_flags (GabbleMediaChannel *self,
       
   691                                       guint *ret,
       
   692                                       GError **error)
       
   693 {
       
   694   return gabble_group_mixin_get_group_flags (G_OBJECT (self), ret, error);
       
   695 }
       
   696 
       
   697 
       
   698 /**
       
   699  * gabble_media_channel_get_handle
       
   700  *
       
   701  * Implements D-Bus method GetHandle
       
   702  * on interface org.freedesktop.Telepathy.Channel
       
   703  *
       
   704  * @error: Used to return a pointer to a GError detailing any error
       
   705  *         that occurred, D-Bus will throw the error only if this
       
   706  *         function returns FALSE.
       
   707  *
       
   708  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   709  */
       
   710 gboolean
       
   711 gabble_media_channel_get_handle (GabbleMediaChannel *self,
       
   712                                  guint *ret,
       
   713                                  guint *ret1,
       
   714                                  GError **error)
       
   715 {
       
   716   GabbleMediaChannelPrivate *priv;
       
   717 
       
   718   g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
       
   719 
       
   720   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
   721 
       
   722   *ret = 0;
       
   723   *ret1 = 0;
       
   724 
       
   725   return TRUE;
       
   726 }
       
   727 
       
   728 
       
   729 /**
       
   730  * gabble_media_channel_get_handle_owners
       
   731  *
       
   732  * Implements D-Bus method GetHandleOwners
       
   733  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
   734  *
       
   735  * @error: Used to return a pointer to a GError detailing any error
       
   736  *         that occurred, D-Bus will throw the error only if this
       
   737  *         function returns FALSE.
       
   738  *
       
   739  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   740  */
       
   741 gboolean
       
   742 gabble_media_channel_get_handle_owners (GabbleMediaChannel *self,
       
   743                                         const GArray *handles,
       
   744                                         GArray **ret,
       
   745                                         GError **error)
       
   746 {
       
   747   return gabble_group_mixin_get_handle_owners (G_OBJECT (self), handles, ret,
       
   748       error);
       
   749 }
       
   750 
       
   751 
       
   752 /**
       
   753  * gabble_media_channel_get_interfaces
       
   754  *
       
   755  * Implements D-Bus method GetInterfaces
       
   756  * on interface org.freedesktop.Telepathy.Channel
       
   757  *
       
   758  * @error: Used to return a pointer to a GError detailing any error
       
   759  *         that occurred, D-Bus will throw the error only if this
       
   760  *         function returns FALSE.
       
   761  *
       
   762  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   763  */
       
   764 gboolean
       
   765 gabble_media_channel_get_interfaces (GabbleMediaChannel *self,
       
   766                                      gchar ***ret,
       
   767                                      GError **error)
       
   768 {
       
   769   const gchar *interfaces[] = {
       
   770       TP_IFACE_CHANNEL_INTERFACE_GROUP,
       
   771       TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
       
   772       NULL
       
   773   };
       
   774 
       
   775   *ret = g_strdupv ((gchar **) interfaces);
       
   776 
       
   777   return TRUE;
       
   778 }
       
   779 
       
   780 
       
   781 /**
       
   782  * gabble_media_channel_get_local_pending_members
       
   783  *
       
   784  * Implements D-Bus method GetLocalPendingMembers
       
   785  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
   786  *
       
   787  * @error: Used to return a pointer to a GError detailing any error
       
   788  *         that occurred, D-Bus will throw the error only if this
       
   789  *         function returns FALSE.
       
   790  *
       
   791  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   792  */
       
   793 gboolean
       
   794 gabble_media_channel_get_local_pending_members (GabbleMediaChannel *self,
       
   795                                                 GArray **ret,
       
   796                                                 GError **error)
       
   797 {
       
   798   return gabble_group_mixin_get_local_pending_members (G_OBJECT (self), ret,
       
   799       error);
       
   800 }
       
   801 
       
   802 
       
   803 /**
       
   804  * gabble_media_channel_get_members
       
   805  *
       
   806  * Implements D-Bus method GetMembers
       
   807  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
   808  *
       
   809  * @error: Used to return a pointer to a GError detailing any error
       
   810  *         that occurred, D-Bus will throw the error only if this
       
   811  *         function returns FALSE.
       
   812  *
       
   813  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   814  */
       
   815 gboolean
       
   816 gabble_media_channel_get_members (GabbleMediaChannel *self,
       
   817                                   GArray **ret,
       
   818                                   GError **error)
       
   819 {
       
   820   return gabble_group_mixin_get_members (G_OBJECT (self), ret, error);
       
   821 }
       
   822 
       
   823 
       
   824 /**
       
   825  * gabble_media_channel_get_remote_pending_members
       
   826  *
       
   827  * Implements D-Bus method GetRemotePendingMembers
       
   828  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
   829  *
       
   830  * @error: Used to return a pointer to a GError detailing any error
       
   831  *         that occurred, D-Bus will throw the error only if this
       
   832  *         function returns FALSE.
       
   833  *
       
   834  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   835  */
       
   836 gboolean
       
   837 gabble_media_channel_get_remote_pending_members (GabbleMediaChannel *self,
       
   838                                                  GArray **ret,
       
   839                                                  GError **error)
       
   840 {
       
   841   return gabble_group_mixin_get_remote_pending_members (G_OBJECT (self), ret,
       
   842       error);
       
   843 }
       
   844 
       
   845 
       
   846 /**
       
   847  * gabble_media_channel_get_self_handle
       
   848  *
       
   849  * Implements D-Bus method GetSelfHandle
       
   850  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
   851  *
       
   852  * @error: Used to return a pointer to a GError detailing any error
       
   853  *         that occurred, D-Bus will throw the error only if this
       
   854  *         function returns FALSE.
       
   855  *
       
   856  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   857  */
       
   858 gboolean
       
   859 gabble_media_channel_get_self_handle (GabbleMediaChannel *self,
       
   860                                       guint *ret,
       
   861                                       GError **error)
       
   862 {
       
   863   return gabble_group_mixin_get_self_handle (G_OBJECT (self), ret, error);
       
   864 }
       
   865 
       
   866 
       
   867 /**
       
   868  * gabble_media_channel_get_session_handlers
       
   869  *
       
   870  * Implements D-Bus method GetSessionHandlers
       
   871  * on interface org.freedesktop.Telepathy.Channel.Interface.MediaSignalling
       
   872  *
       
   873  * @error: Used to return a pointer to a GError detailing any error
       
   874  *         that occurred, D-Bus will throw the error only if this
       
   875  *         function returns FALSE.
       
   876  *
       
   877  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   878  */
       
   879 gboolean
       
   880 gabble_media_channel_get_session_handlers (GabbleMediaChannel *self,
       
   881                                            GPtrArray **ret,
       
   882                                            GError **error)
       
   883 {
       
   884   GabbleMediaChannelPrivate *priv;
       
   885 
       
   886   g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
       
   887 
       
   888   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
   889 
       
   890   if (priv->session)
       
   891     {
       
   892       GValue handler = { 0, };
       
   893       GabbleHandle member;
       
   894       gchar *path;
       
   895 
       
   896       g_value_init (&handler, TP_SESSION_HANDLER_SET_TYPE);
       
   897       g_value_take_boxed (&handler,
       
   898           dbus_g_type_specialized_construct (TP_SESSION_HANDLER_SET_TYPE));
       
   899 
       
   900       g_object_get (priv->session,
       
   901                     "peer", &member,
       
   902                     "object-path", &path,
       
   903                     NULL);
       
   904 
       
   905       dbus_g_type_struct_set (&handler,
       
   906           0, path,
       
   907           1, "rtp",
       
   908           G_MAXUINT);
       
   909 
       
   910       g_free (path);
       
   911 
       
   912       *ret = g_ptr_array_sized_new (1);
       
   913       g_ptr_array_add (*ret, g_value_get_boxed (&handler));
       
   914     }
       
   915   else
       
   916     {
       
   917       *ret = g_ptr_array_sized_new (0);
       
   918     }
       
   919 
       
   920   return TRUE;
       
   921 }
       
   922 
       
   923 
       
   924 static GPtrArray *
       
   925 make_stream_list (GabbleMediaChannel *self,
       
   926                   GPtrArray *streams)
       
   927 {
       
   928   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
   929   GPtrArray *ret;
       
   930   guint i;
       
   931 
       
   932   ret = g_ptr_array_sized_new (streams->len);
       
   933 
       
   934   for (i = 0; i < streams->len; i++)
       
   935     {
       
   936       GabbleMediaStream *stream = g_ptr_array_index (streams, i);
       
   937       GValue entry = { 0, };
       
   938       guint id;
       
   939       GabbleHandle peer;
       
   940       TpCodecMediaType type;
       
   941       TpMediaStreamState connection_state;
       
   942       CombinedStreamDirection combined_direction;
       
   943 
       
   944       g_object_get (stream,
       
   945           "id", &id,
       
   946           "media-type", &type,
       
   947           "connection-state", &connection_state,
       
   948           "combined-direction", &combined_direction,
       
   949           NULL);
       
   950 
       
   951       g_object_get (priv->session, "peer", &peer, NULL);
       
   952 
       
   953       g_value_init (&entry, TP_CHANNEL_STREAM_TYPE);
       
   954       g_value_take_boxed (&entry,
       
   955           dbus_g_type_specialized_construct (TP_CHANNEL_STREAM_TYPE));
       
   956 
       
   957       dbus_g_type_struct_set (&entry,
       
   958           0, id,
       
   959           1, peer,
       
   960           2, type,
       
   961           3, connection_state,
       
   962           4, COMBINED_DIRECTION_GET_DIRECTION (combined_direction),
       
   963           5, COMBINED_DIRECTION_GET_PENDING_SEND (combined_direction),
       
   964           G_MAXUINT);
       
   965 
       
   966       g_ptr_array_add (ret, g_value_get_boxed (&entry));
       
   967     }
       
   968 
       
   969   return ret;
       
   970 }
       
   971 
       
   972 /**
       
   973  * gabble_media_channel_list_streams
       
   974  *
       
   975  * Implements D-Bus method ListStreams
       
   976  * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
       
   977  *
       
   978  * @error: Used to return a pointer to a GError detailing any error
       
   979  *         that occurred, D-Bus will throw the error only if this
       
   980  *         function returns FALSE.
       
   981  *
       
   982  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   983  */
       
   984 gboolean
       
   985 gabble_media_channel_list_streams (GabbleMediaChannel *self,
       
   986                                    GPtrArray **ret,
       
   987                                    GError **error)
       
   988 {
       
   989   GabbleMediaChannelPrivate *priv;
       
   990 
       
   991   g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
       
   992 
       
   993   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
   994 
       
   995   /* no session yet? return an empty array */
       
   996   if (priv->session == NULL)
       
   997     {
       
   998       *ret = g_ptr_array_new ();
       
   999 
       
  1000       return TRUE;
       
  1001     }
       
  1002 
       
  1003   *ret = make_stream_list (self, priv->streams);
       
  1004 
       
  1005   return TRUE;
       
  1006 }
       
  1007 
       
  1008 
       
  1009 /**
       
  1010  * gabble_media_channel_remove_members
       
  1011  *
       
  1012  * Implements D-Bus method RemoveMembers
       
  1013  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  1014  *
       
  1015  * @error: Used to return a pointer to a GError detailing any error
       
  1016  *         that occurred, D-Bus will throw the error only if this
       
  1017  *         function returns FALSE.
       
  1018  *
       
  1019  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  1020  */
       
  1021 gboolean
       
  1022 gabble_media_channel_remove_members (GabbleMediaChannel *self,
       
  1023                                      const GArray *contacts,
       
  1024                                      const gchar *message,
       
  1025                                      GError **error)
       
  1026 {
       
  1027   return gabble_group_mixin_remove_members (G_OBJECT (self), contacts, message,
       
  1028       error);
       
  1029 }
       
  1030 
       
  1031 
       
  1032 static GabbleMediaStream *
       
  1033 _find_stream_by_id (GabbleMediaChannel *chan, guint stream_id)
       
  1034 {
       
  1035   GabbleMediaChannelPrivate *priv;
       
  1036   guint i;
       
  1037 
       
  1038   g_assert (GABBLE_IS_MEDIA_CHANNEL (chan));
       
  1039 
       
  1040   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
  1041 
       
  1042   for (i = 0; i < priv->streams->len; i++)
       
  1043     {
       
  1044       GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i);
       
  1045       guint id;
       
  1046 
       
  1047       g_object_get (stream, "id", &id, NULL);
       
  1048       if (id == stream_id)
       
  1049         return stream;
       
  1050     }
       
  1051 
       
  1052   return NULL;
       
  1053 }
       
  1054 
       
  1055 /**
       
  1056  * gabble_media_channel_remove_streams
       
  1057  *
       
  1058  * Implements DBus method RemoveStreams
       
  1059  * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
       
  1060  *
       
  1061  * @error: Used to return a pointer to a GError detailing any error
       
  1062  *         that occured, DBus will throw the error only if this
       
  1063  *         function returns false.
       
  1064  *
       
  1065  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  1066  */
       
  1067 gboolean gabble_media_channel_remove_streams (GabbleMediaChannel *obj, const GArray * streams, GError **error)
       
  1068 {
       
  1069   GabbleMediaChannelPrivate *priv;
       
  1070   GPtrArray *stream_objs;
       
  1071   guint i;
       
  1072 
       
  1073   g_assert (GABBLE_IS_MEDIA_CHANNEL (obj));
       
  1074 
       
  1075   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (obj);
       
  1076 
       
  1077   *error = NULL;
       
  1078 
       
  1079   stream_objs = g_ptr_array_sized_new (streams->len);
       
  1080 
       
  1081   /* check that all stream ids are valid and at the same time build an array
       
  1082    * of stream objects so we don't have to look them up again after verifying
       
  1083    * all stream identifiers. */
       
  1084   for (i = 0; i < streams->len; i++)
       
  1085     {
       
  1086       guint id = g_array_index (streams, guint, i);
       
  1087       GabbleMediaStream *stream;
       
  1088       guint j;
       
  1089 
       
  1090       stream = _find_stream_by_id (obj, id);
       
  1091       if (stream == NULL)
       
  1092         {
       
  1093           g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
  1094               "given stream id %u does not exist", id);
       
  1095           goto OUT;
       
  1096         }
       
  1097 
       
  1098       /* make sure we don't allow the client to repeatedly remove the same stream */
       
  1099       for (j = 0; j < stream_objs->len; j++)
       
  1100         {
       
  1101           GabbleMediaStream *tmp = g_ptr_array_index (stream_objs, j);
       
  1102 
       
  1103           if (tmp == stream)
       
  1104             {
       
  1105               stream = NULL;
       
  1106               break;
       
  1107             }
       
  1108         }
       
  1109 
       
  1110       if (stream != NULL)
       
  1111         g_ptr_array_add (stream_objs, stream);
       
  1112     }
       
  1113 
       
  1114   /* groovy, it's all good dude, let's remove them */
       
  1115   if (stream_objs->len > 0)
       
  1116     _gabble_media_session_remove_streams (priv->session, (GabbleMediaStream **)
       
  1117         stream_objs->pdata, stream_objs->len);
       
  1118 
       
  1119 OUT:
       
  1120   g_ptr_array_free (stream_objs, TRUE);
       
  1121 
       
  1122   return (*error == NULL);
       
  1123 }
       
  1124 
       
  1125 
       
  1126 /**
       
  1127  * gabble_media_channel_request_stream_direction
       
  1128  *
       
  1129  * Implements D-Bus method RequestStreamDirection
       
  1130  * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
       
  1131  *
       
  1132  * @error: Used to return a pointer to a GError detailing any error
       
  1133  *         that occurred, D-Bus will throw the error only if this
       
  1134  *         function returns FALSE.
       
  1135  *
       
  1136  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  1137  */
       
  1138 gboolean
       
  1139 gabble_media_channel_request_stream_direction (GabbleMediaChannel *self,
       
  1140                                                guint stream_id,
       
  1141                                                guint stream_direction,
       
  1142                                                GError **error)
       
  1143 {
       
  1144   GabbleMediaChannelPrivate *priv;
       
  1145   GabbleMediaStream *stream;
       
  1146 
       
  1147   g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
       
  1148 
       
  1149   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
  1150 
       
  1151   if (stream_direction > TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)
       
  1152     {
       
  1153       g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
  1154           "given stream direction %u is not valid", stream_direction);
       
  1155       return FALSE;
       
  1156     }
       
  1157 
       
  1158   stream = _find_stream_by_id (self, stream_id);
       
  1159   if (stream == NULL)
       
  1160     {
       
  1161       g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
  1162           "given stream id %u does not exist", stream_id);
       
  1163       return FALSE;
       
  1164     }
       
  1165 
       
  1166   /* streams with no session? I think not... */
       
  1167   g_assert (priv->session != NULL);
       
  1168 
       
  1169   return _gabble_media_session_request_stream_direction (priv->session, stream,
       
  1170       stream_direction, error);
       
  1171 }
       
  1172 
       
  1173 
       
  1174 /**
       
  1175  * gabble_media_channel_request_streams
       
  1176  *
       
  1177  * Implements D-Bus method RequestStreams
       
  1178  * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
       
  1179  *
       
  1180  * @error: Used to return a pointer to a GError detailing any error
       
  1181  *         that occurred, D-Bus will throw the error only if this
       
  1182  *         function returns FALSE.
       
  1183  *
       
  1184  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  1185  */
       
  1186 gboolean
       
  1187 gabble_media_channel_request_streams (GabbleMediaChannel *self,
       
  1188                                       guint contact_handle,
       
  1189                                       const GArray *types,
       
  1190                                       GPtrArray **ret,
       
  1191                                       GError **error)
       
  1192 {
       
  1193   GabbleMediaChannelPrivate *priv;
       
  1194   GPtrArray *streams;
       
  1195 
       
  1196   g_assert (GABBLE_IS_MEDIA_CHANNEL (self));
       
  1197 
       
  1198   priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self);
       
  1199 
       
  1200   if (!gabble_handle_is_valid (priv->conn->handles, TP_HANDLE_TYPE_CONTACT,
       
  1201         contact_handle, error))
       
  1202     return FALSE;
       
  1203 
       
  1204   if (!handle_set_is_member (self->group.members, contact_handle) &&
       
  1205       !handle_set_is_member (self->group.remote_pending, contact_handle))
       
  1206     {
       
  1207       g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
  1208           "given handle %u is not a member of the channel", contact_handle);
       
  1209       return FALSE;
       
  1210     }
       
  1211 
       
  1212   /* if the person is a channel member, we should have a session */
       
  1213   g_assert (priv->session != NULL);
       
  1214 
       
  1215   if (!_gabble_media_session_request_streams (priv->session, types, &streams,
       
  1216         error))
       
  1217     return FALSE;
       
  1218 
       
  1219   *ret = make_stream_list (self, streams);
       
  1220 
       
  1221   g_ptr_array_free (streams, TRUE);
       
  1222 
       
  1223   return TRUE;
       
  1224 }
       
  1225 
       
  1226 
       
  1227 gboolean
       
  1228 _gabble_media_channel_add_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error)
       
  1229 {
       
  1230   GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj);
       
  1231   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
  1232   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
  1233 
       
  1234   /* did we create this channel? */
       
  1235   if (priv->creator == mixin->self_handle)
       
  1236     {
       
  1237       GabblePresence *presence;
       
  1238       GIntSet *set;
       
  1239 
       
  1240       /* yes: check the peer's capabilities */
       
  1241 
       
  1242       presence = gabble_presence_cache_get (priv->conn->presence_cache, handle);
       
  1243 
       
  1244       if (presence == NULL ||
       
  1245           !(presence->caps & PRESENCE_CAP_GOOGLE_VOICE ||
       
  1246             presence->caps & PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO ||
       
  1247             presence->caps & PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO))
       
  1248         {
       
  1249           if (presence == NULL)
       
  1250             gabble_debug (DEBUG_FLAG, "failed to add contact %d (%s) to media channel: "
       
  1251                    "no presence available", handle,
       
  1252                    gabble_handle_inspect (priv->conn->handles,
       
  1253                      TP_HANDLE_TYPE_CONTACT, handle));
       
  1254           else
       
  1255             gabble_debug (DEBUG_FLAG, "failed to add contact %d (%s) to media channel: "
       
  1256                    "caps %x aren't sufficient", handle,
       
  1257                    gabble_handle_inspect (priv->conn->handles,
       
  1258                      TP_HANDLE_TYPE_CONTACT, handle),
       
  1259                    presence->caps);
       
  1260 
       
  1261           g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  1262               "handle %u has no media capabilities", handle);
       
  1263           return FALSE;
       
  1264         }
       
  1265 
       
  1266       /* yes: invite the peer */
       
  1267 
       
  1268 
       
  1269       /* create a new session */
       
  1270       create_session (chan, handle, NULL, NULL);
       
  1271 
       
  1272       /* make the peer remote pending */
       
  1273       set = g_intset_new ();
       
  1274       g_intset_add (set, handle);
       
  1275 
       
  1276       gabble_group_mixin_change_members (obj, "", NULL, NULL, NULL, set, 0, 0);
       
  1277 
       
  1278       g_intset_destroy (set);
       
  1279 
       
  1280       /* and update flags accordingly */
       
  1281       gabble_group_mixin_change_flags (obj,
       
  1282                                        TP_CHANNEL_GROUP_FLAG_CAN_REMOVE |
       
  1283                                        TP_CHANNEL_GROUP_FLAG_CAN_RESCIND,
       
  1284                                        TP_CHANNEL_GROUP_FLAG_CAN_ADD);
       
  1285 
       
  1286       return TRUE;
       
  1287     }
       
  1288   else
       
  1289     {
       
  1290       /* no: has a session been created, is the handle being added ours,
       
  1291        *     and are we in local pending? */
       
  1292 
       
  1293       if (priv->session &&
       
  1294           handle == mixin->self_handle &&
       
  1295           handle_set_is_member (mixin->local_pending, handle))
       
  1296         {
       
  1297           /* yes: accept the request */
       
  1298 
       
  1299           GIntSet *set;
       
  1300 
       
  1301           /* make us a member */
       
  1302           set = g_intset_new ();
       
  1303           g_intset_add (set, handle);
       
  1304 
       
  1305           gabble_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0, 0);
       
  1306 
       
  1307           g_intset_destroy (set);
       
  1308 
       
  1309           /* update flags */
       
  1310           gabble_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD);
       
  1311 
       
  1312           /* signal acceptance */
       
  1313           _gabble_media_session_accept (priv->session);
       
  1314 
       
  1315           return TRUE;
       
  1316         }
       
  1317     }
       
  1318 
       
  1319   g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  1320       "handle %u cannot be added in the current state", handle);
       
  1321   return FALSE;
       
  1322 }
       
  1323 
       
  1324 static gboolean
       
  1325 gabble_media_channel_remove_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error)
       
  1326 {
       
  1327   GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj);
       
  1328   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
  1329   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (obj);
       
  1330   GIntSet *set;
       
  1331 
       
  1332   if (priv->session == NULL)
       
  1333     {
       
  1334       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  1335           "handle %u cannot be removed in the current state", handle);
       
  1336 
       
  1337       return FALSE;
       
  1338     }
       
  1339 
       
  1340   if (priv->creator != mixin->self_handle &&
       
  1341       handle != mixin->self_handle)
       
  1342     {
       
  1343       g_set_error (error, TELEPATHY_ERRORS, PermissionDenied,
       
  1344           "handle %u cannot be removed because you are not the creator of the"
       
  1345           " channel", handle);
       
  1346 
       
  1347       return FALSE;
       
  1348     }
       
  1349 
       
  1350   _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
       
  1351 
       
  1352   /* remove the member */
       
  1353   set = g_intset_new ();
       
  1354   g_intset_add (set, handle);
       
  1355 
       
  1356   gabble_group_mixin_change_members (obj, "", NULL, set, NULL, NULL, 0, 0);
       
  1357 
       
  1358   g_intset_destroy (set);
       
  1359 
       
  1360   /* and update flags accordingly */
       
  1361   gabble_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD,
       
  1362                                    TP_CHANNEL_GROUP_FLAG_CAN_REMOVE |
       
  1363                                    TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
       
  1364 
       
  1365   return TRUE;
       
  1366 }
       
  1367 
       
  1368 static void
       
  1369 session_terminated_cb (GabbleMediaSession *session,
       
  1370                        guint terminator,
       
  1371                        guint reason,
       
  1372                        gpointer user_data)
       
  1373 {
       
  1374   GabbleMediaChannel *channel = (GabbleMediaChannel *) user_data;
       
  1375   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
       
  1376   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (channel);
       
  1377   GError *error = NULL;
       
  1378   gchar *sid;
       
  1379   JingleSessionState state;
       
  1380   GabbleHandle peer;
       
  1381   GIntSet *set;
       
  1382 
       
  1383   g_object_get (session,
       
  1384                 "state", &state,
       
  1385                 "peer", &peer,
       
  1386                 NULL);
       
  1387 
       
  1388   set = g_intset_new ();
       
  1389 
       
  1390   /* remove us and the peer from the member list */
       
  1391   g_intset_add (set, mixin->self_handle);
       
  1392   g_intset_add (set, peer);
       
  1393 
       
  1394   gabble_group_mixin_change_members (G_OBJECT (channel), "", NULL, set, NULL, NULL, terminator, reason);
       
  1395 
       
  1396   /* update flags accordingly -- allow adding, deny removal */
       
  1397   gabble_group_mixin_change_flags (G_OBJECT (channel), TP_CHANNEL_GROUP_FLAG_CAN_ADD,
       
  1398                                    TP_CHANNEL_GROUP_FLAG_CAN_REMOVE);
       
  1399 
       
  1400   /* free the session ID */
       
  1401   g_object_get (priv->session, "session-id", &sid, NULL);
       
  1402   _gabble_media_factory_free_sid (priv->factory, sid);
       
  1403   g_free (sid);
       
  1404 
       
  1405   /* unref streams */
       
  1406   if (priv->streams != NULL)
       
  1407     {
       
  1408       GPtrArray *tmp = priv->streams;
       
  1409 
       
  1410       /* move priv->streams aside so that the stream_close_cb
       
  1411        * doesn't double unref */
       
  1412       priv->streams = NULL;
       
  1413       g_ptr_array_foreach (tmp, (GFunc) g_object_unref, NULL);
       
  1414       g_ptr_array_free (tmp, TRUE);
       
  1415     }
       
  1416 
       
  1417   /* remove the session */
       
  1418   g_object_unref (priv->session);
       
  1419   priv->session = NULL;
       
  1420 
       
  1421   /* close the channel */
       
  1422   if (!gabble_media_channel_close (channel, &error))
       
  1423     {
       
  1424       g_warning ("%s: failed to close media channel: %s", G_STRFUNC,
       
  1425           error->message);
       
  1426     }
       
  1427 }
       
  1428 
       
  1429 
       
  1430 static void
       
  1431 session_state_changed_cb (GabbleMediaSession *session,
       
  1432                           GParamSpec *arg1,
       
  1433                           GabbleMediaChannel *channel)
       
  1434 {
       
  1435   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel);
       
  1436   GabbleGroupMixin *mixin = GABBLE_GROUP_MIXIN (channel);
       
  1437   JingleSessionState state;
       
  1438   GabbleHandle peer;
       
  1439   GIntSet *set;
       
  1440 
       
  1441   g_object_get (session,
       
  1442                 "state", &state,
       
  1443                 "peer", &peer,
       
  1444                 NULL);
       
  1445 
       
  1446   if (state != JS_STATE_ACTIVE)
       
  1447     return;
       
  1448 
       
  1449   if (priv->creator != mixin->self_handle)
       
  1450     return;
       
  1451 
       
  1452   set = g_intset_new ();
       
  1453 
       
  1454   /* add the peer to the member list */
       
  1455   g_intset_add (set, peer);
       
  1456 
       
  1457   gabble_group_mixin_change_members (G_OBJECT (channel), "", set, NULL, NULL, NULL, 0, 0);
       
  1458 
       
  1459   /* update flags accordingly -- allow removal, deny adding and rescinding */
       
  1460   gabble_group_mixin_change_flags (G_OBJECT (channel),
       
  1461       TP_CHANNEL_GROUP_FLAG_CAN_REMOVE,
       
  1462       TP_CHANNEL_GROUP_FLAG_CAN_ADD |
       
  1463       TP_CHANNEL_GROUP_FLAG_CAN_RESCIND);
       
  1464 
       
  1465   g_intset_destroy (set);
       
  1466 }
       
  1467 
       
  1468 static void
       
  1469 stream_close_cb (GabbleMediaStream *stream,
       
  1470                  GabbleMediaChannel *chan)
       
  1471 {
       
  1472   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
  1473   guint id;
       
  1474 
       
  1475   g_object_get (stream, "id", &id, NULL);
       
  1476 
       
  1477   g_signal_emit (chan, signals[STREAM_REMOVED], 0, id);
       
  1478 
       
  1479   if (priv->streams != NULL)
       
  1480     {
       
  1481       g_ptr_array_remove (priv->streams, stream);
       
  1482       g_object_unref (stream);
       
  1483     }
       
  1484 }
       
  1485 
       
  1486 static void
       
  1487 stream_error_cb (GabbleMediaStream *stream,
       
  1488                  TpMediaStreamError errno,
       
  1489                  const gchar *message,
       
  1490                  GabbleMediaChannel *chan)
       
  1491 {
       
  1492   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
  1493   guint id;
       
  1494 
       
  1495   /* emit signal */
       
  1496   g_object_get (stream, "id", &id, NULL);
       
  1497   g_signal_emit (chan, signals[STREAM_ERROR], 0, id, errno, message);
       
  1498 
       
  1499   /* remove stream from session */
       
  1500   _gabble_media_session_remove_streams (priv->session, &stream, 1);
       
  1501 }
       
  1502 
       
  1503 static void
       
  1504 stream_state_changed_cb (GabbleMediaStream *stream,
       
  1505                          GParamSpec *pspec,
       
  1506                          GabbleMediaChannel *chan)
       
  1507 {
       
  1508   guint id;
       
  1509   TpMediaStreamState connection_state;
       
  1510 
       
  1511   g_object_get (stream, "id", &id, "connection-state", &connection_state, NULL);
       
  1512 
       
  1513   g_signal_emit (chan, signals[STREAM_STATE_CHANGED], 0, id, connection_state);
       
  1514 }
       
  1515 
       
  1516 static void
       
  1517 stream_direction_changed_cb (GabbleMediaStream *stream,
       
  1518                              GParamSpec *pspec,
       
  1519                              GabbleMediaChannel *chan)
       
  1520 {
       
  1521   guint id;
       
  1522   CombinedStreamDirection combined;
       
  1523   TpMediaStreamDirection direction;
       
  1524   TpMediaStreamPendingSend pending_send;
       
  1525 
       
  1526   g_object_get (stream,
       
  1527       "id", &id,
       
  1528       "combined-direction", &combined,
       
  1529       NULL);
       
  1530 
       
  1531   direction = COMBINED_DIRECTION_GET_DIRECTION (combined);
       
  1532   pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined);
       
  1533 
       
  1534   g_signal_emit (chan, signals[STREAM_DIRECTION_CHANGED], 0, id, direction,
       
  1535       pending_send);
       
  1536 }
       
  1537 
       
  1538 static void
       
  1539 session_stream_added_cb (GabbleMediaSession *session,
       
  1540                          GabbleMediaStream  *stream,
       
  1541                          GabbleMediaChannel *chan)
       
  1542 {
       
  1543   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
  1544 
       
  1545   guint id, handle, type;
       
  1546 
       
  1547   /* keep track of the stream */
       
  1548   g_object_ref (stream);
       
  1549   g_ptr_array_add (priv->streams, stream);
       
  1550 
       
  1551   g_signal_connect (stream, "close",
       
  1552                     (GCallback) stream_close_cb, chan);
       
  1553   g_signal_connect (stream, "error",
       
  1554                     (GCallback) stream_error_cb, chan);
       
  1555   g_signal_connect (stream, "notify::connection-state",
       
  1556                     (GCallback) stream_state_changed_cb, chan);
       
  1557   g_signal_connect (stream, "notify::combined-direction",
       
  1558                     (GCallback) stream_direction_changed_cb, chan);
       
  1559 
       
  1560   /* emit StreamAdded */
       
  1561   g_object_get (session, "peer", &handle, NULL);
       
  1562   g_object_get (stream, "id", &id, "media-type", &type, NULL);
       
  1563 
       
  1564   g_signal_emit (chan, signals[STREAM_ADDED], 0, id, handle, type);
       
  1565 }
       
  1566 
       
  1567 guint
       
  1568 _gabble_media_channel_get_stream_id (GabbleMediaChannel *chan)
       
  1569 {
       
  1570   GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan);
       
  1571 
       
  1572   return priv->next_stream_id++;
       
  1573 }
       
  1574 
       
  1575 #define AUDIO_CAPS \
       
  1576   ( PRESENCE_CAP_GOOGLE_VOICE | PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO )
       
  1577 
       
  1578 #define VIDEO_CAPS \
       
  1579   ( PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO )
       
  1580 
       
  1581 GabblePresenceCapabilities
       
  1582 _gabble_media_channel_typeflags_to_caps (TpChannelMediaCapabilities flags)
       
  1583 {
       
  1584   GabblePresenceCapabilities caps = 0;
       
  1585 
       
  1586   if (flags & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO)
       
  1587     caps |= AUDIO_CAPS;
       
  1588 
       
  1589   if (flags & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO)
       
  1590     caps |= VIDEO_CAPS;
       
  1591 
       
  1592   return caps;
       
  1593 }
       
  1594 
       
  1595 TpChannelMediaCapabilities
       
  1596 _gabble_media_channel_caps_to_typeflags (GabblePresenceCapabilities caps)
       
  1597 {
       
  1598   TpChannelMediaCapabilities typeflags = 0;
       
  1599 
       
  1600   if (caps & AUDIO_CAPS)
       
  1601     typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO;
       
  1602 
       
  1603   if (caps & VIDEO_CAPS)
       
  1604     typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_VIDEO;
       
  1605 
       
  1606   return typeflags;
       
  1607 }
       
  1608 
       
  1609 //Added to avoid type casting error in winscw
       
  1610 GabblePresenceCapabilities
       
  1611 _gabble_media_channel_typeflags_to_caps_tmp (guint flags) 
       
  1612 { 
       
  1613 return _gabble_media_channel_typeflags_to_caps( (TpChannelMediaCapabilities) flags);
       
  1614 }
       
  1615 
       
  1616 //Added to avoid type casting error in winscw
       
  1617 guint
       
  1618 _gabble_media_channel_caps_to_typeflags_tmp (GabblePresenceCapabilities caps) 
       
  1619 { 
       
  1620 return (guint) _gabble_media_channel_caps_to_typeflags(caps);
       
  1621 }
       
  1622