auth: simplify auth checks
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-client.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include <stdio.h>
21 #include <string.h>
22
23 #include "rtsp-client.h"
24 #include "rtsp-sdp.h"
25 #include "rtsp-params.h"
26
27 #define GST_RTSP_CLIENT_GET_PRIVATE(obj)  \
28    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_CLIENT, GstRTSPClientPrivate))
29
30 /* locking order:
31  * send_lock, lock, tunnels_lock
32  */
33
34 struct _GstRTSPClientPrivate
35 {
36   GMutex lock;                  /* protects everything else */
37   GMutex send_lock;
38   GstRTSPConnection *connection;
39   GstRTSPWatch *watch;
40   guint close_seq;
41   gchar *server_ip;
42   gboolean is_ipv6;
43   gboolean use_client_settings;
44
45   GstRTSPClientSendFunc send_func;      /* protected by send_lock */
46   gpointer send_data;           /* protected by send_lock */
47   GDestroyNotify send_notify;   /* protected by send_lock */
48
49   GstRTSPSessionPool *session_pool;
50   GstRTSPMountPoints *mount_points;
51   GstRTSPAuth *auth;
52
53   /* used to cache the media in the last requested DESCRIBE so that
54    * we can pick it up in the next SETUP immediately */
55   gchar *path;
56   GstRTSPMedia *media;
57
58   GList *transports;
59   GList *sessions;
60 };
61
62 static GMutex tunnels_lock;
63 static GHashTable *tunnels;     /* protected by tunnels_lock */
64
65 #define DEFAULT_SESSION_POOL            NULL
66 #define DEFAULT_MOUNT_POINTS            NULL
67 #define DEFAULT_USE_CLIENT_SETTINGS     FALSE
68
69 enum
70 {
71   PROP_0,
72   PROP_SESSION_POOL,
73   PROP_MOUNT_POINTS,
74   PROP_USE_CLIENT_SETTINGS,
75   PROP_LAST
76 };
77
78 enum
79 {
80   SIGNAL_CLOSED,
81   SIGNAL_NEW_SESSION,
82   SIGNAL_OPTIONS_REQUEST,
83   SIGNAL_DESCRIBE_REQUEST,
84   SIGNAL_SETUP_REQUEST,
85   SIGNAL_PLAY_REQUEST,
86   SIGNAL_PAUSE_REQUEST,
87   SIGNAL_TEARDOWN_REQUEST,
88   SIGNAL_SET_PARAMETER_REQUEST,
89   SIGNAL_GET_PARAMETER_REQUEST,
90   SIGNAL_LAST
91 };
92
93 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
94 #define GST_CAT_DEFAULT rtsp_client_debug
95
96 static guint gst_rtsp_client_signals[SIGNAL_LAST] = { 0 };
97
98 static void gst_rtsp_client_get_property (GObject * object, guint propid,
99     GValue * value, GParamSpec * pspec);
100 static void gst_rtsp_client_set_property (GObject * object, guint propid,
101     const GValue * value, GParamSpec * pspec);
102 static void gst_rtsp_client_finalize (GObject * obj);
103
104 static GstSDPMessage *create_sdp (GstRTSPClient * client, GstRTSPMedia * media);
105 static void client_session_finalized (GstRTSPClient * client,
106     GstRTSPSession * session);
107 static void unlink_session_transports (GstRTSPClient * client,
108     GstRTSPSession * session, GstRTSPSessionMedia * sessmedia);
109 static gboolean default_configure_client_transport (GstRTSPClient * client,
110     GstRTSPClientState * state, GstRTSPTransport * ct);
111 static GstRTSPResult default_params_set (GstRTSPClient * client,
112     GstRTSPClientState * state);
113 static GstRTSPResult default_params_get (GstRTSPClient * client,
114     GstRTSPClientState * state);
115
116 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
117
118 static void
119 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
120 {
121   GObjectClass *gobject_class;
122
123   g_type_class_add_private (klass, sizeof (GstRTSPClientPrivate));
124
125   gobject_class = G_OBJECT_CLASS (klass);
126
127   gobject_class->get_property = gst_rtsp_client_get_property;
128   gobject_class->set_property = gst_rtsp_client_set_property;
129   gobject_class->finalize = gst_rtsp_client_finalize;
130
131   klass->create_sdp = create_sdp;
132   klass->configure_client_transport = default_configure_client_transport;
133   klass->params_set = default_params_set;
134   klass->params_get = default_params_get;
135
136   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
137       g_param_spec_object ("session-pool", "Session Pool",
138           "The session pool to use for client session",
139           GST_TYPE_RTSP_SESSION_POOL,
140           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
141
142   g_object_class_install_property (gobject_class, PROP_MOUNT_POINTS,
143       g_param_spec_object ("mount-points", "Mount Points",
144           "The mount points to use for client session",
145           GST_TYPE_RTSP_MOUNT_POINTS,
146           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
147
148   g_object_class_install_property (gobject_class, PROP_USE_CLIENT_SETTINGS,
149       g_param_spec_boolean ("use-client-settings", "Use Client Settings",
150           "Use client settings for ttl and destination in multicast",
151           DEFAULT_USE_CLIENT_SETTINGS,
152           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
153
154   gst_rtsp_client_signals[SIGNAL_CLOSED] =
155       g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
156       G_STRUCT_OFFSET (GstRTSPClientClass, closed), NULL, NULL,
157       g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
158
159   gst_rtsp_client_signals[SIGNAL_NEW_SESSION] =
160       g_signal_new ("new-session", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
161       G_STRUCT_OFFSET (GstRTSPClientClass, new_session), NULL, NULL,
162       g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_RTSP_SESSION);
163
164   gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST] =
165       g_signal_new ("options-request", G_TYPE_FROM_CLASS (klass),
166       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, options_request),
167       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
168       G_TYPE_POINTER);
169
170   gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST] =
171       g_signal_new ("describe-request", G_TYPE_FROM_CLASS (klass),
172       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, describe_request),
173       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
174       G_TYPE_POINTER);
175
176   gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST] =
177       g_signal_new ("setup-request", G_TYPE_FROM_CLASS (klass),
178       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, setup_request),
179       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
180       G_TYPE_POINTER);
181
182   gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST] =
183       g_signal_new ("play-request", G_TYPE_FROM_CLASS (klass),
184       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, play_request),
185       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
186       G_TYPE_POINTER);
187
188   gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST] =
189       g_signal_new ("pause-request", G_TYPE_FROM_CLASS (klass),
190       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, pause_request),
191       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
192       G_TYPE_POINTER);
193
194   gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST] =
195       g_signal_new ("teardown-request", G_TYPE_FROM_CLASS (klass),
196       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, teardown_request),
197       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
198       G_TYPE_POINTER);
199
200   gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST] =
201       g_signal_new ("set-parameter-request", G_TYPE_FROM_CLASS (klass),
202       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
203           set_parameter_request), NULL, NULL, g_cclosure_marshal_VOID__POINTER,
204       G_TYPE_NONE, 1, G_TYPE_POINTER);
205
206   gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST] =
207       g_signal_new ("get-parameter-request", G_TYPE_FROM_CLASS (klass),
208       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
209           get_parameter_request), NULL, NULL, g_cclosure_marshal_VOID__POINTER,
210       G_TYPE_NONE, 1, G_TYPE_POINTER);
211
212   tunnels =
213       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
214   g_mutex_init (&tunnels_lock);
215
216   GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
217 }
218
219 static void
220 gst_rtsp_client_init (GstRTSPClient * client)
221 {
222   GstRTSPClientPrivate *priv = GST_RTSP_CLIENT_GET_PRIVATE (client);
223
224   client->priv = priv;
225
226   g_mutex_init (&priv->lock);
227   g_mutex_init (&priv->send_lock);
228   priv->use_client_settings = DEFAULT_USE_CLIENT_SETTINGS;
229   priv->close_seq = 0;
230 }
231
232 static GstRTSPFilterResult
233 filter_session (GstRTSPSession * sess, GstRTSPSessionMedia * sessmedia,
234     gpointer user_data)
235 {
236   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
237
238   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
239   unlink_session_transports (client, sess, sessmedia);
240
241   /* unmanage the media in the session */
242   return GST_RTSP_FILTER_REMOVE;
243 }
244
245 static void
246 client_unlink_session (GstRTSPClient * client, GstRTSPSession * session)
247 {
248   /* unlink all media managed in this session */
249   gst_rtsp_session_filter (session, filter_session, client);
250 }
251
252 static void
253 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
254 {
255   GstRTSPClientPrivate *priv = client->priv;
256   GList *walk;
257
258   for (walk = priv->sessions; walk; walk = g_list_next (walk)) {
259     GstRTSPSession *msession = (GstRTSPSession *) walk->data;
260
261     /* we already know about this session */
262     if (msession == session)
263       return;
264   }
265
266   GST_INFO ("watching session %p", session);
267
268   g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized,
269       client);
270   priv->sessions = g_list_prepend (priv->sessions, session);
271 }
272
273 static void
274 client_unwatch_session (GstRTSPClient * client, GstRTSPSession * session)
275 {
276   GstRTSPClientPrivate *priv = client->priv;
277
278   GST_INFO ("unwatching session %p", session);
279
280   g_object_weak_unref (G_OBJECT (session),
281       (GWeakNotify) client_session_finalized, client);
282   priv->sessions = g_list_remove (priv->sessions, session);
283 }
284
285 static void
286 client_cleanup_session (GstRTSPClient * client, GstRTSPSession * session)
287 {
288   g_object_weak_unref (G_OBJECT (session),
289       (GWeakNotify) client_session_finalized, client);
290   client_unlink_session (client, session);
291 }
292
293 static void
294 client_cleanup_sessions (GstRTSPClient * client)
295 {
296   GstRTSPClientPrivate *priv = client->priv;
297   GList *sessions;
298
299   /* remove weak-ref from sessions */
300   for (sessions = priv->sessions; sessions; sessions = g_list_next (sessions)) {
301     client_cleanup_session (client, (GstRTSPSession *) sessions->data);
302   }
303   g_list_free (priv->sessions);
304   priv->sessions = NULL;
305 }
306
307 /* A client is finalized when the connection is broken */
308 static void
309 gst_rtsp_client_finalize (GObject * obj)
310 {
311   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
312   GstRTSPClientPrivate *priv = client->priv;
313
314   GST_INFO ("finalize client %p", client);
315
316   gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
317
318   if (priv->watch)
319     g_source_destroy ((GSource *) priv->watch);
320
321   client_cleanup_sessions (client);
322
323   if (priv->connection)
324     gst_rtsp_connection_free (priv->connection);
325   if (priv->session_pool)
326     g_object_unref (priv->session_pool);
327   if (priv->mount_points)
328     g_object_unref (priv->mount_points);
329   if (priv->auth)
330     g_object_unref (priv->auth);
331
332   if (priv->path)
333     g_free (priv->path);
334   if (priv->media) {
335     gst_rtsp_media_unprepare (priv->media);
336     g_object_unref (priv->media);
337   }
338
339   g_free (priv->server_ip);
340   g_mutex_clear (&priv->lock);
341   g_mutex_clear (&priv->send_lock);
342
343   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
344 }
345
346 static void
347 gst_rtsp_client_get_property (GObject * object, guint propid,
348     GValue * value, GParamSpec * pspec)
349 {
350   GstRTSPClient *client = GST_RTSP_CLIENT (object);
351
352   switch (propid) {
353     case PROP_SESSION_POOL:
354       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
355       break;
356     case PROP_MOUNT_POINTS:
357       g_value_take_object (value, gst_rtsp_client_get_mount_points (client));
358       break;
359     case PROP_USE_CLIENT_SETTINGS:
360       g_value_set_boolean (value,
361           gst_rtsp_client_get_use_client_settings (client));
362       break;
363     default:
364       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
365   }
366 }
367
368 static void
369 gst_rtsp_client_set_property (GObject * object, guint propid,
370     const GValue * value, GParamSpec * pspec)
371 {
372   GstRTSPClient *client = GST_RTSP_CLIENT (object);
373
374   switch (propid) {
375     case PROP_SESSION_POOL:
376       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
377       break;
378     case PROP_MOUNT_POINTS:
379       gst_rtsp_client_set_mount_points (client, g_value_get_object (value));
380       break;
381     case PROP_USE_CLIENT_SETTINGS:
382       gst_rtsp_client_set_use_client_settings (client,
383           g_value_get_boolean (value));
384       break;
385     default:
386       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
387   }
388 }
389
390 /**
391  * gst_rtsp_client_new:
392  *
393  * Create a new #GstRTSPClient instance.
394  *
395  * Returns: a new #GstRTSPClient
396  */
397 GstRTSPClient *
398 gst_rtsp_client_new (void)
399 {
400   GstRTSPClient *result;
401
402   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
403
404   return result;
405 }
406
407 static void
408 send_message (GstRTSPClient * client, GstRTSPSession * session,
409     GstRTSPMessage * message, gboolean close)
410 {
411   GstRTSPClientPrivate *priv = client->priv;
412
413   gst_rtsp_message_add_header (message, GST_RTSP_HDR_SERVER,
414       "GStreamer RTSP server");
415
416   /* remove any previous header */
417   gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
418
419   /* add the new session header for new session ids */
420   if (session) {
421     gst_rtsp_message_take_header (message, GST_RTSP_HDR_SESSION,
422         gst_rtsp_session_get_header (session));
423   }
424
425   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
426     gst_rtsp_message_dump (message);
427   }
428
429   if (close)
430     gst_rtsp_message_add_header (message, GST_RTSP_HDR_CONNECTION, "close");
431
432   g_mutex_lock (&priv->send_lock);
433   if (priv->send_func)
434     priv->send_func (client, message, close, priv->send_data);
435   g_mutex_unlock (&priv->send_lock);
436
437   gst_rtsp_message_unset (message);
438 }
439
440 static void
441 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
442     GstRTSPClientState * state)
443 {
444   gst_rtsp_message_init_response (state->response, code,
445       gst_rtsp_status_as_text (code), state->request);
446
447   send_message (client, NULL, state->response, FALSE);
448 }
449
450 static void
451 handle_unauthorized_request (GstRTSPClient * client, GstRTSPAuth * auth,
452     GstRTSPClientState * state)
453 {
454   gst_rtsp_message_init_response (state->response, GST_RTSP_STS_UNAUTHORIZED,
455       gst_rtsp_status_as_text (GST_RTSP_STS_UNAUTHORIZED), state->request);
456
457   if (auth) {
458     /* and let the authentication manager setup the auth tokens */
459     gst_rtsp_auth_setup (auth, state);
460   }
461
462   send_message (client, state->session, state->response, FALSE);
463 }
464
465
466 static gboolean
467 paths_are_equal (const gchar * path1, const gchar * path2, gint len2)
468 {
469   if (path1 == NULL || path2 == NULL)
470     return FALSE;
471
472   if (strlen (path1) != len2)
473     return FALSE;
474
475   if (strncmp (path1, path2, len2))
476     return FALSE;
477
478   return TRUE;
479 }
480
481 /* this function is called to initially find the media for the DESCRIBE request
482  * but is cached for when the same client (without breaking the connection) is
483  * doing a setup for the exact same url. */
484 static GstRTSPMedia *
485 find_media (GstRTSPClient * client, GstRTSPClientState * state, gint * matched)
486 {
487   GstRTSPClientPrivate *priv = client->priv;
488   GstRTSPMediaFactory *factory;
489   GstRTSPMedia *media;
490   gchar *path;
491   gint path_len;
492
493   if (!priv->mount_points)
494     goto no_mount_points;
495
496   path = state->uri->abspath;
497
498   /* find the longest matching factory for the uri first */
499   if (!(factory = gst_rtsp_mount_points_match (priv->mount_points,
500               path, matched)))
501     goto no_factory;
502
503   state->factory = factory;
504
505   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_ACCESS))
506     goto no_factory_access;
507
508   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_CONSTRUCT))
509     goto not_authorized;
510
511   if (matched)
512     path_len = *matched;
513   else
514     path_len = strlen (path);
515
516   if (!paths_are_equal (priv->path, path, path_len)) {
517     /* remove any previously cached values before we try to construct a new
518      * media for uri */
519     if (priv->path)
520       g_free (priv->path);
521     priv->path = NULL;
522     if (priv->media) {
523       gst_rtsp_media_unprepare (priv->media);
524       g_object_unref (priv->media);
525     }
526     priv->media = NULL;
527
528     /* prepare the media and add it to the pipeline */
529     if (!(media = gst_rtsp_media_factory_construct (factory, state->uri)))
530       goto no_media;
531
532     /* prepare the media */
533     if (!(gst_rtsp_media_prepare (media, NULL)))
534       goto no_prepare;
535
536     /* now keep track of the uri and the media */
537     priv->path = g_strndup (path, path_len);
538     priv->media = media;
539     state->media = media;
540   } else {
541     /* we have seen this path before, used cached media */
542     media = priv->media;
543     state->media = media;
544     GST_INFO ("reusing cached media %p for path %s", media, priv->path);
545   }
546
547   g_object_unref (factory);
548   state->factory = NULL;
549
550   if (media)
551     g_object_ref (media);
552
553   return media;
554
555   /* ERRORS */
556 no_mount_points:
557   {
558     GST_ERROR ("client %p: no mount points configured", client);
559     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
560     return NULL;
561   }
562 no_factory:
563   {
564     GST_ERROR ("client %p: no factory for uri %s", client, path);
565     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
566     return NULL;
567   }
568 no_factory_access:
569   {
570     GST_ERROR ("client %p: not authorized to see factory uri %s", client, path);
571     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
572     return NULL;
573   }
574 not_authorized:
575   {
576     GST_ERROR ("client %p: not authorized for factory uri %s", client, path);
577     handle_unauthorized_request (client, priv->auth, state);
578     return NULL;
579   }
580 no_media:
581   {
582     GST_ERROR ("client %p: can't create media", client);
583     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, state);
584     g_object_unref (factory);
585     state->factory = NULL;
586     return NULL;
587   }
588 no_prepare:
589   {
590     GST_ERROR ("client %p: can't prepare media", client);
591     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, state);
592     g_object_unref (media);
593     state->media = media;
594     g_object_unref (factory);
595     state->factory = NULL;
596     return NULL;
597   }
598 }
599
600 static gboolean
601 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
602 {
603   GstRTSPClientPrivate *priv = client->priv;
604   GstRTSPMessage message = { 0 };
605   GstMapInfo map_info;
606   guint8 *data;
607   guint usize;
608
609   gst_rtsp_message_init_data (&message, channel);
610
611   /* FIXME, need some sort of iovec RTSPMessage here */
612   if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ))
613     return FALSE;
614
615   gst_rtsp_message_take_body (&message, map_info.data, map_info.size);
616
617   g_mutex_lock (&priv->send_lock);
618   if (priv->send_func)
619     priv->send_func (client, &message, FALSE, priv->send_data);
620   g_mutex_unlock (&priv->send_lock);
621
622   gst_rtsp_message_steal_body (&message, &data, &usize);
623   gst_buffer_unmap (buffer, &map_info);
624
625   gst_rtsp_message_unset (&message);
626
627   return TRUE;
628 }
629
630 static void
631 link_transport (GstRTSPClient * client, GstRTSPSession * session,
632     GstRTSPStreamTransport * trans)
633 {
634   GstRTSPClientPrivate *priv = client->priv;
635
636   GST_DEBUG ("client %p: linking transport %p", client, trans);
637
638   gst_rtsp_stream_transport_set_callbacks (trans,
639       (GstRTSPSendFunc) do_send_data,
640       (GstRTSPSendFunc) do_send_data, client, NULL);
641
642   priv->transports = g_list_prepend (priv->transports, trans);
643
644   /* make sure our session can't expire */
645   gst_rtsp_session_prevent_expire (session);
646 }
647
648 static void
649 unlink_transport (GstRTSPClient * client, GstRTSPSession * session,
650     GstRTSPStreamTransport * trans)
651 {
652   GstRTSPClientPrivate *priv = client->priv;
653
654   GST_DEBUG ("client %p: unlinking transport %p", client, trans);
655
656   gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
657
658   priv->transports = g_list_remove (priv->transports, trans);
659
660   /* our session can now expire */
661   gst_rtsp_session_allow_expire (session);
662 }
663
664 static void
665 unlink_session_transports (GstRTSPClient * client, GstRTSPSession * session,
666     GstRTSPSessionMedia * sessmedia)
667 {
668   guint n_streams, i;
669
670   n_streams =
671       gst_rtsp_media_n_streams (gst_rtsp_session_media_get_media (sessmedia));
672   for (i = 0; i < n_streams; i++) {
673     GstRTSPStreamTransport *trans;
674     const GstRTSPTransport *tr;
675
676     /* get the transport, if there is no transport configured, skip this stream */
677     trans = gst_rtsp_session_media_get_transport (sessmedia, i);
678     if (trans == NULL)
679       continue;
680
681     tr = gst_rtsp_stream_transport_get_transport (trans);
682
683     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
684       /* for TCP, unlink the stream from the TCP connection of the client */
685       unlink_transport (client, session, trans);
686     }
687   }
688 }
689
690 static void
691 close_connection (GstRTSPClient * client)
692 {
693   GstRTSPClientPrivate *priv = client->priv;
694   const gchar *tunnelid;
695
696   GST_DEBUG ("client %p: closing connection", client);
697
698   if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
699     g_mutex_lock (&tunnels_lock);
700     /* remove from tunnelids */
701     g_hash_table_remove (tunnels, tunnelid);
702     g_mutex_unlock (&tunnels_lock);
703   }
704
705   gst_rtsp_connection_close (priv->connection);
706 }
707
708 static gboolean
709 handle_teardown_request (GstRTSPClient * client, GstRTSPClientState * state)
710 {
711   GstRTSPClientPrivate *priv = client->priv;
712   GstRTSPSession *session;
713   GstRTSPSessionMedia *sessmedia;
714   GstRTSPStatusCode code;
715   const gchar *path;
716   gint matched;
717
718   if (!state->session)
719     goto no_session;
720
721   session = state->session;
722
723   if (!state->uri)
724     goto no_uri;
725
726   path = state->uri->abspath;
727
728   /* get a handle to the configuration of the media in the session */
729   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
730   if (!sessmedia)
731     goto not_found;
732
733   /* only aggregate control for now.. */
734   if (path[matched] != '\0')
735     goto no_aggregate;
736
737   state->sessmedia = sessmedia;
738
739   /* we emit the signal before closing the connection */
740   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST],
741       0, state);
742
743   /* unlink the all TCP callbacks */
744   unlink_session_transports (client, session, sessmedia);
745
746   /* remove the session from the watched sessions */
747   client_unwatch_session (client, session);
748
749   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
750
751   /* unmanage the media in the session, returns false if all media session
752    * are torn down. */
753   if (!gst_rtsp_session_release_media (session, sessmedia)) {
754     /* remove the session */
755     gst_rtsp_session_pool_remove (priv->session_pool, session);
756   }
757   /* construct the response now */
758   code = GST_RTSP_STS_OK;
759   gst_rtsp_message_init_response (state->response, code,
760       gst_rtsp_status_as_text (code), state->request);
761
762   send_message (client, session, state->response, TRUE);
763
764   return TRUE;
765
766   /* ERRORS */
767 no_session:
768   {
769     GST_ERROR ("client %p: no session", client);
770     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, state);
771     return FALSE;
772   }
773 no_uri:
774   {
775     GST_ERROR ("client %p: no uri supplied", client);
776     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, state);
777     return FALSE;
778   }
779 not_found:
780   {
781     GST_ERROR ("client %p: no media for uri", client);
782     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
783     return FALSE;
784   }
785 no_aggregate:
786   {
787     GST_ERROR ("client %p: no aggregate path %s", client, path);
788     send_generic_response (client,
789         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, state);
790     return FALSE;
791   }
792 }
793
794 static GstRTSPResult
795 default_params_set (GstRTSPClient * client, GstRTSPClientState * state)
796 {
797   GstRTSPResult res;
798
799   res = gst_rtsp_params_set (client, state);
800
801   return res;
802 }
803
804 static GstRTSPResult
805 default_params_get (GstRTSPClient * client, GstRTSPClientState * state)
806 {
807   GstRTSPResult res;
808
809   res = gst_rtsp_params_get (client, state);
810
811   return res;
812 }
813
814 static gboolean
815 handle_get_param_request (GstRTSPClient * client, GstRTSPClientState * state)
816 {
817   GstRTSPResult res;
818   guint8 *data;
819   guint size;
820
821   res = gst_rtsp_message_get_body (state->request, &data, &size);
822   if (res != GST_RTSP_OK)
823     goto bad_request;
824
825   if (size == 0) {
826     /* no body, keep-alive request */
827     send_generic_response (client, GST_RTSP_STS_OK, state);
828   } else {
829     /* there is a body, handle the params */
830     res = GST_RTSP_CLIENT_GET_CLASS (client)->params_get (client, state);
831     if (res != GST_RTSP_OK)
832       goto bad_request;
833
834     send_message (client, state->session, state->response, FALSE);
835   }
836
837   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST],
838       0, state);
839
840   return TRUE;
841
842   /* ERRORS */
843 bad_request:
844   {
845     GST_ERROR ("client %p: bad request", client);
846     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, state);
847     return FALSE;
848   }
849 }
850
851 static gboolean
852 handle_set_param_request (GstRTSPClient * client, GstRTSPClientState * state)
853 {
854   GstRTSPResult res;
855   guint8 *data;
856   guint size;
857
858   res = gst_rtsp_message_get_body (state->request, &data, &size);
859   if (res != GST_RTSP_OK)
860     goto bad_request;
861
862   if (size == 0) {
863     /* no body, keep-alive request */
864     send_generic_response (client, GST_RTSP_STS_OK, state);
865   } else {
866     /* there is a body, handle the params */
867     res = GST_RTSP_CLIENT_GET_CLASS (client)->params_set (client, state);
868     if (res != GST_RTSP_OK)
869       goto bad_request;
870
871     send_message (client, state->session, state->response, FALSE);
872   }
873
874   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST],
875       0, state);
876
877   return TRUE;
878
879   /* ERRORS */
880 bad_request:
881   {
882     GST_ERROR ("client %p: bad request", client);
883     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, state);
884     return FALSE;
885   }
886 }
887
888 static gboolean
889 handle_pause_request (GstRTSPClient * client, GstRTSPClientState * state)
890 {
891   GstRTSPSession *session;
892   GstRTSPSessionMedia *sessmedia;
893   GstRTSPStatusCode code;
894   GstRTSPState rtspstate;
895   const gchar *path;
896   gint matched;
897
898   if (!(session = state->session))
899     goto no_session;
900
901   if (!state->uri)
902     goto no_uri;
903
904   path = state->uri->abspath;
905
906   /* get a handle to the configuration of the media in the session */
907   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
908   if (!sessmedia)
909     goto not_found;
910
911   if (path[matched] != '\0')
912     goto no_aggregate;
913
914   state->sessmedia = sessmedia;
915
916   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
917   /* the session state must be playing or recording */
918   if (rtspstate != GST_RTSP_STATE_PLAYING &&
919       rtspstate != GST_RTSP_STATE_RECORDING)
920     goto invalid_state;
921
922   /* unlink the all TCP callbacks */
923   unlink_session_transports (client, session, sessmedia);
924
925   /* then pause sending */
926   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PAUSED);
927
928   /* construct the response now */
929   code = GST_RTSP_STS_OK;
930   gst_rtsp_message_init_response (state->response, code,
931       gst_rtsp_status_as_text (code), state->request);
932
933   send_message (client, session, state->response, FALSE);
934
935   /* the state is now READY */
936   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
937
938   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST],
939       0, state);
940
941   return TRUE;
942
943   /* ERRORS */
944 no_session:
945   {
946     GST_ERROR ("client %p: no seesion", client);
947     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, state);
948     return FALSE;
949   }
950 no_uri:
951   {
952     GST_ERROR ("client %p: no uri supplied", client);
953     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, state);
954     return FALSE;
955   }
956 not_found:
957   {
958     GST_ERROR ("client %p: no media for uri", client);
959     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
960     return FALSE;
961   }
962 no_aggregate:
963   {
964     GST_ERROR ("client %p: no aggregate path %s", client, path);
965     send_generic_response (client,
966         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, state);
967     return FALSE;
968   }
969 invalid_state:
970   {
971     GST_ERROR ("client %p: not PLAYING or RECORDING", client);
972     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
973         state);
974     return FALSE;
975   }
976 }
977
978 static gboolean
979 handle_play_request (GstRTSPClient * client, GstRTSPClientState * state)
980 {
981   GstRTSPSession *session;
982   GstRTSPSessionMedia *sessmedia;
983   GstRTSPMedia *media;
984   GstRTSPStatusCode code;
985   GString *rtpinfo;
986   guint n_streams, i, infocount;
987   gchar *str;
988   GstRTSPTimeRange *range;
989   GstRTSPResult res;
990   GstRTSPState rtspstate;
991   GstRTSPRangeUnit unit = GST_RTSP_RANGE_NPT;
992   const gchar *path;
993   gint matched;
994
995   if (!(session = state->session))
996     goto no_session;
997
998   if (!state->uri)
999     goto no_uri;
1000
1001   path = state->uri->abspath;
1002
1003   /* get a handle to the configuration of the media in the session */
1004   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1005   if (!sessmedia)
1006     goto not_found;
1007
1008   if (path[matched] != '\0')
1009     goto no_aggregate;
1010
1011   state->sessmedia = sessmedia;
1012   state->media = media = gst_rtsp_session_media_get_media (sessmedia);
1013
1014   /* the session state must be playing or ready */
1015   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1016   if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
1017     goto invalid_state;
1018
1019   /* parse the range header if we have one */
1020   res =
1021       gst_rtsp_message_get_header (state->request, GST_RTSP_HDR_RANGE, &str, 0);
1022   if (res == GST_RTSP_OK) {
1023     if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
1024       /* we have a range, seek to the position */
1025       unit = range->unit;
1026       gst_rtsp_media_seek (media, range);
1027       gst_rtsp_range_free (range);
1028     }
1029   }
1030
1031   /* grab RTPInfo from the payloaders now */
1032   rtpinfo = g_string_new ("");
1033
1034   n_streams = gst_rtsp_media_n_streams (media);
1035   for (i = 0, infocount = 0; i < n_streams; i++) {
1036     GstRTSPStreamTransport *trans;
1037     GstRTSPStream *stream;
1038     const GstRTSPTransport *tr;
1039     gchar *uristr;
1040     guint rtptime, seq;
1041
1042     /* get the transport, if there is no transport configured, skip this stream */
1043     trans = gst_rtsp_session_media_get_transport (sessmedia, i);
1044     if (trans == NULL) {
1045       GST_INFO ("stream %d is not configured", i);
1046       continue;
1047     }
1048     tr = gst_rtsp_stream_transport_get_transport (trans);
1049
1050     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1051       /* for TCP, link the stream to the TCP connection of the client */
1052       link_transport (client, session, trans);
1053     }
1054
1055     stream = gst_rtsp_stream_transport_get_stream (trans);
1056     if (gst_rtsp_stream_get_rtpinfo (stream, &rtptime, &seq)) {
1057       if (infocount > 0)
1058         g_string_append (rtpinfo, ", ");
1059
1060       uristr = gst_rtsp_url_get_request_uri (state->uri);
1061       g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u",
1062           uristr, i, seq, rtptime);
1063       g_free (uristr);
1064
1065       infocount++;
1066     } else {
1067       GST_WARNING ("RTP-Info cannot be determined for stream %d", i);
1068     }
1069   }
1070
1071   /* construct the response now */
1072   code = GST_RTSP_STS_OK;
1073   gst_rtsp_message_init_response (state->response, code,
1074       gst_rtsp_status_as_text (code), state->request);
1075
1076   /* add the RTP-Info header */
1077   if (infocount > 0) {
1078     str = g_string_free (rtpinfo, FALSE);
1079     gst_rtsp_message_take_header (state->response, GST_RTSP_HDR_RTP_INFO, str);
1080   } else {
1081     g_string_free (rtpinfo, TRUE);
1082   }
1083
1084   /* add the range */
1085   str = gst_rtsp_media_get_range_string (media, TRUE, unit);
1086   gst_rtsp_message_take_header (state->response, GST_RTSP_HDR_RANGE, str);
1087
1088   send_message (client, session, state->response, FALSE);
1089
1090   /* start playing after sending the request */
1091   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PLAYING);
1092
1093   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_PLAYING);
1094
1095   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST],
1096       0, state);
1097
1098   return TRUE;
1099
1100   /* ERRORS */
1101 no_session:
1102   {
1103     GST_ERROR ("client %p: no session", client);
1104     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, state);
1105     return FALSE;
1106   }
1107 no_uri:
1108   {
1109     GST_ERROR ("client %p: no uri supplied", client);
1110     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, state);
1111     return FALSE;
1112   }
1113 not_found:
1114   {
1115     GST_ERROR ("client %p: media not found", client);
1116     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
1117     return FALSE;
1118   }
1119 no_aggregate:
1120   {
1121     GST_ERROR ("client %p: no aggregate path %s", client, path);
1122     send_generic_response (client,
1123         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, state);
1124     return FALSE;
1125   }
1126 invalid_state:
1127   {
1128     GST_ERROR ("client %p: not PLAYING or READY", client);
1129     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1130         state);
1131     return FALSE;
1132   }
1133 }
1134
1135 static void
1136 do_keepalive (GstRTSPSession * session)
1137 {
1138   GST_INFO ("keep session %p alive", session);
1139   gst_rtsp_session_touch (session);
1140 }
1141
1142 /* parse @transport and return a valid transport in @tr. only transports
1143  * from @supported are returned. Returns FALSE if no valid transport
1144  * was found. */
1145 static gboolean
1146 parse_transport (const char *transport, GstRTSPLowerTrans supported,
1147     GstRTSPTransport * tr)
1148 {
1149   gint i;
1150   gboolean res;
1151   gchar **transports;
1152
1153   res = FALSE;
1154   gst_rtsp_transport_init (tr);
1155
1156   GST_DEBUG ("parsing transports %s", transport);
1157
1158   transports = g_strsplit (transport, ",", 0);
1159
1160   /* loop through the transports, try to parse */
1161   for (i = 0; transports[i]; i++) {
1162     res = gst_rtsp_transport_parse (transports[i], tr);
1163     if (res != GST_RTSP_OK) {
1164       /* no valid transport, search some more */
1165       GST_WARNING ("could not parse transport %s", transports[i]);
1166       goto next;
1167     }
1168
1169     /* we have a transport, see if it's RTP/AVP */
1170     if (tr->trans != GST_RTSP_TRANS_RTP || tr->profile != GST_RTSP_PROFILE_AVP) {
1171       GST_WARNING ("invalid transport %s", transports[i]);
1172       goto next;
1173     }
1174
1175     if (!(tr->lower_transport & supported)) {
1176       GST_WARNING ("unsupported transport %s", transports[i]);
1177       goto next;
1178     }
1179
1180     /* we have a valid transport */
1181     GST_INFO ("found valid transport %s", transports[i]);
1182     res = TRUE;
1183     break;
1184
1185   next:
1186     gst_rtsp_transport_init (tr);
1187   }
1188   g_strfreev (transports);
1189
1190   return res;
1191 }
1192
1193 static gboolean
1194 handle_blocksize (GstRTSPMedia * media, GstRTSPStream * stream,
1195     GstRTSPMessage * request)
1196 {
1197   gchar *blocksize_str;
1198   gboolean ret = TRUE;
1199
1200   if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_BLOCKSIZE,
1201           &blocksize_str, 0) == GST_RTSP_OK) {
1202     guint64 blocksize;
1203     gchar *end;
1204
1205     blocksize = g_ascii_strtoull (blocksize_str, &end, 10);
1206     if (end == blocksize_str) {
1207       GST_ERROR ("failed to parse blocksize");
1208       ret = FALSE;
1209     } else {
1210       /* we don't want to change the mtu when this media
1211        * can be shared because it impacts other clients */
1212       if (gst_rtsp_media_is_shared (media))
1213         return TRUE;
1214
1215       if (blocksize > G_MAXUINT)
1216         blocksize = G_MAXUINT;
1217       gst_rtsp_stream_set_mtu (stream, blocksize);
1218     }
1219   }
1220   return ret;
1221 }
1222
1223 static gboolean
1224 default_configure_client_transport (GstRTSPClient * client,
1225     GstRTSPClientState * state, GstRTSPTransport * ct)
1226 {
1227   GstRTSPClientPrivate *priv = client->priv;
1228
1229   /* we have a valid transport now, set the destination of the client. */
1230   if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
1231     if (ct->destination && priv->use_client_settings) {
1232       GstRTSPAddress *addr;
1233
1234       addr = gst_rtsp_stream_reserve_address (state->stream, ct->destination,
1235           ct->port.min, ct->port.max - ct->port.min + 1, ct->ttl);
1236
1237       if (addr == NULL)
1238         goto no_address;
1239
1240       gst_rtsp_address_free (addr);
1241     } else {
1242       GstRTSPAddress *addr;
1243       GSocketFamily family;
1244
1245       family = priv->is_ipv6 ? G_SOCKET_FAMILY_IPV6 : G_SOCKET_FAMILY_IPV4;
1246
1247       addr = gst_rtsp_stream_get_multicast_address (state->stream, family);
1248       if (addr == NULL)
1249         goto no_address;
1250
1251       g_free (ct->destination);
1252       ct->destination = g_strdup (addr->address);
1253       ct->port.min = addr->port;
1254       ct->port.max = addr->port + addr->n_ports - 1;
1255       ct->ttl = addr->ttl;
1256
1257       gst_rtsp_address_free (addr);
1258     }
1259   } else {
1260     GstRTSPUrl *url;
1261
1262     url = gst_rtsp_connection_get_url (priv->connection);
1263     g_free (ct->destination);
1264     ct->destination = g_strdup (url->host);
1265
1266     if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
1267       /* check if the client selected channels for TCP */
1268       if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
1269         gst_rtsp_session_media_alloc_channels (state->sessmedia,
1270             &ct->interleaved);
1271       }
1272     }
1273   }
1274   return TRUE;
1275
1276   /* ERRORS */
1277 no_address:
1278   {
1279     GST_ERROR_OBJECT (client, "failed to acquire address for stream");
1280     return FALSE;
1281   }
1282 }
1283
1284 static GstRTSPTransport *
1285 make_server_transport (GstRTSPClient * client, GstRTSPClientState * state,
1286     GstRTSPTransport * ct)
1287 {
1288   GstRTSPTransport *st;
1289   GInetAddress *addr;
1290   GSocketFamily family;
1291
1292   /* prepare the server transport */
1293   gst_rtsp_transport_new (&st);
1294
1295   st->trans = ct->trans;
1296   st->profile = ct->profile;
1297   st->lower_transport = ct->lower_transport;
1298
1299   addr = g_inet_address_new_from_string (ct->destination);
1300
1301   if (!addr) {
1302     GST_ERROR ("failed to get inet addr from client destination");
1303     family = G_SOCKET_FAMILY_IPV4;
1304   } else {
1305     family = g_inet_address_get_family (addr);
1306     g_object_unref (addr);
1307     addr = NULL;
1308   }
1309
1310   switch (st->lower_transport) {
1311     case GST_RTSP_LOWER_TRANS_UDP:
1312       st->client_port = ct->client_port;
1313       gst_rtsp_stream_get_server_port (state->stream, &st->server_port, family);
1314       break;
1315     case GST_RTSP_LOWER_TRANS_UDP_MCAST:
1316       st->port = ct->port;
1317       st->destination = g_strdup (ct->destination);
1318       st->ttl = ct->ttl;
1319       break;
1320     case GST_RTSP_LOWER_TRANS_TCP:
1321       st->interleaved = ct->interleaved;
1322     default:
1323       break;
1324   }
1325
1326   gst_rtsp_stream_get_ssrc (state->stream, &st->ssrc);
1327
1328   return st;
1329 }
1330
1331 static gboolean
1332 handle_setup_request (GstRTSPClient * client, GstRTSPClientState * state)
1333 {
1334   GstRTSPClientPrivate *priv = client->priv;
1335   GstRTSPResult res;
1336   GstRTSPUrl *uri;
1337   gchar *transport;
1338   GstRTSPTransport *ct, *st;
1339   GstRTSPLowerTrans supported;
1340   GstRTSPStatusCode code;
1341   GstRTSPSession *session;
1342   GstRTSPStreamTransport *trans;
1343   gchar *trans_str;
1344   GstRTSPSessionMedia *sessmedia;
1345   GstRTSPMedia *media;
1346   GstRTSPStream *stream;
1347   GstRTSPState rtspstate;
1348   GstRTSPClientClass *klass;
1349   gchar *path, *control;
1350   gint matched;
1351
1352   if (!state->uri)
1353     goto no_uri;
1354
1355   uri = state->uri;
1356   path = uri->abspath;
1357
1358   /* parse the transport */
1359   res =
1360       gst_rtsp_message_get_header (state->request, GST_RTSP_HDR_TRANSPORT,
1361       &transport, 0);
1362   if (res != GST_RTSP_OK)
1363     goto no_transport;
1364
1365   /* we create the session after parsing stuff so that we don't make
1366    * a session for malformed requests */
1367   if (priv->session_pool == NULL)
1368     goto no_pool;
1369
1370   session = state->session;
1371
1372   if (session) {
1373     g_object_ref (session);
1374     /* get a handle to the configuration of the media in the session, this can
1375      * return NULL if this is a new url to manage in this session. */
1376     sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1377   } else {
1378     /* we need a new media configuration in this session */
1379     sessmedia = NULL;
1380   }
1381
1382   /* we have no session media, find one and manage it */
1383   if (sessmedia == NULL) {
1384     /* get a handle to the configuration of the media in the session */
1385     media = find_media (client, state, &matched);
1386   } else {
1387     if ((media = gst_rtsp_session_media_get_media (sessmedia)))
1388       g_object_ref (media);
1389   }
1390   /* no media, not found then */
1391   if (media == NULL)
1392     goto media_not_found;
1393
1394   /* path is what matched. We can modify the parsed uri in place */
1395   path[matched] = '\0';
1396   /* control is remainder */
1397   control = &path[matched + 1];
1398
1399   /* find the stream now using the control part */
1400   stream = gst_rtsp_media_find_stream (media, control);
1401   if (stream == NULL)
1402     goto stream_not_found;
1403
1404   /* now we have a uri identifying a valid media and stream */
1405   state->stream = stream;
1406   state->media = media;
1407
1408   if (session == NULL) {
1409     /* create a session if this fails we probably reached our session limit or
1410      * something. */
1411     if (!(session = gst_rtsp_session_pool_create (priv->session_pool)))
1412       goto service_unavailable;
1413
1414     /* make sure this client is closed when the session is closed */
1415     client_watch_session (client, session);
1416
1417     /* signal new session */
1418     g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_NEW_SESSION], 0,
1419         session);
1420
1421     state->session = session;
1422   }
1423
1424   if (sessmedia == NULL) {
1425     /* manage the media in our session now, if not done already  */
1426     sessmedia = gst_rtsp_session_manage_media (session, path, media);
1427     /* if we stil have no media, error */
1428     if (sessmedia == NULL)
1429       goto sessmedia_unavailable;
1430   } else {
1431     g_object_unref (media);
1432   }
1433
1434   state->sessmedia = sessmedia;
1435
1436   /* set blocksize on this stream */
1437   if (!handle_blocksize (media, stream, state->request))
1438     goto invalid_blocksize;
1439
1440   gst_rtsp_transport_new (&ct);
1441
1442   /* our supported transports */
1443   supported = GST_RTSP_LOWER_TRANS_UDP |
1444       GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP;
1445
1446   /* parse and find a usable supported transport */
1447   if (!parse_transport (transport, supported, ct))
1448     goto unsupported_transports;
1449
1450   /* update the client transport */
1451   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1452   if (!klass->configure_client_transport (client, state, ct))
1453     goto unsupported_client_transport;
1454
1455   /* set in the session media transport */
1456   trans = gst_rtsp_session_media_set_transport (sessmedia, stream, ct);
1457
1458   /* configure keepalive for this transport */
1459   gst_rtsp_stream_transport_set_keepalive (trans,
1460       (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
1461
1462   /* create and serialize the server transport */
1463   st = make_server_transport (client, state, ct);
1464   trans_str = gst_rtsp_transport_as_text (st);
1465   gst_rtsp_transport_free (st);
1466
1467   /* construct the response now */
1468   code = GST_RTSP_STS_OK;
1469   gst_rtsp_message_init_response (state->response, code,
1470       gst_rtsp_status_as_text (code), state->request);
1471
1472   gst_rtsp_message_add_header (state->response, GST_RTSP_HDR_TRANSPORT,
1473       trans_str);
1474   g_free (trans_str);
1475
1476   send_message (client, session, state->response, FALSE);
1477
1478   /* update the state */
1479   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1480   switch (rtspstate) {
1481     case GST_RTSP_STATE_PLAYING:
1482     case GST_RTSP_STATE_RECORDING:
1483     case GST_RTSP_STATE_READY:
1484       /* no state change */
1485       break;
1486     default:
1487       gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
1488       break;
1489   }
1490   g_object_unref (session);
1491
1492   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST],
1493       0, state);
1494
1495   return TRUE;
1496
1497   /* ERRORS */
1498 no_uri:
1499   {
1500     GST_ERROR ("client %p: no uri", client);
1501     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, state);
1502     return FALSE;
1503   }
1504 no_transport:
1505   {
1506     GST_ERROR ("client %p: no transport", client);
1507     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, state);
1508     return FALSE;
1509   }
1510 no_pool:
1511   {
1512     GST_ERROR ("client %p: no session pool configured", client);
1513     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, state);
1514     return FALSE;
1515   }
1516 media_not_found:
1517   {
1518     GST_ERROR ("client %p: media '%s' not found", client, path);
1519     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
1520     return FALSE;
1521   }
1522 stream_not_found:
1523   {
1524     GST_ERROR ("client %p: stream '%s' not found", client, control);
1525     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
1526     g_object_unref (media);
1527     return FALSE;
1528   }
1529 service_unavailable:
1530   {
1531     GST_ERROR ("client %p: can't create session", client);
1532     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, state);
1533     g_object_unref (media);
1534     return FALSE;
1535   }
1536 sessmedia_unavailable:
1537   {
1538     GST_ERROR ("client %p: can't create session media", client);
1539     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, state);
1540     g_object_unref (media);
1541     g_object_unref (session);
1542     return FALSE;
1543   }
1544 invalid_blocksize:
1545   {
1546     GST_ERROR ("client %p: invalid blocksize", client);
1547     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, state);
1548     g_object_unref (session);
1549     return FALSE;
1550   }
1551 unsupported_transports:
1552   {
1553     GST_ERROR ("client %p: unsupported transports", client);
1554     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, state);
1555     gst_rtsp_transport_free (ct);
1556     g_object_unref (session);
1557     return FALSE;
1558   }
1559 unsupported_client_transport:
1560   {
1561     GST_ERROR ("client %p: unsupported client transport", client);
1562     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, state);
1563     gst_rtsp_transport_free (ct);
1564     g_object_unref (session);
1565     return FALSE;
1566   }
1567 }
1568
1569 static GstSDPMessage *
1570 create_sdp (GstRTSPClient * client, GstRTSPMedia * media)
1571 {
1572   GstRTSPClientPrivate *priv = client->priv;
1573   GstSDPMessage *sdp;
1574   GstSDPInfo info;
1575   const gchar *proto;
1576
1577   gst_sdp_message_new (&sdp);
1578
1579   /* some standard things first */
1580   gst_sdp_message_set_version (sdp, "0");
1581
1582   if (priv->is_ipv6)
1583     proto = "IP6";
1584   else
1585     proto = "IP4";
1586
1587   gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", proto,
1588       priv->server_ip);
1589
1590   gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
1591   gst_sdp_message_set_information (sdp, "rtsp-server");
1592   gst_sdp_message_add_time (sdp, "0", "0", NULL);
1593   gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
1594   gst_sdp_message_add_attribute (sdp, "type", "broadcast");
1595   gst_sdp_message_add_attribute (sdp, "control", "*");
1596
1597   info.is_ipv6 = priv->is_ipv6;
1598   info.server_ip = priv->server_ip;
1599
1600   /* create an SDP for the media object */
1601   if (!gst_rtsp_sdp_from_media (sdp, &info, media))
1602     goto no_sdp;
1603
1604   return sdp;
1605
1606   /* ERRORS */
1607 no_sdp:
1608   {
1609     GST_ERROR ("client %p: could not create SDP", client);
1610     gst_sdp_message_free (sdp);
1611     return NULL;
1612   }
1613 }
1614
1615 /* for the describe we must generate an SDP */
1616 static gboolean
1617 handle_describe_request (GstRTSPClient * client, GstRTSPClientState * state)
1618 {
1619   GstRTSPResult res;
1620   GstSDPMessage *sdp;
1621   guint i, str_len;
1622   gchar *str, *content_base;
1623   GstRTSPMedia *media;
1624   GstRTSPClientClass *klass;
1625
1626   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1627
1628   if (!state->uri)
1629     goto no_uri;
1630
1631   /* check what kind of format is accepted, we don't really do anything with it
1632    * and always return SDP for now. */
1633   for (i = 0; i++;) {
1634     gchar *accept;
1635
1636     res =
1637         gst_rtsp_message_get_header (state->request, GST_RTSP_HDR_ACCEPT,
1638         &accept, i);
1639     if (res == GST_RTSP_ENOTIMPL)
1640       break;
1641
1642     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
1643       break;
1644   }
1645
1646   /* find the media object for the uri */
1647   if (!(media = find_media (client, state, NULL)))
1648     goto no_media;
1649
1650   /* create an SDP for the media object on this client */
1651   if (!(sdp = klass->create_sdp (client, media)))
1652     goto no_sdp;
1653
1654   g_object_unref (media);
1655
1656   gst_rtsp_message_init_response (state->response, GST_RTSP_STS_OK,
1657       gst_rtsp_status_as_text (GST_RTSP_STS_OK), state->request);
1658
1659   gst_rtsp_message_add_header (state->response, GST_RTSP_HDR_CONTENT_TYPE,
1660       "application/sdp");
1661
1662   /* content base for some clients that might screw up creating the setup uri */
1663   str = gst_rtsp_url_get_request_uri (state->uri);
1664   str_len = strlen (str);
1665
1666   /* check for trailing '/' and append one */
1667   if (str[str_len - 1] != '/') {
1668     content_base = g_malloc (str_len + 2);
1669     memcpy (content_base, str, str_len);
1670     content_base[str_len] = '/';
1671     content_base[str_len + 1] = '\0';
1672     g_free (str);
1673   } else {
1674     content_base = str;
1675   }
1676
1677   GST_INFO ("adding content-base: %s", content_base);
1678
1679   gst_rtsp_message_add_header (state->response, GST_RTSP_HDR_CONTENT_BASE,
1680       content_base);
1681   g_free (content_base);
1682
1683   /* add SDP to the response body */
1684   str = gst_sdp_message_as_text (sdp);
1685   gst_rtsp_message_take_body (state->response, (guint8 *) str, strlen (str));
1686   gst_sdp_message_free (sdp);
1687
1688   send_message (client, state->session, state->response, FALSE);
1689
1690   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST],
1691       0, state);
1692
1693   return TRUE;
1694
1695   /* ERRORS */
1696 no_uri:
1697   {
1698     GST_ERROR ("client %p: no uri", client);
1699     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, state);
1700     return FALSE;
1701   }
1702 no_media:
1703   {
1704     GST_ERROR ("client %p: no media", client);
1705     /* error reply is already sent */
1706     return FALSE;
1707   }
1708 no_sdp:
1709   {
1710     GST_ERROR ("client %p: can't create SDP", client);
1711     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, state);
1712     g_object_unref (media);
1713     return FALSE;
1714   }
1715 }
1716
1717 static gboolean
1718 handle_options_request (GstRTSPClient * client, GstRTSPClientState * state)
1719 {
1720   GstRTSPMethod options;
1721   gchar *str;
1722
1723   options = GST_RTSP_DESCRIBE |
1724       GST_RTSP_OPTIONS |
1725       GST_RTSP_PAUSE |
1726       GST_RTSP_PLAY |
1727       GST_RTSP_SETUP |
1728       GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
1729
1730   str = gst_rtsp_options_as_text (options);
1731
1732   gst_rtsp_message_init_response (state->response, GST_RTSP_STS_OK,
1733       gst_rtsp_status_as_text (GST_RTSP_STS_OK), state->request);
1734
1735   gst_rtsp_message_add_header (state->response, GST_RTSP_HDR_PUBLIC, str);
1736   g_free (str);
1737
1738   send_message (client, state->session, state->response, FALSE);
1739
1740   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST],
1741       0, state);
1742
1743   return TRUE;
1744 }
1745
1746 /* remove duplicate and trailing '/' */
1747 static void
1748 sanitize_uri (GstRTSPUrl * uri)
1749 {
1750   gint i, len;
1751   gchar *s, *d;
1752   gboolean have_slash, prev_slash;
1753
1754   s = d = uri->abspath;
1755   len = strlen (uri->abspath);
1756
1757   prev_slash = FALSE;
1758
1759   for (i = 0; i < len; i++) {
1760     have_slash = s[i] == '/';
1761     *d = s[i];
1762     if (!have_slash || !prev_slash)
1763       d++;
1764     prev_slash = have_slash;
1765   }
1766   len = d - uri->abspath;
1767   /* don't remove the first slash if that's the only thing left */
1768   if (len > 1 && *(d - 1) == '/')
1769     d--;
1770   *d = '\0';
1771 }
1772
1773 static void
1774 client_session_finalized (GstRTSPClient * client, GstRTSPSession * session)
1775 {
1776   GstRTSPClientPrivate *priv = client->priv;
1777
1778   GST_INFO ("client %p: session %p finished", client, session);
1779
1780   /* unlink all media managed in this session */
1781   client_unlink_session (client, session);
1782
1783   /* remove the session */
1784   if (!(priv->sessions = g_list_remove (priv->sessions, session))) {
1785     GST_INFO ("client %p: all sessions finalized, close the connection",
1786         client);
1787     close_connection (client);
1788   }
1789 }
1790
1791 static GPrivate state_key;
1792
1793 /**
1794  * gst_rtsp_client_state_get_current:
1795  *
1796  * Get the current #GstRTSPClientState. This object is retrieved from the
1797  * current thread that is handling the request for a client.
1798  *
1799  * Returns: a #GstRTSPClientState
1800  */
1801 GstRTSPClientState *
1802 gst_rtsp_client_state_get_current (void)
1803 {
1804   return g_private_get (&state_key);
1805 }
1806
1807 static void
1808 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
1809 {
1810   GstRTSPClientPrivate *priv = client->priv;
1811   GstRTSPMethod method;
1812   const gchar *uristr;
1813   GstRTSPUrl *uri = NULL;
1814   GstRTSPVersion version;
1815   GstRTSPResult res;
1816   GstRTSPSession *session = NULL;
1817   GstRTSPClientState state = { NULL };
1818   GstRTSPMessage response = { 0 };
1819   gchar *sessid;
1820
1821   state.client = client;
1822   state.request = request;
1823   state.response = &response;
1824   state.auth = priv->auth;
1825   g_private_set (&state_key, &state);
1826
1827   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
1828     gst_rtsp_message_dump (request);
1829   }
1830
1831   GST_INFO ("client %p: received a request", client);
1832
1833   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
1834
1835   /* we can only handle 1.0 requests */
1836   if (version != GST_RTSP_VERSION_1_0)
1837     goto not_supported;
1838
1839   state.method = method;
1840
1841   /* we always try to parse the url first */
1842   if (strcmp (uristr, "*") == 0) {
1843     /* special case where we have * as uri, keep uri = NULL */
1844   } else if (gst_rtsp_url_parse (uristr, &uri) != GST_RTSP_OK)
1845     goto bad_request;
1846
1847   /* get the session if there is any */
1848   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
1849   if (res == GST_RTSP_OK) {
1850     if (priv->session_pool == NULL)
1851       goto no_pool;
1852
1853     /* we had a session in the request, find it again */
1854     if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
1855       goto session_not_found;
1856
1857     /* we add the session to the client list of watched sessions. When a session
1858      * disappears because it times out, we will be notified. If all sessions are
1859      * gone, we will close the connection */
1860     client_watch_session (client, session);
1861   }
1862
1863   /* sanitize the uri */
1864   if (uri)
1865     sanitize_uri (uri);
1866   state.uri = uri;
1867   state.session = session;
1868
1869   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_URL))
1870     goto not_authorized;
1871
1872   /* now see what is asked and dispatch to a dedicated handler */
1873   switch (method) {
1874     case GST_RTSP_OPTIONS:
1875       handle_options_request (client, &state);
1876       break;
1877     case GST_RTSP_DESCRIBE:
1878       handle_describe_request (client, &state);
1879       break;
1880     case GST_RTSP_SETUP:
1881       handle_setup_request (client, &state);
1882       break;
1883     case GST_RTSP_PLAY:
1884       handle_play_request (client, &state);
1885       break;
1886     case GST_RTSP_PAUSE:
1887       handle_pause_request (client, &state);
1888       break;
1889     case GST_RTSP_TEARDOWN:
1890       handle_teardown_request (client, &state);
1891       break;
1892     case GST_RTSP_SET_PARAMETER:
1893       handle_set_param_request (client, &state);
1894       break;
1895     case GST_RTSP_GET_PARAMETER:
1896       handle_get_param_request (client, &state);
1897       break;
1898     case GST_RTSP_ANNOUNCE:
1899     case GST_RTSP_RECORD:
1900     case GST_RTSP_REDIRECT:
1901       goto not_implemented;
1902     case GST_RTSP_INVALID:
1903     default:
1904       goto bad_request;
1905   }
1906
1907 done:
1908   g_private_set (&state_key, NULL);
1909   if (session)
1910     g_object_unref (session);
1911   if (uri)
1912     gst_rtsp_url_free (uri);
1913   return;
1914
1915   /* ERRORS */
1916 not_supported:
1917   {
1918     GST_ERROR ("client %p: version %d not supported", client, version);
1919     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
1920         &state);
1921     goto done;
1922   }
1923 bad_request:
1924   {
1925     GST_ERROR ("client %p: bad request", client);
1926     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, &state);
1927     goto done;
1928   }
1929 no_pool:
1930   {
1931     GST_ERROR ("client %p: no pool configured", client);
1932     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, &state);
1933     goto done;
1934   }
1935 session_not_found:
1936   {
1937     GST_ERROR ("client %p: session not found", client);
1938     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, &state);
1939     goto done;
1940   }
1941 not_authorized:
1942   {
1943     GST_ERROR ("client %p: not allowed", client);
1944     handle_unauthorized_request (client, priv->auth, &state);
1945     goto done;
1946   }
1947 not_implemented:
1948   {
1949     GST_ERROR ("client %p: method %d not implemented", client, method);
1950     send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, &state);
1951     goto done;
1952   }
1953 }
1954
1955 static void
1956 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
1957 {
1958   GstRTSPClientPrivate *priv = client->priv;
1959   GstRTSPResult res;
1960   guint8 channel;
1961   GList *walk;
1962   guint8 *data;
1963   guint size;
1964   GstBuffer *buffer;
1965   gboolean handled;
1966
1967   /* find the stream for this message */
1968   res = gst_rtsp_message_parse_data (message, &channel);
1969   if (res != GST_RTSP_OK)
1970     return;
1971
1972   gst_rtsp_message_steal_body (message, &data, &size);
1973
1974   buffer = gst_buffer_new_wrapped (data, size);
1975
1976   handled = FALSE;
1977   for (walk = priv->transports; walk; walk = g_list_next (walk)) {
1978     GstRTSPStreamTransport *trans;
1979     GstRTSPStream *stream;
1980     const GstRTSPTransport *tr;
1981
1982     trans = walk->data;
1983
1984     tr = gst_rtsp_stream_transport_get_transport (trans);
1985     stream = gst_rtsp_stream_transport_get_stream (trans);
1986
1987     /* check for TCP transport */
1988     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1989       /* dispatch to the stream based on the channel number */
1990       if (tr->interleaved.min == channel) {
1991         gst_rtsp_stream_recv_rtp (stream, buffer);
1992         handled = TRUE;
1993         break;
1994       } else if (tr->interleaved.max == channel) {
1995         gst_rtsp_stream_recv_rtcp (stream, buffer);
1996         handled = TRUE;
1997         break;
1998       }
1999     }
2000   }
2001   if (!handled)
2002     gst_buffer_unref (buffer);
2003 }
2004
2005 /**
2006  * gst_rtsp_client_set_session_pool:
2007  * @client: a #GstRTSPClient
2008  * @pool: a #GstRTSPSessionPool
2009  *
2010  * Set @pool as the sessionpool for @client which it will use to find
2011  * or allocate sessions. the sessionpool is usually inherited from the server
2012  * that created the client but can be overridden later.
2013  */
2014 void
2015 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
2016     GstRTSPSessionPool * pool)
2017 {
2018   GstRTSPSessionPool *old;
2019   GstRTSPClientPrivate *priv;
2020
2021   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2022
2023   priv = client->priv;
2024
2025   if (pool)
2026     g_object_ref (pool);
2027
2028   g_mutex_lock (&priv->lock);
2029   old = priv->session_pool;
2030   priv->session_pool = pool;
2031   g_mutex_unlock (&priv->lock);
2032
2033   if (old)
2034     g_object_unref (old);
2035 }
2036
2037 /**
2038  * gst_rtsp_client_get_session_pool:
2039  * @client: a #GstRTSPClient
2040  *
2041  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
2042  *
2043  * Returns: (transfer full): a #GstRTSPSessionPool, unref after usage.
2044  */
2045 GstRTSPSessionPool *
2046 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
2047 {
2048   GstRTSPClientPrivate *priv;
2049   GstRTSPSessionPool *result;
2050
2051   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2052
2053   priv = client->priv;
2054
2055   g_mutex_lock (&priv->lock);
2056   if ((result = priv->session_pool))
2057     g_object_ref (result);
2058   g_mutex_unlock (&priv->lock);
2059
2060   return result;
2061 }
2062
2063 /**
2064  * gst_rtsp_client_set_mount_points:
2065  * @client: a #GstRTSPClient
2066  * @mounts: a #GstRTSPMountPoints
2067  *
2068  * Set @mounts as the mount points for @client which it will use to map urls
2069  * to media streams. These mount points are usually inherited from the server that
2070  * created the client but can be overriden later.
2071  */
2072 void
2073 gst_rtsp_client_set_mount_points (GstRTSPClient * client,
2074     GstRTSPMountPoints * mounts)
2075 {
2076   GstRTSPClientPrivate *priv;
2077   GstRTSPMountPoints *old;
2078
2079   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2080
2081   priv = client->priv;
2082
2083   if (mounts)
2084     g_object_ref (mounts);
2085
2086   g_mutex_lock (&priv->lock);
2087   old = priv->mount_points;
2088   priv->mount_points = mounts;
2089   g_mutex_unlock (&priv->lock);
2090
2091   if (old)
2092     g_object_unref (old);
2093 }
2094
2095 /**
2096  * gst_rtsp_client_get_mount_points:
2097  * @client: a #GstRTSPClient
2098  *
2099  * Get the #GstRTSPMountPoints object that @client uses to manage its sessions.
2100  *
2101  * Returns: (transfer full): a #GstRTSPMountPoints, unref after usage.
2102  */
2103 GstRTSPMountPoints *
2104 gst_rtsp_client_get_mount_points (GstRTSPClient * client)
2105 {
2106   GstRTSPClientPrivate *priv;
2107   GstRTSPMountPoints *result;
2108
2109   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2110
2111   priv = client->priv;
2112
2113   g_mutex_lock (&priv->lock);
2114   if ((result = priv->mount_points))
2115     g_object_ref (result);
2116   g_mutex_unlock (&priv->lock);
2117
2118   return result;
2119 }
2120
2121 /**
2122  * gst_rtsp_client_set_use_client_settings:
2123  * @client: a #GstRTSPClient
2124  * @use_client_settings: whether to use client settings for multicast
2125  *
2126  * Use client transport settings (destination and ttl) for multicast.
2127  * When @use_client_settings is %FALSE, the server settings will be
2128  * used.
2129  */
2130 void
2131 gst_rtsp_client_set_use_client_settings (GstRTSPClient * client,
2132     gboolean use_client_settings)
2133 {
2134   GstRTSPClientPrivate *priv;
2135
2136   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2137
2138   priv = client->priv;
2139
2140   g_mutex_lock (&priv->lock);
2141   priv->use_client_settings = use_client_settings;
2142   g_mutex_unlock (&priv->lock);
2143 }
2144
2145 /**
2146  * gst_rtsp_client_get_use_client_settings:
2147  * @client: a #GstRTSPClient
2148  *
2149  * Check if client transport settings (destination and ttl) for multicast
2150  * will be used.
2151  */
2152 gboolean
2153 gst_rtsp_client_get_use_client_settings (GstRTSPClient * client)
2154 {
2155   GstRTSPClientPrivate *priv;
2156   gboolean res;
2157
2158   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
2159
2160   priv = client->priv;
2161
2162   g_mutex_lock (&priv->lock);
2163   res = priv->use_client_settings;
2164   g_mutex_unlock (&priv->lock);
2165
2166   return res;
2167 }
2168
2169 /**
2170  * gst_rtsp_client_set_auth:
2171  * @client: a #GstRTSPClient
2172  * @auth: a #GstRTSPAuth
2173  *
2174  * configure @auth to be used as the authentication manager of @client.
2175  */
2176 void
2177 gst_rtsp_client_set_auth (GstRTSPClient * client, GstRTSPAuth * auth)
2178 {
2179   GstRTSPClientPrivate *priv;
2180   GstRTSPAuth *old;
2181
2182   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2183
2184   priv = client->priv;
2185
2186   if (auth)
2187     g_object_ref (auth);
2188
2189   g_mutex_lock (&priv->lock);
2190   old = priv->auth;
2191   priv->auth = auth;
2192   g_mutex_unlock (&priv->lock);
2193
2194   if (old)
2195     g_object_unref (old);
2196 }
2197
2198
2199 /**
2200  * gst_rtsp_client_get_auth:
2201  * @client: a #GstRTSPClient
2202  *
2203  * Get the #GstRTSPAuth used as the authentication manager of @client.
2204  *
2205  * Returns: (transfer full): the #GstRTSPAuth of @client. g_object_unref() after
2206  * usage.
2207  */
2208 GstRTSPAuth *
2209 gst_rtsp_client_get_auth (GstRTSPClient * client)
2210 {
2211   GstRTSPClientPrivate *priv;
2212   GstRTSPAuth *result;
2213
2214   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2215
2216   priv = client->priv;
2217
2218   g_mutex_lock (&priv->lock);
2219   if ((result = priv->auth))
2220     g_object_ref (result);
2221   g_mutex_unlock (&priv->lock);
2222
2223   return result;
2224 }
2225
2226 /**
2227  * gst_rtsp_client_set_connection:
2228  * @client: a #GstRTSPClient
2229  * @conn: (transfer full): a #GstRTSPConnection
2230  *
2231  * Set the #GstRTSPConnection of @client. This function takes ownership of
2232  * @conn.
2233  *
2234  * Returns: %TRUE on success.
2235  */
2236 gboolean
2237 gst_rtsp_client_set_connection (GstRTSPClient * client,
2238     GstRTSPConnection * conn)
2239 {
2240   GstRTSPClientPrivate *priv;
2241   GSocket *read_socket;
2242   GSocketAddress *address;
2243   GstRTSPUrl *url;
2244   GError *error = NULL;
2245
2246   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
2247   g_return_val_if_fail (conn != NULL, FALSE);
2248
2249   priv = client->priv;
2250
2251   read_socket = gst_rtsp_connection_get_read_socket (conn);
2252
2253   if (!(address = g_socket_get_local_address (read_socket, &error)))
2254     goto no_address;
2255
2256   g_free (priv->server_ip);
2257   /* keep the original ip that the client connected to */
2258   if (G_IS_INET_SOCKET_ADDRESS (address)) {
2259     GInetAddress *iaddr;
2260
2261     iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address));
2262
2263     /* socket might be ipv6 but adress still ipv4 */
2264     priv->is_ipv6 = g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV6;
2265     priv->server_ip = g_inet_address_to_string (iaddr);
2266     g_object_unref (address);
2267   } else {
2268     priv->is_ipv6 = g_socket_get_family (read_socket) == G_SOCKET_FAMILY_IPV6;
2269     priv->server_ip = g_strdup ("unknown");
2270   }
2271
2272   GST_INFO ("client %p connected to server ip %s, ipv6 = %d", client,
2273       priv->server_ip, priv->is_ipv6);
2274
2275   url = gst_rtsp_connection_get_url (conn);
2276   GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
2277
2278   priv->connection = conn;
2279
2280   return TRUE;
2281
2282   /* ERRORS */
2283 no_address:
2284   {
2285     GST_ERROR ("could not get local address %s", error->message);
2286     g_error_free (error);
2287     return FALSE;
2288   }
2289 }
2290
2291 /**
2292  * gst_rtsp_client_get_connection:
2293  * @client: a #GstRTSPClient
2294  *
2295  * Get the #GstRTSPConnection of @client.
2296  *
2297  * Returns: (transfer none): the #GstRTSPConnection of @client.
2298  * The connection object returned remains valid until the client is freed.
2299  */
2300 GstRTSPConnection *
2301 gst_rtsp_client_get_connection (GstRTSPClient * client)
2302 {
2303   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2304
2305   return client->priv->connection;
2306 }
2307
2308 /**
2309  * gst_rtsp_client_set_send_func:
2310  * @client: a #GstRTSPClient
2311  * @func: a #GstRTSPClientSendFunc
2312  * @user_data: user data passed to @func
2313  * @notify: called when @user_data is no longer in use
2314  *
2315  * Set @func as the callback that will be called when a new message needs to be
2316  * sent to the client. @user_data is passed to @func and @notify is called when
2317  * @user_data is no longer in use.
2318  */
2319 void
2320 gst_rtsp_client_set_send_func (GstRTSPClient * client,
2321     GstRTSPClientSendFunc func, gpointer user_data, GDestroyNotify notify)
2322 {
2323   GstRTSPClientPrivate *priv;
2324   GDestroyNotify old_notify;
2325   gpointer old_data;
2326
2327   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2328
2329   priv = client->priv;
2330
2331   g_mutex_lock (&priv->send_lock);
2332   priv->send_func = func;
2333   old_notify = priv->send_notify;
2334   old_data = priv->send_data;
2335   priv->send_notify = notify;
2336   priv->send_data = user_data;
2337   g_mutex_unlock (&priv->send_lock);
2338
2339   if (old_notify)
2340     old_notify (old_data);
2341 }
2342
2343 /**
2344  * gst_rtsp_client_handle_message:
2345  * @client: a #GstRTSPClient
2346  * @message: an #GstRTSPMessage
2347  *
2348  * Let the client handle @message.
2349  *
2350  * Returns: a #GstRTSPResult.
2351  */
2352 GstRTSPResult
2353 gst_rtsp_client_handle_message (GstRTSPClient * client,
2354     GstRTSPMessage * message)
2355 {
2356   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
2357   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2358
2359   switch (message->type) {
2360     case GST_RTSP_MESSAGE_REQUEST:
2361       handle_request (client, message);
2362       break;
2363     case GST_RTSP_MESSAGE_RESPONSE:
2364       break;
2365     case GST_RTSP_MESSAGE_DATA:
2366       handle_data (client, message);
2367       break;
2368     default:
2369       break;
2370   }
2371   return GST_RTSP_OK;
2372 }
2373
2374 /**
2375  * gst_rtsp_client_send_request:
2376  * @client: a #GstRTSPClient
2377  * @session: a #GstRTSPSession to send the request to or %NULL
2378  * @request: The request #GstRTSPMessage to send
2379  *
2380  * Send a request message to the client.
2381  */
2382 GstRTSPResult
2383 gst_rtsp_client_send_request (GstRTSPClient * client, GstRTSPSession * session,
2384     GstRTSPMessage * request)
2385 {
2386   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
2387   g_return_val_if_fail (request != NULL, GST_RTSP_EINVAL);
2388   g_return_val_if_fail (request->type == GST_RTSP_MESSAGE_REQUEST,
2389       GST_RTSP_EINVAL);
2390
2391   send_message (client, session, request, FALSE);
2392
2393   return GST_RTSP_OK;
2394 }
2395
2396 static GstRTSPResult
2397 do_send_message (GstRTSPClient * client, GstRTSPMessage * message,
2398     gboolean close, gpointer user_data)
2399 {
2400   GstRTSPClientPrivate *priv = client->priv;
2401
2402   /* send the response and store the seq number so we can wait until it's
2403    * written to the client to close the connection */
2404   return gst_rtsp_watch_send_message (priv->watch, message, close ?
2405       &priv->close_seq : NULL);
2406 }
2407
2408 static GstRTSPResult
2409 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
2410     gpointer user_data)
2411 {
2412   return gst_rtsp_client_handle_message (GST_RTSP_CLIENT (user_data), message);
2413 }
2414
2415 static GstRTSPResult
2416 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
2417 {
2418   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2419   GstRTSPClientPrivate *priv = client->priv;
2420
2421   if (priv->close_seq && priv->close_seq == cseq) {
2422     priv->close_seq = 0;
2423     close_connection (client);
2424   }
2425
2426   return GST_RTSP_OK;
2427 }
2428
2429 static GstRTSPResult
2430 closed (GstRTSPWatch * watch, gpointer user_data)
2431 {
2432   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2433   GstRTSPClientPrivate *priv = client->priv;
2434   const gchar *tunnelid;
2435
2436   GST_INFO ("client %p: connection closed", client);
2437
2438   if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
2439     g_mutex_lock (&tunnels_lock);
2440     /* remove from tunnelids */
2441     g_hash_table_remove (tunnels, tunnelid);
2442     g_mutex_unlock (&tunnels_lock);
2443   }
2444
2445   gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
2446
2447   return GST_RTSP_OK;
2448 }
2449
2450 static GstRTSPResult
2451 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
2452 {
2453   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2454   gchar *str;
2455
2456   str = gst_rtsp_strresult (result);
2457   GST_INFO ("client %p: received an error %s", client, str);
2458   g_free (str);
2459
2460   return GST_RTSP_OK;
2461 }
2462
2463 static GstRTSPResult
2464 error_full (GstRTSPWatch * watch, GstRTSPResult result,
2465     GstRTSPMessage * message, guint id, gpointer user_data)
2466 {
2467   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2468   gchar *str;
2469
2470   str = gst_rtsp_strresult (result);
2471   GST_INFO
2472       ("client %p: error when handling message %p with id %d: %s",
2473       client, message, id, str);
2474   g_free (str);
2475
2476   return GST_RTSP_OK;
2477 }
2478
2479 static gboolean
2480 remember_tunnel (GstRTSPClient * client)
2481 {
2482   GstRTSPClientPrivate *priv = client->priv;
2483   const gchar *tunnelid;
2484
2485   /* store client in the pending tunnels */
2486   tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
2487   if (tunnelid == NULL)
2488     goto no_tunnelid;
2489
2490   GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
2491
2492   /* we can't have two clients connecting with the same tunnelid */
2493   g_mutex_lock (&tunnels_lock);
2494   if (g_hash_table_lookup (tunnels, tunnelid))
2495     goto tunnel_existed;
2496
2497   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
2498   g_mutex_unlock (&tunnels_lock);
2499
2500   return TRUE;
2501
2502   /* ERRORS */
2503 no_tunnelid:
2504   {
2505     GST_ERROR ("client %p: no tunnelid provided", client);
2506     return FALSE;
2507   }
2508 tunnel_existed:
2509   {
2510     g_mutex_unlock (&tunnels_lock);
2511     GST_ERROR ("client %p: tunnel session %s already existed", client,
2512         tunnelid);
2513     return FALSE;
2514   }
2515 }
2516
2517 static GstRTSPStatusCode
2518 tunnel_start (GstRTSPWatch * watch, gpointer user_data)
2519 {
2520   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2521   GstRTSPClientPrivate *priv = client->priv;
2522
2523   GST_INFO ("client %p: tunnel start (connection %p)", client,
2524       priv->connection);
2525
2526   if (!remember_tunnel (client))
2527     goto tunnel_error;
2528
2529   return GST_RTSP_STS_OK;
2530
2531   /* ERRORS */
2532 tunnel_error:
2533   {
2534     GST_ERROR ("client %p: error starting tunnel", client);
2535     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
2536   }
2537 }
2538
2539 static GstRTSPResult
2540 tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
2541 {
2542   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2543   GstRTSPClientPrivate *priv = client->priv;
2544
2545   GST_WARNING ("client %p: tunnel lost (connection %p)", client,
2546       priv->connection);
2547
2548   /* ignore error, it'll only be a problem when the client does a POST again */
2549   remember_tunnel (client);
2550
2551   return GST_RTSP_OK;
2552 }
2553
2554 static GstRTSPResult
2555 tunnel_complete (GstRTSPWatch * watch, gpointer user_data)
2556 {
2557   const gchar *tunnelid;
2558   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
2559   GstRTSPClientPrivate *priv = client->priv;
2560   GstRTSPClient *oclient;
2561   GstRTSPClientPrivate *opriv;
2562
2563   GST_INFO ("client %p: tunnel complete", client);
2564
2565   /* find previous tunnel */
2566   tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
2567   if (tunnelid == NULL)
2568     goto no_tunnelid;
2569
2570   g_mutex_lock (&tunnels_lock);
2571   if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
2572     goto no_tunnel;
2573
2574   /* remove the old client from the table. ref before because removing it will
2575    * remove the ref to it. */
2576   g_object_ref (oclient);
2577   g_hash_table_remove (tunnels, tunnelid);
2578
2579   opriv = oclient->priv;
2580
2581   if (opriv->watch == NULL)
2582     goto tunnel_closed;
2583   g_mutex_unlock (&tunnels_lock);
2584
2585   GST_INFO ("client %p: found tunnel %p (old %p, new %p)", client, oclient,
2586       opriv->connection, priv->connection);
2587
2588   /* merge the tunnels into the first client */
2589   gst_rtsp_connection_do_tunnel (opriv->connection, priv->connection);
2590   gst_rtsp_watch_reset (opriv->watch);
2591   g_object_unref (oclient);
2592
2593   return GST_RTSP_OK;
2594
2595   /* ERRORS */
2596 no_tunnelid:
2597   {
2598     GST_ERROR ("client %p: no tunnelid provided", client);
2599     return GST_RTSP_ERROR;
2600   }
2601 no_tunnel:
2602   {
2603     g_mutex_unlock (&tunnels_lock);
2604     GST_ERROR ("client %p: tunnel session %s not found", client, tunnelid);
2605     return GST_RTSP_ERROR;
2606   }
2607 tunnel_closed:
2608   {
2609     g_mutex_unlock (&tunnels_lock);
2610     GST_ERROR ("client %p: tunnel session %s was closed", client, tunnelid);
2611     g_object_unref (oclient);
2612     return GST_RTSP_ERROR;
2613   }
2614 }
2615
2616 static GstRTSPWatchFuncs watch_funcs = {
2617   message_received,
2618   message_sent,
2619   closed,
2620   error,
2621   tunnel_start,
2622   tunnel_complete,
2623   error_full,
2624   tunnel_lost
2625 };
2626
2627 static void
2628 client_watch_notify (GstRTSPClient * client)
2629 {
2630   GstRTSPClientPrivate *priv = client->priv;
2631
2632   GST_INFO ("client %p: watch destroyed", client);
2633   priv->watch = NULL;
2634   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_CLOSED], 0, NULL);
2635   g_object_unref (client);
2636 }
2637
2638 /**
2639  * gst_rtsp_client_attach:
2640  * @client: a #GstRTSPClient
2641  * @context: (allow-none): a #GMainContext
2642  *
2643  * Attaches @client to @context. When the mainloop for @context is run, the
2644  * client will be dispatched. When @context is NULL, the default context will be
2645  * used).
2646  *
2647  * This function should be called when the client properties and urls are fully
2648  * configured and the client is ready to start.
2649  *
2650  * Returns: the ID (greater than 0) for the source within the GMainContext.
2651  */
2652 guint
2653 gst_rtsp_client_attach (GstRTSPClient * client, GMainContext * context)
2654 {
2655   GstRTSPClientPrivate *priv;
2656   guint res;
2657
2658   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), 0);
2659   priv = client->priv;
2660   g_return_val_if_fail (priv->watch == NULL, 0);
2661
2662   /* create watch for the connection and attach */
2663   priv->watch = gst_rtsp_watch_new (priv->connection, &watch_funcs,
2664       g_object_ref (client), (GDestroyNotify) client_watch_notify);
2665   gst_rtsp_client_set_send_func (client, do_send_message, priv->watch,
2666       (GDestroyNotify) gst_rtsp_watch_unref);
2667
2668   /* FIXME make this configurable. We don't want to do this yet because it will
2669    * be superceeded by a cache object later */
2670   gst_rtsp_watch_set_send_backlog (priv->watch, 0, 100);
2671
2672   GST_INFO ("attaching to context %p", context);
2673   res = gst_rtsp_watch_attach (priv->watch, context);
2674
2675   return res;
2676 }
2677
2678 /**
2679  * gst_rtsp_client_session_filter:
2680  * @client: a #GstRTSPclient
2681  * @func: (scope call): a callback
2682  * @user_data: user data passed to @func
2683  *
2684  * Call @func for each session managed by @client. The result value of @func
2685  * determines what happens to the session. @func will be called with @client
2686  * locked so no further actions on @client can be performed from @func.
2687  *
2688  * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be removed from
2689  * @client.
2690  *
2691  * If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @client.
2692  *
2693  * If @func returns #GST_RTSP_FILTER_REF, the session will remain in @client but
2694  * will also be added with an additional ref to the result #GList of this
2695  * function..
2696  *
2697  * Returns: (element-type GstRTSPSession) (transfer full): a #GList with all
2698  * sessions for which @func returned #GST_RTSP_FILTER_REF. After usage, each
2699  * element in the #GList should be unreffed before the list is freed.
2700  */
2701 GList *
2702 gst_rtsp_client_session_filter (GstRTSPClient * client,
2703     GstRTSPClientSessionFilterFunc func, gpointer user_data)
2704 {
2705   GstRTSPClientPrivate *priv;
2706   GList *result, *walk, *next;
2707
2708   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2709   g_return_val_if_fail (func != NULL, NULL);
2710
2711   priv = client->priv;
2712
2713   result = NULL;
2714
2715   g_mutex_lock (&priv->lock);
2716   for (walk = priv->sessions; walk; walk = next) {
2717     GstRTSPSession *sess = walk->data;
2718
2719     next = g_list_next (walk);
2720
2721     switch (func (client, sess, user_data)) {
2722       case GST_RTSP_FILTER_REMOVE:
2723         /* stop watching the session and pretent it went away */
2724         client_cleanup_session (client, sess);
2725         break;
2726       case GST_RTSP_FILTER_REF:
2727         result = g_list_prepend (result, g_object_ref (sess));
2728         break;
2729       case GST_RTSP_FILTER_KEEP:
2730       default:
2731         break;
2732     }
2733   }
2734   g_mutex_unlock (&priv->lock);
2735
2736   return result;
2737 }