client: store TCP ports in transport
[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  * SECTION:rtsp-client
21  * @short_description: A client connection state
22  * @see_also: #GstRTSPServer, #GstRTSPThreadPool
23  *
24  * The client object handles the connection with a client for as long as a TCP
25  * connection is open.
26  *
27  * A #GstRTSPClient is created by #GstRTSPServer when a new connection is
28  * accepted and it inherits the #GstRTSPMountPoints, #GstRTSPSessionPool,
29  * #GstRTSPAuth and #GstRTSPThreadPool from the server.
30  *
31  * The client connection should be configured with the #GstRTSPConnection using
32  * gst_rtsp_client_set_connection() before it can be attached to a #GMainContext
33  * using gst_rtsp_client_attach(). From then on the client will handle requests
34  * on the connection.
35  *
36  * Use gst_rtsp_client_session_filter() to iterate or modify all the
37  * #GstRTSPSession objects managed by the client object.
38  *
39  * Last reviewed on 2013-07-11 (1.0.0)
40  */
41
42 #include <stdio.h>
43 #include <string.h>
44
45 #include <gst/sdp/gstmikey.h>
46
47 #include "rtsp-client.h"
48 #include "rtsp-sdp.h"
49 #include "rtsp-params.h"
50
51 #define GST_RTSP_CLIENT_GET_PRIVATE(obj)  \
52    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_CLIENT, GstRTSPClientPrivate))
53
54 /* locking order:
55  * send_lock, lock, tunnels_lock
56  */
57
58 struct _GstRTSPClientPrivate
59 {
60   GMutex lock;                  /* protects everything else */
61   GMutex send_lock;
62   GstRTSPConnection *connection;
63   GstRTSPWatch *watch;
64   guint close_seq;
65   gchar *server_ip;
66   gboolean is_ipv6;
67
68   GstRTSPClientSendFunc send_func;      /* protected by send_lock */
69   gpointer send_data;           /* protected by send_lock */
70   GDestroyNotify send_notify;   /* protected by send_lock */
71
72   GstRTSPSessionPool *session_pool;
73   GstRTSPMountPoints *mount_points;
74   GstRTSPAuth *auth;
75   GstRTSPThreadPool *thread_pool;
76
77   /* used to cache the media in the last requested DESCRIBE so that
78    * we can pick it up in the next SETUP immediately */
79   gchar *path;
80   GstRTSPMedia *media;
81
82   GList *transports;
83   GList *sessions;
84
85   gboolean drop_backlog;
86 };
87
88 static GMutex tunnels_lock;
89 static GHashTable *tunnels;     /* protected by tunnels_lock */
90
91 #define DEFAULT_SESSION_POOL            NULL
92 #define DEFAULT_MOUNT_POINTS            NULL
93 #define DEFAULT_DROP_BACKLOG            TRUE
94
95 enum
96 {
97   PROP_0,
98   PROP_SESSION_POOL,
99   PROP_MOUNT_POINTS,
100   PROP_DROP_BACKLOG,
101   PROP_LAST
102 };
103
104 enum
105 {
106   SIGNAL_CLOSED,
107   SIGNAL_NEW_SESSION,
108   SIGNAL_OPTIONS_REQUEST,
109   SIGNAL_DESCRIBE_REQUEST,
110   SIGNAL_SETUP_REQUEST,
111   SIGNAL_PLAY_REQUEST,
112   SIGNAL_PAUSE_REQUEST,
113   SIGNAL_TEARDOWN_REQUEST,
114   SIGNAL_SET_PARAMETER_REQUEST,
115   SIGNAL_GET_PARAMETER_REQUEST,
116   SIGNAL_HANDLE_RESPONSE,
117   SIGNAL_SEND_MESSAGE,
118   SIGNAL_LAST
119 };
120
121 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
122 #define GST_CAT_DEFAULT rtsp_client_debug
123
124 static guint gst_rtsp_client_signals[SIGNAL_LAST] = { 0 };
125
126 static void gst_rtsp_client_get_property (GObject * object, guint propid,
127     GValue * value, GParamSpec * pspec);
128 static void gst_rtsp_client_set_property (GObject * object, guint propid,
129     const GValue * value, GParamSpec * pspec);
130 static void gst_rtsp_client_finalize (GObject * obj);
131
132 static GstSDPMessage *create_sdp (GstRTSPClient * client, GstRTSPMedia * media);
133 static void client_session_finalized (GstRTSPClient * client,
134     GstRTSPSession * session);
135 static void unlink_session_transports (GstRTSPClient * client,
136     GstRTSPSession * session, GstRTSPSessionMedia * sessmedia);
137 static gboolean default_configure_client_media (GstRTSPClient * client,
138     GstRTSPMedia * media, GstRTSPStream * stream, GstRTSPContext * ctx);
139 static gboolean default_configure_client_transport (GstRTSPClient * client,
140     GstRTSPContext * ctx, GstRTSPTransport * ct);
141 static GstRTSPResult default_params_set (GstRTSPClient * client,
142     GstRTSPContext * ctx);
143 static GstRTSPResult default_params_get (GstRTSPClient * client,
144     GstRTSPContext * ctx);
145 static gchar *default_make_path_from_uri (GstRTSPClient * client,
146     const GstRTSPUrl * uri);
147
148 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
149
150 static void
151 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
152 {
153   GObjectClass *gobject_class;
154
155   g_type_class_add_private (klass, sizeof (GstRTSPClientPrivate));
156
157   gobject_class = G_OBJECT_CLASS (klass);
158
159   gobject_class->get_property = gst_rtsp_client_get_property;
160   gobject_class->set_property = gst_rtsp_client_set_property;
161   gobject_class->finalize = gst_rtsp_client_finalize;
162
163   klass->create_sdp = create_sdp;
164   klass->configure_client_media = default_configure_client_media;
165   klass->configure_client_transport = default_configure_client_transport;
166   klass->params_set = default_params_set;
167   klass->params_get = default_params_get;
168   klass->make_path_from_uri = default_make_path_from_uri;
169
170   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
171       g_param_spec_object ("session-pool", "Session Pool",
172           "The session pool to use for client session",
173           GST_TYPE_RTSP_SESSION_POOL,
174           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
175
176   g_object_class_install_property (gobject_class, PROP_MOUNT_POINTS,
177       g_param_spec_object ("mount-points", "Mount Points",
178           "The mount points to use for client session",
179           GST_TYPE_RTSP_MOUNT_POINTS,
180           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
181
182   g_object_class_install_property (gobject_class, PROP_DROP_BACKLOG,
183       g_param_spec_boolean ("drop-backlog", "Drop Backlog",
184           "Drop data when the backlog queue is full",
185           DEFAULT_DROP_BACKLOG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
186
187   gst_rtsp_client_signals[SIGNAL_CLOSED] =
188       g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
189       G_STRUCT_OFFSET (GstRTSPClientClass, closed), NULL, NULL,
190       g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
191
192   gst_rtsp_client_signals[SIGNAL_NEW_SESSION] =
193       g_signal_new ("new-session", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
194       G_STRUCT_OFFSET (GstRTSPClientClass, new_session), NULL, NULL,
195       g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_RTSP_SESSION);
196
197   gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST] =
198       g_signal_new ("options-request", G_TYPE_FROM_CLASS (klass),
199       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, options_request),
200       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
201       G_TYPE_POINTER);
202
203   gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST] =
204       g_signal_new ("describe-request", G_TYPE_FROM_CLASS (klass),
205       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, describe_request),
206       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
207       G_TYPE_POINTER);
208
209   gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST] =
210       g_signal_new ("setup-request", G_TYPE_FROM_CLASS (klass),
211       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, setup_request),
212       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
213       G_TYPE_POINTER);
214
215   gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST] =
216       g_signal_new ("play-request", G_TYPE_FROM_CLASS (klass),
217       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, play_request),
218       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
219       G_TYPE_POINTER);
220
221   gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST] =
222       g_signal_new ("pause-request", G_TYPE_FROM_CLASS (klass),
223       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, pause_request),
224       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
225       G_TYPE_POINTER);
226
227   gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST] =
228       g_signal_new ("teardown-request", G_TYPE_FROM_CLASS (klass),
229       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, teardown_request),
230       NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
231       G_TYPE_POINTER);
232
233   gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST] =
234       g_signal_new ("set-parameter-request", G_TYPE_FROM_CLASS (klass),
235       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
236           set_parameter_request), NULL, NULL, g_cclosure_marshal_VOID__POINTER,
237       G_TYPE_NONE, 1, G_TYPE_POINTER);
238
239   gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST] =
240       g_signal_new ("get-parameter-request", G_TYPE_FROM_CLASS (klass),
241       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
242           get_parameter_request), NULL, NULL, g_cclosure_marshal_VOID__POINTER,
243       G_TYPE_NONE, 1, G_TYPE_POINTER);
244
245   gst_rtsp_client_signals[SIGNAL_HANDLE_RESPONSE] =
246       g_signal_new ("handle-response", G_TYPE_FROM_CLASS (klass),
247       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
248           handle_response), NULL, NULL, g_cclosure_marshal_VOID__POINTER,
249       G_TYPE_NONE, 1, G_TYPE_POINTER);
250
251   gst_rtsp_client_signals[SIGNAL_SEND_MESSAGE] =
252       g_signal_new ("send-message", G_TYPE_FROM_CLASS (klass),
253       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
254       G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
255
256   tunnels =
257       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
258   g_mutex_init (&tunnels_lock);
259
260   GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
261 }
262
263 static void
264 gst_rtsp_client_init (GstRTSPClient * client)
265 {
266   GstRTSPClientPrivate *priv = GST_RTSP_CLIENT_GET_PRIVATE (client);
267
268   client->priv = priv;
269
270   g_mutex_init (&priv->lock);
271   g_mutex_init (&priv->send_lock);
272   priv->close_seq = 0;
273   priv->drop_backlog = DEFAULT_DROP_BACKLOG;
274 }
275
276 static GstRTSPFilterResult
277 filter_session (GstRTSPSession * sess, GstRTSPSessionMedia * sessmedia,
278     gpointer user_data)
279 {
280   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
281
282   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
283   unlink_session_transports (client, sess, sessmedia);
284
285   /* unmanage the media in the session */
286   return GST_RTSP_FILTER_REMOVE;
287 }
288
289 static void
290 client_unlink_session (GstRTSPClient * client, GstRTSPSession * session)
291 {
292   /* unlink all media managed in this session */
293   gst_rtsp_session_filter (session, filter_session, client);
294 }
295
296 static void
297 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
298 {
299   GstRTSPClientPrivate *priv = client->priv;
300   GList *walk;
301
302   for (walk = priv->sessions; walk; walk = g_list_next (walk)) {
303     GstRTSPSession *msession = (GstRTSPSession *) walk->data;
304
305     /* we already know about this session */
306     if (msession == session)
307       return;
308   }
309
310   GST_INFO ("watching session %p", session);
311
312   g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized,
313       client);
314   priv->sessions = g_list_prepend (priv->sessions, session);
315 }
316
317 static void
318 client_unwatch_session (GstRTSPClient * client, GstRTSPSession * session)
319 {
320   GstRTSPClientPrivate *priv = client->priv;
321
322   GST_INFO ("unwatching session %p", session);
323
324   g_object_weak_unref (G_OBJECT (session),
325       (GWeakNotify) client_session_finalized, client);
326   priv->sessions = g_list_remove (priv->sessions, session);
327 }
328
329 static void
330 client_cleanup_session (GstRTSPClient * client, GstRTSPSession * session)
331 {
332   g_object_weak_unref (G_OBJECT (session),
333       (GWeakNotify) client_session_finalized, client);
334   client_unlink_session (client, session);
335 }
336
337 static void
338 client_cleanup_sessions (GstRTSPClient * client)
339 {
340   GstRTSPClientPrivate *priv = client->priv;
341   GList *sessions;
342
343   /* remove weak-ref from sessions */
344   for (sessions = priv->sessions; sessions; sessions = g_list_next (sessions)) {
345     client_cleanup_session (client, (GstRTSPSession *) sessions->data);
346   }
347   g_list_free (priv->sessions);
348   priv->sessions = NULL;
349 }
350
351 /* A client is finalized when the connection is broken */
352 static void
353 gst_rtsp_client_finalize (GObject * obj)
354 {
355   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
356   GstRTSPClientPrivate *priv = client->priv;
357
358   GST_INFO ("finalize client %p", client);
359
360   if (priv->watch)
361     gst_rtsp_watch_set_flushing (priv->watch, TRUE);
362   gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
363
364   if (priv->watch)
365     g_source_destroy ((GSource *) priv->watch);
366
367   client_cleanup_sessions (client);
368
369   if (priv->connection)
370     gst_rtsp_connection_free (priv->connection);
371   if (priv->session_pool)
372     g_object_unref (priv->session_pool);
373   if (priv->mount_points)
374     g_object_unref (priv->mount_points);
375   if (priv->auth)
376     g_object_unref (priv->auth);
377   if (priv->thread_pool)
378     g_object_unref (priv->thread_pool);
379
380   if (priv->path)
381     g_free (priv->path);
382   if (priv->media) {
383     gst_rtsp_media_unprepare (priv->media);
384     g_object_unref (priv->media);
385   }
386
387   g_free (priv->server_ip);
388   g_mutex_clear (&priv->lock);
389   g_mutex_clear (&priv->send_lock);
390
391   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
392 }
393
394 static void
395 gst_rtsp_client_get_property (GObject * object, guint propid,
396     GValue * value, GParamSpec * pspec)
397 {
398   GstRTSPClient *client = GST_RTSP_CLIENT (object);
399   GstRTSPClientPrivate *priv = client->priv;
400
401   switch (propid) {
402     case PROP_SESSION_POOL:
403       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
404       break;
405     case PROP_MOUNT_POINTS:
406       g_value_take_object (value, gst_rtsp_client_get_mount_points (client));
407       break;
408     case PROP_DROP_BACKLOG:
409       g_value_set_boolean (value, priv->drop_backlog);
410       break;
411     default:
412       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
413   }
414 }
415
416 static void
417 gst_rtsp_client_set_property (GObject * object, guint propid,
418     const GValue * value, GParamSpec * pspec)
419 {
420   GstRTSPClient *client = GST_RTSP_CLIENT (object);
421   GstRTSPClientPrivate *priv = client->priv;
422
423   switch (propid) {
424     case PROP_SESSION_POOL:
425       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
426       break;
427     case PROP_MOUNT_POINTS:
428       gst_rtsp_client_set_mount_points (client, g_value_get_object (value));
429       break;
430     case PROP_DROP_BACKLOG:
431       g_mutex_lock (&priv->lock);
432       priv->drop_backlog = g_value_get_boolean (value);
433       g_mutex_unlock (&priv->lock);
434       break;
435     default:
436       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
437   }
438 }
439
440 /**
441  * gst_rtsp_client_new:
442  *
443  * Create a new #GstRTSPClient instance.
444  *
445  * Returns: (transfer full): a new #GstRTSPClient
446  */
447 GstRTSPClient *
448 gst_rtsp_client_new (void)
449 {
450   GstRTSPClient *result;
451
452   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
453
454   return result;
455 }
456
457 static void
458 send_message (GstRTSPClient * client, GstRTSPContext * ctx,
459     GstRTSPMessage * message, gboolean close)
460 {
461   GstRTSPClientPrivate *priv = client->priv;
462
463   gst_rtsp_message_add_header (message, GST_RTSP_HDR_SERVER,
464       "GStreamer RTSP server");
465
466   /* remove any previous header */
467   gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
468
469   /* add the new session header for new session ids */
470   if (ctx->session) {
471     gst_rtsp_message_take_header (message, GST_RTSP_HDR_SESSION,
472         gst_rtsp_session_get_header (ctx->session));
473   }
474
475   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
476     gst_rtsp_message_dump (message);
477   }
478
479   if (close)
480     gst_rtsp_message_add_header (message, GST_RTSP_HDR_CONNECTION, "close");
481
482   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SEND_MESSAGE],
483       0, ctx, message);
484
485   g_mutex_lock (&priv->send_lock);
486   if (priv->send_func)
487     priv->send_func (client, message, close, priv->send_data);
488   g_mutex_unlock (&priv->send_lock);
489
490   gst_rtsp_message_unset (message);
491 }
492
493 static void
494 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
495     GstRTSPContext * ctx)
496 {
497   gst_rtsp_message_init_response (ctx->response, code,
498       gst_rtsp_status_as_text (code), ctx->request);
499
500   ctx->session = NULL;
501
502   send_message (client, ctx, ctx->response, FALSE);
503 }
504
505 static void
506 send_option_not_supported_response (GstRTSPClient * client,
507     GstRTSPContext * ctx, const gchar * unsupported_options)
508 {
509   GstRTSPStatusCode code = GST_RTSP_STS_OPTION_NOT_SUPPORTED;
510
511   gst_rtsp_message_init_response (ctx->response, code,
512       gst_rtsp_status_as_text (code), ctx->request);
513
514   if (unsupported_options != NULL) {
515     gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_UNSUPPORTED,
516         unsupported_options);
517   }
518
519   ctx->session = NULL;
520
521   send_message (client, ctx, ctx->response, FALSE);
522 }
523
524 static gboolean
525 paths_are_equal (const gchar * path1, const gchar * path2, gint len2)
526 {
527   if (path1 == NULL || path2 == NULL)
528     return FALSE;
529
530   if (strlen (path1) != len2)
531     return FALSE;
532
533   if (strncmp (path1, path2, len2))
534     return FALSE;
535
536   return TRUE;
537 }
538
539 /* this function is called to initially find the media for the DESCRIBE request
540  * but is cached for when the same client (without breaking the connection) is
541  * doing a setup for the exact same url. */
542 static GstRTSPMedia *
543 find_media (GstRTSPClient * client, GstRTSPContext * ctx, gchar * path,
544     gint * matched)
545 {
546   GstRTSPClientPrivate *priv = client->priv;
547   GstRTSPMediaFactory *factory;
548   GstRTSPMedia *media;
549   gint path_len;
550
551   /* find the longest matching factory for the uri first */
552   if (!(factory = gst_rtsp_mount_points_match (priv->mount_points,
553               path, matched)))
554     goto no_factory;
555
556   ctx->factory = factory;
557
558   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_ACCESS))
559     goto no_factory_access;
560
561   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_CONSTRUCT))
562     goto not_authorized;
563
564   if (matched)
565     path_len = *matched;
566   else
567     path_len = strlen (path);
568
569   if (!paths_are_equal (priv->path, path, path_len)) {
570     GstRTSPThread *thread;
571
572     /* remove any previously cached values before we try to construct a new
573      * media for uri */
574     if (priv->path)
575       g_free (priv->path);
576     priv->path = NULL;
577     if (priv->media) {
578       gst_rtsp_media_unprepare (priv->media);
579       g_object_unref (priv->media);
580     }
581     priv->media = NULL;
582
583     /* prepare the media and add it to the pipeline */
584     if (!(media = gst_rtsp_media_factory_construct (factory, ctx->uri)))
585       goto no_media;
586
587     ctx->media = media;
588
589     thread = gst_rtsp_thread_pool_get_thread (priv->thread_pool,
590         GST_RTSP_THREAD_TYPE_MEDIA, ctx);
591     if (thread == NULL)
592       goto no_thread;
593
594     /* prepare the media */
595     if (!(gst_rtsp_media_prepare (media, thread)))
596       goto no_prepare;
597
598     /* now keep track of the uri and the media */
599     priv->path = g_strndup (path, path_len);
600     priv->media = media;
601   } else {
602     /* we have seen this path before, used cached media */
603     media = priv->media;
604     ctx->media = media;
605     GST_INFO ("reusing cached media %p for path %s", media, priv->path);
606   }
607
608   g_object_unref (factory);
609   ctx->factory = NULL;
610
611   if (media)
612     g_object_ref (media);
613
614   return media;
615
616   /* ERRORS */
617 no_factory:
618   {
619     GST_ERROR ("client %p: no factory for path %s", client, path);
620     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
621     return NULL;
622   }
623 no_factory_access:
624   {
625     GST_ERROR ("client %p: not authorized to see factory path %s", client,
626         path);
627     /* error reply is already sent */
628     return NULL;
629   }
630 not_authorized:
631   {
632     GST_ERROR ("client %p: not authorized for factory path %s", client, path);
633     /* error reply is already sent */
634     return NULL;
635   }
636 no_media:
637   {
638     GST_ERROR ("client %p: can't create media", client);
639     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
640     g_object_unref (factory);
641     ctx->factory = NULL;
642     return NULL;
643   }
644 no_thread:
645   {
646     GST_ERROR ("client %p: can't create thread", client);
647     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
648     g_object_unref (media);
649     ctx->media = NULL;
650     g_object_unref (factory);
651     ctx->factory = NULL;
652     return NULL;
653   }
654 no_prepare:
655   {
656     GST_ERROR ("client %p: can't prepare media", client);
657     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
658     g_object_unref (media);
659     ctx->media = NULL;
660     g_object_unref (factory);
661     ctx->factory = NULL;
662     return NULL;
663   }
664 }
665
666 static gboolean
667 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
668 {
669   GstRTSPClientPrivate *priv = client->priv;
670   GstRTSPMessage message = { 0 };
671   GstMapInfo map_info;
672   guint8 *data;
673   guint usize;
674
675   gst_rtsp_message_init_data (&message, channel);
676
677   /* FIXME, need some sort of iovec RTSPMessage here */
678   if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ))
679     return FALSE;
680
681   gst_rtsp_message_take_body (&message, map_info.data, map_info.size);
682
683   g_mutex_lock (&priv->send_lock);
684   if (priv->send_func)
685     priv->send_func (client, &message, FALSE, priv->send_data);
686   g_mutex_unlock (&priv->send_lock);
687
688   gst_rtsp_message_steal_body (&message, &data, &usize);
689   gst_buffer_unmap (buffer, &map_info);
690
691   gst_rtsp_message_unset (&message);
692
693   return TRUE;
694 }
695
696 static void
697 link_transport (GstRTSPClient * client, GstRTSPSession * session,
698     GstRTSPStreamTransport * trans)
699 {
700   GstRTSPClientPrivate *priv = client->priv;
701
702   GST_DEBUG ("client %p: linking transport %p", client, trans);
703
704   gst_rtsp_stream_transport_set_callbacks (trans,
705       (GstRTSPSendFunc) do_send_data,
706       (GstRTSPSendFunc) do_send_data, client, NULL);
707
708   priv->transports = g_list_prepend (priv->transports, trans);
709
710   /* make sure our session can't expire */
711   gst_rtsp_session_prevent_expire (session);
712 }
713
714 static void
715 link_session_transports (GstRTSPClient * client, GstRTSPSession * session,
716     GstRTSPSessionMedia * sessmedia)
717 {
718   guint n_streams, i;
719
720   n_streams =
721       gst_rtsp_media_n_streams (gst_rtsp_session_media_get_media (sessmedia));
722   for (i = 0; i < n_streams; i++) {
723     GstRTSPStreamTransport *trans;
724     const GstRTSPTransport *tr;
725
726     /* get the transport, if there is no transport configured, skip this stream */
727     trans = gst_rtsp_session_media_get_transport (sessmedia, i);
728     if (trans == NULL)
729       continue;
730
731     tr = gst_rtsp_stream_transport_get_transport (trans);
732
733     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
734       /* for TCP, link the stream to the TCP connection of the client */
735       link_transport (client, session, trans);
736     }
737   }
738 }
739
740 static void
741 unlink_transport (GstRTSPClient * client, GstRTSPSession * session,
742     GstRTSPStreamTransport * trans)
743 {
744   GstRTSPClientPrivate *priv = client->priv;
745
746   GST_DEBUG ("client %p: unlinking transport %p", client, trans);
747
748   gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
749
750   priv->transports = g_list_remove (priv->transports, trans);
751
752   /* our session can now expire */
753   gst_rtsp_session_allow_expire (session);
754 }
755
756 static void
757 unlink_session_transports (GstRTSPClient * client, GstRTSPSession * session,
758     GstRTSPSessionMedia * sessmedia)
759 {
760   guint n_streams, i;
761
762   n_streams =
763       gst_rtsp_media_n_streams (gst_rtsp_session_media_get_media (sessmedia));
764   for (i = 0; i < n_streams; i++) {
765     GstRTSPStreamTransport *trans;
766     const GstRTSPTransport *tr;
767
768     /* get the transport, if there is no transport configured, skip this stream */
769     trans = gst_rtsp_session_media_get_transport (sessmedia, i);
770     if (trans == NULL)
771       continue;
772
773     tr = gst_rtsp_stream_transport_get_transport (trans);
774
775     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
776       /* for TCP, unlink the stream from the TCP connection of the client */
777       unlink_transport (client, session, trans);
778     }
779   }
780 }
781
782 static void
783 close_connection (GstRTSPClient * client)
784 {
785   GstRTSPClientPrivate *priv = client->priv;
786   const gchar *tunnelid;
787
788   GST_DEBUG ("client %p: closing connection", client);
789
790   if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
791     g_mutex_lock (&tunnels_lock);
792     /* remove from tunnelids */
793     g_hash_table_remove (tunnels, tunnelid);
794     g_mutex_unlock (&tunnels_lock);
795   }
796
797   gst_rtsp_connection_close (priv->connection);
798 }
799
800 static gchar *
801 default_make_path_from_uri (GstRTSPClient * client, const GstRTSPUrl * uri)
802 {
803   gchar *path;
804
805   if (uri->query)
806     path = g_strconcat (uri->abspath, "?", uri->query, NULL);
807   else
808     path = g_strdup (uri->abspath);
809
810   return path;
811 }
812
813 static gboolean
814 handle_teardown_request (GstRTSPClient * client, GstRTSPContext * ctx)
815 {
816   GstRTSPClientPrivate *priv = client->priv;
817   GstRTSPClientClass *klass;
818   GstRTSPSession *session;
819   GstRTSPSessionMedia *sessmedia;
820   GstRTSPStatusCode code;
821   gchar *path;
822   gint matched;
823
824   if (!ctx->session)
825     goto no_session;
826
827   session = ctx->session;
828
829   if (!ctx->uri)
830     goto no_uri;
831
832   klass = GST_RTSP_CLIENT_GET_CLASS (client);
833   path = klass->make_path_from_uri (client, ctx->uri);
834
835   /* get a handle to the configuration of the media in the session */
836   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
837   if (!sessmedia)
838     goto not_found;
839
840   /* only aggregate control for now.. */
841   if (path[matched] != '\0')
842     goto no_aggregate;
843
844   g_free (path);
845
846   ctx->sessmedia = sessmedia;
847
848   /* we emit the signal before closing the connection */
849   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST],
850       0, ctx);
851
852   /* make sure we unblock the backlog and don't accept new messages
853    * on the watch */
854   gst_rtsp_watch_set_flushing (priv->watch, TRUE);
855
856   /* unlink the all TCP callbacks */
857   unlink_session_transports (client, session, sessmedia);
858
859   /* remove the session from the watched sessions */
860   client_unwatch_session (client, session);
861
862   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
863
864   /* allow messages again so that we can send the reply */
865   gst_rtsp_watch_set_flushing (priv->watch, FALSE);
866
867   /* unmanage the media in the session, returns false if all media session
868    * are torn down. */
869   if (!gst_rtsp_session_release_media (session, sessmedia)) {
870     /* remove the session */
871     gst_rtsp_session_pool_remove (priv->session_pool, session);
872   }
873   /* construct the response now */
874   code = GST_RTSP_STS_OK;
875   gst_rtsp_message_init_response (ctx->response, code,
876       gst_rtsp_status_as_text (code), ctx->request);
877
878   send_message (client, ctx, ctx->response, TRUE);
879
880   return TRUE;
881
882   /* ERRORS */
883 no_session:
884   {
885     GST_ERROR ("client %p: no session", client);
886     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
887     return FALSE;
888   }
889 no_uri:
890   {
891     GST_ERROR ("client %p: no uri supplied", client);
892     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
893     return FALSE;
894   }
895 not_found:
896   {
897     GST_ERROR ("client %p: no media for uri", client);
898     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
899     g_free (path);
900     return FALSE;
901   }
902 no_aggregate:
903   {
904     GST_ERROR ("client %p: no aggregate path %s", client, path);
905     send_generic_response (client,
906         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
907     g_free (path);
908     return FALSE;
909   }
910 }
911
912 static GstRTSPResult
913 default_params_set (GstRTSPClient * client, GstRTSPContext * ctx)
914 {
915   GstRTSPResult res;
916
917   res = gst_rtsp_params_set (client, ctx);
918
919   return res;
920 }
921
922 static GstRTSPResult
923 default_params_get (GstRTSPClient * client, GstRTSPContext * ctx)
924 {
925   GstRTSPResult res;
926
927   res = gst_rtsp_params_get (client, ctx);
928
929   return res;
930 }
931
932 static gboolean
933 handle_get_param_request (GstRTSPClient * client, GstRTSPContext * ctx)
934 {
935   GstRTSPResult res;
936   guint8 *data;
937   guint size;
938
939   res = gst_rtsp_message_get_body (ctx->request, &data, &size);
940   if (res != GST_RTSP_OK)
941     goto bad_request;
942
943   if (size == 0) {
944     /* no body, keep-alive request */
945     send_generic_response (client, GST_RTSP_STS_OK, ctx);
946   } else {
947     /* there is a body, handle the params */
948     res = GST_RTSP_CLIENT_GET_CLASS (client)->params_get (client, ctx);
949     if (res != GST_RTSP_OK)
950       goto bad_request;
951
952     send_message (client, ctx, ctx->response, FALSE);
953   }
954
955   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST],
956       0, ctx);
957
958   return TRUE;
959
960   /* ERRORS */
961 bad_request:
962   {
963     GST_ERROR ("client %p: bad request", client);
964     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
965     return FALSE;
966   }
967 }
968
969 static gboolean
970 handle_set_param_request (GstRTSPClient * client, GstRTSPContext * ctx)
971 {
972   GstRTSPResult res;
973   guint8 *data;
974   guint size;
975
976   res = gst_rtsp_message_get_body (ctx->request, &data, &size);
977   if (res != GST_RTSP_OK)
978     goto bad_request;
979
980   if (size == 0) {
981     /* no body, keep-alive request */
982     send_generic_response (client, GST_RTSP_STS_OK, ctx);
983   } else {
984     /* there is a body, handle the params */
985     res = GST_RTSP_CLIENT_GET_CLASS (client)->params_set (client, ctx);
986     if (res != GST_RTSP_OK)
987       goto bad_request;
988
989     send_message (client, ctx, ctx->response, FALSE);
990   }
991
992   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST],
993       0, ctx);
994
995   return TRUE;
996
997   /* ERRORS */
998 bad_request:
999   {
1000     GST_ERROR ("client %p: bad request", client);
1001     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1002     return FALSE;
1003   }
1004 }
1005
1006 static gboolean
1007 handle_pause_request (GstRTSPClient * client, GstRTSPContext * ctx)
1008 {
1009   GstRTSPSession *session;
1010   GstRTSPClientClass *klass;
1011   GstRTSPSessionMedia *sessmedia;
1012   GstRTSPStatusCode code;
1013   GstRTSPState rtspstate;
1014   gchar *path;
1015   gint matched;
1016
1017   if (!(session = ctx->session))
1018     goto no_session;
1019
1020   if (!ctx->uri)
1021     goto no_uri;
1022
1023   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1024   path = klass->make_path_from_uri (client, ctx->uri);
1025
1026   /* get a handle to the configuration of the media in the session */
1027   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1028   if (!sessmedia)
1029     goto not_found;
1030
1031   if (path[matched] != '\0')
1032     goto no_aggregate;
1033
1034   g_free (path);
1035
1036   ctx->sessmedia = sessmedia;
1037
1038   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1039   /* the session state must be playing or recording */
1040   if (rtspstate != GST_RTSP_STATE_PLAYING &&
1041       rtspstate != GST_RTSP_STATE_RECORDING)
1042     goto invalid_state;
1043
1044   /* unlink the all TCP callbacks */
1045   unlink_session_transports (client, session, sessmedia);
1046
1047   /* then pause sending */
1048   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PAUSED);
1049
1050   /* construct the response now */
1051   code = GST_RTSP_STS_OK;
1052   gst_rtsp_message_init_response (ctx->response, code,
1053       gst_rtsp_status_as_text (code), ctx->request);
1054
1055   send_message (client, ctx, ctx->response, FALSE);
1056
1057   /* the state is now READY */
1058   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
1059
1060   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST], 0, ctx);
1061
1062   return TRUE;
1063
1064   /* ERRORS */
1065 no_session:
1066   {
1067     GST_ERROR ("client %p: no seesion", client);
1068     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1069     return FALSE;
1070   }
1071 no_uri:
1072   {
1073     GST_ERROR ("client %p: no uri supplied", client);
1074     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1075     return FALSE;
1076   }
1077 not_found:
1078   {
1079     GST_ERROR ("client %p: no media for uri", client);
1080     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1081     g_free (path);
1082     return FALSE;
1083   }
1084 no_aggregate:
1085   {
1086     GST_ERROR ("client %p: no aggregate path %s", client, path);
1087     send_generic_response (client,
1088         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
1089     g_free (path);
1090     return FALSE;
1091   }
1092 invalid_state:
1093   {
1094     GST_ERROR ("client %p: not PLAYING or RECORDING", client);
1095     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1096         ctx);
1097     return FALSE;
1098   }
1099 }
1100
1101 /* convert @url and @path to a URL used as a content base for the factory
1102  * located at @path */
1103 static gchar *
1104 make_base_url (GstRTSPClient * client, GstRTSPUrl * url, const gchar * path)
1105 {
1106   GstRTSPUrl tmp;
1107   gchar *result;
1108   const gchar *trail;
1109
1110   /* check for trailing '/' and append one */
1111   trail = (path[strlen (path) - 1] != '/' ? "/" : "");
1112
1113   tmp = *url;
1114   tmp.user = NULL;
1115   tmp.passwd = NULL;
1116   tmp.abspath = g_strdup_printf ("%s%s", path, trail);
1117   tmp.query = NULL;
1118   result = gst_rtsp_url_get_request_uri (&tmp);
1119   g_free (tmp.abspath);
1120
1121   return result;
1122 }
1123
1124 static gboolean
1125 handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
1126 {
1127   GstRTSPSession *session;
1128   GstRTSPClientClass *klass;
1129   GstRTSPSessionMedia *sessmedia;
1130   GstRTSPMedia *media;
1131   GstRTSPStatusCode code;
1132   GstRTSPUrl *uri;
1133   gchar *str;
1134   GstRTSPTimeRange *range;
1135   GstRTSPResult res;
1136   GstRTSPState rtspstate;
1137   GstRTSPRangeUnit unit = GST_RTSP_RANGE_NPT;
1138   gchar *path, *rtpinfo;
1139   gint matched;
1140
1141   if (!(session = ctx->session))
1142     goto no_session;
1143
1144   if (!(uri = ctx->uri))
1145     goto no_uri;
1146
1147   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1148   path = klass->make_path_from_uri (client, uri);
1149
1150   /* get a handle to the configuration of the media in the session */
1151   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1152   if (!sessmedia)
1153     goto not_found;
1154
1155   if (path[matched] != '\0')
1156     goto no_aggregate;
1157
1158   g_free (path);
1159
1160   ctx->sessmedia = sessmedia;
1161   ctx->media = media = gst_rtsp_session_media_get_media (sessmedia);
1162
1163   /* the session state must be playing or ready */
1164   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1165   if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
1166     goto invalid_state;
1167
1168   /* in play we first unsuspend, media could be suspended from SDP or PAUSED */
1169   if (!gst_rtsp_media_unsuspend (media))
1170     goto unsuspend_failed;
1171
1172   /* parse the range header if we have one */
1173   res = gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RANGE, &str, 0);
1174   if (res == GST_RTSP_OK) {
1175     if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
1176       /* we have a range, seek to the position */
1177       unit = range->unit;
1178       gst_rtsp_media_seek (media, range);
1179       gst_rtsp_range_free (range);
1180     }
1181   }
1182
1183   /* link the all TCP callbacks */
1184   link_session_transports (client, session, sessmedia);
1185
1186   /* grab RTPInfo from the media now */
1187   rtpinfo = gst_rtsp_session_media_get_rtpinfo (sessmedia);
1188
1189   /* construct the response now */
1190   code = GST_RTSP_STS_OK;
1191   gst_rtsp_message_init_response (ctx->response, code,
1192       gst_rtsp_status_as_text (code), ctx->request);
1193
1194   /* add the RTP-Info header */
1195   if (rtpinfo)
1196     gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_RTP_INFO,
1197         rtpinfo);
1198
1199   /* add the range */
1200   str = gst_rtsp_media_get_range_string (media, TRUE, unit);
1201   if (str)
1202     gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_RANGE, str);
1203
1204   send_message (client, ctx, ctx->response, FALSE);
1205
1206   /* start playing after sending the response */
1207   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PLAYING);
1208
1209   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_PLAYING);
1210
1211   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST], 0, ctx);
1212
1213   return TRUE;
1214
1215   /* ERRORS */
1216 no_session:
1217   {
1218     GST_ERROR ("client %p: no session", client);
1219     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1220     return FALSE;
1221   }
1222 no_uri:
1223   {
1224     GST_ERROR ("client %p: no uri supplied", client);
1225     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1226     return FALSE;
1227   }
1228 not_found:
1229   {
1230     GST_ERROR ("client %p: media not found", client);
1231     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1232     return FALSE;
1233   }
1234 no_aggregate:
1235   {
1236     GST_ERROR ("client %p: no aggregate path %s", client, path);
1237     send_generic_response (client,
1238         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
1239     g_free (path);
1240     return FALSE;
1241   }
1242 invalid_state:
1243   {
1244     GST_ERROR ("client %p: not PLAYING or READY", client);
1245     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1246         ctx);
1247     return FALSE;
1248   }
1249 unsuspend_failed:
1250   {
1251     GST_ERROR ("client %p: unsuspend failed", client);
1252     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1253     return FALSE;
1254   }
1255 }
1256
1257 static void
1258 do_keepalive (GstRTSPSession * session)
1259 {
1260   GST_INFO ("keep session %p alive", session);
1261   gst_rtsp_session_touch (session);
1262 }
1263
1264 /* parse @transport and return a valid transport in @tr. only transports
1265  * supported by @stream are returned. Returns FALSE if no valid transport
1266  * was found. */
1267 static gboolean
1268 parse_transport (const char *transport, GstRTSPStream * stream,
1269     GstRTSPTransport * tr)
1270 {
1271   gint i;
1272   gboolean res;
1273   gchar **transports;
1274
1275   res = FALSE;
1276   gst_rtsp_transport_init (tr);
1277
1278   GST_DEBUG ("parsing transports %s", transport);
1279
1280   transports = g_strsplit (transport, ",", 0);
1281
1282   /* loop through the transports, try to parse */
1283   for (i = 0; transports[i]; i++) {
1284     res = gst_rtsp_transport_parse (transports[i], tr);
1285     if (res != GST_RTSP_OK) {
1286       /* no valid transport, search some more */
1287       GST_WARNING ("could not parse transport %s", transports[i]);
1288       goto next;
1289     }
1290
1291     /* we have a transport, see if it's supported */
1292     if (!gst_rtsp_stream_is_transport_supported (stream, tr)) {
1293       GST_WARNING ("unsupported transport %s", transports[i]);
1294       goto next;
1295     }
1296
1297     /* we have a valid transport */
1298     GST_INFO ("found valid transport %s", transports[i]);
1299     res = TRUE;
1300     break;
1301
1302   next:
1303     gst_rtsp_transport_init (tr);
1304   }
1305   g_strfreev (transports);
1306
1307   return res;
1308 }
1309
1310 static gboolean
1311 default_configure_client_media (GstRTSPClient * client, GstRTSPMedia * media,
1312     GstRTSPStream * stream, GstRTSPContext * ctx)
1313 {
1314   GstRTSPMessage *request = ctx->request;
1315   gchar *blocksize_str;
1316
1317   if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_BLOCKSIZE,
1318           &blocksize_str, 0) == GST_RTSP_OK) {
1319     guint64 blocksize;
1320     gchar *end;
1321
1322     blocksize = g_ascii_strtoull (blocksize_str, &end, 10);
1323     if (end == blocksize_str)
1324       goto parse_failed;
1325
1326     /* we don't want to change the mtu when this media
1327      * can be shared because it impacts other clients */
1328     if (gst_rtsp_media_is_shared (media))
1329       goto done;
1330
1331     if (blocksize > G_MAXUINT)
1332       blocksize = G_MAXUINT;
1333
1334     gst_rtsp_stream_set_mtu (stream, blocksize);
1335   }
1336 done:
1337   return TRUE;
1338
1339   /* ERRORS */
1340 parse_failed:
1341   {
1342     GST_ERROR_OBJECT (client, "failed to parse blocksize");
1343     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1344     return FALSE;
1345   }
1346 }
1347
1348 static gboolean
1349 default_configure_client_transport (GstRTSPClient * client,
1350     GstRTSPContext * ctx, GstRTSPTransport * ct)
1351 {
1352   GstRTSPClientPrivate *priv = client->priv;
1353
1354   /* we have a valid transport now, set the destination of the client. */
1355   if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
1356     gboolean use_client_settings;
1357
1358     use_client_settings =
1359         gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS);
1360
1361     if (ct->destination && use_client_settings) {
1362       GstRTSPAddress *addr;
1363
1364       addr = gst_rtsp_stream_reserve_address (ctx->stream, ct->destination,
1365           ct->port.min, ct->port.max - ct->port.min + 1, ct->ttl);
1366
1367       if (addr == NULL)
1368         goto no_address;
1369
1370       gst_rtsp_address_free (addr);
1371     } else {
1372       GstRTSPAddress *addr;
1373       GSocketFamily family;
1374
1375       family = priv->is_ipv6 ? G_SOCKET_FAMILY_IPV6 : G_SOCKET_FAMILY_IPV4;
1376
1377       addr = gst_rtsp_stream_get_multicast_address (ctx->stream, family);
1378       if (addr == NULL)
1379         goto no_address;
1380
1381       g_free (ct->destination);
1382       ct->destination = g_strdup (addr->address);
1383       ct->port.min = addr->port;
1384       ct->port.max = addr->port + addr->n_ports - 1;
1385       ct->ttl = addr->ttl;
1386
1387       gst_rtsp_address_free (addr);
1388     }
1389   } else {
1390     GstRTSPUrl *url;
1391
1392     url = gst_rtsp_connection_get_url (priv->connection);
1393     g_free (ct->destination);
1394     ct->destination = g_strdup (url->host);
1395
1396     if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
1397       GSocket *sock;
1398       GSocketAddress *addr;
1399
1400       sock = gst_rtsp_connection_get_read_socket (priv->connection);
1401       if ((addr = g_socket_get_remote_address (sock, NULL))) {
1402         /* our read port is the sender port of client */
1403         ct->client_port.min =
1404             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1405         g_object_unref (addr);
1406       }
1407       if ((addr = g_socket_get_local_address (sock, NULL))) {
1408         ct->server_port.max =
1409             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1410         g_object_unref (addr);
1411       }
1412       sock = gst_rtsp_connection_get_write_socket (priv->connection);
1413       if ((addr = g_socket_get_remote_address (sock, NULL))) {
1414         /* our write port is the receiver port of client */
1415         ct->client_port.max =
1416             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1417         g_object_unref (addr);
1418       }
1419       if ((addr = g_socket_get_local_address (sock, NULL))) {
1420         ct->server_port.min =
1421             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1422         g_object_unref (addr);
1423       }
1424       /* check if the client selected channels for TCP */
1425       if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
1426         gst_rtsp_session_media_alloc_channels (ctx->sessmedia,
1427             &ct->interleaved);
1428       }
1429     }
1430   }
1431   return TRUE;
1432
1433   /* ERRORS */
1434 no_address:
1435   {
1436     GST_ERROR_OBJECT (client, "failed to acquire address for stream");
1437     return FALSE;
1438   }
1439 }
1440
1441 static GstRTSPTransport *
1442 make_server_transport (GstRTSPClient * client, GstRTSPContext * ctx,
1443     GstRTSPTransport * ct)
1444 {
1445   GstRTSPTransport *st;
1446   GInetAddress *addr;
1447   GSocketFamily family;
1448
1449   /* prepare the server transport */
1450   gst_rtsp_transport_new (&st);
1451
1452   st->trans = ct->trans;
1453   st->profile = ct->profile;
1454   st->lower_transport = ct->lower_transport;
1455
1456   addr = g_inet_address_new_from_string (ct->destination);
1457
1458   if (!addr) {
1459     GST_ERROR ("failed to get inet addr from client destination");
1460     family = G_SOCKET_FAMILY_IPV4;
1461   } else {
1462     family = g_inet_address_get_family (addr);
1463     g_object_unref (addr);
1464     addr = NULL;
1465   }
1466
1467   switch (st->lower_transport) {
1468     case GST_RTSP_LOWER_TRANS_UDP:
1469       st->client_port = ct->client_port;
1470       gst_rtsp_stream_get_server_port (ctx->stream, &st->server_port, family);
1471       break;
1472     case GST_RTSP_LOWER_TRANS_UDP_MCAST:
1473       st->port = ct->port;
1474       st->destination = g_strdup (ct->destination);
1475       st->ttl = ct->ttl;
1476       break;
1477     case GST_RTSP_LOWER_TRANS_TCP:
1478       st->interleaved = ct->interleaved;
1479       st->client_port = ct->client_port;
1480       st->server_port = ct->server_port;
1481     default:
1482       break;
1483   }
1484
1485   gst_rtsp_stream_get_ssrc (ctx->stream, &st->ssrc);
1486
1487   return st;
1488 }
1489
1490 static gboolean
1491 mikey_apply_policy (GstCaps * caps, GstMIKEYMessage * msg, guint8 policy)
1492 {
1493   const gchar *srtp_cipher;
1494   const gchar *srtp_auth;
1495   const GstMIKEYPayload *sp;
1496   guint i;
1497
1498   /* loop over Security policy until we find one containing policy */
1499   for (i = 0;; i++) {
1500     if ((sp = gst_mikey_message_find_payload (msg, GST_MIKEY_PT_SP, i)) == NULL)
1501       break;
1502
1503     if (((GstMIKEYPayloadSP *) sp)->policy == policy)
1504       break;
1505   }
1506
1507   /* the default ciphers */
1508   srtp_cipher = "aes-128-icm";
1509   srtp_auth = "hmac-sha1-80";
1510
1511   /* now override the defaults with what is in the Security Policy */
1512   if (sp != NULL) {
1513     guint len;
1514
1515     /* collect all the params and go over them */
1516     len = gst_mikey_payload_sp_get_n_params (sp);
1517     for (i = 0; i < len; i++) {
1518       const GstMIKEYPayloadSPParam *param =
1519           gst_mikey_payload_sp_get_param (sp, i);
1520
1521       switch (param->type) {
1522         case GST_MIKEY_SP_SRTP_ENC_ALG:
1523           switch (param->val[0]) {
1524             case 0:
1525               srtp_cipher = "null";
1526               break;
1527             case 2:
1528             case 1:
1529               srtp_cipher = "aes-128-icm";
1530               break;
1531             default:
1532               break;
1533           }
1534           break;
1535         case GST_MIKEY_SP_SRTP_AUTH_ALG:
1536           switch (param->val[0]) {
1537             case 0:
1538               srtp_auth = "null";
1539               break;
1540             case 2:
1541             case 1:
1542               srtp_auth = "hmac-sha1-80";
1543               break;
1544             default:
1545               break;
1546           }
1547           break;
1548         case GST_MIKEY_SP_SRTP_SRTP_ENC:
1549           break;
1550         case GST_MIKEY_SP_SRTP_SRTCP_ENC:
1551           break;
1552         default:
1553           break;
1554       }
1555     }
1556   }
1557   /* now configure the SRTP parameters */
1558   gst_caps_set_simple (caps,
1559       "srtp-cipher", G_TYPE_STRING, srtp_cipher,
1560       "srtp-auth", G_TYPE_STRING, srtp_auth,
1561       "srtcp-cipher", G_TYPE_STRING, srtp_cipher,
1562       "srtcp-auth", G_TYPE_STRING, srtp_auth, NULL);
1563
1564   return TRUE;
1565 }
1566
1567 static gboolean
1568 handle_mikey_data (GstRTSPClient * client, GstRTSPContext * ctx,
1569     guint8 * data, gsize size)
1570 {
1571   GstMIKEYMessage *msg;
1572   guint i, n_cs;
1573   GstCaps *caps = NULL;
1574   GstMIKEYPayloadKEMAC *kemac;
1575   const GstMIKEYPayloadKeyData *pkd;
1576   GstBuffer *key;
1577
1578   /* the MIKEY message contains a CSB or crypto session bundle. It is a
1579    * set of Crypto Sessions protected with the same master key.
1580    * In the context of SRTP, an RTP and its RTCP stream is part of a
1581    * crypto session */
1582   if ((msg = gst_mikey_message_new_from_data (data, size, NULL, NULL)) == NULL)
1583     goto parse_failed;
1584
1585   /* we can only handle SRTP crypto sessions for now */
1586   if (msg->map_type != GST_MIKEY_MAP_TYPE_SRTP)
1587     goto invalid_map_type;
1588
1589   /* get the number of crypto sessions. This maps SSRC to its
1590    * security parameters */
1591   n_cs = gst_mikey_message_get_n_cs (msg);
1592   if (n_cs == 0)
1593     goto no_crypto_sessions;
1594
1595   /* we also need keys */
1596   if (!(kemac = (GstMIKEYPayloadKEMAC *) gst_mikey_message_find_payload
1597           (msg, GST_MIKEY_PT_KEMAC, 0)))
1598     goto no_keys;
1599
1600   /* we don't support encrypted keys */
1601   if (kemac->enc_alg != GST_MIKEY_ENC_NULL
1602       || kemac->mac_alg != GST_MIKEY_MAC_NULL)
1603     goto unsupported_encryption;
1604
1605   /* get Key data sub-payload */
1606   pkd = (const GstMIKEYPayloadKeyData *)
1607       gst_mikey_payload_kemac_get_sub (&kemac->pt, 0);
1608
1609   key =
1610       gst_buffer_new_wrapped (g_memdup (pkd->key_data, pkd->key_len),
1611       pkd->key_len);
1612
1613   /* go over all crypto sessions and create the security policy for each
1614    * SSRC */
1615   for (i = 0; i < n_cs; i++) {
1616     const GstMIKEYMapSRTP *map = gst_mikey_message_get_cs_srtp (msg, i);
1617
1618     caps = gst_caps_new_simple ("application/x-srtp",
1619         "ssrc", G_TYPE_UINT, map->ssrc,
1620         "roc", G_TYPE_UINT, map->roc, "srtp-key", GST_TYPE_BUFFER, key, NULL);
1621     mikey_apply_policy (caps, msg, map->policy);
1622
1623     gst_rtsp_stream_update_crypto (ctx->stream, map->ssrc, caps);
1624     gst_caps_unref (caps);
1625   }
1626   gst_mikey_message_free (msg);
1627
1628   return TRUE;
1629
1630   /* ERRORS */
1631 parse_failed:
1632   {
1633     GST_DEBUG_OBJECT (client, "failed to parse MIKEY message");
1634     return FALSE;
1635   }
1636 invalid_map_type:
1637   {
1638     GST_DEBUG_OBJECT (client, "invalid map type %d", msg->map_type);
1639     goto cleanup_message;
1640   }
1641 no_crypto_sessions:
1642   {
1643     GST_DEBUG_OBJECT (client, "no crypto sessions");
1644     goto cleanup_message;
1645   }
1646 no_keys:
1647   {
1648     GST_DEBUG_OBJECT (client, "no keys found");
1649     goto cleanup_message;
1650   }
1651 unsupported_encryption:
1652   {
1653     GST_DEBUG_OBJECT (client, "unsupported key encryption");
1654     goto cleanup_message;
1655   }
1656 cleanup_message:
1657   {
1658     gst_mikey_message_free (msg);
1659     return FALSE;
1660   }
1661 }
1662
1663 #define IS_STRIP_CHAR(c) (g_ascii_isspace ((guchar)(c)) || ((c) == '\"'))
1664
1665 static void
1666 strip_chars (gchar * str)
1667 {
1668   gchar *s;
1669   gsize len;
1670
1671   len = strlen (str);
1672   while (len--) {
1673     if (!IS_STRIP_CHAR (str[len]))
1674       break;
1675     str[len] = '\0';
1676   }
1677   for (s = str; *s && IS_STRIP_CHAR (*s); s++);
1678   memmove (str, s, len + 1);
1679 }
1680
1681 /**
1682  * KeyMgmt = "KeyMgmt" ":" key-mgmt-spec 0*("," key-mgmt-spec)
1683  * key-mgmt-spec = "prot" "=" KMPID ";" ["uri" "=" %x22 URI %x22 ";"]
1684  */
1685 static gboolean
1686 handle_keymgmt (GstRTSPClient * client, GstRTSPContext * ctx, gchar * keymgmt)
1687 {
1688   gchar **specs;
1689   gint i, j;
1690
1691   specs = g_strsplit (keymgmt, ",", 0);
1692   for (i = 0; specs[i]; i++) {
1693     gchar **split;
1694
1695     split = g_strsplit (specs[i], ";", 0);
1696     for (j = 0; split[j]; j++) {
1697       g_strstrip (split[j]);
1698       if (g_str_has_prefix (split[j], "prot=")) {
1699         g_strstrip (split[j] + 5);
1700         if (!g_str_equal (split[j] + 5, "mikey"))
1701           break;
1702         GST_DEBUG ("found mikey");
1703       } else if (g_str_has_prefix (split[j], "uri=")) {
1704         strip_chars (split[j] + 4);
1705         GST_DEBUG ("found uri '%s'", split[j] + 4);
1706       } else if (g_str_has_prefix (split[j], "data=")) {
1707         guchar *data;
1708         gsize size;
1709         strip_chars (split[j] + 5);
1710         GST_DEBUG ("found data '%s'", split[j] + 5);
1711         data = g_base64_decode_inplace (split[j] + 5, &size);
1712         handle_mikey_data (client, ctx, data, size);
1713       }
1714     }
1715   }
1716   return TRUE;
1717 }
1718
1719 static gboolean
1720 handle_setup_request (GstRTSPClient * client, GstRTSPContext * ctx)
1721 {
1722   GstRTSPClientPrivate *priv = client->priv;
1723   GstRTSPResult res;
1724   GstRTSPUrl *uri;
1725   gchar *transport, *keymgmt;
1726   GstRTSPTransport *ct, *st;
1727   GstRTSPStatusCode code;
1728   GstRTSPSession *session;
1729   GstRTSPStreamTransport *trans;
1730   gchar *trans_str;
1731   GstRTSPSessionMedia *sessmedia;
1732   GstRTSPMedia *media;
1733   GstRTSPStream *stream;
1734   GstRTSPState rtspstate;
1735   GstRTSPClientClass *klass;
1736   gchar *path, *control;
1737   gint matched;
1738
1739   if (!ctx->uri)
1740     goto no_uri;
1741
1742   uri = ctx->uri;
1743   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1744   path = klass->make_path_from_uri (client, uri);
1745
1746   /* parse the transport */
1747   res =
1748       gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_TRANSPORT,
1749       &transport, 0);
1750   if (res != GST_RTSP_OK)
1751     goto no_transport;
1752
1753   /* we create the session after parsing stuff so that we don't make
1754    * a session for malformed requests */
1755   if (priv->session_pool == NULL)
1756     goto no_pool;
1757
1758   session = ctx->session;
1759
1760   if (session) {
1761     g_object_ref (session);
1762     /* get a handle to the configuration of the media in the session, this can
1763      * return NULL if this is a new url to manage in this session. */
1764     sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1765   } else {
1766     /* we need a new media configuration in this session */
1767     sessmedia = NULL;
1768   }
1769
1770   /* we have no session media, find one and manage it */
1771   if (sessmedia == NULL) {
1772     /* get a handle to the configuration of the media in the session */
1773     media = find_media (client, ctx, path, &matched);
1774   } else {
1775     if ((media = gst_rtsp_session_media_get_media (sessmedia)))
1776       g_object_ref (media);
1777     else
1778       goto media_not_found;
1779   }
1780   /* no media, not found then */
1781   if (media == NULL)
1782     goto media_not_found_no_reply;
1783
1784   if (path[matched] == '\0')
1785     goto control_not_found;
1786
1787   /* path is what matched. */
1788   path[matched] = '\0';
1789   /* control is remainder */
1790   control = &path[matched + 1];
1791
1792   /* find the stream now using the control part */
1793   stream = gst_rtsp_media_find_stream (media, control);
1794   if (stream == NULL)
1795     goto stream_not_found;
1796
1797   /* now we have a uri identifying a valid media and stream */
1798   ctx->stream = stream;
1799   ctx->media = media;
1800
1801   if (session == NULL) {
1802     /* create a session if this fails we probably reached our session limit or
1803      * something. */
1804     if (!(session = gst_rtsp_session_pool_create (priv->session_pool)))
1805       goto service_unavailable;
1806
1807     /* make sure this client is closed when the session is closed */
1808     client_watch_session (client, session);
1809
1810     /* signal new session */
1811     g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_NEW_SESSION], 0,
1812         session);
1813
1814     ctx->session = session;
1815   }
1816
1817   if (sessmedia == NULL) {
1818     /* manage the media in our session now, if not done already  */
1819     sessmedia = gst_rtsp_session_manage_media (session, path, media);
1820     /* if we stil have no media, error */
1821     if (sessmedia == NULL)
1822       goto sessmedia_unavailable;
1823   } else {
1824     g_object_unref (media);
1825   }
1826
1827   ctx->sessmedia = sessmedia;
1828
1829   if (!klass->configure_client_media (client, media, stream, ctx))
1830     goto configure_media_failed_no_reply;
1831
1832   gst_rtsp_transport_new (&ct);
1833
1834   /* parse and find a usable supported transport */
1835   if (!parse_transport (transport, stream, ct))
1836     goto unsupported_transports;
1837
1838   /* update the client transport */
1839   if (!klass->configure_client_transport (client, ctx, ct))
1840     goto unsupported_client_transport;
1841
1842   /* parse the keymgmt */
1843   if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_KEYMGMT,
1844           &keymgmt, 0) == GST_RTSP_OK) {
1845     if (!handle_keymgmt (client, ctx, keymgmt))
1846       goto keymgmt_error;
1847   }
1848
1849   /* set in the session media transport */
1850   trans = gst_rtsp_session_media_set_transport (sessmedia, stream, ct);
1851
1852   /* configure the url used to set this transport, this we will use when
1853    * generating the response for the PLAY request */
1854   gst_rtsp_stream_transport_set_url (trans, uri);
1855
1856   /* configure keepalive for this transport */
1857   gst_rtsp_stream_transport_set_keepalive (trans,
1858       (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
1859
1860   /* create and serialize the server transport */
1861   st = make_server_transport (client, ctx, ct);
1862   trans_str = gst_rtsp_transport_as_text (st);
1863   gst_rtsp_transport_free (st);
1864
1865   /* construct the response now */
1866   code = GST_RTSP_STS_OK;
1867   gst_rtsp_message_init_response (ctx->response, code,
1868       gst_rtsp_status_as_text (code), ctx->request);
1869
1870   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_TRANSPORT,
1871       trans_str);
1872   g_free (trans_str);
1873
1874   send_message (client, ctx, ctx->response, FALSE);
1875
1876   /* update the state */
1877   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1878   switch (rtspstate) {
1879     case GST_RTSP_STATE_PLAYING:
1880     case GST_RTSP_STATE_RECORDING:
1881     case GST_RTSP_STATE_READY:
1882       /* no state change */
1883       break;
1884     default:
1885       gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
1886       break;
1887   }
1888   g_object_unref (session);
1889   g_free (path);
1890
1891   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST], 0, ctx);
1892
1893   return TRUE;
1894
1895   /* ERRORS */
1896 no_uri:
1897   {
1898     GST_ERROR ("client %p: no uri", client);
1899     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1900     return FALSE;
1901   }
1902 no_transport:
1903   {
1904     GST_ERROR ("client %p: no transport", client);
1905     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
1906     goto cleanup_path;
1907   }
1908 no_pool:
1909   {
1910     GST_ERROR ("client %p: no session pool configured", client);
1911     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1912     goto cleanup_path;
1913   }
1914 media_not_found_no_reply:
1915   {
1916     GST_ERROR ("client %p: media '%s' not found", client, path);
1917     /* error reply is already sent */
1918     goto cleanup_path;
1919   }
1920 media_not_found:
1921   {
1922     GST_ERROR ("client %p: media '%s' not found", client, path);
1923     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1924     goto cleanup_path;
1925   }
1926 control_not_found:
1927   {
1928     GST_ERROR ("client %p: no control in path '%s'", client, path);
1929     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1930     g_object_unref (media);
1931     goto cleanup_path;
1932   }
1933 stream_not_found:
1934   {
1935     GST_ERROR ("client %p: stream '%s' not found", client, control);
1936     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1937     g_object_unref (media);
1938     goto cleanup_path;
1939   }
1940 service_unavailable:
1941   {
1942     GST_ERROR ("client %p: can't create session", client);
1943     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1944     g_object_unref (media);
1945     goto cleanup_path;
1946   }
1947 sessmedia_unavailable:
1948   {
1949     GST_ERROR ("client %p: can't create session media", client);
1950     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1951     g_object_unref (media);
1952     goto cleanup_session;
1953   }
1954 configure_media_failed_no_reply:
1955   {
1956     GST_ERROR ("client %p: configure_media failed", client);
1957     /* error reply is already sent */
1958     goto cleanup_session;
1959   }
1960 unsupported_transports:
1961   {
1962     GST_ERROR ("client %p: unsupported transports", client);
1963     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
1964     goto cleanup_transport;
1965   }
1966 unsupported_client_transport:
1967   {
1968     GST_ERROR ("client %p: unsupported client transport", client);
1969     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
1970     goto cleanup_transport;
1971   }
1972 keymgmt_error:
1973   {
1974     GST_ERROR ("client %p: keymgmt error", client);
1975     send_generic_response (client, GST_RTSP_STS_KEY_MANAGEMENT_FAILURE, ctx);
1976     goto cleanup_transport;
1977   }
1978   {
1979   cleanup_transport:
1980     gst_rtsp_transport_free (ct);
1981   cleanup_session:
1982     g_object_unref (session);
1983   cleanup_path:
1984     g_free (path);
1985     return FALSE;
1986   }
1987 }
1988
1989 static GstSDPMessage *
1990 create_sdp (GstRTSPClient * client, GstRTSPMedia * media)
1991 {
1992   GstRTSPClientPrivate *priv = client->priv;
1993   GstSDPMessage *sdp;
1994   GstSDPInfo info;
1995   const gchar *proto;
1996
1997   gst_sdp_message_new (&sdp);
1998
1999   /* some standard things first */
2000   gst_sdp_message_set_version (sdp, "0");
2001
2002   if (priv->is_ipv6)
2003     proto = "IP6";
2004   else
2005     proto = "IP4";
2006
2007   gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", proto,
2008       priv->server_ip);
2009
2010   gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
2011   gst_sdp_message_set_information (sdp, "rtsp-server");
2012   gst_sdp_message_add_time (sdp, "0", "0", NULL);
2013   gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
2014   gst_sdp_message_add_attribute (sdp, "type", "broadcast");
2015   gst_sdp_message_add_attribute (sdp, "control", "*");
2016
2017   info.is_ipv6 = priv->is_ipv6;
2018   info.server_ip = priv->server_ip;
2019
2020   /* create an SDP for the media object */
2021   if (!gst_rtsp_media_setup_sdp (media, sdp, &info))
2022     goto no_sdp;
2023
2024   return sdp;
2025
2026   /* ERRORS */
2027 no_sdp:
2028   {
2029     GST_ERROR ("client %p: could not create SDP", client);
2030     gst_sdp_message_free (sdp);
2031     return NULL;
2032   }
2033 }
2034
2035 /* for the describe we must generate an SDP */
2036 static gboolean
2037 handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
2038 {
2039   GstRTSPClientPrivate *priv = client->priv;
2040   GstRTSPResult res;
2041   GstSDPMessage *sdp;
2042   guint i;
2043   gchar *path, *str;
2044   GstRTSPMedia *media;
2045   GstRTSPClientClass *klass;
2046
2047   klass = GST_RTSP_CLIENT_GET_CLASS (client);
2048
2049   if (!ctx->uri)
2050     goto no_uri;
2051
2052   /* check what kind of format is accepted, we don't really do anything with it
2053    * and always return SDP for now. */
2054   for (i = 0;; i++) {
2055     gchar *accept;
2056
2057     res =
2058         gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_ACCEPT,
2059         &accept, i);
2060     if (res == GST_RTSP_ENOTIMPL)
2061       break;
2062
2063     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
2064       break;
2065   }
2066
2067   if (!priv->mount_points)
2068     goto no_mount_points;
2069
2070   if (!(path = gst_rtsp_mount_points_make_path (priv->mount_points, ctx->uri)))
2071     goto no_path;
2072
2073   /* find the media object for the uri */
2074   if (!(media = find_media (client, ctx, path, NULL)))
2075     goto no_media;
2076
2077   /* create an SDP for the media object on this client */
2078   if (!(sdp = klass->create_sdp (client, media)))
2079     goto no_sdp;
2080
2081   /* we suspend after the describe */
2082   gst_rtsp_media_suspend (media);
2083   g_object_unref (media);
2084
2085   gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
2086       gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
2087
2088   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_CONTENT_TYPE,
2089       "application/sdp");
2090
2091   /* content base for some clients that might screw up creating the setup uri */
2092   str = make_base_url (client, ctx->uri, path);
2093   g_free (path);
2094
2095   GST_INFO ("adding content-base: %s", str);
2096   gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_CONTENT_BASE, str);
2097
2098   /* add SDP to the response body */
2099   str = gst_sdp_message_as_text (sdp);
2100   gst_rtsp_message_take_body (ctx->response, (guint8 *) str, strlen (str));
2101   gst_sdp_message_free (sdp);
2102
2103   send_message (client, ctx, ctx->response, FALSE);
2104
2105   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST],
2106       0, ctx);
2107
2108   return TRUE;
2109
2110   /* ERRORS */
2111 no_uri:
2112   {
2113     GST_ERROR ("client %p: no uri", client);
2114     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2115     return FALSE;
2116   }
2117 no_mount_points:
2118   {
2119     GST_ERROR ("client %p: no mount points configured", client);
2120     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2121     return FALSE;
2122   }
2123 no_path:
2124   {
2125     GST_ERROR ("client %p: can't find path for url", client);
2126     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2127     return FALSE;
2128   }
2129 no_media:
2130   {
2131     GST_ERROR ("client %p: no media", client);
2132     g_free (path);
2133     /* error reply is already sent */
2134     return FALSE;
2135   }
2136 no_sdp:
2137   {
2138     GST_ERROR ("client %p: can't create SDP", client);
2139     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
2140     g_free (path);
2141     g_object_unref (media);
2142     return FALSE;
2143   }
2144 }
2145
2146 static gboolean
2147 handle_options_request (GstRTSPClient * client, GstRTSPContext * ctx)
2148 {
2149   GstRTSPMethod options;
2150   gchar *str;
2151
2152   options = GST_RTSP_DESCRIBE |
2153       GST_RTSP_OPTIONS |
2154       GST_RTSP_PAUSE |
2155       GST_RTSP_PLAY |
2156       GST_RTSP_SETUP |
2157       GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
2158
2159   str = gst_rtsp_options_as_text (options);
2160
2161   gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
2162       gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
2163
2164   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_PUBLIC, str);
2165   g_free (str);
2166
2167   send_message (client, ctx, ctx->response, FALSE);
2168
2169   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST],
2170       0, ctx);
2171
2172   return TRUE;
2173 }
2174
2175 /* remove duplicate and trailing '/' */
2176 static void
2177 sanitize_uri (GstRTSPUrl * uri)
2178 {
2179   gint i, len;
2180   gchar *s, *d;
2181   gboolean have_slash, prev_slash;
2182
2183   s = d = uri->abspath;
2184   len = strlen (uri->abspath);
2185
2186   prev_slash = FALSE;
2187
2188   for (i = 0; i < len; i++) {
2189     have_slash = s[i] == '/';
2190     *d = s[i];
2191     if (!have_slash || !prev_slash)
2192       d++;
2193     prev_slash = have_slash;
2194   }
2195   len = d - uri->abspath;
2196   /* don't remove the first slash if that's the only thing left */
2197   if (len > 1 && *(d - 1) == '/')
2198     d--;
2199   *d = '\0';
2200 }
2201
2202 static void
2203 client_session_finalized (GstRTSPClient * client, GstRTSPSession * session)
2204 {
2205   GstRTSPClientPrivate *priv = client->priv;
2206
2207   GST_INFO ("client %p: session %p finished", client, session);
2208
2209   /* unlink all media managed in this session */
2210   client_unlink_session (client, session);
2211
2212   /* remove the session */
2213   if (!(priv->sessions = g_list_remove (priv->sessions, session))) {
2214     GST_INFO ("client %p: all sessions finalized, close the connection",
2215         client);
2216     close_connection (client);
2217   }
2218 }
2219
2220 /* Returns TRUE if there are no Require headers, otherwise returns FALSE
2221  * and also returns a newly-allocated string of (comma-separated) unsupported
2222  * options in the unsupported_reqs variable .
2223  *
2224  * There may be multiple Require headers, but we must send one single
2225  * Unsupported header with all the unsupported options as response. If
2226  * an incoming Require header contained a comma-separated list of options
2227  * GstRtspConnection will already have split that list up into multiple
2228  * headers.
2229  *
2230  * TODO: allow the application to decide what features are supported
2231  */
2232 static gboolean
2233 check_request_requirements (GstRTSPMessage * msg, gchar ** unsupported_reqs)
2234 {
2235   GstRTSPResult res;
2236   GPtrArray *arr = NULL;
2237   gchar *reqs = NULL;
2238   gint i;
2239
2240   i = 0;
2241   do {
2242     res = gst_rtsp_message_get_header (msg, GST_RTSP_HDR_REQUIRE, &reqs, i++);
2243
2244     if (res == GST_RTSP_ENOTIMPL)
2245       break;
2246
2247     if (arr == NULL)
2248       arr = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
2249
2250     g_ptr_array_add (arr, g_strdup (reqs));
2251   }
2252   while (TRUE);
2253
2254   /* if we don't have any Require headers at all, all is fine */
2255   if (i == 1)
2256     return TRUE;
2257
2258   /* otherwise we've now processed at all the Require headers */
2259   g_ptr_array_add (arr, NULL);
2260
2261   /* for now we don't commit to supporting anything, so will just report
2262    * all of the required options as unsupported */
2263   *unsupported_reqs = g_strjoinv (", ", (gchar **) arr->pdata);
2264
2265   g_ptr_array_unref (arr);
2266   return FALSE;
2267 }
2268
2269 static void
2270 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
2271 {
2272   GstRTSPClientPrivate *priv = client->priv;
2273   GstRTSPMethod method;
2274   const gchar *uristr;
2275   GstRTSPUrl *uri = NULL;
2276   GstRTSPVersion version;
2277   GstRTSPResult res;
2278   GstRTSPSession *session = NULL;
2279   GstRTSPContext sctx = { NULL }, *ctx;
2280   GstRTSPMessage response = { 0 };
2281   gchar *unsupported_reqs = NULL;
2282   gchar *sessid;
2283
2284   if (!(ctx = gst_rtsp_context_get_current ())) {
2285     ctx = &sctx;
2286     ctx->auth = priv->auth;
2287     gst_rtsp_context_push_current (ctx);
2288   }
2289
2290   ctx->conn = priv->connection;
2291   ctx->client = client;
2292   ctx->request = request;
2293   ctx->response = &response;
2294
2295   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
2296     gst_rtsp_message_dump (request);
2297   }
2298
2299   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
2300
2301   GST_INFO ("client %p: received a request %s %s %s", client,
2302       gst_rtsp_method_as_text (method), uristr,
2303       gst_rtsp_version_as_text (version));
2304
2305   /* we can only handle 1.0 requests */
2306   if (version != GST_RTSP_VERSION_1_0)
2307     goto not_supported;
2308
2309   ctx->method = method;
2310
2311   /* we always try to parse the url first */
2312   if (strcmp (uristr, "*") == 0) {
2313     /* special case where we have * as uri, keep uri = NULL */
2314   } else if (gst_rtsp_url_parse (uristr, &uri) != GST_RTSP_OK) {
2315     /* check if the uristr is an absolute path <=> scheme and host information
2316      * is missing */
2317     gchar *scheme;
2318
2319     scheme = g_uri_parse_scheme (uristr);
2320     if (scheme == NULL && g_str_has_prefix (uristr, "/")) {
2321       gchar *absolute_uristr = NULL;
2322
2323       GST_WARNING_OBJECT (client, "request doesn't contain absolute url");
2324       if (priv->server_ip == NULL) {
2325         GST_WARNING_OBJECT (client, "host information missing");
2326         goto bad_request;
2327       }
2328
2329       absolute_uristr =
2330           g_strdup_printf ("rtsp://%s%s", priv->server_ip, uristr);
2331
2332       GST_DEBUG_OBJECT (client, "absolute url: %s", absolute_uristr);
2333       if (gst_rtsp_url_parse (absolute_uristr, &uri) != GST_RTSP_OK) {
2334         g_free (absolute_uristr);
2335         goto bad_request;
2336       }
2337       g_free (absolute_uristr);
2338     } else {
2339       g_free (scheme);
2340       goto bad_request;
2341     }
2342   }
2343
2344   /* get the session if there is any */
2345   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
2346   if (res == GST_RTSP_OK) {
2347     if (priv->session_pool == NULL)
2348       goto no_pool;
2349
2350     /* we had a session in the request, find it again */
2351     if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
2352       goto session_not_found;
2353
2354     /* we add the session to the client list of watched sessions. When a session
2355      * disappears because it times out, we will be notified. If all sessions are
2356      * gone, we will close the connection */
2357     client_watch_session (client, session);
2358   }
2359
2360   /* sanitize the uri */
2361   if (uri)
2362     sanitize_uri (uri);
2363   ctx->uri = uri;
2364   ctx->session = session;
2365
2366   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_URL))
2367     goto not_authorized;
2368
2369   /* handle any 'Require' headers */
2370   if (!check_request_requirements (ctx->request, &unsupported_reqs))
2371     goto unsupported_requirement;
2372
2373   /* now see what is asked and dispatch to a dedicated handler */
2374   switch (method) {
2375     case GST_RTSP_OPTIONS:
2376       handle_options_request (client, ctx);
2377       break;
2378     case GST_RTSP_DESCRIBE:
2379       handle_describe_request (client, ctx);
2380       break;
2381     case GST_RTSP_SETUP:
2382       handle_setup_request (client, ctx);
2383       break;
2384     case GST_RTSP_PLAY:
2385       handle_play_request (client, ctx);
2386       break;
2387     case GST_RTSP_PAUSE:
2388       handle_pause_request (client, ctx);
2389       break;
2390     case GST_RTSP_TEARDOWN:
2391       handle_teardown_request (client, ctx);
2392       break;
2393     case GST_RTSP_SET_PARAMETER:
2394       handle_set_param_request (client, ctx);
2395       break;
2396     case GST_RTSP_GET_PARAMETER:
2397       handle_get_param_request (client, ctx);
2398       break;
2399     case GST_RTSP_ANNOUNCE:
2400     case GST_RTSP_RECORD:
2401     case GST_RTSP_REDIRECT:
2402       goto not_implemented;
2403     case GST_RTSP_INVALID:
2404     default:
2405       goto bad_request;
2406   }
2407
2408 done:
2409   if (ctx == &sctx)
2410     gst_rtsp_context_pop_current (ctx);
2411   if (session)
2412     g_object_unref (session);
2413   if (uri)
2414     gst_rtsp_url_free (uri);
2415   return;
2416
2417   /* ERRORS */
2418 not_supported:
2419   {
2420     GST_ERROR ("client %p: version %d not supported", client, version);
2421     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
2422         ctx);
2423     goto done;
2424   }
2425 bad_request:
2426   {
2427     GST_ERROR ("client %p: bad request", client);
2428     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2429     goto done;
2430   }
2431 no_pool:
2432   {
2433     GST_ERROR ("client %p: no pool configured", client);
2434     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
2435     goto done;
2436   }
2437 session_not_found:
2438   {
2439     GST_ERROR ("client %p: session not found", client);
2440     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
2441     goto done;
2442   }
2443 not_authorized:
2444   {
2445     GST_ERROR ("client %p: not allowed", client);
2446     /* error reply is already sent */
2447     goto done;
2448   }
2449 unsupported_requirement:
2450   {
2451     GST_ERROR ("client %p: Required option is not supported (%s)", client,
2452         unsupported_reqs);
2453     send_option_not_supported_response (client, ctx, unsupported_reqs);
2454     g_free (unsupported_reqs);
2455     goto done;
2456   }
2457 not_implemented:
2458   {
2459     GST_ERROR ("client %p: method %d not implemented", client, method);
2460     send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, ctx);
2461     goto done;
2462   }
2463 }
2464
2465
2466 static void
2467 handle_response (GstRTSPClient * client, GstRTSPMessage * response)
2468 {
2469   GstRTSPClientPrivate *priv = client->priv;
2470   GstRTSPResult res;
2471   GstRTSPSession *session = NULL;
2472   GstRTSPContext sctx = { NULL }, *ctx;
2473   gchar *sessid;
2474
2475   if (!(ctx = gst_rtsp_context_get_current ())) {
2476     ctx = &sctx;
2477     ctx->auth = priv->auth;
2478     gst_rtsp_context_push_current (ctx);
2479   }
2480
2481   ctx->conn = priv->connection;
2482   ctx->client = client;
2483   ctx->request = NULL;
2484   ctx->uri = NULL;
2485   ctx->method = GST_RTSP_INVALID;
2486   ctx->response = response;
2487
2488   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
2489     gst_rtsp_message_dump (response);
2490   }
2491
2492   GST_INFO ("client %p: received a response", client);
2493
2494   /* get the session if there is any */
2495   res =
2496       gst_rtsp_message_get_header (response, GST_RTSP_HDR_SESSION, &sessid, 0);
2497   if (res == GST_RTSP_OK) {
2498     if (priv->session_pool == NULL)
2499       goto no_pool;
2500
2501     /* we had a session in the request, find it again */
2502     if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
2503       goto session_not_found;
2504
2505     /* we add the session to the client list of watched sessions. When a session
2506      * disappears because it times out, we will be notified. If all sessions are
2507      * gone, we will close the connection */
2508     client_watch_session (client, session);
2509   }
2510
2511   ctx->session = session;
2512
2513   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_HANDLE_RESPONSE],
2514       0, ctx);
2515
2516 done:
2517   if (ctx == &sctx)
2518     gst_rtsp_context_pop_current (ctx);
2519   if (session)
2520     g_object_unref (session);
2521   return;
2522
2523 no_pool:
2524   {
2525     GST_ERROR ("client %p: no pool configured", client);
2526     goto done;
2527   }
2528 session_not_found:
2529   {
2530     GST_ERROR ("client %p: session not found", client);
2531     goto done;
2532   }
2533 }
2534
2535 static void
2536 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
2537 {
2538   GstRTSPClientPrivate *priv = client->priv;
2539   GstRTSPResult res;
2540   guint8 channel;
2541   GList *walk;
2542   guint8 *data;
2543   guint size;
2544   GstBuffer *buffer;
2545   gboolean handled;
2546
2547   /* find the stream for this message */
2548   res = gst_rtsp_message_parse_data (message, &channel);
2549   if (res != GST_RTSP_OK)
2550     return;
2551
2552   gst_rtsp_message_steal_body (message, &data, &size);
2553
2554   buffer = gst_buffer_new_wrapped (data, size);
2555
2556   handled = FALSE;
2557   for (walk = priv->transports; walk; walk = g_list_next (walk)) {
2558     GstRTSPStreamTransport *trans;
2559     GstRTSPStream *stream;
2560     const GstRTSPTransport *tr;
2561
2562     trans = walk->data;
2563
2564     tr = gst_rtsp_stream_transport_get_transport (trans);
2565     stream = gst_rtsp_stream_transport_get_stream (trans);
2566
2567     /* check for TCP transport */
2568     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
2569       /* dispatch to the stream based on the channel number */
2570       if (tr->interleaved.min == channel) {
2571         gst_rtsp_stream_recv_rtp (stream, buffer);
2572         handled = TRUE;
2573         break;
2574       } else if (tr->interleaved.max == channel) {
2575         gst_rtsp_stream_recv_rtcp (stream, buffer);
2576         handled = TRUE;
2577         break;
2578       }
2579     }
2580   }
2581   if (!handled)
2582     gst_buffer_unref (buffer);
2583 }
2584
2585 /**
2586  * gst_rtsp_client_set_session_pool:
2587  * @client: a #GstRTSPClient
2588  * @pool: (transfer none): a #GstRTSPSessionPool
2589  *
2590  * Set @pool as the sessionpool for @client which it will use to find
2591  * or allocate sessions. the sessionpool is usually inherited from the server
2592  * that created the client but can be overridden later.
2593  */
2594 void
2595 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
2596     GstRTSPSessionPool * pool)
2597 {
2598   GstRTSPSessionPool *old;
2599   GstRTSPClientPrivate *priv;
2600
2601   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2602
2603   priv = client->priv;
2604
2605   if (pool)
2606     g_object_ref (pool);
2607
2608   g_mutex_lock (&priv->lock);
2609   old = priv->session_pool;
2610   priv->session_pool = pool;
2611   g_mutex_unlock (&priv->lock);
2612
2613   if (old)
2614     g_object_unref (old);
2615 }
2616
2617 /**
2618  * gst_rtsp_client_get_session_pool:
2619  * @client: a #GstRTSPClient
2620  *
2621  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
2622  *
2623  * Returns: (transfer full): a #GstRTSPSessionPool, unref after usage.
2624  */
2625 GstRTSPSessionPool *
2626 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
2627 {
2628   GstRTSPClientPrivate *priv;
2629   GstRTSPSessionPool *result;
2630
2631   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2632
2633   priv = client->priv;
2634
2635   g_mutex_lock (&priv->lock);
2636   if ((result = priv->session_pool))
2637     g_object_ref (result);
2638   g_mutex_unlock (&priv->lock);
2639
2640   return result;
2641 }
2642
2643 /**
2644  * gst_rtsp_client_set_mount_points:
2645  * @client: a #GstRTSPClient
2646  * @mounts: (transfer none): a #GstRTSPMountPoints
2647  *
2648  * Set @mounts as the mount points for @client which it will use to map urls
2649  * to media streams. These mount points are usually inherited from the server that
2650  * created the client but can be overriden later.
2651  */
2652 void
2653 gst_rtsp_client_set_mount_points (GstRTSPClient * client,
2654     GstRTSPMountPoints * mounts)
2655 {
2656   GstRTSPClientPrivate *priv;
2657   GstRTSPMountPoints *old;
2658
2659   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2660
2661   priv = client->priv;
2662
2663   if (mounts)
2664     g_object_ref (mounts);
2665
2666   g_mutex_lock (&priv->lock);
2667   old = priv->mount_points;
2668   priv->mount_points = mounts;
2669   g_mutex_unlock (&priv->lock);
2670
2671   if (old)
2672     g_object_unref (old);
2673 }
2674
2675 /**
2676  * gst_rtsp_client_get_mount_points:
2677  * @client: a #GstRTSPClient
2678  *
2679  * Get the #GstRTSPMountPoints object that @client uses to manage its sessions.
2680  *
2681  * Returns: (transfer full): a #GstRTSPMountPoints, unref after usage.
2682  */
2683 GstRTSPMountPoints *
2684 gst_rtsp_client_get_mount_points (GstRTSPClient * client)
2685 {
2686   GstRTSPClientPrivate *priv;
2687   GstRTSPMountPoints *result;
2688
2689   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2690
2691   priv = client->priv;
2692
2693   g_mutex_lock (&priv->lock);
2694   if ((result = priv->mount_points))
2695     g_object_ref (result);
2696   g_mutex_unlock (&priv->lock);
2697
2698   return result;
2699 }
2700
2701 /**
2702  * gst_rtsp_client_set_auth:
2703  * @client: a #GstRTSPClient
2704  * @auth: (transfer none): a #GstRTSPAuth
2705  *
2706  * configure @auth to be used as the authentication manager of @client.
2707  */
2708 void
2709 gst_rtsp_client_set_auth (GstRTSPClient * client, GstRTSPAuth * auth)
2710 {
2711   GstRTSPClientPrivate *priv;
2712   GstRTSPAuth *old;
2713
2714   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2715
2716   priv = client->priv;
2717
2718   if (auth)
2719     g_object_ref (auth);
2720
2721   g_mutex_lock (&priv->lock);
2722   old = priv->auth;
2723   priv->auth = auth;
2724   g_mutex_unlock (&priv->lock);
2725
2726   if (old)
2727     g_object_unref (old);
2728 }
2729
2730
2731 /**
2732  * gst_rtsp_client_get_auth:
2733  * @client: a #GstRTSPClient
2734  *
2735  * Get the #GstRTSPAuth used as the authentication manager of @client.
2736  *
2737  * Returns: (transfer full): the #GstRTSPAuth of @client. g_object_unref() after
2738  * usage.
2739  */
2740 GstRTSPAuth *
2741 gst_rtsp_client_get_auth (GstRTSPClient * client)
2742 {
2743   GstRTSPClientPrivate *priv;
2744   GstRTSPAuth *result;
2745
2746   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2747
2748   priv = client->priv;
2749
2750   g_mutex_lock (&priv->lock);
2751   if ((result = priv->auth))
2752     g_object_ref (result);
2753   g_mutex_unlock (&priv->lock);
2754
2755   return result;
2756 }
2757
2758 /**
2759  * gst_rtsp_client_set_thread_pool:
2760  * @client: a #GstRTSPClient
2761  * @pool: (transfer none): a #GstRTSPThreadPool
2762  *
2763  * configure @pool to be used as the thread pool of @client.
2764  */
2765 void
2766 gst_rtsp_client_set_thread_pool (GstRTSPClient * client,
2767     GstRTSPThreadPool * pool)
2768 {
2769   GstRTSPClientPrivate *priv;
2770   GstRTSPThreadPool *old;
2771
2772   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2773
2774   priv = client->priv;
2775
2776   if (pool)
2777     g_object_ref (pool);
2778
2779   g_mutex_lock (&priv->lock);
2780   old = priv->thread_pool;
2781   priv->thread_pool = pool;
2782   g_mutex_unlock (&priv->lock);
2783
2784   if (old)
2785     g_object_unref (old);
2786 }
2787
2788 /**
2789  * gst_rtsp_client_get_thread_pool:
2790  * @client: a #GstRTSPClient
2791  *
2792  * Get the #GstRTSPThreadPool used as the thread pool of @client.
2793  *
2794  * Returns: (transfer full): the #GstRTSPThreadPool of @client. g_object_unref() after
2795  * usage.
2796  */
2797 GstRTSPThreadPool *
2798 gst_rtsp_client_get_thread_pool (GstRTSPClient * client)
2799 {
2800   GstRTSPClientPrivate *priv;
2801   GstRTSPThreadPool *result;
2802
2803   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2804
2805   priv = client->priv;
2806
2807   g_mutex_lock (&priv->lock);
2808   if ((result = priv->thread_pool))
2809     g_object_ref (result);
2810   g_mutex_unlock (&priv->lock);
2811
2812   return result;
2813 }
2814
2815 /**
2816  * gst_rtsp_client_set_connection:
2817  * @client: a #GstRTSPClient
2818  * @conn: (transfer full): a #GstRTSPConnection
2819  *
2820  * Set the #GstRTSPConnection of @client. This function takes ownership of
2821  * @conn.
2822  *
2823  * Returns: %TRUE on success.
2824  */
2825 gboolean
2826 gst_rtsp_client_set_connection (GstRTSPClient * client,
2827     GstRTSPConnection * conn)
2828 {
2829   GstRTSPClientPrivate *priv;
2830   GSocket *read_socket;
2831   GSocketAddress *address;
2832   GstRTSPUrl *url;
2833   GError *error = NULL;
2834
2835   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
2836   g_return_val_if_fail (conn != NULL, FALSE);
2837
2838   priv = client->priv;
2839
2840   read_socket = gst_rtsp_connection_get_read_socket (conn);
2841
2842   if (!(address = g_socket_get_local_address (read_socket, &error)))
2843     goto no_address;
2844
2845   g_free (priv->server_ip);
2846   /* keep the original ip that the client connected to */
2847   if (G_IS_INET_SOCKET_ADDRESS (address)) {
2848     GInetAddress *iaddr;
2849
2850     iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address));
2851
2852     /* socket might be ipv6 but adress still ipv4 */
2853     priv->is_ipv6 = g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV6;
2854     priv->server_ip = g_inet_address_to_string (iaddr);
2855     g_object_unref (address);
2856   } else {
2857     priv->is_ipv6 = g_socket_get_family (read_socket) == G_SOCKET_FAMILY_IPV6;
2858     priv->server_ip = g_strdup ("unknown");
2859   }
2860
2861   GST_INFO ("client %p connected to server ip %s, ipv6 = %d", client,
2862       priv->server_ip, priv->is_ipv6);
2863
2864   url = gst_rtsp_connection_get_url (conn);
2865   GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
2866
2867   priv->connection = conn;
2868
2869   return TRUE;
2870
2871   /* ERRORS */
2872 no_address:
2873   {
2874     GST_ERROR ("could not get local address %s", error->message);
2875     g_error_free (error);
2876     return FALSE;
2877   }
2878 }
2879
2880 /**
2881  * gst_rtsp_client_get_connection:
2882  * @client: a #GstRTSPClient
2883  *
2884  * Get the #GstRTSPConnection of @client.
2885  *
2886  * Returns: (transfer none): the #GstRTSPConnection of @client.
2887  * The connection object returned remains valid until the client is freed.
2888  */
2889 GstRTSPConnection *
2890 gst_rtsp_client_get_connection (GstRTSPClient * client)
2891 {
2892   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
2893
2894   return client->priv->connection;
2895 }
2896
2897 /**
2898  * gst_rtsp_client_set_send_func:
2899  * @client: a #GstRTSPClient
2900  * @func: (scope notified): a #GstRTSPClientSendFunc
2901  * @user_data: (closure): user data passed to @func
2902  * @notify: (allow-none): called when @user_data is no longer in use
2903  *
2904  * Set @func as the callback that will be called when a new message needs to be
2905  * sent to the client. @user_data is passed to @func and @notify is called when
2906  * @user_data is no longer in use.
2907  *
2908  * By default, the client will send the messages on the #GstRTSPConnection that
2909  * was configured with gst_rtsp_client_attach() was called.
2910  */
2911 void
2912 gst_rtsp_client_set_send_func (GstRTSPClient * client,
2913     GstRTSPClientSendFunc func, gpointer user_data, GDestroyNotify notify)
2914 {
2915   GstRTSPClientPrivate *priv;
2916   GDestroyNotify old_notify;
2917   gpointer old_data;
2918
2919   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
2920
2921   priv = client->priv;
2922
2923   g_mutex_lock (&priv->send_lock);
2924   priv->send_func = func;
2925   old_notify = priv->send_notify;
2926   old_data = priv->send_data;
2927   priv->send_notify = notify;
2928   priv->send_data = user_data;
2929   g_mutex_unlock (&priv->send_lock);
2930
2931   if (old_notify)
2932     old_notify (old_data);
2933 }
2934
2935 /**
2936  * gst_rtsp_client_handle_message:
2937  * @client: a #GstRTSPClient
2938  * @message: (transfer none): an #GstRTSPMessage
2939  *
2940  * Let the client handle @message.
2941  *
2942  * Returns: a #GstRTSPResult.
2943  */
2944 GstRTSPResult
2945 gst_rtsp_client_handle_message (GstRTSPClient * client,
2946     GstRTSPMessage * message)
2947 {
2948   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
2949   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2950
2951   switch (message->type) {
2952     case GST_RTSP_MESSAGE_REQUEST:
2953       handle_request (client, message);
2954       break;
2955     case GST_RTSP_MESSAGE_RESPONSE:
2956       handle_response (client, message);
2957       break;
2958     case GST_RTSP_MESSAGE_DATA:
2959       handle_data (client, message);
2960       break;
2961     default:
2962       break;
2963   }
2964   return GST_RTSP_OK;
2965 }
2966
2967 /**
2968  * gst_rtsp_client_send_message:
2969  * @client: a #GstRTSPClient
2970  * @session: (transfer none): a #GstRTSPSession to send the message to or %NULL
2971  * @message: (transfer none): The #GstRTSPMessage to send
2972  *
2973  * Send a message message to the remote end. @message must be a
2974  * #GST_RTSP_MESSAGE_REQUEST or a #GST_RTSP_MESSAGE_RESPONSE.
2975  */
2976 GstRTSPResult
2977 gst_rtsp_client_send_message (GstRTSPClient * client, GstRTSPSession * session,
2978     GstRTSPMessage * message)
2979 {
2980   GstRTSPContext sctx = { NULL }
2981   , *ctx;
2982   GstRTSPClientPrivate *priv;
2983
2984   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
2985   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2986   g_return_val_if_fail (message->type == GST_RTSP_MESSAGE_REQUEST ||
2987       message->type == GST_RTSP_MESSAGE_RESPONSE, GST_RTSP_EINVAL);
2988
2989   priv = client->priv;
2990
2991   if (!(ctx = gst_rtsp_context_get_current ())) {
2992     ctx = &sctx;
2993     ctx->auth = priv->auth;
2994     gst_rtsp_context_push_current (ctx);
2995   }
2996
2997   ctx->conn = priv->connection;
2998   ctx->client = client;
2999   ctx->session = session;
3000
3001   send_message (client, ctx, message, FALSE);
3002
3003   if (ctx == &sctx)
3004     gst_rtsp_context_pop_current (ctx);
3005
3006   return GST_RTSP_OK;
3007 }
3008
3009 static GstRTSPResult
3010 do_send_message (GstRTSPClient * client, GstRTSPMessage * message,
3011     gboolean close, gpointer user_data)
3012 {
3013   GstRTSPClientPrivate *priv = client->priv;
3014   GstRTSPResult ret;
3015   GTimeVal time;
3016
3017   time.tv_sec = 1;
3018   time.tv_usec = 0;
3019
3020   do {
3021     /* send the response and store the seq number so we can wait until it's
3022      * written to the client to close the connection */
3023     ret =
3024         gst_rtsp_watch_send_message (priv->watch, message,
3025         close ? &priv->close_seq : NULL);
3026     if (ret == GST_RTSP_OK)
3027       break;
3028
3029     if (ret != GST_RTSP_ENOMEM)
3030       goto error;
3031
3032     /* drop backlog */
3033     if (priv->drop_backlog)
3034       break;
3035
3036     /* queue was full, wait for more space */
3037     GST_DEBUG_OBJECT (client, "waiting for backlog");
3038     ret = gst_rtsp_watch_wait_backlog (priv->watch, &time);
3039     GST_DEBUG_OBJECT (client, "Resend due to backlog full");
3040   } while (ret != GST_RTSP_EINTR);
3041
3042   return ret;
3043
3044   /* ERRORS */
3045 error:
3046   {
3047     GST_DEBUG_OBJECT (client, "got error %d", ret);
3048     return ret;
3049   }
3050 }
3051
3052 static GstRTSPResult
3053 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
3054     gpointer user_data)
3055 {
3056   return gst_rtsp_client_handle_message (GST_RTSP_CLIENT (user_data), message);
3057 }
3058
3059 static GstRTSPResult
3060 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
3061 {
3062   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3063   GstRTSPClientPrivate *priv = client->priv;
3064
3065   if (priv->close_seq && priv->close_seq == cseq) {
3066     priv->close_seq = 0;
3067     close_connection (client);
3068   }
3069
3070   return GST_RTSP_OK;
3071 }
3072
3073 static GstRTSPResult
3074 closed (GstRTSPWatch * watch, gpointer user_data)
3075 {
3076   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3077   GstRTSPClientPrivate *priv = client->priv;
3078   const gchar *tunnelid;
3079
3080   GST_INFO ("client %p: connection closed", client);
3081
3082   if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
3083     g_mutex_lock (&tunnels_lock);
3084     /* remove from tunnelids */
3085     g_hash_table_remove (tunnels, tunnelid);
3086     g_mutex_unlock (&tunnels_lock);
3087   }
3088
3089   gst_rtsp_watch_set_flushing (watch, TRUE);
3090   gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
3091
3092   return GST_RTSP_OK;
3093 }
3094
3095 static GstRTSPResult
3096 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
3097 {
3098   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3099   gchar *str;
3100
3101   str = gst_rtsp_strresult (result);
3102   GST_INFO ("client %p: received an error %s", client, str);
3103   g_free (str);
3104
3105   return GST_RTSP_OK;
3106 }
3107
3108 static GstRTSPResult
3109 error_full (GstRTSPWatch * watch, GstRTSPResult result,
3110     GstRTSPMessage * message, guint id, gpointer user_data)
3111 {
3112   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3113   gchar *str;
3114
3115   str = gst_rtsp_strresult (result);
3116   GST_INFO
3117       ("client %p: error when handling message %p with id %d: %s",
3118       client, message, id, str);
3119   g_free (str);
3120
3121   return GST_RTSP_OK;
3122 }
3123
3124 static gboolean
3125 remember_tunnel (GstRTSPClient * client)
3126 {
3127   GstRTSPClientPrivate *priv = client->priv;
3128   const gchar *tunnelid;
3129
3130   /* store client in the pending tunnels */
3131   tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
3132   if (tunnelid == NULL)
3133     goto no_tunnelid;
3134
3135   GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
3136
3137   /* we can't have two clients connecting with the same tunnelid */
3138   g_mutex_lock (&tunnels_lock);
3139   if (g_hash_table_lookup (tunnels, tunnelid))
3140     goto tunnel_existed;
3141
3142   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
3143   g_mutex_unlock (&tunnels_lock);
3144
3145   return TRUE;
3146
3147   /* ERRORS */
3148 no_tunnelid:
3149   {
3150     GST_ERROR ("client %p: no tunnelid provided", client);
3151     return FALSE;
3152   }
3153 tunnel_existed:
3154   {
3155     g_mutex_unlock (&tunnels_lock);
3156     GST_ERROR ("client %p: tunnel session %s already existed", client,
3157         tunnelid);
3158     return FALSE;
3159   }
3160 }
3161
3162 static GstRTSPResult
3163 tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
3164 {
3165   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3166   GstRTSPClientPrivate *priv = client->priv;
3167
3168   GST_WARNING ("client %p: tunnel lost (connection %p)", client,
3169       priv->connection);
3170
3171   /* ignore error, it'll only be a problem when the client does a POST again */
3172   remember_tunnel (client);
3173
3174   return GST_RTSP_OK;
3175 }
3176
3177 static gboolean
3178 handle_tunnel (GstRTSPClient * client)
3179 {
3180   GstRTSPClientPrivate *priv = client->priv;
3181   GstRTSPClient *oclient;
3182   GstRTSPClientPrivate *opriv;
3183   const gchar *tunnelid;
3184
3185   tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
3186   if (tunnelid == NULL)
3187     goto no_tunnelid;
3188
3189   /* check for previous tunnel */
3190   g_mutex_lock (&tunnels_lock);
3191   oclient = g_hash_table_lookup (tunnels, tunnelid);
3192
3193   if (oclient == NULL) {
3194     /* no previous tunnel, remember tunnel */
3195     g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
3196     g_mutex_unlock (&tunnels_lock);
3197
3198     GST_INFO ("client %p: no previous tunnel found, remembering tunnel (%p)",
3199         client, priv->connection);
3200   } else {
3201     /* merge both tunnels into the first client */
3202     /* remove the old client from the table. ref before because removing it will
3203      * remove the ref to it. */
3204     g_object_ref (oclient);
3205     g_hash_table_remove (tunnels, tunnelid);
3206     g_mutex_unlock (&tunnels_lock);
3207
3208     opriv = oclient->priv;
3209
3210     if (opriv->watch == NULL)
3211       goto tunnel_closed;
3212
3213     GST_INFO ("client %p: found previous tunnel %p (old %p, new %p)", client,
3214         oclient, opriv->connection, priv->connection);
3215
3216     gst_rtsp_connection_do_tunnel (opriv->connection, priv->connection);
3217     gst_rtsp_watch_reset (priv->watch);
3218     gst_rtsp_watch_reset (opriv->watch);
3219     g_object_unref (oclient);
3220
3221     /* the old client owns the tunnel now, the new one will be freed */
3222     g_source_destroy ((GSource *) priv->watch);
3223     priv->watch = NULL;
3224     gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
3225   }
3226
3227   return TRUE;
3228
3229   /* ERRORS */
3230 no_tunnelid:
3231   {
3232     GST_ERROR ("client %p: no tunnelid provided", client);
3233     return FALSE;
3234   }
3235 tunnel_closed:
3236   {
3237     GST_ERROR ("client %p: tunnel session %s was closed", client, tunnelid);
3238     g_object_unref (oclient);
3239     return FALSE;
3240   }
3241 }
3242
3243 static GstRTSPStatusCode
3244 tunnel_get (GstRTSPWatch * watch, gpointer user_data)
3245 {
3246   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3247
3248   GST_INFO ("client %p: tunnel get (connection %p)", client,
3249       client->priv->connection);
3250
3251   if (!handle_tunnel (client)) {
3252     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
3253   }
3254
3255   return GST_RTSP_STS_OK;
3256 }
3257
3258 static GstRTSPResult
3259 tunnel_post (GstRTSPWatch * watch, gpointer user_data)
3260 {
3261   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3262
3263   GST_INFO ("client %p: tunnel post (connection %p)", client,
3264       client->priv->connection);
3265
3266   if (!handle_tunnel (client)) {
3267     return GST_RTSP_ERROR;
3268   }
3269
3270   return GST_RTSP_OK;
3271 }
3272
3273 static GstRTSPResult
3274 tunnel_http_response (GstRTSPWatch * watch, GstRTSPMessage * request,
3275     GstRTSPMessage * response, gpointer user_data)
3276 {
3277   GstRTSPClientClass *klass;
3278
3279   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
3280   klass = GST_RTSP_CLIENT_GET_CLASS (client);
3281
3282   if (klass->tunnel_http_response) {
3283     klass->tunnel_http_response (client, request, response);
3284   }
3285
3286   return GST_RTSP_OK;
3287 }
3288
3289 static GstRTSPWatchFuncs watch_funcs = {
3290   message_received,
3291   message_sent,
3292   closed,
3293   error,
3294   tunnel_get,
3295   tunnel_post,
3296   error_full,
3297   tunnel_lost,
3298   tunnel_http_response
3299 };
3300
3301 static void
3302 client_watch_notify (GstRTSPClient * client)
3303 {
3304   GstRTSPClientPrivate *priv = client->priv;
3305
3306   GST_INFO ("client %p: watch destroyed", client);
3307   priv->watch = NULL;
3308   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_CLOSED], 0, NULL);
3309   g_object_unref (client);
3310 }
3311
3312 /**
3313  * gst_rtsp_client_attach:
3314  * @client: a #GstRTSPClient
3315  * @context: (allow-none): a #GMainContext
3316  *
3317  * Attaches @client to @context. When the mainloop for @context is run, the
3318  * client will be dispatched. When @context is %NULL, the default context will be
3319  * used).
3320  *
3321  * This function should be called when the client properties and urls are fully
3322  * configured and the client is ready to start.
3323  *
3324  * Returns: the ID (greater than 0) for the source within the GMainContext.
3325  */
3326 guint
3327 gst_rtsp_client_attach (GstRTSPClient * client, GMainContext * context)
3328 {
3329   GstRTSPClientPrivate *priv;
3330   guint res;
3331
3332   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), 0);
3333   priv = client->priv;
3334   g_return_val_if_fail (priv->connection != NULL, 0);
3335   g_return_val_if_fail (priv->watch == NULL, 0);
3336
3337   /* create watch for the connection and attach */
3338   priv->watch = gst_rtsp_watch_new (priv->connection, &watch_funcs,
3339       g_object_ref (client), (GDestroyNotify) client_watch_notify);
3340   gst_rtsp_client_set_send_func (client, do_send_message, priv->watch,
3341       (GDestroyNotify) gst_rtsp_watch_unref);
3342
3343   /* FIXME make this configurable. We don't want to do this yet because it will
3344    * be superceeded by a cache object later */
3345   gst_rtsp_watch_set_send_backlog (priv->watch, 0, 100);
3346
3347   GST_INFO ("attaching to context %p", context);
3348   res = gst_rtsp_watch_attach (priv->watch, context);
3349
3350   return res;
3351 }
3352
3353 /**
3354  * gst_rtsp_client_session_filter:
3355  * @client: a #GstRTSPClient
3356  * @func: (scope call) (allow-none): a callback
3357  * @user_data: user data passed to @func
3358  *
3359  * Call @func for each session managed by @client. The result value of @func
3360  * determines what happens to the session. @func will be called with @client
3361  * locked so no further actions on @client can be performed from @func.
3362  *
3363  * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be removed from
3364  * @client.
3365  *
3366  * If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @client.
3367  *
3368  * If @func returns #GST_RTSP_FILTER_REF, the session will remain in @client but
3369  * will also be added with an additional ref to the result #GList of this
3370  * function..
3371  *
3372  * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for each session.
3373  *
3374  * Returns: (element-type GstRTSPSession) (transfer full): a #GList with all
3375  * sessions for which @func returned #GST_RTSP_FILTER_REF. After usage, each
3376  * element in the #GList should be unreffed before the list is freed.
3377  */
3378 GList *
3379 gst_rtsp_client_session_filter (GstRTSPClient * client,
3380     GstRTSPClientSessionFilterFunc func, gpointer user_data)
3381 {
3382   GstRTSPClientPrivate *priv;
3383   GList *result, *walk, *next;
3384
3385   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
3386
3387   priv = client->priv;
3388
3389   result = NULL;
3390
3391   g_mutex_lock (&priv->lock);
3392   for (walk = priv->sessions; walk; walk = next) {
3393     GstRTSPSession *sess = walk->data;
3394     GstRTSPFilterResult res;
3395
3396     next = g_list_next (walk);
3397
3398     if (func)
3399       res = func (client, sess, user_data);
3400     else
3401       res = GST_RTSP_FILTER_REF;
3402
3403     switch (res) {
3404       case GST_RTSP_FILTER_REMOVE:
3405         /* stop watching the session and pretent it went away */
3406         client_cleanup_session (client, sess);
3407         break;
3408       case GST_RTSP_FILTER_REF:
3409         result = g_list_prepend (result, g_object_ref (sess));
3410         break;
3411       case GST_RTSP_FILTER_KEEP:
3412       default:
3413         break;
3414     }
3415   }
3416   g_mutex_unlock (&priv->lock);
3417
3418   return result;
3419 }