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