rtsp-stream: extract handle_keymgmt from rtsp-client
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-client.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  * Copyright (C) 2015 Centricular Ltd
4  *     Author: Sebastian Dröge <sebastian@centricular.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 /**
22  * SECTION:rtsp-client
23  * @short_description: A client connection state
24  * @see_also: #GstRTSPServer, #GstRTSPThreadPool
25  *
26  * The client object handles the connection with a client for as long as a TCP
27  * connection is open.
28  *
29  * A #GstRTSPClient is created by #GstRTSPServer when a new connection is
30  * accepted and it inherits the #GstRTSPMountPoints, #GstRTSPSessionPool,
31  * #GstRTSPAuth and #GstRTSPThreadPool from the server.
32  *
33  * The client connection should be configured with the #GstRTSPConnection using
34  * gst_rtsp_client_set_connection() before it can be attached to a #GMainContext
35  * using gst_rtsp_client_attach(). From then on the client will handle requests
36  * on the connection.
37  *
38  * Use gst_rtsp_client_session_filter() to iterate or modify all the
39  * #GstRTSPSession objects managed by the client object.
40  *
41  * Last reviewed on 2013-07-11 (1.0.0)
42  */
43
44 #include <stdio.h>
45 #include <string.h>
46
47 #include <gst/sdp/gstmikey.h>
48 #include <gst/rtsp/gstrtsp-enumtypes.h>
49
50 #include "rtsp-client.h"
51 #include "rtsp-sdp.h"
52 #include "rtsp-params.h"
53
54 #define GST_RTSP_CLIENT_GET_PRIVATE(obj)  \
55    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_CLIENT, GstRTSPClientPrivate))
56
57 typedef enum
58 {
59   TUNNEL_STATE_UNKNOWN,
60   TUNNEL_STATE_GET,
61   TUNNEL_STATE_POST
62 } GstRTSPTunnelState;
63
64 /* locking order:
65  * send_lock, lock, tunnels_lock
66  */
67
68 struct _GstRTSPClientPrivate
69 {
70   GMutex lock;                  /* protects everything else */
71   GMutex send_lock;
72   GMutex watch_lock;
73   GstRTSPConnection *connection;
74   GstRTSPWatch *watch;
75   GMainContext *watch_context;
76   guint close_seq;
77   gchar *server_ip;
78   gboolean is_ipv6;
79
80   GstRTSPClientSendFunc send_func;      /* protected by send_lock */
81   gpointer send_data;           /* protected by send_lock */
82   GDestroyNotify send_notify;   /* protected by send_lock */
83
84   GstRTSPSessionPool *session_pool;
85   gulong session_removed_id;
86   GstRTSPMountPoints *mount_points;
87   GstRTSPAuth *auth;
88   GstRTSPThreadPool *thread_pool;
89
90   /* used to cache the media in the last requested DESCRIBE so that
91    * we can pick it up in the next SETUP immediately */
92   gchar *path;
93   GstRTSPMedia *media;
94
95   GHashTable *transports;
96   GList *sessions;
97   guint sessions_cookie;
98
99   gboolean drop_backlog;
100
101   guint rtsp_ctrl_timeout_id;
102   guint rtsp_ctrl_timeout_cnt;
103
104   /* The version currently being used */
105   GstRTSPVersion version;
106
107   GHashTable *pipelined_requests;       /* pipelined_request_id -> session_id */
108   GstRTSPTunnelState tstate;
109 };
110
111 static GMutex tunnels_lock;
112 static GHashTable *tunnels;     /* protected by tunnels_lock */
113
114 /* FIXME make this configurable. We don't want to do this yet because it will
115  * be superceeded by a cache object later */
116 #define WATCH_BACKLOG_SIZE              100
117
118 #define DEFAULT_SESSION_POOL            NULL
119 #define DEFAULT_MOUNT_POINTS            NULL
120 #define DEFAULT_DROP_BACKLOG            TRUE
121
122 #define RTSP_CTRL_CB_INTERVAL           1
123 #define RTSP_CTRL_TIMEOUT_VALUE         60
124
125 enum
126 {
127   PROP_0,
128   PROP_SESSION_POOL,
129   PROP_MOUNT_POINTS,
130   PROP_DROP_BACKLOG,
131   PROP_LAST
132 };
133
134 enum
135 {
136   SIGNAL_CLOSED,
137   SIGNAL_NEW_SESSION,
138   SIGNAL_PRE_OPTIONS_REQUEST,
139   SIGNAL_OPTIONS_REQUEST,
140   SIGNAL_PRE_DESCRIBE_REQUEST,
141   SIGNAL_DESCRIBE_REQUEST,
142   SIGNAL_PRE_SETUP_REQUEST,
143   SIGNAL_SETUP_REQUEST,
144   SIGNAL_PRE_PLAY_REQUEST,
145   SIGNAL_PLAY_REQUEST,
146   SIGNAL_PRE_PAUSE_REQUEST,
147   SIGNAL_PAUSE_REQUEST,
148   SIGNAL_PRE_TEARDOWN_REQUEST,
149   SIGNAL_TEARDOWN_REQUEST,
150   SIGNAL_PRE_SET_PARAMETER_REQUEST,
151   SIGNAL_SET_PARAMETER_REQUEST,
152   SIGNAL_PRE_GET_PARAMETER_REQUEST,
153   SIGNAL_GET_PARAMETER_REQUEST,
154   SIGNAL_HANDLE_RESPONSE,
155   SIGNAL_SEND_MESSAGE,
156   SIGNAL_PRE_ANNOUNCE_REQUEST,
157   SIGNAL_ANNOUNCE_REQUEST,
158   SIGNAL_PRE_RECORD_REQUEST,
159   SIGNAL_RECORD_REQUEST,
160   SIGNAL_CHECK_REQUIREMENTS,
161   SIGNAL_LAST
162 };
163
164 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
165 #define GST_CAT_DEFAULT rtsp_client_debug
166
167 static guint gst_rtsp_client_signals[SIGNAL_LAST] = { 0 };
168
169 static void gst_rtsp_client_get_property (GObject * object, guint propid,
170     GValue * value, GParamSpec * pspec);
171 static void gst_rtsp_client_set_property (GObject * object, guint propid,
172     const GValue * value, GParamSpec * pspec);
173 static void gst_rtsp_client_finalize (GObject * obj);
174
175 static GstSDPMessage *create_sdp (GstRTSPClient * client, GstRTSPMedia * media);
176 static gboolean handle_sdp (GstRTSPClient * client, GstRTSPContext * ctx,
177     GstRTSPMedia * media, GstSDPMessage * sdp);
178 static gboolean default_configure_client_media (GstRTSPClient * client,
179     GstRTSPMedia * media, GstRTSPStream * stream, GstRTSPContext * ctx);
180 static gboolean default_configure_client_transport (GstRTSPClient * client,
181     GstRTSPContext * ctx, GstRTSPTransport * ct);
182 static GstRTSPResult default_params_set (GstRTSPClient * client,
183     GstRTSPContext * ctx);
184 static GstRTSPResult default_params_get (GstRTSPClient * client,
185     GstRTSPContext * ctx);
186 static gchar *default_make_path_from_uri (GstRTSPClient * client,
187     const GstRTSPUrl * uri);
188 static void client_session_removed (GstRTSPSessionPool * pool,
189     GstRTSPSession * session, GstRTSPClient * client);
190 static GstRTSPStatusCode default_pre_signal_handler (GstRTSPClient * client,
191     GstRTSPContext * ctx);
192 static gboolean pre_signal_accumulator (GSignalInvocationHint * ihint,
193     GValue * return_accu, const GValue * handler_return, gpointer data);
194
195 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
196
197 static void
198 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
199 {
200   GObjectClass *gobject_class;
201
202   g_type_class_add_private (klass, sizeof (GstRTSPClientPrivate));
203
204   gobject_class = G_OBJECT_CLASS (klass);
205
206   gobject_class->get_property = gst_rtsp_client_get_property;
207   gobject_class->set_property = gst_rtsp_client_set_property;
208   gobject_class->finalize = gst_rtsp_client_finalize;
209
210   klass->create_sdp = create_sdp;
211   klass->handle_sdp = handle_sdp;
212   klass->configure_client_media = default_configure_client_media;
213   klass->configure_client_transport = default_configure_client_transport;
214   klass->params_set = default_params_set;
215   klass->params_get = default_params_get;
216   klass->make_path_from_uri = default_make_path_from_uri;
217
218   klass->pre_options_request = default_pre_signal_handler;
219   klass->pre_describe_request = default_pre_signal_handler;
220   klass->pre_setup_request = default_pre_signal_handler;
221   klass->pre_play_request = default_pre_signal_handler;
222   klass->pre_pause_request = default_pre_signal_handler;
223   klass->pre_teardown_request = default_pre_signal_handler;
224   klass->pre_set_parameter_request = default_pre_signal_handler;
225   klass->pre_get_parameter_request = default_pre_signal_handler;
226   klass->pre_announce_request = default_pre_signal_handler;
227   klass->pre_record_request = default_pre_signal_handler;
228
229   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
230       g_param_spec_object ("session-pool", "Session Pool",
231           "The session pool to use for client session",
232           GST_TYPE_RTSP_SESSION_POOL,
233           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
234
235   g_object_class_install_property (gobject_class, PROP_MOUNT_POINTS,
236       g_param_spec_object ("mount-points", "Mount Points",
237           "The mount points to use for client session",
238           GST_TYPE_RTSP_MOUNT_POINTS,
239           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240
241   g_object_class_install_property (gobject_class, PROP_DROP_BACKLOG,
242       g_param_spec_boolean ("drop-backlog", "Drop Backlog",
243           "Drop data when the backlog queue is full",
244           DEFAULT_DROP_BACKLOG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
245
246   gst_rtsp_client_signals[SIGNAL_CLOSED] =
247       g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
248       G_STRUCT_OFFSET (GstRTSPClientClass, closed), NULL, NULL,
249       g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
250
251   gst_rtsp_client_signals[SIGNAL_NEW_SESSION] =
252       g_signal_new ("new-session", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
253       G_STRUCT_OFFSET (GstRTSPClientClass, new_session), NULL, NULL,
254       g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_RTSP_SESSION);
255
256   /**
257    * GstRTSPClient::pre-options-request:
258    * @client: a #GstRTSPClient
259    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
260    *
261    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
262    *          otherwise an appropriate return code
263    *
264    * Since: 1.12
265    */
266   gst_rtsp_client_signals[SIGNAL_PRE_OPTIONS_REQUEST] =
267       g_signal_new ("pre-options-request", G_TYPE_FROM_CLASS (klass),
268       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
269           pre_options_request), pre_signal_accumulator, NULL,
270       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
271       GST_TYPE_RTSP_CONTEXT);
272
273   /**
274    * GstRTSPClient::options-request:
275    * @client: a #GstRTSPClient
276    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
277    */
278   gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST] =
279       g_signal_new ("options-request", G_TYPE_FROM_CLASS (klass),
280       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, options_request),
281       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
282       GST_TYPE_RTSP_CONTEXT);
283
284   /**
285    * GstRTSPClient::pre-describe-request:
286    * @client: a #GstRTSPClient
287    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
288    *
289    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
290    *          otherwise an appropriate return code
291    *
292    * Since: 1.12
293    */
294   gst_rtsp_client_signals[SIGNAL_PRE_DESCRIBE_REQUEST] =
295       g_signal_new ("pre-describe-request", G_TYPE_FROM_CLASS (klass),
296       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
297           pre_describe_request), pre_signal_accumulator, NULL,
298       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
299       GST_TYPE_RTSP_CONTEXT);
300
301   /**
302    * GstRTSPClient::describe-request:
303    * @client: a #GstRTSPClient
304    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
305    */
306   gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST] =
307       g_signal_new ("describe-request", G_TYPE_FROM_CLASS (klass),
308       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, describe_request),
309       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
310       GST_TYPE_RTSP_CONTEXT);
311
312   /**
313    * GstRTSPClient::pre-setup-request:
314    * @client: a #GstRTSPClient
315    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
316    *
317    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
318    *          otherwise an appropriate return code
319    *
320    * Since: 1.12
321    */
322   gst_rtsp_client_signals[SIGNAL_PRE_SETUP_REQUEST] =
323       g_signal_new ("pre-setup-request", G_TYPE_FROM_CLASS (klass),
324       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
325           pre_setup_request), pre_signal_accumulator, NULL,
326       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
327       GST_TYPE_RTSP_CONTEXT);
328
329   /**
330    * GstRTSPClient::setup-request:
331    * @client: a #GstRTSPClient
332    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
333    */
334   gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST] =
335       g_signal_new ("setup-request", G_TYPE_FROM_CLASS (klass),
336       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, setup_request),
337       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
338       GST_TYPE_RTSP_CONTEXT);
339
340   /**
341    * GstRTSPClient::pre-play-request:
342    * @client: a #GstRTSPClient
343    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
344    *
345    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
346    *          otherwise an appropriate return code
347    *
348    * Since: 1.12
349    */
350   gst_rtsp_client_signals[SIGNAL_PRE_PLAY_REQUEST] =
351       g_signal_new ("pre-play-request", G_TYPE_FROM_CLASS (klass),
352       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
353           pre_play_request), pre_signal_accumulator, NULL,
354       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
355       GST_TYPE_RTSP_CONTEXT);
356
357   /**
358    * GstRTSPClient::play-request:
359    * @client: a #GstRTSPClient
360    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
361    */
362   gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST] =
363       g_signal_new ("play-request", G_TYPE_FROM_CLASS (klass),
364       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, play_request),
365       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
366       GST_TYPE_RTSP_CONTEXT);
367
368   /**
369    * GstRTSPClient::pre-pause-request:
370    * @client: a #GstRTSPClient
371    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
372    *
373    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
374    *          otherwise an appropriate return code
375    *
376    * Since: 1.12
377    */
378   gst_rtsp_client_signals[SIGNAL_PRE_PAUSE_REQUEST] =
379       g_signal_new ("pre-pause-request", G_TYPE_FROM_CLASS (klass),
380       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
381           pre_pause_request), pre_signal_accumulator, NULL,
382       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
383       GST_TYPE_RTSP_CONTEXT);
384
385   /**
386    * GstRTSPClient::pause-request:
387    * @client: a #GstRTSPClient
388    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
389    */
390   gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST] =
391       g_signal_new ("pause-request", G_TYPE_FROM_CLASS (klass),
392       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, pause_request),
393       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
394       GST_TYPE_RTSP_CONTEXT);
395
396   /**
397    * GstRTSPClient::pre-teardown-request:
398    * @client: a #GstRTSPClient
399    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
400    *
401    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
402    *          otherwise an appropriate return code
403    *
404    * Since: 1.12
405    */
406   gst_rtsp_client_signals[SIGNAL_PRE_TEARDOWN_REQUEST] =
407       g_signal_new ("pre-teardown-request", G_TYPE_FROM_CLASS (klass),
408       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
409           pre_teardown_request), pre_signal_accumulator, NULL,
410       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
411       GST_TYPE_RTSP_CONTEXT);
412
413   /**
414    * GstRTSPClient::teardown-request:
415    * @client: a #GstRTSPClient
416    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
417    */
418   gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST] =
419       g_signal_new ("teardown-request", G_TYPE_FROM_CLASS (klass),
420       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, teardown_request),
421       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
422       GST_TYPE_RTSP_CONTEXT);
423
424   /**
425    * GstRTSPClient::pre-set-parameter-request:
426    * @client: a #GstRTSPClient
427    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
428    *
429    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
430    *          otherwise an appropriate return code
431    *
432    * Since: 1.12
433    */
434   gst_rtsp_client_signals[SIGNAL_PRE_SET_PARAMETER_REQUEST] =
435       g_signal_new ("pre-set-parameter-request", G_TYPE_FROM_CLASS (klass),
436       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
437           pre_set_parameter_request), pre_signal_accumulator, NULL,
438       g_cclosure_marshal_generic,
439       GST_TYPE_RTSP_STATUS_CODE, 1, GST_TYPE_RTSP_CONTEXT);
440
441   /**
442    * GstRTSPClient::set-parameter-request:
443    * @client: a #GstRTSPClient
444    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
445    */
446   gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST] =
447       g_signal_new ("set-parameter-request", G_TYPE_FROM_CLASS (klass),
448       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
449           set_parameter_request), NULL, NULL, g_cclosure_marshal_generic,
450       G_TYPE_NONE, 1, GST_TYPE_RTSP_CONTEXT);
451
452   /**
453    * GstRTSPClient::pre-get-parameter-request:
454    * @client: a #GstRTSPClient
455    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
456    *
457    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
458    *          otherwise an appropriate return code
459    *
460    * Since: 1.12
461    */
462   gst_rtsp_client_signals[SIGNAL_PRE_GET_PARAMETER_REQUEST] =
463       g_signal_new ("pre-get-parameter-request", G_TYPE_FROM_CLASS (klass),
464       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
465           pre_get_parameter_request), pre_signal_accumulator, NULL,
466       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
467       GST_TYPE_RTSP_CONTEXT);
468
469   /**
470    * GstRTSPClient::get-parameter-request:
471    * @client: a #GstRTSPClient
472    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
473    */
474   gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST] =
475       g_signal_new ("get-parameter-request", G_TYPE_FROM_CLASS (klass),
476       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
477           get_parameter_request), NULL, NULL, g_cclosure_marshal_generic,
478       G_TYPE_NONE, 1, GST_TYPE_RTSP_CONTEXT);
479
480   /**
481    * GstRTSPClient::handle-response:
482    * @client: a #GstRTSPClient
483    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
484    */
485   gst_rtsp_client_signals[SIGNAL_HANDLE_RESPONSE] =
486       g_signal_new ("handle-response", G_TYPE_FROM_CLASS (klass),
487       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
488           handle_response), NULL, NULL, g_cclosure_marshal_generic,
489       G_TYPE_NONE, 1, GST_TYPE_RTSP_CONTEXT);
490
491   /**
492    * GstRTSPClient::send-message:
493    * @client: The RTSP client
494    * @session: (type GstRtspServer.RTSPSession): The session
495    * @message: (type GstRtsp.RTSPMessage): The message
496    */
497   gst_rtsp_client_signals[SIGNAL_SEND_MESSAGE] =
498       g_signal_new ("send-message", G_TYPE_FROM_CLASS (klass),
499       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
500           send_message), NULL, NULL, g_cclosure_marshal_generic,
501       G_TYPE_NONE, 2, GST_TYPE_RTSP_CONTEXT, G_TYPE_POINTER);
502
503   /**
504    * GstRTSPClient::pre-announce-request:
505    * @client: a #GstRTSPClient
506    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
507    *
508    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
509    *          otherwise an appropriate return code
510    *
511    * Since: 1.12
512    */
513   gst_rtsp_client_signals[SIGNAL_PRE_ANNOUNCE_REQUEST] =
514       g_signal_new ("pre-announce-request", G_TYPE_FROM_CLASS (klass),
515       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
516           pre_announce_request), pre_signal_accumulator, NULL,
517       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
518       GST_TYPE_RTSP_CONTEXT);
519
520   /**
521    * GstRTSPClient::announce-request:
522    * @client: a #GstRTSPClient
523    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
524    */
525   gst_rtsp_client_signals[SIGNAL_ANNOUNCE_REQUEST] =
526       g_signal_new ("announce-request", G_TYPE_FROM_CLASS (klass),
527       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, announce_request),
528       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
529       GST_TYPE_RTSP_CONTEXT);
530
531   /**
532    * GstRTSPClient::pre-record-request:
533    * @client: a #GstRTSPClient
534    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
535    *
536    * Returns: a #GstRTSPStatusCode, GST_RTSP_STS_OK in case of success,
537    *          otherwise an appropriate return code
538    *
539    * Since: 1.12
540    */
541   gst_rtsp_client_signals[SIGNAL_PRE_RECORD_REQUEST] =
542       g_signal_new ("pre-record-request", G_TYPE_FROM_CLASS (klass),
543       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
544           pre_record_request), pre_signal_accumulator, NULL,
545       g_cclosure_marshal_generic, GST_TYPE_RTSP_STATUS_CODE, 1,
546       GST_TYPE_RTSP_CONTEXT);
547
548   /**
549    * GstRTSPClient::record-request:
550    * @client: a #GstRTSPClient
551    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
552    */
553   gst_rtsp_client_signals[SIGNAL_RECORD_REQUEST] =
554       g_signal_new ("record-request", G_TYPE_FROM_CLASS (klass),
555       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, record_request),
556       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
557       GST_TYPE_RTSP_CONTEXT);
558
559   /**
560    * GstRTSPClient::check-requirements:
561    * @client: a #GstRTSPClient
562    * @ctx: (type GstRtspServer.RTSPContext): a #GstRTSPContext
563    * @arr: a NULL-terminated array of strings
564    *
565    * Returns: a newly allocated string with comma-separated list of
566    *          unsupported options. An empty string must be returned if
567    *          all options are supported.
568    *
569    * Since: 1.6
570    */
571   gst_rtsp_client_signals[SIGNAL_CHECK_REQUIREMENTS] =
572       g_signal_new ("check-requirements", G_TYPE_FROM_CLASS (klass),
573       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
574           check_requirements), NULL, NULL, g_cclosure_marshal_generic,
575       G_TYPE_STRING, 2, GST_TYPE_RTSP_CONTEXT, G_TYPE_STRV);
576
577   tunnels =
578       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
579   g_mutex_init (&tunnels_lock);
580
581   GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
582 }
583
584 static void
585 gst_rtsp_client_init (GstRTSPClient * client)
586 {
587   GstRTSPClientPrivate *priv = GST_RTSP_CLIENT_GET_PRIVATE (client);
588
589   client->priv = priv;
590
591   g_mutex_init (&priv->lock);
592   g_mutex_init (&priv->send_lock);
593   g_mutex_init (&priv->watch_lock);
594   priv->close_seq = 0;
595   priv->drop_backlog = DEFAULT_DROP_BACKLOG;
596   priv->transports =
597       g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
598       g_object_unref);
599   priv->pipelined_requests = g_hash_table_new_full (g_str_hash,
600       g_str_equal, g_free, g_free);
601   priv->tstate = TUNNEL_STATE_UNKNOWN;
602 }
603
604 static GstRTSPFilterResult
605 filter_session_media (GstRTSPSession * sess, GstRTSPSessionMedia * sessmedia,
606     gpointer user_data)
607 {
608   gboolean *closed = user_data;
609   GstRTSPMedia *media;
610   guint i, n_streams;
611   gboolean is_all_udp = TRUE;
612
613   media = gst_rtsp_session_media_get_media (sessmedia);
614   n_streams = gst_rtsp_media_n_streams (media);
615
616   for (i = 0; i < n_streams; i++) {
617     GstRTSPStreamTransport *transport =
618         gst_rtsp_session_media_get_transport (sessmedia, i);
619     const GstRTSPTransport *rtsp_transport;
620
621     if (!transport)
622       continue;
623
624     rtsp_transport = gst_rtsp_stream_transport_get_transport (transport);
625     if (rtsp_transport
626         && rtsp_transport->lower_transport != GST_RTSP_LOWER_TRANS_UDP
627         && rtsp_transport->lower_transport != GST_RTSP_LOWER_TRANS_UDP_MCAST) {
628       is_all_udp = FALSE;
629       break;
630     }
631   }
632
633   if (!is_all_udp || gst_rtsp_media_is_stop_on_disconnect (media)) {
634     gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
635     return GST_RTSP_FILTER_REMOVE;
636   } else {
637     *closed = FALSE;
638     return GST_RTSP_FILTER_KEEP;
639   }
640 }
641
642 static void
643 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
644 {
645   GstRTSPClientPrivate *priv = client->priv;
646
647   g_mutex_lock (&priv->lock);
648   /* check if we already know about this session */
649   if (g_list_find (priv->sessions, session) == NULL) {
650     GST_INFO ("watching session %p", session);
651
652     priv->sessions = g_list_prepend (priv->sessions, g_object_ref (session));
653     priv->sessions_cookie++;
654
655     /* connect removed session handler, it will be disconnected when the last
656      * session gets removed  */
657     if (priv->session_removed_id == 0)
658       priv->session_removed_id = g_signal_connect_data (priv->session_pool,
659           "session-removed", G_CALLBACK (client_session_removed),
660           g_object_ref (client), (GClosureNotify) g_object_unref, 0);
661   }
662   g_mutex_unlock (&priv->lock);
663
664   return;
665 }
666
667 /* should be called with lock */
668 static void
669 client_unwatch_session (GstRTSPClient * client, GstRTSPSession * session,
670     GList * link)
671 {
672   GstRTSPClientPrivate *priv = client->priv;
673
674   GST_INFO ("client %p: unwatch session %p", client, session);
675
676   if (link == NULL) {
677     link = g_list_find (priv->sessions, session);
678     if (link == NULL)
679       return;
680   }
681
682   priv->sessions = g_list_delete_link (priv->sessions, link);
683   priv->sessions_cookie++;
684
685   /* if this was the last session, disconnect the handler.
686    * This will also drop the extra client ref */
687   if (!priv->sessions) {
688     g_signal_handler_disconnect (priv->session_pool, priv->session_removed_id);
689     priv->session_removed_id = 0;
690   }
691
692   if (!priv->drop_backlog) {
693     /* unlink all media managed in this session */
694     gst_rtsp_session_filter (session, filter_session_media, client);
695   }
696
697   /* remove the session */
698   g_object_unref (session);
699 }
700
701 static GstRTSPFilterResult
702 cleanup_session (GstRTSPClient * client, GstRTSPSession * sess,
703     gpointer user_data)
704 {
705   gboolean *closed = user_data;
706   GstRTSPClientPrivate *priv = client->priv;
707
708   if (priv->drop_backlog) {
709     /* unlink all media managed in this session. This needs to happen
710      * without the client lock, so we really want to do it here. */
711     gst_rtsp_session_filter (sess, filter_session_media, user_data);
712   }
713
714   if (*closed)
715     return GST_RTSP_FILTER_REMOVE;
716   else
717     return GST_RTSP_FILTER_KEEP;
718 }
719
720 static void
721 clean_cached_media (GstRTSPClient * client, gboolean unprepare)
722 {
723   GstRTSPClientPrivate *priv = client->priv;
724
725   if (priv->path) {
726     g_free (priv->path);
727     priv->path = NULL;
728   }
729   if (priv->media) {
730     if (unprepare)
731       gst_rtsp_media_unprepare (priv->media);
732     g_object_unref (priv->media);
733     priv->media = NULL;
734   }
735 }
736
737 /* A client is finalized when the connection is broken */
738 static void
739 gst_rtsp_client_finalize (GObject * obj)
740 {
741   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
742   GstRTSPClientPrivate *priv = client->priv;
743
744   GST_INFO ("finalize client %p", client);
745
746   if (priv->watch)
747     gst_rtsp_watch_set_flushing (priv->watch, TRUE);
748   gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
749
750   if (priv->watch)
751     g_source_destroy ((GSource *) priv->watch);
752
753   if (priv->watch_context)
754     g_main_context_unref (priv->watch_context);
755
756   /* all sessions should have been removed by now. We keep a ref to
757    * the client object for the session removed handler. The ref is
758    * dropped when the last session is removed from the list. */
759   g_assert (priv->sessions == NULL);
760   g_assert (priv->session_removed_id == 0);
761
762   g_hash_table_unref (priv->transports);
763   g_hash_table_unref (priv->pipelined_requests);
764
765   if (priv->connection)
766     gst_rtsp_connection_free (priv->connection);
767   if (priv->session_pool) {
768     g_object_unref (priv->session_pool);
769   }
770   if (priv->mount_points)
771     g_object_unref (priv->mount_points);
772   if (priv->auth)
773     g_object_unref (priv->auth);
774   if (priv->thread_pool)
775     g_object_unref (priv->thread_pool);
776
777   clean_cached_media (client, TRUE);
778
779   g_free (priv->server_ip);
780   g_mutex_clear (&priv->lock);
781   g_mutex_clear (&priv->send_lock);
782   g_mutex_clear (&priv->watch_lock);
783
784   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
785 }
786
787 static void
788 gst_rtsp_client_get_property (GObject * object, guint propid,
789     GValue * value, GParamSpec * pspec)
790 {
791   GstRTSPClient *client = GST_RTSP_CLIENT (object);
792   GstRTSPClientPrivate *priv = client->priv;
793
794   switch (propid) {
795     case PROP_SESSION_POOL:
796       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
797       break;
798     case PROP_MOUNT_POINTS:
799       g_value_take_object (value, gst_rtsp_client_get_mount_points (client));
800       break;
801     case PROP_DROP_BACKLOG:
802       g_value_set_boolean (value, priv->drop_backlog);
803       break;
804     default:
805       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
806   }
807 }
808
809 static void
810 gst_rtsp_client_set_property (GObject * object, guint propid,
811     const GValue * value, GParamSpec * pspec)
812 {
813   GstRTSPClient *client = GST_RTSP_CLIENT (object);
814   GstRTSPClientPrivate *priv = client->priv;
815
816   switch (propid) {
817     case PROP_SESSION_POOL:
818       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
819       break;
820     case PROP_MOUNT_POINTS:
821       gst_rtsp_client_set_mount_points (client, g_value_get_object (value));
822       break;
823     case PROP_DROP_BACKLOG:
824       g_mutex_lock (&priv->lock);
825       priv->drop_backlog = g_value_get_boolean (value);
826       g_mutex_unlock (&priv->lock);
827       break;
828     default:
829       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
830   }
831 }
832
833 /**
834  * gst_rtsp_client_new:
835  *
836  * Create a new #GstRTSPClient instance.
837  *
838  * Returns: (transfer full): a new #GstRTSPClient
839  */
840 GstRTSPClient *
841 gst_rtsp_client_new (void)
842 {
843   GstRTSPClient *result;
844
845   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
846
847   return result;
848 }
849
850 static void
851 send_message (GstRTSPClient * client, GstRTSPContext * ctx,
852     GstRTSPMessage * message, gboolean close)
853 {
854   GstRTSPClientPrivate *priv = client->priv;
855
856   gst_rtsp_message_add_header (message, GST_RTSP_HDR_SERVER,
857       "GStreamer RTSP server");
858
859   /* remove any previous header */
860   gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
861
862   /* add the new session header for new session ids */
863   if (ctx->session) {
864     gst_rtsp_message_take_header (message, GST_RTSP_HDR_SESSION,
865         gst_rtsp_session_get_header (ctx->session));
866   }
867
868   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
869     gst_rtsp_message_dump (message);
870   }
871
872   if (close)
873     gst_rtsp_message_add_header (message, GST_RTSP_HDR_CONNECTION, "close");
874
875   if (ctx->request)
876     message->type_data.response.version =
877         ctx->request->type_data.request.version;
878
879   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SEND_MESSAGE],
880       0, ctx, message);
881
882   g_mutex_lock (&priv->send_lock);
883   if (priv->send_func)
884     priv->send_func (client, message, close, priv->send_data);
885   g_mutex_unlock (&priv->send_lock);
886
887   gst_rtsp_message_unset (message);
888 }
889
890 static void
891 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
892     GstRTSPContext * ctx)
893 {
894   gst_rtsp_message_init_response (ctx->response, code,
895       gst_rtsp_status_as_text (code), ctx->request);
896
897   ctx->session = NULL;
898
899   send_message (client, ctx, ctx->response, FALSE);
900 }
901
902 static void
903 send_option_not_supported_response (GstRTSPClient * client,
904     GstRTSPContext * ctx, const gchar * unsupported_options)
905 {
906   GstRTSPStatusCode code = GST_RTSP_STS_OPTION_NOT_SUPPORTED;
907
908   gst_rtsp_message_init_response (ctx->response, code,
909       gst_rtsp_status_as_text (code), ctx->request);
910
911   if (unsupported_options != NULL) {
912     gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_UNSUPPORTED,
913         unsupported_options);
914   }
915
916   ctx->session = NULL;
917
918   send_message (client, ctx, ctx->response, FALSE);
919 }
920
921 static gboolean
922 paths_are_equal (const gchar * path1, const gchar * path2, gint len2)
923 {
924   if (path1 == NULL || path2 == NULL)
925     return FALSE;
926
927   if (strlen (path1) != len2)
928     return FALSE;
929
930   if (strncmp (path1, path2, len2))
931     return FALSE;
932
933   return TRUE;
934 }
935
936 /* this function is called to initially find the media for the DESCRIBE request
937  * but is cached for when the same client (without breaking the connection) is
938  * doing a setup for the exact same url. */
939 static GstRTSPMedia *
940 find_media (GstRTSPClient * client, GstRTSPContext * ctx, gchar * path,
941     gint * matched)
942 {
943   GstRTSPClientPrivate *priv = client->priv;
944   GstRTSPMediaFactory *factory;
945   GstRTSPMedia *media;
946   gint path_len;
947
948   /* find the longest matching factory for the uri first */
949   if (!(factory = gst_rtsp_mount_points_match (priv->mount_points,
950               path, matched)))
951     goto no_factory;
952
953   ctx->factory = factory;
954
955   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_ACCESS))
956     goto no_factory_access;
957
958   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_CONSTRUCT))
959     goto not_authorized;
960
961   if (matched)
962     path_len = *matched;
963   else
964     path_len = strlen (path);
965
966   if (!paths_are_equal (priv->path, path, path_len)) {
967     /* remove any previously cached values before we try to construct a new
968      * media for uri */
969     clean_cached_media (client, TRUE);
970
971     /* prepare the media and add it to the pipeline */
972     if (!(media = gst_rtsp_media_factory_construct (factory, ctx->uri)))
973       goto no_media;
974
975     ctx->media = media;
976
977     if (!(gst_rtsp_media_get_transport_mode (media) &
978             GST_RTSP_TRANSPORT_MODE_RECORD)) {
979       GstRTSPThread *thread;
980
981       thread = gst_rtsp_thread_pool_get_thread (priv->thread_pool,
982           GST_RTSP_THREAD_TYPE_MEDIA, ctx);
983       if (thread == NULL)
984         goto no_thread;
985
986       /* prepare the media */
987       if (!gst_rtsp_media_prepare (media, thread))
988         goto no_prepare;
989     }
990
991     /* now keep track of the uri and the media */
992     priv->path = g_strndup (path, path_len);
993     priv->media = media;
994   } else {
995     /* we have seen this path before, used cached media */
996     media = priv->media;
997     ctx->media = media;
998     GST_INFO ("reusing cached media %p for path %s", media, priv->path);
999   }
1000
1001   g_object_unref (factory);
1002   ctx->factory = NULL;
1003
1004   if (media)
1005     g_object_ref (media);
1006
1007   return media;
1008
1009   /* ERRORS */
1010 no_factory:
1011   {
1012     GST_ERROR ("client %p: no factory for path %s", client, path);
1013     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1014     return NULL;
1015   }
1016 no_factory_access:
1017   {
1018     g_object_unref (factory);
1019     ctx->factory = NULL;
1020     GST_ERROR ("client %p: not authorized to see factory path %s", client,
1021         path);
1022     /* error reply is already sent */
1023     return NULL;
1024   }
1025 not_authorized:
1026   {
1027     g_object_unref (factory);
1028     ctx->factory = NULL;
1029     GST_ERROR ("client %p: not authorized for factory path %s", client, path);
1030     /* error reply is already sent */
1031     return NULL;
1032   }
1033 no_media:
1034   {
1035     GST_ERROR ("client %p: can't create media", client);
1036     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1037     g_object_unref (factory);
1038     ctx->factory = NULL;
1039     return NULL;
1040   }
1041 no_thread:
1042   {
1043     GST_ERROR ("client %p: can't create thread", client);
1044     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1045     g_object_unref (media);
1046     ctx->media = NULL;
1047     g_object_unref (factory);
1048     ctx->factory = NULL;
1049     return NULL;
1050   }
1051 no_prepare:
1052   {
1053     GST_ERROR ("client %p: can't prepare media", client);
1054     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1055     g_object_unref (media);
1056     ctx->media = NULL;
1057     g_object_unref (factory);
1058     ctx->factory = NULL;
1059     return NULL;
1060   }
1061 }
1062
1063 static gboolean
1064 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
1065 {
1066   GstRTSPClientPrivate *priv = client->priv;
1067   GstRTSPMessage message = { 0 };
1068   GstRTSPResult res = GST_RTSP_OK;
1069   GstMapInfo map_info;
1070   guint8 *data;
1071   guint usize;
1072
1073   gst_rtsp_message_init_data (&message, channel);
1074
1075   /* FIXME, need some sort of iovec RTSPMessage here */
1076   if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ))
1077     return FALSE;
1078
1079   gst_rtsp_message_take_body (&message, map_info.data, map_info.size);
1080
1081   g_mutex_lock (&priv->send_lock);
1082   if (priv->send_func)
1083     res = priv->send_func (client, &message, FALSE, priv->send_data);
1084   g_mutex_unlock (&priv->send_lock);
1085
1086   gst_rtsp_message_steal_body (&message, &data, &usize);
1087   gst_buffer_unmap (buffer, &map_info);
1088
1089   gst_rtsp_message_unset (&message);
1090
1091   return res == GST_RTSP_OK;
1092 }
1093
1094 /**
1095  * gst_rtsp_client_close:
1096  * @client: a #GstRTSPClient
1097  *
1098  * Close the connection of @client and remove all media it was managing.
1099  *
1100  * Since: 1.4
1101  */
1102 void
1103 gst_rtsp_client_close (GstRTSPClient * client)
1104 {
1105   GstRTSPClientPrivate *priv = client->priv;
1106   const gchar *tunnelid;
1107
1108   GST_DEBUG ("client %p: closing connection", client);
1109
1110   if (priv->connection) {
1111     if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
1112       g_mutex_lock (&tunnels_lock);
1113       /* remove from tunnelids */
1114       g_hash_table_remove (tunnels, tunnelid);
1115       g_mutex_unlock (&tunnels_lock);
1116     }
1117     gst_rtsp_connection_close (priv->connection);
1118   }
1119
1120   /* connection is now closed, destroy the watch which will also cause the
1121    * closed signal to be emitted */
1122   if (priv->watch) {
1123     GST_DEBUG ("client %p: destroying watch", client);
1124     g_source_destroy ((GSource *) priv->watch);
1125     priv->watch = NULL;
1126     gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
1127     g_main_context_unref (priv->watch_context);
1128     priv->watch_context = NULL;
1129   }
1130 }
1131
1132 static gchar *
1133 default_make_path_from_uri (GstRTSPClient * client, const GstRTSPUrl * uri)
1134 {
1135   gchar *path;
1136
1137   if (uri->query)
1138     path = g_strconcat (uri->abspath, "?", uri->query, NULL);
1139   else
1140     path = g_strdup (uri->abspath);
1141
1142   return path;
1143 }
1144
1145 /* Default signal handler function for all "pre-command" signals, like
1146  * pre-options-request. It just returns the RTSP return code 200.
1147  * Subclasses can override this to get another default behaviour.
1148  */
1149 static GstRTSPStatusCode
1150 default_pre_signal_handler (GstRTSPClient * client, GstRTSPContext * ctx)
1151 {
1152   GST_LOG_OBJECT (client, "returning GST_RTSP_STS_OK");
1153   return GST_RTSP_STS_OK;
1154 }
1155
1156 /* The pre-signal accumulator function checks the return value of the signal
1157  * handlers. If any of them returns an RTSP status code that does not start
1158  * with 2 it will return FALSE, no more signal handlers will be called, and
1159  * this last RTSP status code will be the result of the signal emission.
1160  */
1161 static gboolean
1162 pre_signal_accumulator (GSignalInvocationHint * ihint, GValue * return_accu,
1163     const GValue * handler_return, gpointer data)
1164 {
1165   GstRTSPStatusCode handler_value = g_value_get_enum (handler_return);
1166   GstRTSPStatusCode accumulated_value = g_value_get_enum (return_accu);
1167
1168   if (handler_value < 200 || handler_value > 299) {
1169     GST_DEBUG ("handler_value : %d, returning FALSE", handler_value);
1170     g_value_set_enum (return_accu, handler_value);
1171     return FALSE;
1172   }
1173
1174   /* the accumulated value is initiated to 0 by GLib. if current handler value is
1175    * bigger then use that instead
1176    *
1177    * FIXME: Should we prioritize the 2xx codes in a smarter way?
1178    *        Like, "201 Created" > "250 Low On Storage Space" > "200 OK"?
1179    */
1180   if (handler_value > accumulated_value)
1181     g_value_set_enum (return_accu, handler_value);
1182
1183   return TRUE;
1184 }
1185
1186 static gboolean
1187 handle_teardown_request (GstRTSPClient * client, GstRTSPContext * ctx)
1188 {
1189   GstRTSPClientPrivate *priv = client->priv;
1190   GstRTSPClientClass *klass;
1191   GstRTSPSession *session;
1192   GstRTSPSessionMedia *sessmedia;
1193   GstRTSPStatusCode code;
1194   gchar *path;
1195   gint matched;
1196   gboolean keep_session;
1197   GstRTSPStatusCode sig_result;
1198
1199   if (!ctx->session)
1200     goto no_session;
1201
1202   session = ctx->session;
1203
1204   if (!ctx->uri)
1205     goto no_uri;
1206
1207   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1208   path = klass->make_path_from_uri (client, ctx->uri);
1209
1210   /* get a handle to the configuration of the media in the session */
1211   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1212   if (!sessmedia)
1213     goto not_found;
1214
1215   /* only aggregate control for now.. */
1216   if (path[matched] != '\0')
1217     goto no_aggregate;
1218
1219   g_free (path);
1220
1221   ctx->sessmedia = sessmedia;
1222
1223   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_TEARDOWN_REQUEST],
1224       0, ctx, &sig_result);
1225   if (sig_result != GST_RTSP_STS_OK) {
1226     goto sig_failed;
1227   }
1228
1229   /* we emit the signal before closing the connection */
1230   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_TEARDOWN_REQUEST],
1231       0, ctx);
1232
1233   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_NULL);
1234
1235   /* unmanage the media in the session, returns false if all media session
1236    * are torn down. */
1237   keep_session = gst_rtsp_session_release_media (session, sessmedia);
1238
1239   /* construct the response now */
1240   code = GST_RTSP_STS_OK;
1241   gst_rtsp_message_init_response (ctx->response, code,
1242       gst_rtsp_status_as_text (code), ctx->request);
1243
1244   send_message (client, ctx, ctx->response, TRUE);
1245
1246   if (!keep_session) {
1247     /* remove the session */
1248     gst_rtsp_session_pool_remove (priv->session_pool, session);
1249   }
1250
1251   return TRUE;
1252
1253   /* ERRORS */
1254 no_session:
1255   {
1256     GST_ERROR ("client %p: no session", client);
1257     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1258     return FALSE;
1259   }
1260 no_uri:
1261   {
1262     GST_ERROR ("client %p: no uri supplied", client);
1263     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1264     return FALSE;
1265   }
1266 not_found:
1267   {
1268     GST_ERROR ("client %p: no media for uri", client);
1269     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1270     g_free (path);
1271     return FALSE;
1272   }
1273 no_aggregate:
1274   {
1275     GST_ERROR ("client %p: no aggregate path %s", client, path);
1276     send_generic_response (client,
1277         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
1278     g_free (path);
1279     return FALSE;
1280   }
1281 sig_failed:
1282   {
1283     GST_ERROR ("client %p: pre signal returned error: %s", client,
1284         gst_rtsp_status_as_text (sig_result));
1285     send_generic_response (client, sig_result, ctx);
1286     return FALSE;
1287   }
1288 }
1289
1290 static GstRTSPResult
1291 default_params_set (GstRTSPClient * client, GstRTSPContext * ctx)
1292 {
1293   GstRTSPResult res;
1294
1295   res = gst_rtsp_params_set (client, ctx);
1296
1297   return res;
1298 }
1299
1300 static GstRTSPResult
1301 default_params_get (GstRTSPClient * client, GstRTSPContext * ctx)
1302 {
1303   GstRTSPResult res;
1304
1305   res = gst_rtsp_params_get (client, ctx);
1306
1307   return res;
1308 }
1309
1310 static gboolean
1311 handle_get_param_request (GstRTSPClient * client, GstRTSPContext * ctx)
1312 {
1313   GstRTSPResult res;
1314   guint8 *data;
1315   guint size;
1316   GstRTSPStatusCode sig_result;
1317
1318   g_signal_emit (client,
1319       gst_rtsp_client_signals[SIGNAL_PRE_GET_PARAMETER_REQUEST], 0, ctx,
1320       &sig_result);
1321   if (sig_result != GST_RTSP_STS_OK) {
1322     goto sig_failed;
1323   }
1324
1325   res = gst_rtsp_message_get_body (ctx->request, &data, &size);
1326   if (res != GST_RTSP_OK)
1327     goto bad_request;
1328
1329   if (size == 0 || !data || strlen ((char *) data) == 0) {
1330     if (ctx->request->type_data.request.version >= GST_RTSP_VERSION_2_0) {
1331       GST_ERROR_OBJECT (client, "Using PLAY request for keep-alive is forbidden"
1332           " in RTSP 2.0");
1333       goto bad_request;
1334     }
1335
1336     /* no body (or only '\0'), keep-alive request */
1337     send_generic_response (client, GST_RTSP_STS_OK, ctx);
1338   } else {
1339     /* there is a body, handle the params */
1340     res = GST_RTSP_CLIENT_GET_CLASS (client)->params_get (client, ctx);
1341     if (res != GST_RTSP_OK)
1342       goto bad_request;
1343
1344     send_message (client, ctx, ctx->response, FALSE);
1345   }
1346
1347   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_GET_PARAMETER_REQUEST],
1348       0, ctx);
1349
1350   return TRUE;
1351
1352   /* ERRORS */
1353 sig_failed:
1354   {
1355     GST_ERROR ("client %p: pre signal returned error: %s", client,
1356         gst_rtsp_status_as_text (sig_result));
1357     send_generic_response (client, sig_result, ctx);
1358     return FALSE;
1359   }
1360 bad_request:
1361   {
1362     GST_ERROR ("client %p: bad request", client);
1363     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1364     return FALSE;
1365   }
1366 }
1367
1368 static gboolean
1369 handle_set_param_request (GstRTSPClient * client, GstRTSPContext * ctx)
1370 {
1371   GstRTSPResult res;
1372   guint8 *data;
1373   guint size;
1374   GstRTSPStatusCode sig_result;
1375
1376   g_signal_emit (client,
1377       gst_rtsp_client_signals[SIGNAL_PRE_SET_PARAMETER_REQUEST], 0, ctx,
1378       &sig_result);
1379   if (sig_result != GST_RTSP_STS_OK) {
1380     goto sig_failed;
1381   }
1382
1383   res = gst_rtsp_message_get_body (ctx->request, &data, &size);
1384   if (res != GST_RTSP_OK)
1385     goto bad_request;
1386
1387   if (size == 0 || !data || strlen ((char *) data) == 0) {
1388     /* no body (or only '\0'), keep-alive request */
1389     send_generic_response (client, GST_RTSP_STS_OK, ctx);
1390   } else {
1391     /* there is a body, handle the params */
1392     res = GST_RTSP_CLIENT_GET_CLASS (client)->params_set (client, ctx);
1393     if (res != GST_RTSP_OK)
1394       goto bad_request;
1395
1396     send_message (client, ctx, ctx->response, FALSE);
1397   }
1398
1399   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SET_PARAMETER_REQUEST],
1400       0, ctx);
1401
1402   return TRUE;
1403
1404   /* ERRORS */
1405 sig_failed:
1406   {
1407     GST_ERROR ("client %p: pre signal returned error: %s", client,
1408         gst_rtsp_status_as_text (sig_result));
1409     send_generic_response (client, sig_result, ctx);
1410     return FALSE;
1411   }
1412 bad_request:
1413   {
1414     GST_ERROR ("client %p: bad request", client);
1415     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1416     return FALSE;
1417   }
1418 }
1419
1420 static gboolean
1421 handle_pause_request (GstRTSPClient * client, GstRTSPContext * ctx)
1422 {
1423   GstRTSPSession *session;
1424   GstRTSPClientClass *klass;
1425   GstRTSPSessionMedia *sessmedia;
1426   GstRTSPMedia *media;
1427   GstRTSPStatusCode code;
1428   GstRTSPState rtspstate;
1429   gchar *path;
1430   gint matched;
1431   GstRTSPStatusCode sig_result;
1432   guint i, n;
1433
1434   if (!(session = ctx->session))
1435     goto no_session;
1436
1437   if (!ctx->uri)
1438     goto no_uri;
1439
1440   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1441   path = klass->make_path_from_uri (client, ctx->uri);
1442
1443   /* get a handle to the configuration of the media in the session */
1444   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1445   if (!sessmedia)
1446     goto not_found;
1447
1448   if (path[matched] != '\0')
1449     goto no_aggregate;
1450
1451   g_free (path);
1452
1453   media = gst_rtsp_session_media_get_media (sessmedia);
1454   n = gst_rtsp_media_n_streams (media);
1455   for (i = 0; i < n; i++) {
1456     GstRTSPStream *stream = gst_rtsp_media_get_stream (media, i);
1457
1458     if (gst_rtsp_stream_get_publish_clock_mode (stream) ==
1459         GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET)
1460       goto not_supported;
1461   }
1462
1463   ctx->sessmedia = sessmedia;
1464
1465   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_PAUSE_REQUEST], 0,
1466       ctx, &sig_result);
1467   if (sig_result != GST_RTSP_STS_OK) {
1468     goto sig_failed;
1469   }
1470
1471   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1472   /* the session state must be playing or recording */
1473   if (rtspstate != GST_RTSP_STATE_PLAYING &&
1474       rtspstate != GST_RTSP_STATE_RECORDING)
1475     goto invalid_state;
1476
1477   /* then pause sending */
1478   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PAUSED);
1479
1480   /* construct the response now */
1481   code = GST_RTSP_STS_OK;
1482   gst_rtsp_message_init_response (ctx->response, code,
1483       gst_rtsp_status_as_text (code), ctx->request);
1484
1485   send_message (client, ctx, ctx->response, FALSE);
1486
1487   /* the state is now READY */
1488   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
1489
1490   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PAUSE_REQUEST], 0, ctx);
1491
1492   return TRUE;
1493
1494   /* ERRORS */
1495 no_session:
1496   {
1497     GST_ERROR ("client %p: no session", client);
1498     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1499     return FALSE;
1500   }
1501 no_uri:
1502   {
1503     GST_ERROR ("client %p: no uri supplied", client);
1504     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1505     return FALSE;
1506   }
1507 not_found:
1508   {
1509     GST_ERROR ("client %p: no media for uri", client);
1510     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1511     g_free (path);
1512     return FALSE;
1513   }
1514 no_aggregate:
1515   {
1516     GST_ERROR ("client %p: no aggregate path %s", client, path);
1517     send_generic_response (client,
1518         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
1519     g_free (path);
1520     return FALSE;
1521   }
1522 sig_failed:
1523   {
1524     GST_ERROR ("client %p: pre signal returned error: %s", client,
1525         gst_rtsp_status_as_text (sig_result));
1526     send_generic_response (client, sig_result, ctx);
1527     return FALSE;
1528   }
1529 invalid_state:
1530   {
1531     GST_ERROR ("client %p: not PLAYING or RECORDING", client);
1532     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1533         ctx);
1534     return FALSE;
1535   }
1536 not_supported:
1537   {
1538     GST_ERROR ("client %p: pausing not supported", client);
1539     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1540     return FALSE;
1541   }
1542 }
1543
1544 /* convert @url and @path to a URL used as a content base for the factory
1545  * located at @path */
1546 static gchar *
1547 make_base_url (GstRTSPClient * client, GstRTSPUrl * url, const gchar * path)
1548 {
1549   GstRTSPUrl tmp;
1550   gchar *result;
1551   const gchar *trail;
1552
1553   /* check for trailing '/' and append one */
1554   trail = (path[strlen (path) - 1] != '/' ? "/" : "");
1555
1556   tmp = *url;
1557   tmp.user = NULL;
1558   tmp.passwd = NULL;
1559   tmp.abspath = g_strdup_printf ("%s%s", path, trail);
1560   tmp.query = NULL;
1561   result = gst_rtsp_url_get_request_uri (&tmp);
1562   g_free (tmp.abspath);
1563
1564   return result;
1565 }
1566
1567 static gboolean
1568 handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
1569 {
1570   GstRTSPSession *session;
1571   GstRTSPClientClass *klass;
1572   GstRTSPSessionMedia *sessmedia;
1573   GstRTSPMedia *media;
1574   GstRTSPStatusCode code;
1575   GstRTSPUrl *uri;
1576   gchar *str;
1577   GstRTSPTimeRange *range;
1578   GstRTSPResult res;
1579   GstRTSPState rtspstate;
1580   GstRTSPRangeUnit unit = GST_RTSP_RANGE_NPT;
1581   gchar *path, *rtpinfo;
1582   gint matched;
1583   gchar *seek_style = NULL;
1584   GstRTSPStatusCode sig_result;
1585   GPtrArray *transports;
1586
1587   if (!(session = ctx->session))
1588     goto no_session;
1589
1590   if (!(uri = ctx->uri))
1591     goto no_uri;
1592
1593   klass = GST_RTSP_CLIENT_GET_CLASS (client);
1594   path = klass->make_path_from_uri (client, uri);
1595
1596   /* get a handle to the configuration of the media in the session */
1597   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
1598   if (!sessmedia)
1599     goto not_found;
1600
1601   if (path[matched] != '\0')
1602     goto no_aggregate;
1603
1604   g_free (path);
1605
1606   ctx->sessmedia = sessmedia;
1607   ctx->media = media = gst_rtsp_session_media_get_media (sessmedia);
1608
1609   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_PLAY_REQUEST], 0,
1610       ctx, &sig_result);
1611   if (sig_result != GST_RTSP_STS_OK) {
1612     goto sig_failed;
1613   }
1614
1615   if (!(gst_rtsp_media_get_transport_mode (media) &
1616           GST_RTSP_TRANSPORT_MODE_PLAY))
1617     goto unsupported_mode;
1618
1619   /* the session state must be playing or ready */
1620   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
1621   if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
1622     goto invalid_state;
1623
1624   /* update the pipeline */
1625   transports = gst_rtsp_session_media_get_transports (sessmedia);
1626   if (!gst_rtsp_media_complete_pipeline (media, transports)) {
1627     g_ptr_array_unref (transports);
1628     goto pipeline_error;
1629   }
1630   g_ptr_array_unref (transports);
1631
1632   /* in play we first unsuspend, media could be suspended from SDP or PAUSED */
1633   if (!gst_rtsp_media_unsuspend (media))
1634     goto unsuspend_failed;
1635
1636   /* parse the range header if we have one */
1637   res = gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RANGE, &str, 0);
1638   if (res == GST_RTSP_OK) {
1639     if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
1640       GstRTSPMediaStatus media_status;
1641       GstSeekFlags flags = 0;
1642
1643       if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_SEEK_STYLE,
1644               &seek_style, 0)) {
1645         if (g_strcmp0 (seek_style, "RAP") == 0)
1646           flags = GST_SEEK_FLAG_ACCURATE;
1647         else if (g_strcmp0 (seek_style, "CoRAP") == 0)
1648           flags = GST_SEEK_FLAG_KEY_UNIT;
1649         else if (g_strcmp0 (seek_style, "First-Prior") == 0)
1650           flags = GST_SEEK_FLAG_KEY_UNIT & GST_SEEK_FLAG_SNAP_BEFORE;
1651         else if (g_strcmp0 (seek_style, "Next") == 0)
1652           flags = GST_SEEK_FLAG_KEY_UNIT & GST_SEEK_FLAG_SNAP_AFTER;
1653         else
1654           GST_FIXME_OBJECT (client, "Add support for seek style %s",
1655               seek_style);
1656       }
1657
1658       /* we have a range, seek to the position */
1659       unit = range->unit;
1660       gst_rtsp_media_seek_full (media, range, flags);
1661       gst_rtsp_range_free (range);
1662
1663       media_status = gst_rtsp_media_get_status (media);
1664       if (media_status == GST_RTSP_MEDIA_STATUS_ERROR)
1665         goto seek_failed;
1666     }
1667   }
1668
1669   /* grab RTPInfo from the media now */
1670   rtpinfo = gst_rtsp_session_media_get_rtpinfo (sessmedia);
1671
1672   /* construct the response now */
1673   code = GST_RTSP_STS_OK;
1674   gst_rtsp_message_init_response (ctx->response, code,
1675       gst_rtsp_status_as_text (code), ctx->request);
1676
1677   /* add the RTP-Info header */
1678   if (rtpinfo)
1679     gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_RTP_INFO,
1680         rtpinfo);
1681   if (seek_style)
1682     gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_SEEK_STYLE,
1683         seek_style);
1684
1685   /* add the range */
1686   str = gst_rtsp_media_get_range_string (media, TRUE, unit);
1687   if (str)
1688     gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_RANGE, str);
1689
1690   send_message (client, ctx, ctx->response, FALSE);
1691
1692   /* start playing after sending the response */
1693   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PLAYING);
1694
1695   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_PLAYING);
1696
1697   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PLAY_REQUEST], 0, ctx);
1698
1699   return TRUE;
1700
1701   /* ERRORS */
1702 no_session:
1703   {
1704     GST_ERROR ("client %p: no session", client);
1705     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
1706     return FALSE;
1707   }
1708 no_uri:
1709   {
1710     GST_ERROR ("client %p: no uri supplied", client);
1711     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1712     return FALSE;
1713   }
1714 not_found:
1715   {
1716     GST_ERROR ("client %p: media not found", client);
1717     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
1718     return FALSE;
1719   }
1720 no_aggregate:
1721   {
1722     GST_ERROR ("client %p: no aggregate path %s", client, path);
1723     send_generic_response (client,
1724         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
1725     g_free (path);
1726     return FALSE;
1727   }
1728 sig_failed:
1729   {
1730     GST_ERROR ("client %p: pre signal returned error: %s", client,
1731         gst_rtsp_status_as_text (sig_result));
1732     send_generic_response (client, sig_result, ctx);
1733     return FALSE;
1734   }
1735 invalid_state:
1736   {
1737     GST_ERROR ("client %p: not PLAYING or READY", client);
1738     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1739         ctx);
1740     return FALSE;
1741   }
1742 pipeline_error:
1743   {
1744     GST_ERROR ("client %p: failed to configure the pipeline", client);
1745     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
1746         ctx);
1747     return FALSE;
1748   }
1749 unsuspend_failed:
1750   {
1751     GST_ERROR ("client %p: unsuspend failed", client);
1752     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1753     return FALSE;
1754   }
1755 seek_failed:
1756   {
1757     GST_ERROR ("client %p: seek failed", client);
1758     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
1759     return FALSE;
1760   }
1761 unsupported_mode:
1762   {
1763     GST_ERROR ("client %p: media does not support PLAY", client);
1764     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
1765     return FALSE;
1766   }
1767 }
1768
1769 static void
1770 do_keepalive (GstRTSPSession * session)
1771 {
1772   GST_INFO ("keep session %p alive", session);
1773   gst_rtsp_session_touch (session);
1774 }
1775
1776 /* parse @transport and return a valid transport in @tr. only transports
1777  * supported by @stream are returned. Returns FALSE if no valid transport
1778  * was found. */
1779 static gboolean
1780 parse_transport (const char *transport, GstRTSPStream * stream,
1781     GstRTSPTransport * tr)
1782 {
1783   gint i;
1784   gboolean res;
1785   gchar **transports;
1786
1787   res = FALSE;
1788   gst_rtsp_transport_init (tr);
1789
1790   GST_DEBUG ("parsing transports %s", transport);
1791
1792   transports = g_strsplit (transport, ",", 0);
1793
1794   /* loop through the transports, try to parse */
1795   for (i = 0; transports[i]; i++) {
1796     res = gst_rtsp_transport_parse (transports[i], tr);
1797     if (res != GST_RTSP_OK) {
1798       /* no valid transport, search some more */
1799       GST_WARNING ("could not parse transport %s", transports[i]);
1800       goto next;
1801     }
1802
1803     /* we have a transport, see if it's supported */
1804     if (!gst_rtsp_stream_is_transport_supported (stream, tr)) {
1805       GST_WARNING ("unsupported transport %s", transports[i]);
1806       goto next;
1807     }
1808
1809     /* we have a valid transport */
1810     GST_INFO ("found valid transport %s", transports[i]);
1811     res = TRUE;
1812     break;
1813
1814   next:
1815     gst_rtsp_transport_init (tr);
1816   }
1817   g_strfreev (transports);
1818
1819   return res;
1820 }
1821
1822 static gboolean
1823 default_configure_client_media (GstRTSPClient * client, GstRTSPMedia * media,
1824     GstRTSPStream * stream, GstRTSPContext * ctx)
1825 {
1826   GstRTSPMessage *request = ctx->request;
1827   gchar *blocksize_str;
1828
1829   if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_BLOCKSIZE,
1830           &blocksize_str, 0) == GST_RTSP_OK) {
1831     guint64 blocksize;
1832     gchar *end;
1833
1834     blocksize = g_ascii_strtoull (blocksize_str, &end, 10);
1835     if (end == blocksize_str)
1836       goto parse_failed;
1837
1838     /* we don't want to change the mtu when this media
1839      * can be shared because it impacts other clients */
1840     if (gst_rtsp_media_is_shared (media))
1841       goto done;
1842
1843     if (blocksize > G_MAXUINT)
1844       blocksize = G_MAXUINT;
1845
1846     gst_rtsp_stream_set_mtu (stream, blocksize);
1847   }
1848 done:
1849   return TRUE;
1850
1851   /* ERRORS */
1852 parse_failed:
1853   {
1854     GST_ERROR_OBJECT (client, "failed to parse blocksize");
1855     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
1856     return FALSE;
1857   }
1858 }
1859
1860 static gboolean
1861 default_configure_client_transport (GstRTSPClient * client,
1862     GstRTSPContext * ctx, GstRTSPTransport * ct)
1863 {
1864   GstRTSPClientPrivate *priv = client->priv;
1865
1866   /* we have a valid transport now, set the destination of the client. */
1867   if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST ||
1868       ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP) {
1869
1870     /* allocate UDP ports */
1871     GSocketFamily family;
1872     gboolean use_client_settings = FALSE;
1873
1874     family = priv->is_ipv6 ? G_SOCKET_FAMILY_IPV6 : G_SOCKET_FAMILY_IPV4;
1875     if ((ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) &&
1876         gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS) &&
1877         (ct->destination != NULL))
1878       use_client_settings = TRUE;
1879
1880     if (!gst_rtsp_stream_allocate_udp_sockets (ctx->stream, family, ct,
1881             use_client_settings))
1882       goto error_allocating_ports;
1883
1884     if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
1885       GstRTSPAddress *addr = NULL;
1886
1887       if (use_client_settings) {
1888         /* the address has been successfully allocated, let's check if it's
1889          * the one requested by the client */
1890         addr = gst_rtsp_stream_reserve_address (ctx->stream, ct->destination,
1891             ct->port.min, ct->port.max - ct->port.min + 1, ct->ttl);
1892
1893         if (addr == NULL)
1894           goto no_address;
1895       } else {
1896         g_free (ct->destination);
1897         addr = gst_rtsp_stream_get_multicast_address (ctx->stream, family);
1898         if (addr == NULL)
1899           goto no_address;
1900         ct->destination = g_strdup (addr->address);
1901         ct->port.min = addr->port;
1902         ct->port.max = addr->port + addr->n_ports - 1;
1903         ct->ttl = addr->ttl;
1904       }
1905
1906       gst_rtsp_address_free (addr);
1907     } else {
1908       GstRTSPUrl *url;
1909
1910       url = gst_rtsp_connection_get_url (priv->connection);
1911       g_free (ct->destination);
1912       ct->destination = g_strdup (url->host);
1913     }
1914   } else {
1915     GstRTSPUrl *url;
1916
1917     url = gst_rtsp_connection_get_url (priv->connection);
1918     g_free (ct->destination);
1919     ct->destination = g_strdup (url->host);
1920
1921     if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
1922       GSocket *sock;
1923       GSocketAddress *addr;
1924
1925       sock = gst_rtsp_connection_get_read_socket (priv->connection);
1926       if ((addr = g_socket_get_remote_address (sock, NULL))) {
1927         /* our read port is the sender port of client */
1928         ct->client_port.min =
1929             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1930         g_object_unref (addr);
1931       }
1932       if ((addr = g_socket_get_local_address (sock, NULL))) {
1933         ct->server_port.max =
1934             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1935         g_object_unref (addr);
1936       }
1937       sock = gst_rtsp_connection_get_write_socket (priv->connection);
1938       if ((addr = g_socket_get_remote_address (sock, NULL))) {
1939         /* our write port is the receiver port of client */
1940         ct->client_port.max =
1941             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1942         g_object_unref (addr);
1943       }
1944       if ((addr = g_socket_get_local_address (sock, NULL))) {
1945         ct->server_port.min =
1946             g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1947         g_object_unref (addr);
1948       }
1949       /* check if the client selected channels for TCP */
1950       if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
1951         gst_rtsp_session_media_alloc_channels (ctx->sessmedia,
1952             &ct->interleaved);
1953       }
1954     }
1955   }
1956   return TRUE;
1957
1958   /* ERRORS */
1959 error_allocating_ports:
1960   {
1961     GST_ERROR_OBJECT (client, "Failed to allocate UDP ports");
1962     return FALSE;
1963   }
1964 no_address:
1965   {
1966     GST_ERROR_OBJECT (client, "Failed to acquire address for stream");
1967     return FALSE;
1968   }
1969 }
1970
1971 static GstRTSPTransport *
1972 make_server_transport (GstRTSPClient * client, GstRTSPMedia * media,
1973     GstRTSPContext * ctx, GstRTSPTransport * ct)
1974 {
1975   GstRTSPTransport *st;
1976   GInetAddress *addr;
1977   GSocketFamily family;
1978
1979   /* prepare the server transport */
1980   gst_rtsp_transport_new (&st);
1981
1982   st->trans = ct->trans;
1983   st->profile = ct->profile;
1984   st->lower_transport = ct->lower_transport;
1985   st->mode_play = ct->mode_play;
1986   st->mode_record = ct->mode_record;
1987
1988   addr = g_inet_address_new_from_string (ct->destination);
1989
1990   if (!addr) {
1991     GST_ERROR ("failed to get inet addr from client destination");
1992     family = G_SOCKET_FAMILY_IPV4;
1993   } else {
1994     family = g_inet_address_get_family (addr);
1995     g_object_unref (addr);
1996     addr = NULL;
1997   }
1998
1999   switch (st->lower_transport) {
2000     case GST_RTSP_LOWER_TRANS_UDP:
2001       st->client_port = ct->client_port;
2002       gst_rtsp_stream_get_server_port (ctx->stream, &st->server_port, family);
2003       break;
2004     case GST_RTSP_LOWER_TRANS_UDP_MCAST:
2005       st->port = ct->port;
2006       st->destination = g_strdup (ct->destination);
2007       st->ttl = ct->ttl;
2008       break;
2009     case GST_RTSP_LOWER_TRANS_TCP:
2010       st->interleaved = ct->interleaved;
2011       st->client_port = ct->client_port;
2012       st->server_port = ct->server_port;
2013     default:
2014       break;
2015   }
2016
2017   if ((gst_rtsp_media_get_transport_mode (media) &
2018           GST_RTSP_TRANSPORT_MODE_PLAY))
2019     gst_rtsp_stream_get_ssrc (ctx->stream, &st->ssrc);
2020
2021   return st;
2022 }
2023
2024 static gboolean
2025 rtsp_ctrl_timeout_cb (gpointer user_data)
2026 {
2027   gboolean res = G_SOURCE_CONTINUE;
2028   GstRTSPClient *client = (GstRTSPClient *) user_data;
2029   GstRTSPClientPrivate *priv = client->priv;
2030
2031   priv->rtsp_ctrl_timeout_cnt += RTSP_CTRL_CB_INTERVAL;
2032
2033   if (priv->rtsp_ctrl_timeout_cnt > RTSP_CTRL_TIMEOUT_VALUE) {
2034     GST_DEBUG ("rtsp control session timeout id=%u expired, closing client.",
2035         priv->rtsp_ctrl_timeout_id);
2036     g_mutex_lock (&priv->lock);
2037     priv->rtsp_ctrl_timeout_id = 0;
2038     priv->rtsp_ctrl_timeout_cnt = 0;
2039     g_mutex_unlock (&priv->lock);
2040     gst_rtsp_client_close (client);
2041
2042     res = G_SOURCE_REMOVE;
2043   }
2044
2045   return res;
2046 }
2047
2048 static void
2049 rtsp_ctrl_timeout_remove (GstRTSPClientPrivate * priv)
2050 {
2051   g_mutex_lock (&priv->lock);
2052
2053   if (priv->rtsp_ctrl_timeout_id != 0) {
2054     g_source_destroy (g_main_context_find_source_by_id (priv->watch_context,
2055             priv->rtsp_ctrl_timeout_id));
2056     GST_DEBUG ("rtsp control session removed timeout id=%u.",
2057         priv->rtsp_ctrl_timeout_id);
2058     priv->rtsp_ctrl_timeout_id = 0;
2059     priv->rtsp_ctrl_timeout_cnt = 0;
2060   }
2061
2062   g_mutex_unlock (&priv->lock);
2063 }
2064
2065 static gboolean
2066 handle_setup_request (GstRTSPClient * client, GstRTSPContext * ctx)
2067 {
2068   GstRTSPClientPrivate *priv = client->priv;
2069   GstRTSPResult res;
2070   GstRTSPUrl *uri;
2071   gchar *transport, *keymgmt;
2072   GstRTSPTransport *ct, *st;
2073   GstRTSPStatusCode code;
2074   GstRTSPSession *session;
2075   GstRTSPStreamTransport *trans;
2076   gchar *trans_str;
2077   GstRTSPSessionMedia *sessmedia;
2078   GstRTSPMedia *media;
2079   GstRTSPStream *stream;
2080   GstRTSPState rtspstate;
2081   GstRTSPClientClass *klass;
2082   gchar *path, *control = NULL;
2083   gint matched;
2084   gboolean new_session = FALSE;
2085   GstRTSPStatusCode sig_result;
2086   gchar *pipelined_request_id = NULL, *accept_range = NULL;
2087
2088   if (!ctx->uri)
2089     goto no_uri;
2090
2091   uri = ctx->uri;
2092   klass = GST_RTSP_CLIENT_GET_CLASS (client);
2093   path = klass->make_path_from_uri (client, uri);
2094
2095   /* parse the transport */
2096   res =
2097       gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_TRANSPORT,
2098       &transport, 0);
2099   if (res != GST_RTSP_OK)
2100     goto no_transport;
2101
2102   /* Handle Pipelined-requests if using >= 2.0 */
2103   if (ctx->request->type_data.request.version >= GST_RTSP_VERSION_2_0)
2104     gst_rtsp_message_get_header (ctx->request,
2105         GST_RTSP_HDR_PIPELINED_REQUESTS, &pipelined_request_id, 0);
2106
2107   /* we create the session after parsing stuff so that we don't make
2108    * a session for malformed requests */
2109   if (priv->session_pool == NULL)
2110     goto no_pool;
2111
2112   session = ctx->session;
2113
2114   if (session) {
2115     g_object_ref (session);
2116     /* get a handle to the configuration of the media in the session, this can
2117      * return NULL if this is a new url to manage in this session. */
2118     sessmedia = gst_rtsp_session_get_media (session, path, &matched);
2119   } else {
2120     /* we need a new media configuration in this session */
2121     sessmedia = NULL;
2122   }
2123
2124   /* we have no session media, find one and manage it */
2125   if (sessmedia == NULL) {
2126     /* get a handle to the configuration of the media in the session */
2127     media = find_media (client, ctx, path, &matched);
2128     /* need to suspend the media, if the protocol has changed */
2129     if (media != NULL)
2130       gst_rtsp_media_suspend (media);
2131   } else {
2132     if ((media = gst_rtsp_session_media_get_media (sessmedia)))
2133       g_object_ref (media);
2134     else
2135       goto media_not_found;
2136   }
2137   /* no media, not found then */
2138   if (media == NULL)
2139     goto media_not_found_no_reply;
2140
2141   if (path[matched] == '\0') {
2142     if (gst_rtsp_media_n_streams (media) == 1) {
2143       stream = gst_rtsp_media_get_stream (media, 0);
2144     } else {
2145       goto control_not_found;
2146     }
2147   } else {
2148     /* path is what matched. */
2149     path[matched] = '\0';
2150     /* control is remainder */
2151     control = &path[matched + 1];
2152
2153     /* find the stream now using the control part */
2154     stream = gst_rtsp_media_find_stream (media, control);
2155   }
2156
2157   if (stream == NULL)
2158     goto stream_not_found;
2159
2160   /* now we have a uri identifying a valid media and stream */
2161   ctx->stream = stream;
2162   ctx->media = media;
2163
2164   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_SETUP_REQUEST], 0,
2165       ctx, &sig_result);
2166   if (sig_result != GST_RTSP_STS_OK) {
2167     goto sig_failed;
2168   }
2169
2170   if (session == NULL) {
2171     /* create a session if this fails we probably reached our session limit or
2172      * something. */
2173     if (!(session = gst_rtsp_session_pool_create (priv->session_pool)))
2174       goto service_unavailable;
2175
2176     /* Pipelined requests should be cleared between sessions */
2177     g_hash_table_remove_all (priv->pipelined_requests);
2178
2179     /* make sure this client is closed when the session is closed */
2180     client_watch_session (client, session);
2181
2182     new_session = TRUE;
2183     /* signal new session */
2184     g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_NEW_SESSION], 0,
2185         session);
2186
2187     ctx->session = session;
2188   }
2189
2190   if (pipelined_request_id) {
2191     g_hash_table_insert (client->priv->pipelined_requests,
2192         g_strdup (pipelined_request_id),
2193         g_strdup (gst_rtsp_session_get_sessionid (session)));
2194   }
2195   rtsp_ctrl_timeout_remove (priv);
2196
2197   if (!klass->configure_client_media (client, media, stream, ctx))
2198     goto configure_media_failed_no_reply;
2199
2200   gst_rtsp_transport_new (&ct);
2201
2202   /* parse and find a usable supported transport */
2203   if (!parse_transport (transport, stream, ct))
2204     goto unsupported_transports;
2205
2206   if ((ct->mode_play
2207           && !(gst_rtsp_media_get_transport_mode (media) &
2208               GST_RTSP_TRANSPORT_MODE_PLAY)) || (ct->mode_record
2209           && !(gst_rtsp_media_get_transport_mode (media) &
2210               GST_RTSP_TRANSPORT_MODE_RECORD)))
2211     goto unsupported_mode;
2212
2213   /* parse the keymgmt */
2214   if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_KEYMGMT,
2215           &keymgmt, 0) == GST_RTSP_OK) {
2216     if (!gst_rtsp_stream_handle_keymgmt (ctx->stream, keymgmt))
2217       goto keymgmt_error;
2218   }
2219
2220   if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_ACCEPT_RANGES,
2221           &accept_range, 0) == GST_RTSP_OK) {
2222     GEnumValue *runit = NULL;
2223     gint i;
2224     gchar **valid_ranges;
2225     GEnumClass *runit_class = g_type_class_ref (GST_TYPE_RTSP_RANGE_UNIT);
2226
2227     gst_rtsp_message_dump (ctx->request);
2228     valid_ranges = g_strsplit (accept_range, ",", -1);
2229
2230     for (i = 0; valid_ranges[i]; i++) {
2231       gchar *range = valid_ranges[i];
2232
2233       while (*range == ' ')
2234         range++;
2235
2236       runit = g_enum_get_value_by_nick (runit_class, range);
2237       if (runit)
2238         break;
2239     }
2240     g_strfreev (valid_ranges);
2241     g_type_class_unref (runit_class);
2242
2243     if (!runit)
2244       goto unsupported_range_unit;
2245   }
2246
2247   if (sessmedia == NULL) {
2248     /* manage the media in our session now, if not done already  */
2249     sessmedia =
2250         gst_rtsp_session_manage_media (session, path, g_object_ref (media));
2251     /* if we stil have no media, error */
2252     if (sessmedia == NULL)
2253       goto sessmedia_unavailable;
2254
2255     /* don't cache media anymore */
2256     clean_cached_media (client, FALSE);
2257   }
2258
2259   ctx->sessmedia = sessmedia;
2260
2261   /* update the client transport */
2262   if (!klass->configure_client_transport (client, ctx, ct))
2263     goto unsupported_client_transport;
2264
2265   /* set in the session media transport */
2266   trans = gst_rtsp_session_media_set_transport (sessmedia, stream, ct);
2267
2268   ctx->trans = trans;
2269
2270   /* configure the url used to set this transport, this we will use when
2271    * generating the response for the PLAY request */
2272   gst_rtsp_stream_transport_set_url (trans, uri);
2273   /* configure keepalive for this transport */
2274   gst_rtsp_stream_transport_set_keepalive (trans,
2275       (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
2276
2277   if (ct->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
2278     /* our callbacks to send data on this TCP connection */
2279     gst_rtsp_stream_transport_set_callbacks (trans,
2280         (GstRTSPSendFunc) do_send_data,
2281         (GstRTSPSendFunc) do_send_data, client, NULL);
2282
2283     g_hash_table_insert (priv->transports,
2284         GINT_TO_POINTER (ct->interleaved.min), trans);
2285     g_object_ref (trans);
2286     g_hash_table_insert (priv->transports,
2287         GINT_TO_POINTER (ct->interleaved.max), trans);
2288     g_object_ref (trans);
2289   }
2290
2291   /* create and serialize the server transport */
2292   st = make_server_transport (client, media, ctx, ct);
2293   trans_str = gst_rtsp_transport_as_text (st);
2294   gst_rtsp_transport_free (st);
2295
2296   /* construct the response now */
2297   code = GST_RTSP_STS_OK;
2298   gst_rtsp_message_init_response (ctx->response, code,
2299       gst_rtsp_status_as_text (code), ctx->request);
2300
2301   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_TRANSPORT,
2302       trans_str);
2303   g_free (trans_str);
2304
2305   if (pipelined_request_id)
2306     gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_PIPELINED_REQUESTS,
2307         pipelined_request_id);
2308
2309   if (ctx->request->type_data.request.version >= GST_RTSP_VERSION_2_0) {
2310     GstClockTimeDiff seekable = gst_rtsp_media_seekable (media);
2311     GString *media_properties = g_string_new (NULL);
2312
2313     if (seekable == -1)
2314       g_string_append (media_properties,
2315           "No-Seeking,Time-Progressing,Time-Duration=0.0");
2316     else if (seekable == 0)
2317       g_string_append (media_properties, "Beginning-Only");
2318     else if (seekable == G_MAXINT64)
2319       g_string_append (media_properties, "Random-Access");
2320     else
2321       g_string_append_printf (media_properties,
2322           "Random-Access=%f, Unlimited, Immutable",
2323           (gdouble) seekable / GST_SECOND);
2324
2325     gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_MEDIA_PROPERTIES,
2326         g_string_free (media_properties, FALSE));
2327     /* TODO Check how Accept-Ranges should be filled */
2328     gst_rtsp_message_add_header (ctx->request, GST_RTSP_HDR_ACCEPT_RANGES,
2329         "npt, clock, smpte, clock");
2330   }
2331
2332   send_message (client, ctx, ctx->response, FALSE);
2333
2334   /* update the state */
2335   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
2336   switch (rtspstate) {
2337     case GST_RTSP_STATE_PLAYING:
2338     case GST_RTSP_STATE_RECORDING:
2339     case GST_RTSP_STATE_READY:
2340       /* no state change */
2341       break;
2342     default:
2343       gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_READY);
2344       break;
2345   }
2346   g_object_unref (media);
2347   g_object_unref (session);
2348   g_free (path);
2349
2350   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_SETUP_REQUEST], 0, ctx);
2351
2352   return TRUE;
2353
2354   /* ERRORS */
2355 no_uri:
2356   {
2357     GST_ERROR ("client %p: no uri", client);
2358     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2359     return FALSE;
2360   }
2361 no_transport:
2362   {
2363     GST_ERROR ("client %p: no transport", client);
2364     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
2365     goto cleanup_path;
2366   }
2367 no_pool:
2368   {
2369     GST_ERROR ("client %p: no session pool configured", client);
2370     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
2371     goto cleanup_path;
2372   }
2373 media_not_found_no_reply:
2374   {
2375     GST_ERROR ("client %p: media '%s' not found", client, path);
2376     /* error reply is already sent */
2377     goto cleanup_session;
2378   }
2379 media_not_found:
2380   {
2381     GST_ERROR ("client %p: media '%s' not found", client, path);
2382     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2383     goto cleanup_session;
2384   }
2385 control_not_found:
2386   {
2387     GST_ERROR ("client %p: no control in path '%s'", client, path);
2388     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2389     g_object_unref (media);
2390     goto cleanup_session;
2391   }
2392 stream_not_found:
2393   {
2394     GST_ERROR ("client %p: stream '%s' not found", client,
2395         GST_STR_NULL (control));
2396     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2397     g_object_unref (media);
2398     goto cleanup_session;
2399   }
2400 sig_failed:
2401   {
2402     GST_ERROR ("client %p: pre signal returned error: %s", client,
2403         gst_rtsp_status_as_text (sig_result));
2404     send_generic_response (client, sig_result, ctx);
2405     g_object_unref (media);
2406     goto cleanup_path;
2407   }
2408 service_unavailable:
2409   {
2410     GST_ERROR ("client %p: can't create session", client);
2411     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
2412     g_object_unref (media);
2413     goto cleanup_session;
2414   }
2415 sessmedia_unavailable:
2416   {
2417     GST_ERROR ("client %p: can't create session media", client);
2418     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
2419     goto cleanup_transport;
2420   }
2421 configure_media_failed_no_reply:
2422   {
2423     GST_ERROR ("client %p: configure_media failed", client);
2424     g_object_unref (media);
2425     /* error reply is already sent */
2426     goto cleanup_session;
2427   }
2428 unsupported_transports:
2429   {
2430     GST_ERROR ("client %p: unsupported transports", client);
2431     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
2432     goto cleanup_transport;
2433   }
2434 unsupported_client_transport:
2435   {
2436     GST_ERROR ("client %p: unsupported client transport", client);
2437     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
2438     goto cleanup_transport;
2439   }
2440 unsupported_mode:
2441   {
2442     GST_ERROR ("client %p: unsupported mode (media play: %d, media record: %d, "
2443         "mode play: %d, mode record: %d)", client,
2444         ! !(gst_rtsp_media_get_transport_mode (media) &
2445             GST_RTSP_TRANSPORT_MODE_PLAY),
2446         ! !(gst_rtsp_media_get_transport_mode (media) &
2447             GST_RTSP_TRANSPORT_MODE_RECORD), ct->mode_play, ct->mode_record);
2448     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
2449     goto cleanup_transport;
2450   }
2451 unsupported_range_unit:
2452   {
2453     GST_ERROR ("Client %p: does not support any range format we support",
2454         client);
2455     send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, ctx);
2456     goto cleanup_transport;
2457   }
2458 keymgmt_error:
2459   {
2460     GST_ERROR ("client %p: keymgmt error", client);
2461     send_generic_response (client, GST_RTSP_STS_KEY_MANAGEMENT_FAILURE, ctx);
2462     goto cleanup_transport;
2463   }
2464   {
2465   cleanup_transport:
2466     gst_rtsp_transport_free (ct);
2467     if (media)
2468       g_object_unref (media);
2469   cleanup_session:
2470     if (new_session)
2471       gst_rtsp_session_pool_remove (priv->session_pool, session);
2472     if (session)
2473       g_object_unref (session);
2474   cleanup_path:
2475     g_free (path);
2476     return FALSE;
2477   }
2478 }
2479
2480 static GstSDPMessage *
2481 create_sdp (GstRTSPClient * client, GstRTSPMedia * media)
2482 {
2483   GstRTSPClientPrivate *priv = client->priv;
2484   GstSDPMessage *sdp;
2485   GstSDPInfo info;
2486   const gchar *proto;
2487   guint64 session_id_tmp;
2488   gchar session_id[21];
2489
2490   gst_sdp_message_new (&sdp);
2491
2492   /* some standard things first */
2493   gst_sdp_message_set_version (sdp, "0");
2494
2495   if (priv->is_ipv6)
2496     proto = "IP6";
2497   else
2498     proto = "IP4";
2499
2500   session_id_tmp = (((guint64) g_random_int ()) << 32) | g_random_int ();
2501   g_snprintf (session_id, sizeof (session_id), "%" G_GUINT64_FORMAT,
2502       session_id_tmp);
2503
2504   gst_sdp_message_set_origin (sdp, "-", session_id, "1", "IN", proto,
2505       priv->server_ip);
2506
2507   gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
2508   gst_sdp_message_set_information (sdp, "rtsp-server");
2509   gst_sdp_message_add_time (sdp, "0", "0", NULL);
2510   gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
2511   gst_sdp_message_add_attribute (sdp, "type", "broadcast");
2512   gst_sdp_message_add_attribute (sdp, "control", "*");
2513
2514   info.is_ipv6 = priv->is_ipv6;
2515   info.server_ip = priv->server_ip;
2516
2517   /* create an SDP for the media object */
2518   if (!gst_rtsp_media_setup_sdp (media, sdp, &info))
2519     goto no_sdp;
2520
2521   return sdp;
2522
2523   /* ERRORS */
2524 no_sdp:
2525   {
2526     GST_ERROR ("client %p: could not create SDP", client);
2527     gst_sdp_message_free (sdp);
2528     return NULL;
2529   }
2530 }
2531
2532 /* for the describe we must generate an SDP */
2533 static gboolean
2534 handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
2535 {
2536   GstRTSPClientPrivate *priv = client->priv;
2537   GstRTSPResult res;
2538   GstSDPMessage *sdp;
2539   guint i;
2540   gchar *path, *str;
2541   GstRTSPMedia *media;
2542   GstRTSPClientClass *klass;
2543   GstRTSPStatusCode sig_result;
2544
2545   klass = GST_RTSP_CLIENT_GET_CLASS (client);
2546
2547   if (!ctx->uri)
2548     goto no_uri;
2549
2550   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_DESCRIBE_REQUEST],
2551       0, ctx, &sig_result);
2552   if (sig_result != GST_RTSP_STS_OK) {
2553     goto sig_failed;
2554   }
2555
2556   /* check what kind of format is accepted, we don't really do anything with it
2557    * and always return SDP for now. */
2558   for (i = 0;; i++) {
2559     gchar *accept;
2560
2561     res =
2562         gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_ACCEPT,
2563         &accept, i);
2564     if (res == GST_RTSP_ENOTIMPL)
2565       break;
2566
2567     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
2568       break;
2569   }
2570
2571   if (!priv->mount_points)
2572     goto no_mount_points;
2573
2574   if (!(path = gst_rtsp_mount_points_make_path (priv->mount_points, ctx->uri)))
2575     goto no_path;
2576
2577   /* find the media object for the uri */
2578   if (!(media = find_media (client, ctx, path, NULL)))
2579     goto no_media;
2580
2581   if (!(gst_rtsp_media_get_transport_mode (media) &
2582           GST_RTSP_TRANSPORT_MODE_PLAY))
2583     goto unsupported_mode;
2584
2585   /* create an SDP for the media object on this client */
2586   if (!(sdp = klass->create_sdp (client, media)))
2587     goto no_sdp;
2588
2589   /* we suspend after the describe */
2590   gst_rtsp_media_suspend (media);
2591   g_object_unref (media);
2592
2593   gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
2594       gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
2595
2596   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_CONTENT_TYPE,
2597       "application/sdp");
2598
2599   /* content base for some clients that might screw up creating the setup uri */
2600   str = make_base_url (client, ctx->uri, path);
2601   g_free (path);
2602
2603   GST_INFO ("adding content-base: %s", str);
2604   gst_rtsp_message_take_header (ctx->response, GST_RTSP_HDR_CONTENT_BASE, str);
2605
2606   /* add SDP to the response body */
2607   str = gst_sdp_message_as_text (sdp);
2608   gst_rtsp_message_take_body (ctx->response, (guint8 *) str, strlen (str));
2609   gst_sdp_message_free (sdp);
2610
2611   send_message (client, ctx, ctx->response, FALSE);
2612
2613   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_DESCRIBE_REQUEST],
2614       0, ctx);
2615
2616   return TRUE;
2617
2618   /* ERRORS */
2619 sig_failed:
2620   {
2621     GST_ERROR ("client %p: pre signal returned error: %s", client,
2622         gst_rtsp_status_as_text (sig_result));
2623     send_generic_response (client, sig_result, ctx);
2624     return FALSE;
2625   }
2626 no_uri:
2627   {
2628     GST_ERROR ("client %p: no uri", client);
2629     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2630     return FALSE;
2631   }
2632 no_mount_points:
2633   {
2634     GST_ERROR ("client %p: no mount points configured", client);
2635     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2636     return FALSE;
2637   }
2638 no_path:
2639   {
2640     GST_ERROR ("client %p: can't find path for url", client);
2641     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2642     return FALSE;
2643   }
2644 no_media:
2645   {
2646     GST_ERROR ("client %p: no media", client);
2647     g_free (path);
2648     /* error reply is already sent */
2649     return FALSE;
2650   }
2651 unsupported_mode:
2652   {
2653     GST_ERROR ("client %p: media does not support DESCRIBE", client);
2654     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
2655     g_free (path);
2656     g_object_unref (media);
2657     return FALSE;
2658   }
2659 no_sdp:
2660   {
2661     GST_ERROR ("client %p: can't create SDP", client);
2662     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
2663     g_free (path);
2664     g_object_unref (media);
2665     return FALSE;
2666   }
2667 }
2668
2669 static gboolean
2670 handle_sdp (GstRTSPClient * client, GstRTSPContext * ctx, GstRTSPMedia * media,
2671     GstSDPMessage * sdp)
2672 {
2673   GstRTSPClientPrivate *priv = client->priv;
2674   GstRTSPThread *thread;
2675
2676   /* create an SDP for the media object */
2677   if (!gst_rtsp_media_handle_sdp (media, sdp))
2678     goto unhandled_sdp;
2679
2680   thread = gst_rtsp_thread_pool_get_thread (priv->thread_pool,
2681       GST_RTSP_THREAD_TYPE_MEDIA, ctx);
2682   if (thread == NULL)
2683     goto no_thread;
2684
2685   /* prepare the media */
2686   if (!gst_rtsp_media_prepare (media, thread))
2687     goto no_prepare;
2688
2689   return TRUE;
2690
2691   /* ERRORS */
2692 unhandled_sdp:
2693   {
2694     GST_ERROR ("client %p: could not handle SDP", client);
2695     return FALSE;
2696   }
2697 no_thread:
2698   {
2699     GST_ERROR ("client %p: can't create thread", client);
2700     return FALSE;
2701   }
2702 no_prepare:
2703   {
2704     GST_ERROR ("client %p: can't prepare media", client);
2705     return FALSE;
2706   }
2707 }
2708
2709 static gboolean
2710 handle_announce_request (GstRTSPClient * client, GstRTSPContext * ctx)
2711 {
2712   GstRTSPClientPrivate *priv = client->priv;
2713   GstRTSPClientClass *klass;
2714   GstSDPResult sres;
2715   GstSDPMessage *sdp;
2716   GstRTSPMedia *media;
2717   gchar *path, *cont = NULL;
2718   guint8 *data;
2719   guint size;
2720   GstRTSPStatusCode sig_result;
2721
2722   klass = GST_RTSP_CLIENT_GET_CLASS (client);
2723
2724   if (!ctx->uri)
2725     goto no_uri;
2726
2727   if (!priv->mount_points)
2728     goto no_mount_points;
2729
2730   /* check if reply is SDP */
2731   gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_CONTENT_TYPE, &cont,
2732       0);
2733   /* could not be set but since the request returned OK, we assume it
2734    * was SDP, else check it. */
2735   if (cont) {
2736     if (g_ascii_strcasecmp (cont, "application/sdp") != 0)
2737       goto wrong_content_type;
2738   }
2739
2740   /* get message body and parse as SDP */
2741   gst_rtsp_message_get_body (ctx->request, &data, &size);
2742   if (data == NULL || size == 0)
2743     goto no_message;
2744
2745   GST_DEBUG ("client %p: parse SDP...", client);
2746   gst_sdp_message_new (&sdp);
2747   sres = gst_sdp_message_parse_buffer (data, size, sdp);
2748   if (sres != GST_SDP_OK)
2749     goto sdp_parse_failed;
2750
2751   if (!(path = gst_rtsp_mount_points_make_path (priv->mount_points, ctx->uri)))
2752     goto no_path;
2753
2754   /* find the media object for the uri */
2755   if (!(media = find_media (client, ctx, path, NULL)))
2756     goto no_media;
2757
2758   ctx->media = media;
2759
2760   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_ANNOUNCE_REQUEST],
2761       0, ctx, &sig_result);
2762   if (sig_result != GST_RTSP_STS_OK) {
2763     goto sig_failed;
2764   }
2765
2766   if (!(gst_rtsp_media_get_transport_mode (media) &
2767           GST_RTSP_TRANSPORT_MODE_RECORD))
2768     goto unsupported_mode;
2769
2770   /* Tell client subclass about the media */
2771   if (!klass->handle_sdp (client, ctx, media, sdp))
2772     goto unhandled_sdp;
2773
2774   /* we suspend after the announce */
2775   gst_rtsp_media_suspend (media);
2776   g_object_unref (media);
2777
2778   gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
2779       gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
2780
2781   send_message (client, ctx, ctx->response, FALSE);
2782
2783   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_ANNOUNCE_REQUEST],
2784       0, ctx);
2785
2786   gst_sdp_message_free (sdp);
2787   g_free (path);
2788   return TRUE;
2789
2790 no_uri:
2791   {
2792     GST_ERROR ("client %p: no uri", client);
2793     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2794     return FALSE;
2795   }
2796 no_mount_points:
2797   {
2798     GST_ERROR ("client %p: no mount points configured", client);
2799     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2800     return FALSE;
2801   }
2802 no_path:
2803   {
2804     GST_ERROR ("client %p: can't find path for url", client);
2805     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2806     gst_sdp_message_free (sdp);
2807     return FALSE;
2808   }
2809 wrong_content_type:
2810   {
2811     GST_ERROR ("client %p: unknown content type", client);
2812     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2813     return FALSE;
2814   }
2815 no_message:
2816   {
2817     GST_ERROR ("client %p: can't find SDP message", client);
2818     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2819     return FALSE;
2820   }
2821 sdp_parse_failed:
2822   {
2823     GST_ERROR ("client %p: failed to parse SDP message", client);
2824     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2825     gst_sdp_message_free (sdp);
2826     return FALSE;
2827   }
2828 no_media:
2829   {
2830     GST_ERROR ("client %p: no media", client);
2831     g_free (path);
2832     /* error reply is already sent */
2833     gst_sdp_message_free (sdp);
2834     return FALSE;
2835   }
2836 sig_failed:
2837   {
2838     GST_ERROR ("client %p: pre signal returned error: %s", client,
2839         gst_rtsp_status_as_text (sig_result));
2840     send_generic_response (client, sig_result, ctx);
2841     gst_sdp_message_free (sdp);
2842     return FALSE;
2843   }
2844 unsupported_mode:
2845   {
2846     GST_ERROR ("client %p: media does not support ANNOUNCE", client);
2847     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
2848     g_free (path);
2849     g_object_unref (media);
2850     gst_sdp_message_free (sdp);
2851     return FALSE;
2852   }
2853 unhandled_sdp:
2854   {
2855     GST_ERROR ("client %p: can't handle SDP", client);
2856     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_MEDIA_TYPE, ctx);
2857     g_free (path);
2858     g_object_unref (media);
2859     gst_sdp_message_free (sdp);
2860     return FALSE;
2861   }
2862 }
2863
2864 static gboolean
2865 handle_record_request (GstRTSPClient * client, GstRTSPContext * ctx)
2866 {
2867   GstRTSPSession *session;
2868   GstRTSPClientClass *klass;
2869   GstRTSPSessionMedia *sessmedia;
2870   GstRTSPMedia *media;
2871   GstRTSPUrl *uri;
2872   GstRTSPState rtspstate;
2873   gchar *path;
2874   gint matched;
2875   GstRTSPStatusCode sig_result;
2876   GPtrArray *transports;
2877
2878   if (!(session = ctx->session))
2879     goto no_session;
2880
2881   if (!(uri = ctx->uri))
2882     goto no_uri;
2883
2884   klass = GST_RTSP_CLIENT_GET_CLASS (client);
2885   path = klass->make_path_from_uri (client, uri);
2886
2887   /* get a handle to the configuration of the media in the session */
2888   sessmedia = gst_rtsp_session_get_media (session, path, &matched);
2889   if (!sessmedia)
2890     goto not_found;
2891
2892   if (path[matched] != '\0')
2893     goto no_aggregate;
2894
2895   g_free (path);
2896
2897   ctx->sessmedia = sessmedia;
2898   ctx->media = media = gst_rtsp_session_media_get_media (sessmedia);
2899
2900   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_RECORD_REQUEST], 0,
2901       ctx, &sig_result);
2902   if (sig_result != GST_RTSP_STS_OK) {
2903     goto sig_failed;
2904   }
2905
2906   if (!(gst_rtsp_media_get_transport_mode (media) &
2907           GST_RTSP_TRANSPORT_MODE_RECORD))
2908     goto unsupported_mode;
2909
2910   /* the session state must be playing or ready */
2911   rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
2912   if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
2913     goto invalid_state;
2914
2915   /* update the pipeline */
2916   transports = gst_rtsp_session_media_get_transports (sessmedia);
2917   if (!gst_rtsp_media_complete_pipeline (media, transports)) {
2918     g_ptr_array_unref (transports);
2919     goto pipeline_error;
2920   }
2921   g_ptr_array_unref (transports);
2922
2923   /* in record we first unsuspend, media could be suspended from SDP or PAUSED */
2924   if (!gst_rtsp_media_unsuspend (media))
2925     goto unsuspend_failed;
2926
2927   gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
2928       gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
2929
2930   send_message (client, ctx, ctx->response, FALSE);
2931
2932   /* start playing after sending the response */
2933   gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PLAYING);
2934
2935   gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_PLAYING);
2936
2937   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_RECORD_REQUEST], 0,
2938       ctx);
2939
2940   return TRUE;
2941
2942   /* ERRORS */
2943 no_session:
2944   {
2945     GST_ERROR ("client %p: no session", client);
2946     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
2947     return FALSE;
2948   }
2949 no_uri:
2950   {
2951     GST_ERROR ("client %p: no uri supplied", client);
2952     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
2953     return FALSE;
2954   }
2955 not_found:
2956   {
2957     GST_ERROR ("client %p: media not found", client);
2958     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
2959     return FALSE;
2960   }
2961 no_aggregate:
2962   {
2963     GST_ERROR ("client %p: no aggregate path %s", client, path);
2964     send_generic_response (client,
2965         GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
2966     g_free (path);
2967     return FALSE;
2968   }
2969 sig_failed:
2970   {
2971     GST_ERROR ("client %p: pre signal returned error: %s", client,
2972         gst_rtsp_status_as_text (sig_result));
2973     send_generic_response (client, sig_result, ctx);
2974     return FALSE;
2975   }
2976 unsupported_mode:
2977   {
2978     GST_ERROR ("client %p: media does not support RECORD", client);
2979     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
2980     return FALSE;
2981   }
2982 invalid_state:
2983   {
2984     GST_ERROR ("client %p: not PLAYING or READY", client);
2985     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
2986         ctx);
2987     return FALSE;
2988   }
2989 pipeline_error:
2990   {
2991     GST_ERROR ("client %p: failed to configure the pipeline", client);
2992     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
2993         ctx);
2994     return FALSE;
2995   }
2996 unsuspend_failed:
2997   {
2998     GST_ERROR ("client %p: unsuspend failed", client);
2999     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
3000     return FALSE;
3001   }
3002 }
3003
3004 static gboolean
3005 handle_options_request (GstRTSPClient * client, GstRTSPContext * ctx,
3006     GstRTSPVersion version)
3007 {
3008   GstRTSPMethod options;
3009   gchar *str;
3010   GstRTSPStatusCode sig_result;
3011
3012   options = GST_RTSP_DESCRIBE |
3013       GST_RTSP_OPTIONS |
3014       GST_RTSP_PAUSE |
3015       GST_RTSP_PLAY |
3016       GST_RTSP_SETUP |
3017       GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
3018
3019   if (version < GST_RTSP_VERSION_2_0) {
3020     options |= GST_RTSP_RECORD;
3021     options |= GST_RTSP_ANNOUNCE;
3022   }
3023
3024   str = gst_rtsp_options_as_text (options);
3025
3026   gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
3027       gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
3028
3029   gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_PUBLIC, str);
3030   g_free (str);
3031
3032   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_PRE_OPTIONS_REQUEST], 0,
3033       ctx, &sig_result);
3034   if (sig_result != GST_RTSP_STS_OK) {
3035     goto sig_failed;
3036   }
3037
3038   send_message (client, ctx, ctx->response, FALSE);
3039
3040   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_OPTIONS_REQUEST],
3041       0, ctx);
3042
3043   return TRUE;
3044
3045 /* ERRORS */
3046 sig_failed:
3047   {
3048     GST_ERROR ("client %p: pre signal returned error: %s", client,
3049         gst_rtsp_status_as_text (sig_result));
3050     send_generic_response (client, sig_result, ctx);
3051     gst_rtsp_message_free (ctx->response);
3052     return FALSE;
3053   }
3054 }
3055
3056 /* remove duplicate and trailing '/' */
3057 static void
3058 sanitize_uri (GstRTSPUrl * uri)
3059 {
3060   gint i, len;
3061   gchar *s, *d;
3062   gboolean have_slash, prev_slash;
3063
3064   s = d = uri->abspath;
3065   len = strlen (uri->abspath);
3066
3067   prev_slash = FALSE;
3068
3069   for (i = 0; i < len; i++) {
3070     have_slash = s[i] == '/';
3071     *d = s[i];
3072     if (!have_slash || !prev_slash)
3073       d++;
3074     prev_slash = have_slash;
3075   }
3076   len = d - uri->abspath;
3077   /* don't remove the first slash if that's the only thing left */
3078   if (len > 1 && *(d - 1) == '/')
3079     d--;
3080   *d = '\0';
3081 }
3082
3083 /* is called when the session is removed from its session pool. */
3084 static void
3085 client_session_removed (GstRTSPSessionPool * pool, GstRTSPSession * session,
3086     GstRTSPClient * client)
3087 {
3088   GstRTSPClientPrivate *priv = client->priv;
3089
3090   GST_INFO ("client %p: session %p removed", client, session);
3091
3092   g_mutex_lock (&priv->lock);
3093   if (priv->watch != NULL)
3094     gst_rtsp_watch_set_send_backlog (priv->watch, 0, 0);
3095   client_unwatch_session (client, session, NULL);
3096   if (priv->watch != NULL)
3097     gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);
3098   g_mutex_unlock (&priv->lock);
3099 }
3100
3101 /* Check for Require headers. Returns TRUE if there are no Require headers,
3102  * otherwise lets the application decide which headers are supported.
3103  * By default all headers are unsupported.
3104  * If there are unsupported options, FALSE will be returned together with
3105  * a newly-allocated string of (comma-separated) unsupported options in
3106  * the unsupported_reqs variable.
3107  *
3108  * There may be multiple Require headers, but we must send one single
3109  * Unsupported header with all the unsupported options as response. If
3110  * an incoming Require header contained a comma-separated list of options
3111  * GstRtspConnection will already have split that list up into multiple
3112  * headers.
3113  */
3114 static gboolean
3115 check_request_requirements (GstRTSPContext * ctx, gchar ** unsupported_reqs)
3116 {
3117   GstRTSPResult res;
3118   GPtrArray *arr = NULL;
3119   GstRTSPMessage *msg = ctx->request;
3120   gchar *reqs = NULL;
3121   gint i;
3122   gchar *sig_result = NULL;
3123   gboolean result = TRUE;
3124
3125   i = 0;
3126   do {
3127     res = gst_rtsp_message_get_header (msg, GST_RTSP_HDR_REQUIRE, &reqs, i++);
3128
3129     if (res == GST_RTSP_ENOTIMPL)
3130       break;
3131
3132     if (arr == NULL)
3133       arr = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
3134
3135     g_ptr_array_add (arr, g_strdup (reqs));
3136   }
3137   while (TRUE);
3138
3139   /* if we don't have any Require headers at all, all is fine */
3140   if (i == 1)
3141     return TRUE;
3142
3143   /* otherwise we've now processed at all the Require headers */
3144   g_ptr_array_add (arr, NULL);
3145
3146   g_signal_emit (ctx->client,
3147       gst_rtsp_client_signals[SIGNAL_CHECK_REQUIREMENTS], 0, ctx,
3148       (gchar **) arr->pdata, &sig_result);
3149
3150   if (sig_result == NULL) {
3151     /* no supported options, just report all of the required ones as
3152      * unsupported */
3153     *unsupported_reqs = g_strjoinv (", ", (gchar **) arr->pdata);
3154     result = FALSE;
3155     goto done;
3156   }
3157
3158   if (strlen (sig_result) == 0)
3159     g_free (sig_result);
3160   else {
3161     *unsupported_reqs = sig_result;
3162     result = FALSE;
3163   }
3164
3165 done:
3166   g_ptr_array_unref (arr);
3167   return result;
3168 }
3169
3170 static void
3171 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
3172 {
3173   GstRTSPClientPrivate *priv = client->priv;
3174   GstRTSPMethod method;
3175   const gchar *uristr;
3176   GstRTSPUrl *uri = NULL;
3177   GstRTSPVersion version;
3178   GstRTSPResult res;
3179   GstRTSPSession *session = NULL;
3180   GstRTSPContext sctx = { NULL }, *ctx;
3181   GstRTSPMessage response = { 0 };
3182   gchar *unsupported_reqs = NULL;
3183   gchar *sessid = NULL, *pipelined_request_id = NULL;
3184
3185   if (!(ctx = gst_rtsp_context_get_current ())) {
3186     ctx = &sctx;
3187     ctx->auth = priv->auth;
3188     gst_rtsp_context_push_current (ctx);
3189   }
3190
3191   ctx->conn = priv->connection;
3192   ctx->client = client;
3193   ctx->request = request;
3194   ctx->response = &response;
3195
3196   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
3197     gst_rtsp_message_dump (request);
3198   }
3199
3200   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
3201
3202   GST_INFO ("client %p: received a request %s %s %s", client,
3203       gst_rtsp_method_as_text (method), uristr,
3204       gst_rtsp_version_as_text (version));
3205
3206   /* we can only handle 1.0 requests */
3207   if (version != GST_RTSP_VERSION_1_0 && version != GST_RTSP_VERSION_2_0)
3208     goto not_supported;
3209
3210   ctx->method = method;
3211
3212   /* we always try to parse the url first */
3213   if (strcmp (uristr, "*") == 0) {
3214     /* special case where we have * as uri, keep uri = NULL */
3215   } else if (gst_rtsp_url_parse (uristr, &uri) != GST_RTSP_OK) {
3216     /* check if the uristr is an absolute path <=> scheme and host information
3217      * is missing */
3218     gchar *scheme;
3219
3220     scheme = g_uri_parse_scheme (uristr);
3221     if (scheme == NULL && g_str_has_prefix (uristr, "/")) {
3222       gchar *absolute_uristr = NULL;
3223
3224       GST_WARNING_OBJECT (client, "request doesn't contain absolute url");
3225       if (priv->server_ip == NULL) {
3226         GST_WARNING_OBJECT (client, "host information missing");
3227         goto bad_request;
3228       }
3229
3230       absolute_uristr =
3231           g_strdup_printf ("rtsp://%s%s", priv->server_ip, uristr);
3232
3233       GST_DEBUG_OBJECT (client, "absolute url: %s", absolute_uristr);
3234       if (gst_rtsp_url_parse (absolute_uristr, &uri) != GST_RTSP_OK) {
3235         g_free (absolute_uristr);
3236         goto bad_request;
3237       }
3238       g_free (absolute_uristr);
3239     } else {
3240       g_free (scheme);
3241       goto bad_request;
3242     }
3243   }
3244
3245   /* get the session if there is any */
3246   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_PIPELINED_REQUESTS,
3247       &pipelined_request_id, 0);
3248   if (res == GST_RTSP_OK) {
3249     sessid = g_hash_table_lookup (client->priv->pipelined_requests,
3250         pipelined_request_id);
3251
3252     if (!sessid)
3253       res = GST_RTSP_ERROR;
3254   }
3255
3256   if (res != GST_RTSP_OK)
3257     res =
3258         gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
3259
3260   if (res == GST_RTSP_OK) {
3261     if (priv->session_pool == NULL)
3262       goto no_pool;
3263
3264     /* we had a session in the request, find it again */
3265     if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
3266       goto session_not_found;
3267
3268     /* we add the session to the client list of watched sessions. When a session
3269      * disappears because it times out, we will be notified. If all sessions are
3270      * gone, we will close the connection */
3271     client_watch_session (client, session);
3272   }
3273
3274   /* sanitize the uri */
3275   if (uri)
3276     sanitize_uri (uri);
3277   ctx->uri = uri;
3278   ctx->session = session;
3279
3280   if (!gst_rtsp_auth_check (GST_RTSP_AUTH_CHECK_URL))
3281     goto not_authorized;
3282
3283   /* handle any 'Require' headers */
3284   if (!check_request_requirements (ctx, &unsupported_reqs))
3285     goto unsupported_requirement;
3286
3287   /* the backlog must be unlimited while processing requests.
3288    * the causes of this are two cases of deadlocks while streaming over TCP:
3289    *
3290    * 1. consider the scenario where the media pipeline's streaming thread
3291    * is blocking in the appsink (taking the appsink's preroll lock) because
3292    * the backlog is full. when a PAUSE request is received by the RTSP
3293    * client thread then the the state of the session media ought to change
3294    * to PAUSED. while most elements in the pipeline can change state this
3295    * can never happen for the appsink since its preroll lock is taken by
3296    * another thread.
3297    *
3298    * 2. consider the scenario where the media pipeline's streaming thread
3299    * is blocking in the appsink new_sample callback (taking the send lock
3300    * in RTSP client) because the backlog is full. when e.g. a GET request
3301    * is received by the RTSP client thread then a response ought to be sent
3302    * but this can never happen since it requires taking the send lock
3303    * already taken by another thread.
3304    *
3305    * the reason that the backlog is never emptied is that the source used
3306    * for dequeing messages from the backlog is never dispatched because it
3307    * is attached to the same mainloop as the source receving RTSP requests and
3308    * therefore run by the RTSP client thread which is alreayd blocking.
3309    *
3310    * without significant changes the easiest way to cope with this is to
3311    * not block indefinitely when the backlog is full, but rather let the
3312    * backlog grow in size. this in effect means that there can not be any
3313    * upper boundary on its size.
3314    */
3315   if (priv->watch != NULL)
3316     gst_rtsp_watch_set_send_backlog (priv->watch, 0, 0);
3317
3318   /* now see what is asked and dispatch to a dedicated handler */
3319   switch (method) {
3320     case GST_RTSP_OPTIONS:
3321       priv->version = version;
3322       handle_options_request (client, ctx, version);
3323       break;
3324     case GST_RTSP_DESCRIBE:
3325       handle_describe_request (client, ctx);
3326       break;
3327     case GST_RTSP_SETUP:
3328       handle_setup_request (client, ctx);
3329       break;
3330     case GST_RTSP_PLAY:
3331       handle_play_request (client, ctx);
3332       break;
3333     case GST_RTSP_PAUSE:
3334       handle_pause_request (client, ctx);
3335       break;
3336     case GST_RTSP_TEARDOWN:
3337       handle_teardown_request (client, ctx);
3338       break;
3339     case GST_RTSP_SET_PARAMETER:
3340       handle_set_param_request (client, ctx);
3341       break;
3342     case GST_RTSP_GET_PARAMETER:
3343       handle_get_param_request (client, ctx);
3344       break;
3345     case GST_RTSP_ANNOUNCE:
3346       if (version >= GST_RTSP_VERSION_2_0)
3347         goto invalid_command_for_version;
3348       handle_announce_request (client, ctx);
3349       break;
3350     case GST_RTSP_RECORD:
3351       if (version >= GST_RTSP_VERSION_2_0)
3352         goto invalid_command_for_version;
3353       handle_record_request (client, ctx);
3354       break;
3355     case GST_RTSP_REDIRECT:
3356       if (priv->watch != NULL)
3357         gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);
3358       goto not_implemented;
3359     case GST_RTSP_INVALID:
3360     default:
3361       if (priv->watch != NULL)
3362         gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);
3363       goto bad_request;
3364   }
3365
3366   if (priv->watch != NULL)
3367     gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);
3368
3369 done:
3370   if (ctx == &sctx)
3371     gst_rtsp_context_pop_current (ctx);
3372   if (session)
3373     g_object_unref (session);
3374   if (uri)
3375     gst_rtsp_url_free (uri);
3376   return;
3377
3378   /* ERRORS */
3379 not_supported:
3380   {
3381     GST_ERROR ("client %p: version %d not supported", client, version);
3382     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
3383         ctx);
3384     goto done;
3385   }
3386 invalid_command_for_version:
3387   {
3388     if (priv->watch != NULL)
3389       gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);
3390
3391     GST_ERROR ("client %p: invalid command for version", client);
3392     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
3393     goto done;
3394   }
3395 bad_request:
3396   {
3397     GST_ERROR ("client %p: bad request", client);
3398     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
3399     goto done;
3400   }
3401 no_pool:
3402   {
3403     GST_ERROR ("client %p: no pool configured", client);
3404     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
3405     goto done;
3406   }
3407 session_not_found:
3408   {
3409     GST_ERROR ("client %p: session not found", client);
3410     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
3411     goto done;
3412   }
3413 not_authorized:
3414   {
3415     GST_ERROR ("client %p: not allowed", client);
3416     /* error reply is already sent */
3417     goto done;
3418   }
3419 unsupported_requirement:
3420   {
3421     GST_ERROR ("client %p: Required option is not supported (%s)", client,
3422         unsupported_reqs);
3423     send_option_not_supported_response (client, ctx, unsupported_reqs);
3424     g_free (unsupported_reqs);
3425     goto done;
3426   }
3427 not_implemented:
3428   {
3429     GST_ERROR ("client %p: method %d not implemented", client, method);
3430     send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, ctx);
3431     goto done;
3432   }
3433 }
3434
3435
3436 static void
3437 handle_response (GstRTSPClient * client, GstRTSPMessage * response)
3438 {
3439   GstRTSPClientPrivate *priv = client->priv;
3440   GstRTSPResult res;
3441   GstRTSPSession *session = NULL;
3442   GstRTSPContext sctx = { NULL }, *ctx;
3443   gchar *sessid;
3444
3445   if (!(ctx = gst_rtsp_context_get_current ())) {
3446     ctx = &sctx;
3447     ctx->auth = priv->auth;
3448     gst_rtsp_context_push_current (ctx);
3449   }
3450
3451   ctx->conn = priv->connection;
3452   ctx->client = client;
3453   ctx->request = NULL;
3454   ctx->uri = NULL;
3455   ctx->method = GST_RTSP_INVALID;
3456   ctx->response = response;
3457
3458   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
3459     gst_rtsp_message_dump (response);
3460   }
3461
3462   GST_INFO ("client %p: received a response", client);
3463
3464   /* get the session if there is any */
3465   res =
3466       gst_rtsp_message_get_header (response, GST_RTSP_HDR_SESSION, &sessid, 0);
3467   if (res == GST_RTSP_OK) {
3468     if (priv->session_pool == NULL)
3469       goto no_pool;
3470
3471     /* we had a session in the request, find it again */
3472     if (!(session = gst_rtsp_session_pool_find (priv->session_pool, sessid)))
3473       goto session_not_found;
3474
3475     /* we add the session to the client list of watched sessions. When a session
3476      * disappears because it times out, we will be notified. If all sessions are
3477      * gone, we will close the connection */
3478     client_watch_session (client, session);
3479   }
3480
3481   ctx->session = session;
3482
3483   g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_HANDLE_RESPONSE],
3484       0, ctx);
3485
3486 done:
3487   if (ctx == &sctx)
3488     gst_rtsp_context_pop_current (ctx);
3489   if (session)
3490     g_object_unref (session);
3491   return;
3492
3493 no_pool:
3494   {
3495     GST_ERROR ("client %p: no pool configured", client);
3496     goto done;
3497   }
3498 session_not_found:
3499   {
3500     GST_ERROR ("client %p: session not found", client);
3501     goto done;
3502   }
3503 }
3504
3505 static void
3506 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
3507 {
3508   GstRTSPClientPrivate *priv = client->priv;
3509   GstRTSPResult res;
3510   guint8 channel;
3511   guint8 *data;
3512   guint size;
3513   GstBuffer *buffer;
3514   GstRTSPStreamTransport *trans;
3515
3516   /* find the stream for this message */
3517   res = gst_rtsp_message_parse_data (message, &channel);
3518   if (res != GST_RTSP_OK)
3519     return;
3520
3521   gst_rtsp_message_get_body (message, &data, &size);
3522   if (size < 2)
3523     goto invalid_length;
3524
3525   gst_rtsp_message_steal_body (message, &data, &size);
3526
3527   /* Strip trailing \0 (which GstRTSPConnection adds) */
3528   --size;
3529
3530   buffer = gst_buffer_new_wrapped (data, size);
3531
3532   trans =
3533       g_hash_table_lookup (priv->transports, GINT_TO_POINTER ((gint) channel));
3534   if (trans) {
3535     GSocketAddress *addr;
3536
3537     /* Only create the socket address once for the transport, we don't really
3538      * want to do that for every single packet.
3539      *
3540      * The netaddress meta is later used by the RTP stack to know where
3541      * packets came from and allows us to match it again to a stream transport
3542      *
3543      * In theory we could use the remote socket address of the RTSP connection
3544      * here, but this would fail with a custom configure_client_transport()
3545      * implementation.
3546      */
3547     if (!(addr =
3548             g_object_get_data (G_OBJECT (trans), "rtsp-client.remote-addr"))) {
3549       const GstRTSPTransport *tr;
3550       GInetAddress *iaddr;
3551
3552       tr = gst_rtsp_stream_transport_get_transport (trans);
3553       iaddr = g_inet_address_new_from_string (tr->destination);
3554       if (iaddr) {
3555         addr = g_inet_socket_address_new (iaddr, tr->client_port.min);
3556         g_object_unref (iaddr);
3557         g_object_set_data_full (G_OBJECT (trans), "rtsp-client.remote-addr",
3558             addr, (GDestroyNotify) g_object_unref);
3559       }
3560     }
3561
3562     if (addr) {
3563       gst_buffer_add_net_address_meta (buffer, addr);
3564     }
3565
3566     /* dispatch to the stream based on the channel number */
3567     GST_LOG_OBJECT (client, "%u bytes of data on channel %u", size, channel);
3568     gst_rtsp_stream_transport_recv_data (trans, channel, buffer);
3569   } else {
3570     GST_DEBUG_OBJECT (client, "received %u bytes of data for "
3571         "unknown channel %u", size, channel);
3572     gst_buffer_unref (buffer);
3573   }
3574
3575   return;
3576
3577 /* ERRORS */
3578 invalid_length:
3579   {
3580     GST_DEBUG ("client %p: Short message received, ignoring", client);
3581     return;
3582   }
3583 }
3584
3585 /**
3586  * gst_rtsp_client_set_session_pool:
3587  * @client: a #GstRTSPClient
3588  * @pool: (transfer none) (nullable): a #GstRTSPSessionPool
3589  *
3590  * Set @pool as the sessionpool for @client which it will use to find
3591  * or allocate sessions. the sessionpool is usually inherited from the server
3592  * that created the client but can be overridden later.
3593  */
3594 void
3595 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
3596     GstRTSPSessionPool * pool)
3597 {
3598   GstRTSPSessionPool *old;
3599   GstRTSPClientPrivate *priv;
3600
3601   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
3602
3603   priv = client->priv;
3604
3605   if (pool)
3606     g_object_ref (pool);
3607
3608   g_mutex_lock (&priv->lock);
3609   old = priv->session_pool;
3610   priv->session_pool = pool;
3611
3612   if (priv->session_removed_id) {
3613     g_signal_handler_disconnect (old, priv->session_removed_id);
3614     priv->session_removed_id = 0;
3615   }
3616   g_mutex_unlock (&priv->lock);
3617
3618   /* FIXME, should remove all sessions from the old pool for this client */
3619   if (old)
3620     g_object_unref (old);
3621 }
3622
3623 /**
3624  * gst_rtsp_client_get_session_pool:
3625  * @client: a #GstRTSPClient
3626  *
3627  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
3628  *
3629  * Returns: (transfer full) (nullable): a #GstRTSPSessionPool, unref after usage.
3630  */
3631 GstRTSPSessionPool *
3632 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
3633 {
3634   GstRTSPClientPrivate *priv;
3635   GstRTSPSessionPool *result;
3636
3637   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
3638
3639   priv = client->priv;
3640
3641   g_mutex_lock (&priv->lock);
3642   if ((result = priv->session_pool))
3643     g_object_ref (result);
3644   g_mutex_unlock (&priv->lock);
3645
3646   return result;
3647 }
3648
3649 /**
3650  * gst_rtsp_client_set_mount_points:
3651  * @client: a #GstRTSPClient
3652  * @mounts: (transfer none) (nullable): a #GstRTSPMountPoints
3653  *
3654  * Set @mounts as the mount points for @client which it will use to map urls
3655  * to media streams. These mount points are usually inherited from the server that
3656  * created the client but can be overriden later.
3657  */
3658 void
3659 gst_rtsp_client_set_mount_points (GstRTSPClient * client,
3660     GstRTSPMountPoints * mounts)
3661 {
3662   GstRTSPClientPrivate *priv;
3663   GstRTSPMountPoints *old;
3664
3665   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
3666
3667   priv = client->priv;
3668
3669   if (mounts)
3670     g_object_ref (mounts);
3671
3672   g_mutex_lock (&priv->lock);
3673   old = priv->mount_points;
3674   priv->mount_points = mounts;
3675   g_mutex_unlock (&priv->lock);
3676
3677   if (old)
3678     g_object_unref (old);
3679 }
3680
3681 /**
3682  * gst_rtsp_client_get_mount_points:
3683  * @client: a #GstRTSPClient
3684  *
3685  * Get the #GstRTSPMountPoints object that @client uses to manage its sessions.
3686  *
3687  * Returns: (transfer full) (nullable): a #GstRTSPMountPoints, unref after usage.
3688  */
3689 GstRTSPMountPoints *
3690 gst_rtsp_client_get_mount_points (GstRTSPClient * client)
3691 {
3692   GstRTSPClientPrivate *priv;
3693   GstRTSPMountPoints *result;
3694
3695   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
3696
3697   priv = client->priv;
3698
3699   g_mutex_lock (&priv->lock);
3700   if ((result = priv->mount_points))
3701     g_object_ref (result);
3702   g_mutex_unlock (&priv->lock);
3703
3704   return result;
3705 }
3706
3707 /**
3708  * gst_rtsp_client_set_auth:
3709  * @client: a #GstRTSPClient
3710  * @auth: (transfer none) (nullable): a #GstRTSPAuth
3711  *
3712  * configure @auth to be used as the authentication manager of @client.
3713  */
3714 void
3715 gst_rtsp_client_set_auth (GstRTSPClient * client, GstRTSPAuth * auth)
3716 {
3717   GstRTSPClientPrivate *priv;
3718   GstRTSPAuth *old;
3719
3720   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
3721
3722   priv = client->priv;
3723
3724   if (auth)
3725     g_object_ref (auth);
3726
3727   g_mutex_lock (&priv->lock);
3728   old = priv->auth;
3729   priv->auth = auth;
3730   g_mutex_unlock (&priv->lock);
3731
3732   if (old)
3733     g_object_unref (old);
3734 }
3735
3736
3737 /**
3738  * gst_rtsp_client_get_auth:
3739  * @client: a #GstRTSPClient
3740  *
3741  * Get the #GstRTSPAuth used as the authentication manager of @client.
3742  *
3743  * Returns: (transfer full) (nullable): the #GstRTSPAuth of @client.
3744  * g_object_unref() after usage.
3745  */
3746 GstRTSPAuth *
3747 gst_rtsp_client_get_auth (GstRTSPClient * client)
3748 {
3749   GstRTSPClientPrivate *priv;
3750   GstRTSPAuth *result;
3751
3752   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
3753
3754   priv = client->priv;
3755
3756   g_mutex_lock (&priv->lock);
3757   if ((result = priv->auth))
3758     g_object_ref (result);
3759   g_mutex_unlock (&priv->lock);
3760
3761   return result;
3762 }
3763
3764 /**
3765  * gst_rtsp_client_set_thread_pool:
3766  * @client: a #GstRTSPClient
3767  * @pool: (transfer none) (nullable): a #GstRTSPThreadPool
3768  *
3769  * configure @pool to be used as the thread pool of @client.
3770  */
3771 void
3772 gst_rtsp_client_set_thread_pool (GstRTSPClient * client,
3773     GstRTSPThreadPool * pool)
3774 {
3775   GstRTSPClientPrivate *priv;
3776   GstRTSPThreadPool *old;
3777
3778   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
3779
3780   priv = client->priv;
3781
3782   if (pool)
3783     g_object_ref (pool);
3784
3785   g_mutex_lock (&priv->lock);
3786   old = priv->thread_pool;
3787   priv->thread_pool = pool;
3788   g_mutex_unlock (&priv->lock);
3789
3790   if (old)
3791     g_object_unref (old);
3792 }
3793
3794 /**
3795  * gst_rtsp_client_get_thread_pool:
3796  * @client: a #GstRTSPClient
3797  *
3798  * Get the #GstRTSPThreadPool used as the thread pool of @client.
3799  *
3800  * Returns: (transfer full) (nullable): the #GstRTSPThreadPool of @client. g_object_unref() after
3801  * usage.
3802  */
3803 GstRTSPThreadPool *
3804 gst_rtsp_client_get_thread_pool (GstRTSPClient * client)
3805 {
3806   GstRTSPClientPrivate *priv;
3807   GstRTSPThreadPool *result;
3808
3809   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
3810
3811   priv = client->priv;
3812
3813   g_mutex_lock (&priv->lock);
3814   if ((result = priv->thread_pool))
3815     g_object_ref (result);
3816   g_mutex_unlock (&priv->lock);
3817
3818   return result;
3819 }
3820
3821 /**
3822  * gst_rtsp_client_set_connection:
3823  * @client: a #GstRTSPClient
3824  * @conn: (transfer full): a #GstRTSPConnection
3825  *
3826  * Set the #GstRTSPConnection of @client. This function takes ownership of
3827  * @conn.
3828  *
3829  * Returns: %TRUE on success.
3830  */
3831 gboolean
3832 gst_rtsp_client_set_connection (GstRTSPClient * client,
3833     GstRTSPConnection * conn)
3834 {
3835   GstRTSPClientPrivate *priv;
3836   GSocket *read_socket;
3837   GSocketAddress *address;
3838   GstRTSPUrl *url;
3839   GError *error = NULL;
3840
3841   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
3842   g_return_val_if_fail (conn != NULL, FALSE);
3843
3844   priv = client->priv;
3845
3846   read_socket = gst_rtsp_connection_get_read_socket (conn);
3847
3848   if (!(address = g_socket_get_local_address (read_socket, &error)))
3849     goto no_address;
3850
3851   g_free (priv->server_ip);
3852   /* keep the original ip that the client connected to */
3853   if (G_IS_INET_SOCKET_ADDRESS (address)) {
3854     GInetAddress *iaddr;
3855
3856     iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address));
3857
3858     /* socket might be ipv6 but adress still ipv4 */
3859     priv->is_ipv6 = g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV6;
3860     priv->server_ip = g_inet_address_to_string (iaddr);
3861     g_object_unref (address);
3862   } else {
3863     priv->is_ipv6 = g_socket_get_family (read_socket) == G_SOCKET_FAMILY_IPV6;
3864     priv->server_ip = g_strdup ("unknown");
3865   }
3866
3867   GST_INFO ("client %p connected to server ip %s, ipv6 = %d", client,
3868       priv->server_ip, priv->is_ipv6);
3869
3870   url = gst_rtsp_connection_get_url (conn);
3871   GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
3872
3873   priv->connection = conn;
3874
3875   return TRUE;
3876
3877   /* ERRORS */
3878 no_address:
3879   {
3880     GST_ERROR ("could not get local address %s", error->message);
3881     g_error_free (error);
3882     return FALSE;
3883   }
3884 }
3885
3886 /**
3887  * gst_rtsp_client_get_connection:
3888  * @client: a #GstRTSPClient
3889  *
3890  * Get the #GstRTSPConnection of @client.
3891  *
3892  * Returns: (transfer none) (nullable): the #GstRTSPConnection of @client.
3893  * The connection object returned remains valid until the client is freed.
3894  */
3895 GstRTSPConnection *
3896 gst_rtsp_client_get_connection (GstRTSPClient * client)
3897 {
3898   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
3899
3900   return client->priv->connection;
3901 }
3902
3903 /**
3904  * gst_rtsp_client_set_send_func:
3905  * @client: a #GstRTSPClient
3906  * @func: (scope notified): a #GstRTSPClientSendFunc
3907  * @user_data: (closure): user data passed to @func
3908  * @notify: (allow-none): called when @user_data is no longer in use
3909  *
3910  * Set @func as the callback that will be called when a new message needs to be
3911  * sent to the client. @user_data is passed to @func and @notify is called when
3912  * @user_data is no longer in use.
3913  *
3914  * By default, the client will send the messages on the #GstRTSPConnection that
3915  * was configured with gst_rtsp_client_attach() was called.
3916  */
3917 void
3918 gst_rtsp_client_set_send_func (GstRTSPClient * client,
3919     GstRTSPClientSendFunc func, gpointer user_data, GDestroyNotify notify)
3920 {
3921   GstRTSPClientPrivate *priv;
3922   GDestroyNotify old_notify;
3923   gpointer old_data;
3924
3925   g_return_if_fail (GST_IS_RTSP_CLIENT (client));
3926
3927   priv = client->priv;
3928
3929   g_mutex_lock (&priv->send_lock);
3930   priv->send_func = func;
3931   old_notify = priv->send_notify;
3932   old_data = priv->send_data;
3933   priv->send_notify = notify;
3934   priv->send_data = user_data;
3935   g_mutex_unlock (&priv->send_lock);
3936
3937   if (old_notify)
3938     old_notify (old_data);
3939 }
3940
3941 /**
3942  * gst_rtsp_client_handle_message:
3943  * @client: a #GstRTSPClient
3944  * @message: (transfer none): an #GstRTSPMessage
3945  *
3946  * Let the client handle @message.
3947  *
3948  * Returns: a #GstRTSPResult.
3949  */
3950 GstRTSPResult
3951 gst_rtsp_client_handle_message (GstRTSPClient * client,
3952     GstRTSPMessage * message)
3953 {
3954   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
3955   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3956
3957   switch (message->type) {
3958     case GST_RTSP_MESSAGE_REQUEST:
3959       handle_request (client, message);
3960       break;
3961     case GST_RTSP_MESSAGE_RESPONSE:
3962       handle_response (client, message);
3963       break;
3964     case GST_RTSP_MESSAGE_DATA:
3965       handle_data (client, message);
3966       break;
3967     default:
3968       break;
3969   }
3970   return GST_RTSP_OK;
3971 }
3972
3973 /**
3974  * gst_rtsp_client_send_message:
3975  * @client: a #GstRTSPClient
3976  * @session: (allow-none) (transfer none): a #GstRTSPSession to send
3977  *   the message to or %NULL
3978  * @message: (transfer none): The #GstRTSPMessage to send
3979  *
3980  * Send a message message to the remote end. @message must be a
3981  * #GST_RTSP_MESSAGE_REQUEST or a #GST_RTSP_MESSAGE_RESPONSE.
3982  */
3983 GstRTSPResult
3984 gst_rtsp_client_send_message (GstRTSPClient * client, GstRTSPSession * session,
3985     GstRTSPMessage * message)
3986 {
3987   GstRTSPContext sctx = { NULL }
3988   , *ctx;
3989   GstRTSPClientPrivate *priv;
3990
3991   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), GST_RTSP_EINVAL);
3992   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3993   g_return_val_if_fail (message->type == GST_RTSP_MESSAGE_REQUEST ||
3994       message->type == GST_RTSP_MESSAGE_RESPONSE, GST_RTSP_EINVAL);
3995
3996   priv = client->priv;
3997
3998   if (!(ctx = gst_rtsp_context_get_current ())) {
3999     ctx = &sctx;
4000     ctx->auth = priv->auth;
4001     gst_rtsp_context_push_current (ctx);
4002   }
4003
4004   ctx->conn = priv->connection;
4005   ctx->client = client;
4006   ctx->session = session;
4007
4008   send_message (client, ctx, message, FALSE);
4009
4010   if (ctx == &sctx)
4011     gst_rtsp_context_pop_current (ctx);
4012
4013   return GST_RTSP_OK;
4014 }
4015
4016 static gboolean
4017 do_send_message (GstRTSPClient * client, GstRTSPMessage * message,
4018     gboolean close, gpointer user_data)
4019 {
4020   GstRTSPClientPrivate *priv = client->priv;
4021   GstRTSPResult ret;
4022   GTimeVal time;
4023
4024   time.tv_sec = 1;
4025   time.tv_usec = 0;
4026
4027   do {
4028     /* send the response and store the seq number so we can wait until it's
4029      * written to the client to close the connection */
4030     ret =
4031         gst_rtsp_watch_send_message (priv->watch, message,
4032         close ? &priv->close_seq : NULL);
4033     if (ret == GST_RTSP_OK)
4034       break;
4035
4036     if (ret != GST_RTSP_ENOMEM)
4037       goto error;
4038
4039     /* drop backlog */
4040     if (priv->drop_backlog)
4041       break;
4042
4043     /* queue was full, wait for more space */
4044     GST_DEBUG_OBJECT (client, "waiting for backlog");
4045     ret = gst_rtsp_watch_wait_backlog (priv->watch, &time);
4046     GST_DEBUG_OBJECT (client, "Resend due to backlog full");
4047   } while (ret != GST_RTSP_EINTR);
4048
4049   return ret == GST_RTSP_OK;
4050
4051   /* ERRORS */
4052 error:
4053   {
4054     GST_DEBUG_OBJECT (client, "got error %d", ret);
4055     return ret == GST_RTSP_OK;
4056   }
4057 }
4058
4059 static GstRTSPResult
4060 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
4061     gpointer user_data)
4062 {
4063   return gst_rtsp_client_handle_message (GST_RTSP_CLIENT (user_data), message);
4064 }
4065
4066 static GstRTSPResult
4067 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
4068 {
4069   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
4070   GstRTSPClientPrivate *priv = client->priv;
4071
4072   if (priv->close_seq && priv->close_seq == cseq) {
4073     GST_INFO ("client %p: send close message", client);
4074     priv->close_seq = 0;
4075     gst_rtsp_client_close (client);
4076   }
4077
4078   return GST_RTSP_OK;
4079 }
4080
4081 static GstRTSPResult
4082 closed (GstRTSPWatch * watch, gpointer user_data)
4083 {
4084   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
4085   GstRTSPClientPrivate *priv = client->priv;
4086   const gchar *tunnelid;
4087
4088   GST_INFO ("client %p: connection closed", client);
4089
4090   if ((tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection))) {
4091     g_mutex_lock (&tunnels_lock);
4092     /* remove from tunnelids */
4093     g_hash_table_remove (tunnels, tunnelid);
4094     g_mutex_unlock (&tunnels_lock);
4095   }
4096
4097   gst_rtsp_watch_set_flushing (watch, TRUE);
4098   g_mutex_lock (&priv->watch_lock);
4099   gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
4100   g_mutex_unlock (&priv->watch_lock);
4101
4102   return GST_RTSP_OK;
4103 }
4104
4105 static GstRTSPResult
4106 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
4107 {
4108   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
4109   gchar *str;
4110
4111   str = gst_rtsp_strresult (result);
4112   GST_INFO ("client %p: received an error %s", client, str);
4113   g_free (str);
4114
4115   return GST_RTSP_OK;
4116 }
4117
4118 static GstRTSPResult
4119 error_full (GstRTSPWatch * watch, GstRTSPResult result,
4120     GstRTSPMessage * message, guint id, gpointer user_data)
4121 {
4122   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
4123   gchar *str;
4124
4125   str = gst_rtsp_strresult (result);
4126   GST_INFO
4127       ("client %p: error when handling message %p with id %d: %s",
4128       client, message, id, str);
4129   g_free (str);
4130
4131   return GST_RTSP_OK;
4132 }
4133
4134 static gboolean
4135 remember_tunnel (GstRTSPClient * client)
4136 {
4137   GstRTSPClientPrivate *priv = client->priv;
4138   const gchar *tunnelid;
4139
4140   /* store client in the pending tunnels */
4141   tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
4142   if (tunnelid == NULL)
4143     goto no_tunnelid;
4144
4145   GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
4146
4147   /* we can't have two clients connecting with the same tunnelid */
4148   g_mutex_lock (&tunnels_lock);
4149   if (g_hash_table_lookup (tunnels, tunnelid))
4150     goto tunnel_existed;
4151
4152   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
4153   g_mutex_unlock (&tunnels_lock);
4154
4155   return TRUE;
4156
4157   /* ERRORS */
4158 no_tunnelid:
4159   {
4160     GST_ERROR ("client %p: no tunnelid provided", client);
4161     return FALSE;
4162   }
4163 tunnel_existed:
4164   {
4165     g_mutex_unlock (&tunnels_lock);
4166     GST_ERROR ("client %p: tunnel session %s already existed", client,
4167         tunnelid);
4168     return FALSE;
4169   }
4170 }
4171
4172 static GstRTSPResult
4173 tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
4174 {
4175   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
4176   GstRTSPClientPrivate *priv = client->priv;
4177
4178   GST_WARNING ("client %p: tunnel lost (connection %p)", client,
4179       priv->connection);
4180
4181   /* ignore error, it'll only be a problem when the client does a POST again */
4182   remember_tunnel (client);
4183
4184   return GST_RTSP_OK;
4185 }
4186
4187 static GstRTSPStatusCode
4188 handle_tunnel (GstRTSPClient * client)
4189 {
4190   GstRTSPClientPrivate *priv = client->priv;
4191   GstRTSPClient *oclient;
4192   GstRTSPClientPrivate *opriv;
4193   const gchar *tunnelid;
4194
4195   tunnelid = gst_rtsp_connection_get_tunnelid (priv->connection);
4196   if (tunnelid == NULL)
4197     goto no_tunnelid;
4198
4199   /* check for previous tunnel */
4200   g_mutex_lock (&tunnels_lock);
4201   oclient = g_hash_table_lookup (tunnels, tunnelid);
4202
4203   if (oclient == NULL) {
4204     /* no previous tunnel, remember tunnel */
4205     g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
4206     g_mutex_unlock (&tunnels_lock);
4207
4208     GST_INFO ("client %p: no previous tunnel found, remembering tunnel (%p)",
4209         client, priv->connection);
4210   } else {
4211     /* merge both tunnels into the first client */
4212     /* remove the old client from the table. ref before because removing it will
4213      * remove the ref to it. */
4214     g_object_ref (oclient);
4215     g_hash_table_remove (tunnels, tunnelid);
4216     g_mutex_unlock (&tunnels_lock);
4217
4218     opriv = oclient->priv;
4219
4220     g_mutex_lock (&opriv->watch_lock);
4221     if (opriv->watch == NULL)
4222       goto tunnel_closed;
4223     if (opriv->tstate == priv->tstate)
4224       goto tunnel_duplicate_id;
4225
4226     GST_INFO ("client %p: found previous tunnel %p (old %p, new %p)", client,
4227         oclient, opriv->connection, priv->connection);
4228
4229     gst_rtsp_connection_do_tunnel (opriv->connection, priv->connection);
4230     gst_rtsp_watch_reset (priv->watch);
4231     gst_rtsp_watch_reset (opriv->watch);
4232     g_mutex_unlock (&opriv->watch_lock);
4233     g_object_unref (oclient);
4234
4235     /* the old client owns the tunnel now, the new one will be freed */
4236     g_source_destroy ((GSource *) priv->watch);
4237     priv->watch = NULL;
4238     gst_rtsp_client_set_send_func (client, NULL, NULL, NULL);
4239   }
4240
4241   return GST_RTSP_STS_OK;
4242
4243   /* ERRORS */
4244 no_tunnelid:
4245   {
4246     GST_ERROR ("client %p: no tunnelid provided", client);
4247     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
4248   }
4249 tunnel_closed:
4250   {
4251     GST_ERROR ("client %p: tunnel session %s was closed", client, tunnelid);
4252     g_mutex_unlock (&opriv->watch_lock);
4253     g_object_unref (oclient);
4254     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
4255   }
4256 tunnel_duplicate_id:
4257   {
4258     GST_ERROR ("client %p: tunnel session %s was duplicate", client, tunnelid);
4259     g_mutex_unlock (&opriv->watch_lock);
4260     g_object_unref (oclient);
4261     return GST_RTSP_STS_BAD_REQUEST;
4262   }
4263 }
4264
4265 static GstRTSPStatusCode
4266 tunnel_get (GstRTSPWatch * watch, gpointer user_data)
4267 {
4268   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
4269
4270   GST_INFO ("client %p: tunnel get (connection %p)", client,
4271       client->priv->connection);
4272
4273   g_mutex_lock (&client->priv->lock);
4274   client->priv->tstate = TUNNEL_STATE_GET;
4275   g_mutex_unlock (&client->priv->lock);
4276
4277   return handle_tunnel (client);
4278 }
4279
4280 static GstRTSPResult
4281 tunnel_post (GstRTSPWatch * watch, gpointer user_data)
4282 {
4283   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
4284
4285   GST_INFO ("client %p: tunnel post (connection %p)", client,
4286       client->priv->connection);
4287
4288   g_mutex_lock (&client->priv->lock);
4289   client->priv->tstate = TUNNEL_STATE_POST;
4290   g_mutex_unlock (&client->priv->lock);
4291
4292   if (handle_tunnel (client) != GST_RTSP_STS_OK)
4293     return GST_RTSP_ERROR;
4294
4295   return GST_RTSP_OK;
4296 }
4297
4298 static GstRTSPResult
4299 tunnel_http_response (GstRTSPWatch * watch, GstRTSPMessage * request,
4300     GstRTSPMessage * response, gpointer user_data)
4301 {
4302   GstRTSPClientClass *klass;
4303
4304   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
4305   klass = GST_RTSP_CLIENT_GET_CLASS (client);
4306
4307   if (klass->tunnel_http_response) {
4308     klass->tunnel_http_response (client, request, response);
4309   }
4310
4311   return GST_RTSP_OK;
4312 }
4313
4314 static GstRTSPWatchFuncs watch_funcs = {
4315   message_received,
4316   message_sent,
4317   closed,
4318   error,
4319   tunnel_get,
4320   tunnel_post,
4321   error_full,
4322   tunnel_lost,
4323   tunnel_http_response
4324 };
4325
4326 static void
4327 client_watch_notify (GstRTSPClient * client)
4328 {
4329   GstRTSPClientPrivate *priv = client->priv;
4330   gboolean closed = TRUE;
4331
4332   GST_INFO ("client %p: watch destroyed", client);
4333   priv->watch = NULL;
4334   /* remove all sessions if the media says so and so drop the extra client ref */
4335   rtsp_ctrl_timeout_remove (priv);
4336   gst_rtsp_client_session_filter (client, cleanup_session, &closed);
4337   if (closed)
4338     g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_CLOSED], 0, NULL);
4339   g_object_unref (client);
4340 }
4341
4342 /**
4343  * gst_rtsp_client_attach:
4344  * @client: a #GstRTSPClient
4345  * @context: (allow-none): a #GMainContext
4346  *
4347  * Attaches @client to @context. When the mainloop for @context is run, the
4348  * client will be dispatched. When @context is %NULL, the default context will be
4349  * used).
4350  *
4351  * This function should be called when the client properties and urls are fully
4352  * configured and the client is ready to start.
4353  *
4354  * Returns: the ID (greater than 0) for the source within the GMainContext.
4355  */
4356 guint
4357 gst_rtsp_client_attach (GstRTSPClient * client, GMainContext * context)
4358 {
4359   GstRTSPClientPrivate *priv;
4360   GSource *timer_src;
4361   guint res;
4362
4363   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), 0);
4364   priv = client->priv;
4365   g_return_val_if_fail (priv->connection != NULL, 0);
4366   g_return_val_if_fail (priv->watch == NULL, 0);
4367
4368   /* make sure noone will free the context before the watch is destroyed */
4369   priv->watch_context = g_main_context_ref (context);
4370
4371   /* create watch for the connection and attach */
4372   priv->watch = gst_rtsp_watch_new (priv->connection, &watch_funcs,
4373       g_object_ref (client), (GDestroyNotify) client_watch_notify);
4374   gst_rtsp_client_set_send_func (client, do_send_message, priv->watch,
4375       (GDestroyNotify) gst_rtsp_watch_unref);
4376
4377   gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);
4378
4379   GST_INFO ("client %p: attaching to context %p", client, context);
4380   res = gst_rtsp_watch_attach (priv->watch, context);
4381
4382   /* Setting up a timeout for the RTSP control channel until a session
4383    * is up where it is handling timeouts. */
4384   rtsp_ctrl_timeout_remove (priv);      /* removing old if any */
4385   g_mutex_lock (&priv->lock);
4386
4387   timer_src = g_timeout_source_new_seconds (RTSP_CTRL_CB_INTERVAL);
4388   g_source_set_callback (timer_src, rtsp_ctrl_timeout_cb, client, NULL);
4389   priv->rtsp_ctrl_timeout_id = g_source_attach (timer_src, priv->watch_context);
4390   g_source_unref (timer_src);
4391   GST_DEBUG ("rtsp control setting up session timeout id=%u.",
4392       priv->rtsp_ctrl_timeout_id);
4393
4394   g_mutex_unlock (&priv->lock);
4395
4396   return res;
4397 }
4398
4399 /**
4400  * gst_rtsp_client_session_filter:
4401  * @client: a #GstRTSPClient
4402  * @func: (scope call) (allow-none): a callback
4403  * @user_data: user data passed to @func
4404  *
4405  * Call @func for each session managed by @client. The result value of @func
4406  * determines what happens to the session. @func will be called with @client
4407  * locked so no further actions on @client can be performed from @func.
4408  *
4409  * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be removed from
4410  * @client.
4411  *
4412  * If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @client.
4413  *
4414  * If @func returns #GST_RTSP_FILTER_REF, the session will remain in @client but
4415  * will also be added with an additional ref to the result #GList of this
4416  * function..
4417  *
4418  * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for each session.
4419  *
4420  * Returns: (element-type GstRTSPSession) (transfer full): a #GList with all
4421  * sessions for which @func returned #GST_RTSP_FILTER_REF. After usage, each
4422  * element in the #GList should be unreffed before the list is freed.
4423  */
4424 GList *
4425 gst_rtsp_client_session_filter (GstRTSPClient * client,
4426     GstRTSPClientSessionFilterFunc func, gpointer user_data)
4427 {
4428   GstRTSPClientPrivate *priv;
4429   GList *result, *walk, *next;
4430   GHashTable *visited;
4431   guint cookie;
4432
4433   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), NULL);
4434
4435   priv = client->priv;
4436
4437   result = NULL;
4438   if (func)
4439     visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
4440
4441   g_mutex_lock (&priv->lock);
4442 restart:
4443   cookie = priv->sessions_cookie;
4444   for (walk = priv->sessions; walk; walk = next) {
4445     GstRTSPSession *sess = walk->data;
4446     GstRTSPFilterResult res;
4447     gboolean changed;
4448
4449     next = g_list_next (walk);
4450
4451     if (func) {
4452       /* only visit each session once */
4453       if (g_hash_table_contains (visited, sess))
4454         continue;
4455
4456       g_hash_table_add (visited, g_object_ref (sess));
4457       g_mutex_unlock (&priv->lock);
4458
4459       res = func (client, sess, user_data);
4460
4461       g_mutex_lock (&priv->lock);
4462     } else
4463       res = GST_RTSP_FILTER_REF;
4464
4465     changed = (cookie != priv->sessions_cookie);
4466
4467     switch (res) {
4468       case GST_RTSP_FILTER_REMOVE:
4469         /* stop watching the session and pretend it went away, if the list was
4470          * changed, we can't use the current list position, try to see if we
4471          * still have the session */
4472         client_unwatch_session (client, sess, changed ? NULL : walk);
4473         cookie = priv->sessions_cookie;
4474         break;
4475       case GST_RTSP_FILTER_REF:
4476         result = g_list_prepend (result, g_object_ref (sess));
4477         break;
4478       case GST_RTSP_FILTER_KEEP:
4479       default:
4480         break;
4481     }
4482     if (changed)
4483       goto restart;
4484   }
4485   g_mutex_unlock (&priv->lock);
4486
4487   if (func)
4488     g_hash_table_unref (visited);
4489
4490   return result;
4491 }