Only free the pending tunnel if there is one
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-client.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <sys/ioctl.h>
21
22 #include "rtsp-client.h"
23 #include "rtsp-sdp.h"
24
25 #undef DEBUG
26
27 #define DEFAULT_TIMEOUT  60
28
29 static GMutex *tunnels_lock;
30 static GHashTable *tunnels;
31
32 enum
33 {
34   PROP_0,
35   PROP_TIMEOUT,
36   PROP_SESSION_POOL,
37   PROP_MEDIA_MAPPING,
38   PROP_LAST
39 };
40
41 static void gst_rtsp_client_get_property (GObject *object, guint propid,
42     GValue *value, GParamSpec *pspec);
43 static void gst_rtsp_client_set_property (GObject *object, guint propid,
44     const GValue *value, GParamSpec *pspec);
45 static void gst_rtsp_client_finalize (GObject * obj);
46
47 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
48
49 static void
50 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
51 {
52   GObjectClass *gobject_class;
53
54   gobject_class = G_OBJECT_CLASS (klass);
55
56   gobject_class->get_property = gst_rtsp_client_get_property;
57   gobject_class->set_property = gst_rtsp_client_set_property;
58   gobject_class->finalize = gst_rtsp_client_finalize;
59
60   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
61       g_param_spec_uint ("timeout", "Timeout", "The client timeout",
62           0, G_MAXUINT, DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
63
64   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
65       g_param_spec_object ("session-pool", "Session Pool",
66           "The session pool to use for client session",
67           GST_TYPE_RTSP_SESSION_POOL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
68
69   g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
70       g_param_spec_object ("media-mapping", "Media Mapping",
71           "The media mapping to use for client session",
72           GST_TYPE_RTSP_MEDIA_MAPPING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
73
74   tunnels = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
75   tunnels_lock = g_mutex_new ();
76 }
77
78 static void
79 gst_rtsp_client_init (GstRTSPClient * client)
80 {
81   client->timeout = DEFAULT_TIMEOUT;
82 }
83
84 /* A client is finalized when the connection is broken */
85 static void
86 gst_rtsp_client_finalize (GObject * obj)
87 {
88   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
89
90   g_message ("finalize client %p", client);
91
92   gst_rtsp_connection_free (client->connection);
93   if (client->session_pool)
94     g_object_unref (client->session_pool);
95   if (client->media_mapping)
96     g_object_unref (client->media_mapping);
97
98   if (client->uri)
99     gst_rtsp_url_free (client->uri);
100   if (client->media)
101     g_object_unref (client->media);
102
103   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
104 }
105
106 static void
107 gst_rtsp_client_get_property (GObject *object, guint propid,
108     GValue *value, GParamSpec *pspec)
109 {
110   GstRTSPClient *client = GST_RTSP_CLIENT (object);
111
112   switch (propid) {
113     case PROP_TIMEOUT:
114       g_value_set_uint (value, gst_rtsp_client_get_timeout (client));
115       break;
116     case PROP_SESSION_POOL:
117       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
118       break;
119     case PROP_MEDIA_MAPPING:
120       g_value_take_object (value, gst_rtsp_client_get_media_mapping (client));
121       break;
122     default:
123       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
124   }
125 }
126
127 static void
128 gst_rtsp_client_set_property (GObject *object, guint propid,
129     const GValue *value, GParamSpec *pspec)
130 {
131   GstRTSPClient *client = GST_RTSP_CLIENT (object);
132
133   switch (propid) {
134     case PROP_TIMEOUT:
135       gst_rtsp_client_set_timeout (client, g_value_get_uint (value));
136       break;
137     case PROP_SESSION_POOL:
138       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
139       break;
140     case PROP_MEDIA_MAPPING:
141       gst_rtsp_client_set_media_mapping (client, g_value_get_object (value));
142       break;
143     default:
144       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
145   }
146 }
147
148 /**
149  * gst_rtsp_client_new:
150  *
151  * Create a new #GstRTSPClient instance.
152  */
153 GstRTSPClient *
154 gst_rtsp_client_new (void)
155 {
156   GstRTSPClient *result;
157
158   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
159
160   return result;
161 }
162
163 static void
164 send_response (GstRTSPClient *client, GstRTSPSession *session, GstRTSPMessage *response)
165 {
166   GTimeVal timeout;
167
168   gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER, "GStreamer RTSP server");
169
170 #ifdef DEBUG
171   gst_rtsp_message_dump (response);
172 #endif
173
174   timeout.tv_sec = client->timeout;
175   timeout.tv_usec = 0;
176
177   /* add the new session header for new session ids */
178   if (session) {
179     gchar *str;
180
181     if (session->timeout != 60)
182       str = g_strdup_printf ("%s; timeout=%d", session->sessionid, session->timeout);
183     else
184       str = g_strdup (session->sessionid);
185
186     gst_rtsp_message_take_header (response, GST_RTSP_HDR_SESSION, str);
187   }
188   else {
189     /* remove the session id from the response */
190     gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
191   }
192
193 #if 0
194   gst_rtsp_connection_send (client->connection, response, &timeout);
195 #endif
196   gst_rtsp_watch_queue_message (client->watch, response);
197   gst_rtsp_message_unset (response);
198 }
199
200 static void
201 send_generic_response (GstRTSPClient *client, GstRTSPStatusCode code, 
202     GstRTSPMessage *request)
203 {
204   GstRTSPMessage response = { 0 };
205
206   gst_rtsp_message_init_response (&response, code, 
207         gst_rtsp_status_as_text (code), request);
208
209   send_response (client, NULL, &response);
210 }
211
212 static gboolean
213 compare_uri (const GstRTSPUrl *uri1, const GstRTSPUrl *uri2)
214 {
215   if (uri1 == NULL || uri2 == NULL)
216     return FALSE;
217
218   if (strcmp (uri1->abspath, uri2->abspath))
219     return FALSE;
220
221   return TRUE;
222 }
223
224 /* this function is called to initially find the media for the DESCRIBE request
225  * but is cached for when the same client (without breaking the connection) is
226  * doing a setup for the exact same url. */
227 static GstRTSPMedia *
228 find_media (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
229 {
230   GstRTSPMediaFactory *factory;
231   GstRTSPMedia *media;
232
233   if (!compare_uri (client->uri, uri)) {
234     /* remove any previously cached values before we try to construct a new
235      * media for uri */
236     if (client->uri)
237       gst_rtsp_url_free (client->uri);
238     client->uri = NULL;
239     if (client->media)
240       g_object_unref (client->media);
241     client->media = NULL;
242
243     if (!client->media_mapping)
244       goto no_mapping;
245
246     /* find the factory for the uri first */
247     if (!(factory = gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
248       goto no_factory;
249
250     /* prepare the media and add it to the pipeline */
251     if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
252       goto no_media;
253
254     /* prepare the media */
255     if (!(gst_rtsp_media_prepare (media)))
256       goto no_prepare;
257
258     /* now keep track of the uri and the media */
259     client->uri = gst_rtsp_url_copy (uri);
260     client->media = media;
261   }
262   else {
263     /* we have seen this uri before, used cached media */
264     media = client->media;
265     g_message ("reusing cached media %p", media); 
266   }
267
268   if (media)
269     g_object_ref (media);
270
271   return media;
272
273   /* ERRORS */
274 no_mapping:
275   {
276     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
277     return NULL;
278   }
279 no_factory:
280   {
281     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
282     return NULL;
283   }
284 no_media:
285   {
286     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
287     g_object_unref (factory);
288     return NULL;
289   }
290 no_prepare:
291   {
292     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
293     g_object_unref (media);
294     g_object_unref (factory);
295     return NULL;
296   }
297 }
298
299 static gboolean
300 handle_teardown_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
301 {
302   GstRTSPSessionMedia *media;
303   GstRTSPMessage response = { 0 };
304   GstRTSPStatusCode code;
305
306   if (!session)
307     goto no_session;
308
309   /* get a handle to the configuration of the media in the session */
310   media = gst_rtsp_session_get_media (session, uri);
311   if (!media)
312     goto not_found;
313
314   gst_rtsp_session_media_stop (media);
315
316   /* unmanage the media in the session, returns false if all media session
317    * are torn down. */
318   if (!gst_rtsp_session_release_media (session, media)) {
319     /* remove the session */
320     gst_rtsp_session_pool_remove (client->session_pool, session);
321   }
322   /* construct the response now */
323   code = GST_RTSP_STS_OK;
324   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
325
326   send_response (client, session, &response);
327
328   return FALSE;
329
330   /* ERRORS */
331 no_session:
332   {
333     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
334     return FALSE;
335   }
336 not_found:
337   {
338     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
339     return FALSE;
340   }
341 }
342
343 static gboolean
344 handle_pause_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
345 {
346   GstRTSPSessionMedia *media;
347   GstRTSPMessage response = { 0 };
348   GstRTSPStatusCode code;
349
350   if (!session)
351     goto no_session;
352
353   /* get a handle to the configuration of the media in the session */
354   media = gst_rtsp_session_get_media (session, uri);
355   if (!media)
356     goto not_found;
357
358   /* the session state must be playing or recording */
359   if (media->state != GST_RTSP_STATE_PLAYING &&
360       media->state != GST_RTSP_STATE_RECORDING)
361     goto invalid_state;
362
363   gst_rtsp_session_media_pause (media);
364
365   /* construct the response now */
366   code = GST_RTSP_STS_OK;
367   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
368
369   send_response (client, session, &response);
370
371   /* the state is now READY */
372   media->state = GST_RTSP_STATE_READY;
373
374   return FALSE;
375
376   /* ERRORS */
377 no_session:
378   {
379     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
380     return FALSE;
381   }
382 not_found:
383   {
384     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
385     return FALSE;
386   }
387 invalid_state:
388   {
389     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
390     return FALSE;
391   }
392 }
393
394 static gboolean
395 handle_play_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
396 {
397   GstRTSPSessionMedia *media;
398   GstRTSPMessage response = { 0 };
399   GstRTSPStatusCode code;
400   GString *rtpinfo;
401   guint n_streams, i;
402   guint timestamp, seqnum;
403   gchar *str;
404
405   if (!session)
406     goto no_session;
407
408   /* get a handle to the configuration of the media in the session */
409   media = gst_rtsp_session_get_media (session, uri);
410   if (!media)
411     goto not_found;
412
413   /* the session state must be playing or ready */
414   if (media->state != GST_RTSP_STATE_PLAYING &&
415       media->state != GST_RTSP_STATE_READY)
416     goto invalid_state;
417
418   /* grab RTPInfo from the payloaders now */
419   rtpinfo = g_string_new ("");
420
421   n_streams = gst_rtsp_media_n_streams (media->media);
422   for (i = 0; i < n_streams; i++) {
423     GstRTSPMediaStream *stream;
424     gchar *uristr;
425
426     stream = gst_rtsp_media_get_stream (media->media, i);
427
428     g_object_get (G_OBJECT (stream->payloader), "seqnum", &seqnum, NULL);
429     g_object_get (G_OBJECT (stream->payloader), "timestamp", &timestamp, NULL);
430
431     if (i > 0)
432       g_string_append (rtpinfo, ", ");
433
434     uristr = gst_rtsp_url_get_request_uri (uri);
435     g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u", uristr, i, seqnum, timestamp);
436     g_free (uristr);
437   }
438
439   /* construct the response now */
440   code = GST_RTSP_STS_OK;
441   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
442
443   /* add the RTP-Info header */
444   str = g_string_free (rtpinfo, FALSE);
445   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
446
447   /* add the range */
448   str = gst_rtsp_range_to_string (&media->media->range);
449   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
450
451   send_response (client, session, &response);
452
453   /* start playing after sending the request */
454   gst_rtsp_session_media_play (media);
455
456   media->state = GST_RTSP_STATE_PLAYING;
457
458   return FALSE;
459
460   /* ERRORS */
461 no_session:
462   {
463     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
464     return FALSE;
465   }
466 not_found:
467   {
468     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
469     return FALSE;
470   }
471 invalid_state:
472   {
473     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, request);
474     return FALSE;
475   }
476 }
477
478 static gboolean
479 handle_setup_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
480 {
481   GstRTSPResult res;
482   gchar *transport;
483   gchar **transports;
484   gboolean have_transport;
485   GstRTSPTransport *ct, *st;
486   gint i;
487   GstRTSPLowerTrans supported;
488   GstRTSPMessage response = { 0 };
489   GstRTSPStatusCode code;
490   GstRTSPSessionStream *stream;
491   gchar *trans_str, *pos;
492   guint streamid;
493   GstRTSPSessionMedia *media;
494   gboolean need_session;
495   GstRTSPUrl *url;
496
497   /* the uri contains the stream number we added in the SDP config, which is
498    * always /stream=%d so we need to strip that off 
499    * parse the stream we need to configure, look for the stream in the abspath
500    * first and then in the query. */
501   if (!(pos = strstr (uri->abspath, "/stream="))) {
502     if (!(pos = strstr (uri->query, "/stream=")))
503       goto bad_request;
504   }
505
506   /* we can mofify the parse uri in place */
507   *pos = '\0';
508
509   pos += strlen ("/stream=");
510   if (sscanf (pos, "%u", &streamid) != 1)
511     goto bad_request;
512
513   /* parse the transport */
514   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0);
515   if (res != GST_RTSP_OK)
516     goto no_transport;
517
518   transports = g_strsplit (transport, ",", 0);
519   gst_rtsp_transport_new (&ct);  
520
521   /* loop through the transports, try to parse */
522   have_transport = FALSE;
523   for (i = 0; transports[i]; i++) {
524
525     gst_rtsp_transport_init (ct);  
526     res = gst_rtsp_transport_parse (transports[i], ct);
527     if (res == GST_RTSP_OK) {
528       have_transport = TRUE;
529       break;
530     }
531   }
532   g_strfreev (transports);
533
534   /* we have not found anything usable, error out */
535   if (!have_transport) 
536     goto unsupported_transports;
537
538   /* we have a valid transport, check if we can handle it */
539   if (ct->trans != GST_RTSP_TRANS_RTP)
540     goto unsupported_transports;
541   if (ct->profile != GST_RTSP_PROFILE_AVP)
542     goto unsupported_transports;
543
544   supported = GST_RTSP_LOWER_TRANS_UDP |
545         GST_RTSP_LOWER_TRANS_UDP_MCAST;
546   if (!(ct->lower_transport & supported))
547     goto unsupported_transports;
548
549   if (client->session_pool == NULL)
550     goto no_pool;
551
552   /* we have a valid transport now, set the destination of the client. */
553   g_free (ct->destination);
554   url = gst_rtsp_connection_get_url (client->connection);
555   ct->destination = g_strdup (url->host);
556
557   if (session) {
558     g_object_ref (session);
559     /* get a handle to the configuration of the media in the session, this can
560      * return NULL if this is a new url to manage in this session. */
561     media = gst_rtsp_session_get_media (session, uri);
562
563     need_session = FALSE;
564   }
565   else {
566     /* create a session if this fails we probably reached our session limit or
567      * something. */
568     if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
569       goto service_unavailable;
570
571     /* we need a new media configuration in this session */
572     media = NULL;
573
574     need_session = TRUE;
575   }
576
577   /* we have no media, find one and manage it */
578   if (media == NULL) {
579     GstRTSPMedia *m;
580
581     /* get a handle to the configuration of the media in the session */
582     if ((m = find_media (client, uri, request))) {
583       /* manage the media in our session now */
584       media = gst_rtsp_session_manage_media (session, uri, m);
585     }
586   }
587
588   /* if we stil have no media, error */
589   if (media == NULL)
590     goto not_found;
591
592   /* get a handle to the stream in the media */
593   if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
594     goto no_stream;
595
596   /* setup the server transport from the client transport */
597   st = gst_rtsp_session_stream_set_transport (stream, ct);
598
599   /* serialize the server transport */
600   trans_str = gst_rtsp_transport_as_text (st);
601   gst_rtsp_transport_free (st);
602
603   /* construct the response now */
604   code = GST_RTSP_STS_OK;
605   gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
606
607   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
608   g_free (trans_str);
609
610   send_response (client, session, &response);
611
612   /* update the state */
613   switch (media->state) {
614     case GST_RTSP_STATE_PLAYING:
615     case GST_RTSP_STATE_RECORDING:
616     case GST_RTSP_STATE_READY:
617       /* no state change */
618       break;
619     default:
620       media->state = GST_RTSP_STATE_READY;
621       break;
622   }
623   g_object_unref (session);
624
625   return TRUE;
626
627   /* ERRORS */
628 bad_request:
629   {
630     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
631     return FALSE;
632   }
633 not_found:
634   {
635     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
636     g_object_unref (session);
637     return FALSE;
638   }
639 no_stream:
640   {
641     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
642     g_object_unref (media);
643     g_object_unref (session);
644     return FALSE;
645   }
646 no_transport:
647   {
648     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
649     return FALSE;
650   }
651 unsupported_transports:
652   {
653     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
654     gst_rtsp_transport_free (ct);  
655     return FALSE;
656   }
657 no_pool:
658   {
659     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
660     return FALSE;
661   }
662 service_unavailable:
663   {
664     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
665     return FALSE;
666   }
667 }
668
669 /* for the describe we must generate an SDP */
670 static gboolean
671 handle_describe_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
672 {
673   GstRTSPMessage response = { 0 };
674   GstRTSPResult res;
675   GstSDPMessage *sdp;
676   guint i;
677   gchar *str;
678   GstRTSPMedia *media;
679
680   /* check what kind of format is accepted, we don't really do anything with it
681    * and always return SDP for now. */
682   for (i = 0; i++; ) {
683     gchar *accept;
684
685     res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
686     if (res == GST_RTSP_ENOTIMPL)
687       break;
688
689     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
690       break;
691   }
692
693   /* find the media object for the uri */
694   if (!(media = find_media (client, uri, request)))
695     goto no_media;
696
697   /* create an SDP for the media object */
698   if (!(sdp = gst_rtsp_sdp_from_media (media)))
699     goto no_sdp;
700
701   g_object_unref (media);
702
703   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
704         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
705
706   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE, "application/sdp");
707
708   /* content base for some clients that might screw up creating the setup uri */
709   str = g_strdup_printf ("rtsp://%s:%u%s/", uri->host, uri->port, uri->abspath);
710   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE, str);
711   g_free (str);
712
713   /* add SDP to the response body */
714   str = gst_sdp_message_as_text (sdp);
715   gst_rtsp_message_take_body (&response, (guint8 *)str, strlen (str));
716   gst_sdp_message_free (sdp);
717
718   send_response (client, NULL, &response);
719
720   return TRUE;
721
722   /* ERRORS */
723 no_media:
724   {
725     /* error reply is already sent */
726     return FALSE;
727   }
728 no_sdp:
729   {
730     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
731     g_object_unref (media);
732     return FALSE;
733   }
734 }
735
736 static void
737 handle_options_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
738 {
739   GstRTSPMessage response = { 0 };
740   GstRTSPMethod options;
741   gchar *str;
742
743   options = GST_RTSP_DESCRIBE |
744             GST_RTSP_OPTIONS |
745     //        GST_RTSP_PAUSE |
746             GST_RTSP_PLAY |
747             GST_RTSP_SETUP |
748             GST_RTSP_TEARDOWN;
749
750   str = gst_rtsp_options_as_text (options);
751
752   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, 
753         gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
754
755   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
756   g_free (str);
757
758   send_response (client, NULL, &response);
759 }
760
761 /* remove duplicate and trailing '/' */
762 static void
763 santize_uri (GstRTSPUrl *uri)
764 {
765   gint i, len;
766   gchar *s, *d;
767   gboolean have_slash, prev_slash;
768
769   s = d = uri->abspath;
770   len = strlen (uri->abspath);
771
772   prev_slash = FALSE;
773
774   for (i = 0; i < len; i++) {
775     have_slash = s[i] == '/';
776     *d = s[i];
777     if (!have_slash || !prev_slash)
778       d++;
779     prev_slash = have_slash;
780   }
781   len = d - uri->abspath;
782   /* don't remove the first slash if that's the only thing left */
783   if (len > 1 && *(d-1) == '/')
784     d--;
785   *d = '\0';
786 }
787
788 static void
789 handle_request (GstRTSPClient *client, GstRTSPMessage *request)
790 {
791   GstRTSPMethod method;
792   const gchar *uristr;
793   GstRTSPUrl *uri;
794   GstRTSPVersion version;
795   GstRTSPResult res;
796   GstRTSPSession *session;
797   gchar *sessid;
798
799 #ifdef DEBUG
800   gst_rtsp_message_dump (request);
801 #endif
802
803   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
804
805   if (version != GST_RTSP_VERSION_1_0) {
806     /* we can only handle 1.0 requests */
807     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED, request);
808     return;
809   }
810
811   /* we always try to parse the url first */
812   if ((res = gst_rtsp_url_parse (uristr, &uri)) != GST_RTSP_OK) {
813     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
814     return;
815   }
816
817   /* sanitize the uri */
818   santize_uri (uri);
819
820   /* get the session if there is any */
821   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
822   if (res == GST_RTSP_OK) {
823     if (client->session_pool == NULL)
824       goto no_pool;
825
826     /* we had a session in the request, find it again */
827     if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
828       goto session_not_found;
829
830     client->timeout = gst_rtsp_session_get_timeout (session);
831   }
832   else
833     session = NULL;
834
835   /* now see what is asked and dispatch to a dedicated handler */
836   switch (method) {
837     case GST_RTSP_OPTIONS:
838       handle_options_request (client, uri, session, request);
839       break;
840     case GST_RTSP_DESCRIBE:
841       handle_describe_request (client, uri, session, request);
842       break;
843     case GST_RTSP_SETUP:
844       handle_setup_request (client, uri, session, request);
845       break;
846     case GST_RTSP_PLAY:
847       handle_play_request (client, uri, session, request);
848       break;
849     case GST_RTSP_PAUSE:
850       handle_pause_request (client, uri, session, request);
851       break;
852     case GST_RTSP_TEARDOWN:
853       handle_teardown_request (client, uri, session, request);
854       break;
855     case GST_RTSP_ANNOUNCE:
856     case GST_RTSP_GET_PARAMETER:
857     case GST_RTSP_RECORD:
858     case GST_RTSP_REDIRECT:
859     case GST_RTSP_SET_PARAMETER:
860       send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, request);
861       break;
862     case GST_RTSP_INVALID:
863     default:
864       send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
865       break;
866   }
867   if (session)
868     g_object_unref (session);
869
870   gst_rtsp_url_free (uri);
871   return;
872
873   /* ERRORS */
874 no_pool:
875   {
876     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
877     return;
878   }
879 session_not_found:
880   {
881     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
882     return;
883   }
884 }
885
886 /**
887  * gst_rtsp_client_set_timeout:
888  * @client: a #GstRTSPClient
889  * @timeout: a timeout in seconds
890  *
891  * Set the connection timeout to @timeout seconds for @client.
892  */
893 void
894 gst_rtsp_client_set_timeout (GstRTSPClient *client, guint timeout)
895 {
896   client->timeout = timeout;
897 }
898
899 /**
900  * gst_rtsp_client_get_timeout:
901  * @client: a #GstRTSPClient
902  *
903  * Get the connection timeout @client.
904  *
905  * Returns: the connection timeout for @client in seconds.
906  */
907 guint
908 gst_rtsp_client_get_timeout (GstRTSPClient *client)
909 {
910   return client->timeout;
911 }
912
913 /**
914  * gst_rtsp_client_set_session_pool:
915  * @client: a #GstRTSPClient
916  * @pool: a #GstRTSPSessionPool
917  *
918  * Set @pool as the sessionpool for @client which it will use to find
919  * or allocate sessions. the sessionpool is usually inherited from the server
920  * that created the client but can be overridden later.
921  */
922 void
923 gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *pool)
924 {
925   GstRTSPSessionPool *old;
926
927   old = client->session_pool;
928   if (old != pool) {
929     if (pool)
930       g_object_ref (pool);
931     client->session_pool = pool;
932     if (old)
933       g_object_unref (old);
934   }
935 }
936
937 /**
938  * gst_rtsp_client_get_session_pool:
939  * @client: a #GstRTSPClient
940  *
941  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
942  *
943  * Returns: a #GstRTSPSessionPool, unref after usage.
944  */
945 GstRTSPSessionPool *
946 gst_rtsp_client_get_session_pool (GstRTSPClient *client)
947 {
948   GstRTSPSessionPool *result;
949
950   if ((result = client->session_pool))
951     g_object_ref (result);
952
953   return result;
954 }
955
956 /**
957  * gst_rtsp_client_set_media_mapping:
958  * @client: a #GstRTSPClient
959  * @mapping: a #GstRTSPMediaMapping
960  *
961  * Set @mapping as the media mapping for @client which it will use to map urls
962  * to media streams. These mapping is usually inherited from the server that
963  * created the client but can be overriden later.
964  */
965 void
966 gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *mapping)
967 {
968   GstRTSPMediaMapping *old;
969
970   old = client->media_mapping;
971
972   if (old != mapping) {
973     if (mapping)
974       g_object_ref (mapping);
975     client->media_mapping = mapping;
976     if (old)
977       g_object_unref (old);
978   }
979 }
980
981 /**
982  * gst_rtsp_client_get_media_mapping:
983  * @client: a #GstRTSPClient
984  *
985  * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
986  *
987  * Returns: a #GstRTSPMediaMapping, unref after usage.
988  */
989 GstRTSPMediaMapping *
990 gst_rtsp_client_get_media_mapping (GstRTSPClient *client)
991 {
992   GstRTSPMediaMapping *result;
993
994   if ((result = client->media_mapping))
995     g_object_ref (result);
996
997   return result;
998 }
999
1000 static GstRTSPResult
1001 message_received (GstRTSPWatch *watch, GstRTSPMessage *message, gpointer user_data)
1002 {
1003   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1004
1005   g_message ("client %p: received a message", client);
1006
1007   switch (message->type) {
1008     case GST_RTSP_MESSAGE_REQUEST:
1009       handle_request (client, message);
1010       break;
1011     case GST_RTSP_MESSAGE_RESPONSE:
1012       break;
1013     case GST_RTSP_MESSAGE_DATA:
1014       break;
1015     default:
1016       break;
1017   }
1018   return GST_RTSP_OK;
1019 }
1020
1021 static GstRTSPResult
1022 message_sent (GstRTSPWatch *watch, guint cseq, gpointer user_data)
1023 {
1024   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1025
1026   g_message ("client %p: sent a message with cseq %d", client, cseq);
1027
1028   return GST_RTSP_OK;
1029 }
1030
1031 static GstRTSPResult
1032 closed (GstRTSPWatch *watch, gpointer user_data)
1033 {
1034   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1035   const gchar *tunnelid;
1036
1037   g_message ("client %p: connection closed", client);
1038
1039   if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
1040     g_mutex_lock (tunnels_lock);
1041     g_hash_table_remove (tunnels, tunnelid);
1042     g_mutex_unlock (tunnels_lock);
1043   }
1044
1045   return GST_RTSP_OK;
1046 }
1047
1048 static GstRTSPResult
1049 error (GstRTSPWatch *watch, GstRTSPResult result, gpointer user_data)
1050 {
1051   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1052   gchar *str;
1053
1054   str = gst_rtsp_strresult (result);
1055   g_message ("client %p: received an error %s", client, str);
1056   g_free (str);
1057
1058   return GST_RTSP_OK;
1059 }
1060
1061 static GstRTSPStatusCode
1062 tunnel_start (GstRTSPWatch *watch, gpointer user_data)
1063 {
1064   GstRTSPClient *client;
1065   const gchar *tunnelid;
1066
1067   client = GST_RTSP_CLIENT (user_data);
1068
1069   g_message ("client %p: tunnel start", client);
1070
1071   /* store client in the pending tunnels */
1072   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1073
1074   g_message ("client %p: inserting %s", client, tunnelid);
1075
1076   /* we can't have two clients connecting with the same tunnelid */
1077   g_mutex_lock (tunnels_lock);
1078   if (g_hash_table_lookup (tunnels, tunnelid))
1079     goto tunnel_existed;
1080
1081   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
1082   g_mutex_unlock (tunnels_lock);
1083
1084   return GST_RTSP_STS_OK;
1085
1086   /* ERRORS */
1087 tunnel_existed:
1088   {
1089     g_mutex_unlock (tunnels_lock);
1090     g_message ("client %p: tunnel session %s existed", client, tunnelid);
1091     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1092   }
1093 }
1094
1095 static GstRTSPResult
1096 tunnel_complete (GstRTSPWatch *watch, gpointer user_data)
1097 {
1098   const gchar *tunnelid;
1099   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1100   GstRTSPClient *oclient;
1101
1102   g_message ("client %p: tunnel complete", client);
1103
1104   /* find previous tunnel */
1105   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1106
1107   g_mutex_lock (tunnels_lock);
1108   if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
1109     goto no_tunnel;
1110
1111   /* remove the old client from the table. ref before because removing it will
1112    * remove the ref to it. */
1113   g_object_ref (oclient);
1114   g_hash_table_remove (tunnels, tunnelid);
1115   g_mutex_unlock (tunnels_lock);
1116
1117   g_message ("client %p: found tunnel %p", client, oclient);
1118
1119   /* merge the tunnels into the first client */
1120   gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
1121   gst_rtsp_watch_reset (oclient->watch);
1122   g_object_unref (oclient);
1123
1124   /* we don't need this watch anymore */
1125   g_source_remove (client->watchid);
1126
1127   return GST_RTSP_OK;
1128
1129   /* ERRORS */
1130 no_tunnel:
1131   {
1132     g_mutex_unlock (tunnels_lock);
1133     g_message ("client %p: tunnel session %s not found", client, tunnelid);
1134     return GST_RTSP_OK;
1135   }
1136 }
1137
1138 static GstRTSPWatchFuncs watch_funcs = {
1139   message_received,
1140   message_sent,
1141   closed,
1142   error,
1143   tunnel_start,
1144   tunnel_complete
1145 };
1146
1147 /**
1148  * gst_rtsp_client_attach:
1149  * @client: a #GstRTSPClient
1150  * @channel: a #GIOChannel
1151  *
1152  * Accept a new connection for @client on the socket in @source. 
1153  *
1154  * This function should be called when the client properties and urls are fully
1155  * configured and the client is ready to start.
1156  *
1157  * Returns: %TRUE if the client could be accepted.
1158  */
1159 gboolean
1160 gst_rtsp_client_accept (GstRTSPClient *client, GIOChannel *channel)
1161 {
1162   int sock;
1163   GstRTSPConnection *conn;
1164   GstRTSPResult res;
1165   GSource *source;
1166   GMainContext *context;
1167   GstRTSPUrl *url;
1168
1169   /* a new client connected. */
1170   sock = g_io_channel_unix_get_fd (channel);
1171
1172   GST_RTSP_CHECK (gst_rtsp_connection_accept (sock, &conn), accept_failed);
1173
1174   url = gst_rtsp_connection_get_url (conn);
1175   g_message ("added new client %p ip %s:%d", client,
1176                 url->host, url->port);
1177
1178   client->connection = conn;
1179
1180   /* create watch for the connection and attach */
1181   client->watch = gst_rtsp_watch_new (client->connection, &watch_funcs,
1182                   g_object_ref (client), g_object_unref);
1183
1184   /* find the context to add the watch */
1185   if ((source = g_main_current_source ()))
1186     context = g_source_get_context (source);
1187   else
1188     context = NULL;
1189
1190   g_message ("attaching to context %p", context);
1191
1192   client->watchid = gst_rtsp_watch_attach (client->watch, context);
1193   gst_rtsp_watch_unref (client->watch);
1194
1195   return TRUE;
1196
1197   /* ERRORS */
1198 accept_failed:
1199   {
1200     gchar *str = gst_rtsp_strresult (res);
1201
1202     g_error ("Could not accept client on server socket %d: %s",
1203             sock, str);
1204     g_free (str);
1205     return FALSE;
1206   }
1207 }