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