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