rtsp-server: Run gst-indent
[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 #include "rtsp-params.h"
25
26 /* temporary multicast address until it's configurable somewhere */
27 #define MCAST_ADDRESS "224.2.0.1"
28
29 static GMutex *tunnels_lock;
30 static GHashTable *tunnels;
31
32 enum
33 {
34   PROP_0,
35   PROP_SESSION_POOL,
36   PROP_MEDIA_MAPPING,
37   PROP_LAST
38 };
39
40 GST_DEBUG_CATEGORY_STATIC (rtsp_client_debug);
41 #define GST_CAT_DEFAULT rtsp_client_debug
42
43 static void gst_rtsp_client_get_property (GObject * object, guint propid,
44     GValue * value, GParamSpec * pspec);
45 static void gst_rtsp_client_set_property (GObject * object, guint propid,
46     const GValue * value, GParamSpec * pspec);
47 static void gst_rtsp_client_finalize (GObject * obj);
48
49 static void client_session_finalized (GstRTSPClient * client,
50     GstRTSPSession * session);
51 static void unlink_session_streams (GstRTSPClient * client,
52     GstRTSPSession * session, GstRTSPSessionMedia * media);
53
54 G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
55
56 static void
57 gst_rtsp_client_class_init (GstRTSPClientClass * klass)
58 {
59   GObjectClass *gobject_class;
60
61   gobject_class = G_OBJECT_CLASS (klass);
62
63   gobject_class->get_property = gst_rtsp_client_get_property;
64   gobject_class->set_property = gst_rtsp_client_set_property;
65   gobject_class->finalize = gst_rtsp_client_finalize;
66
67   g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
68       g_param_spec_object ("session-pool", "Session Pool",
69           "The session pool to use for client session",
70           GST_TYPE_RTSP_SESSION_POOL,
71           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
72
73   g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
74       g_param_spec_object ("media-mapping", "Media Mapping",
75           "The media mapping to use for client session",
76           GST_TYPE_RTSP_MEDIA_MAPPING,
77           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
78
79   tunnels =
80       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
81   tunnels_lock = g_mutex_new ();
82
83   GST_DEBUG_CATEGORY_INIT (rtsp_client_debug, "rtspclient", 0, "GstRTSPClient");
84 }
85
86 static void
87 gst_rtsp_client_init (GstRTSPClient * client)
88 {
89 }
90
91 static void
92 client_unlink_session (GstRTSPClient * client, GstRTSPSession * session)
93 {
94   GList *medias;
95
96   /* unlink all media managed in this session */
97   for (medias = session->medias; medias; medias = g_list_next (medias)) {
98     unlink_session_streams (client, session,
99         (GstRTSPSessionMedia *) medias->data);
100   }
101 }
102
103 static void
104 client_cleanup_sessions (GstRTSPClient * client)
105 {
106   GList *sessions;
107
108   /* remove weak-ref from sessions */
109   for (sessions = client->sessions; sessions; sessions = g_list_next (sessions)) {
110     GstRTSPSession *session = (GstRTSPSession *) sessions->data;
111     g_object_weak_unref (G_OBJECT (session),
112         (GWeakNotify) client_session_finalized, client);
113     client_unlink_session (client, session);
114   }
115   g_list_free (client->sessions);
116   client->sessions = NULL;
117 }
118
119 /* A client is finalized when the connection is broken */
120 static void
121 gst_rtsp_client_finalize (GObject * obj)
122 {
123   GstRTSPClient *client = GST_RTSP_CLIENT (obj);
124
125   GST_INFO ("finalize client %p", client);
126
127   client_cleanup_sessions (client);
128
129   gst_rtsp_connection_free (client->connection);
130   if (client->session_pool)
131     g_object_unref (client->session_pool);
132   if (client->media_mapping)
133     g_object_unref (client->media_mapping);
134
135   if (client->uri)
136     gst_rtsp_url_free (client->uri);
137   if (client->media)
138     g_object_unref (client->media);
139
140   g_free (client->server_ip);
141
142   G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj);
143 }
144
145 static void
146 gst_rtsp_client_get_property (GObject * object, guint propid,
147     GValue * value, GParamSpec * pspec)
148 {
149   GstRTSPClient *client = GST_RTSP_CLIENT (object);
150
151   switch (propid) {
152     case PROP_SESSION_POOL:
153       g_value_take_object (value, gst_rtsp_client_get_session_pool (client));
154       break;
155     case PROP_MEDIA_MAPPING:
156       g_value_take_object (value, gst_rtsp_client_get_media_mapping (client));
157       break;
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
160   }
161 }
162
163 static void
164 gst_rtsp_client_set_property (GObject * object, guint propid,
165     const GValue * value, GParamSpec * pspec)
166 {
167   GstRTSPClient *client = GST_RTSP_CLIENT (object);
168
169   switch (propid) {
170     case PROP_SESSION_POOL:
171       gst_rtsp_client_set_session_pool (client, g_value_get_object (value));
172       break;
173     case PROP_MEDIA_MAPPING:
174       gst_rtsp_client_set_media_mapping (client, g_value_get_object (value));
175       break;
176     default:
177       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
178   }
179 }
180
181 /**
182  * gst_rtsp_client_new:
183  *
184  * Create a new #GstRTSPClient instance.
185  */
186 GstRTSPClient *
187 gst_rtsp_client_new (void)
188 {
189   GstRTSPClient *result;
190
191   result = g_object_new (GST_TYPE_RTSP_CLIENT, NULL);
192
193   return result;
194 }
195
196 static void
197 send_response (GstRTSPClient * client, GstRTSPSession * session,
198     GstRTSPMessage * response)
199 {
200   gst_rtsp_message_add_header (response, GST_RTSP_HDR_SERVER,
201       "GStreamer RTSP server");
202
203   /* remove any previous header */
204   gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
205
206   /* add the new session header for new session ids */
207   if (session) {
208     gchar *str;
209
210     if (session->timeout != 60)
211       str =
212           g_strdup_printf ("%s; timeout=%d", session->sessionid,
213           session->timeout);
214     else
215       str = g_strdup (session->sessionid);
216
217     gst_rtsp_message_take_header (response, GST_RTSP_HDR_SESSION, str);
218   }
219
220   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
221     gst_rtsp_message_dump (response);
222   }
223
224   gst_rtsp_watch_send_message (client->watch, response, NULL);
225   gst_rtsp_message_unset (response);
226 }
227
228 static void
229 send_generic_response (GstRTSPClient * client, GstRTSPStatusCode code,
230     GstRTSPMessage * request)
231 {
232   GstRTSPMessage response = { 0 };
233
234   gst_rtsp_message_init_response (&response, code,
235       gst_rtsp_status_as_text (code), request);
236
237   send_response (client, NULL, &response);
238 }
239
240 static gboolean
241 compare_uri (const GstRTSPUrl * uri1, const GstRTSPUrl * uri2)
242 {
243   if (uri1 == NULL || uri2 == NULL)
244     return FALSE;
245
246   if (strcmp (uri1->abspath, uri2->abspath))
247     return FALSE;
248
249   return TRUE;
250 }
251
252 /* this function is called to initially find the media for the DESCRIBE request
253  * but is cached for when the same client (without breaking the connection) is
254  * doing a setup for the exact same url. */
255 static GstRTSPMedia *
256 find_media (GstRTSPClient * client, GstRTSPUrl * uri, GstRTSPMessage * request)
257 {
258   GstRTSPMediaFactory *factory;
259   GstRTSPMedia *media;
260
261   if (!compare_uri (client->uri, uri)) {
262     /* remove any previously cached values before we try to construct a new
263      * media for uri */
264     if (client->uri)
265       gst_rtsp_url_free (client->uri);
266     client->uri = NULL;
267     if (client->media)
268       g_object_unref (client->media);
269     client->media = NULL;
270
271     if (!client->media_mapping)
272       goto no_mapping;
273
274     /* find the factory for the uri first */
275     if (!(factory =
276             gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
277       goto no_factory;
278
279     /* prepare the media and add it to the pipeline */
280     if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
281       goto no_media;
282
283     /* set ipv6 on the media before preparing */
284     media->is_ipv6 = client->is_ipv6;
285
286     /* prepare the media */
287     if (!(gst_rtsp_media_prepare (media)))
288       goto no_prepare;
289
290     /* now keep track of the uri and the media */
291     client->uri = gst_rtsp_url_copy (uri);
292     client->media = media;
293   } else {
294     /* we have seen this uri before, used cached media */
295     media = client->media;
296     GST_INFO ("reusing cached media %p", media);
297   }
298
299   if (media)
300     g_object_ref (media);
301
302   return media;
303
304   /* ERRORS */
305 no_mapping:
306   {
307     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
308     return NULL;
309   }
310 no_factory:
311   {
312     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
313     return NULL;
314   }
315 no_media:
316   {
317     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
318     g_object_unref (factory);
319     return NULL;
320   }
321 no_prepare:
322   {
323     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
324     g_object_unref (media);
325     g_object_unref (factory);
326     return NULL;
327   }
328 }
329
330 static gboolean
331 do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
332 {
333   GstRTSPMessage message = { 0 };
334   guint8 *data;
335   guint size;
336
337   gst_rtsp_message_init_data (&message, channel);
338
339   data = GST_BUFFER_DATA (buffer);
340   size = GST_BUFFER_SIZE (buffer);
341   gst_rtsp_message_take_body (&message, data, size);
342
343   /* FIXME, client->watch could have been finalized here, we need to keep an
344    * extra refcount to the watch.  */
345   gst_rtsp_watch_send_message (client->watch, &message, NULL);
346
347   gst_rtsp_message_steal_body (&message, &data, &size);
348   gst_rtsp_message_unset (&message);
349
350   return TRUE;
351 }
352
353 static void
354 link_stream (GstRTSPClient * client, GstRTSPSession * session,
355     GstRTSPSessionStream * stream)
356 {
357   GST_DEBUG ("client %p: linking stream %p", client, stream);
358   gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
359       (GstRTSPSendFunc) do_send_data, client, NULL);
360   client->streams = g_list_prepend (client->streams, stream);
361   /* make sure our session can't expire */
362   gst_rtsp_session_prevent_expire (session);
363 }
364
365 static void
366 unlink_stream (GstRTSPClient * client, GstRTSPSession * session,
367     GstRTSPSessionStream * stream)
368 {
369   GST_DEBUG ("client %p: unlinking stream %p", client, stream);
370   gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL);
371   client->streams = g_list_remove (client->streams, stream);
372   /* our session can now expire */
373   gst_rtsp_session_allow_expire (session);
374 }
375
376 static void
377 unlink_session_streams (GstRTSPClient * client, GstRTSPSession * session,
378     GstRTSPSessionMedia * media)
379 {
380   guint n_streams, i;
381
382   n_streams = gst_rtsp_media_n_streams (media->media);
383   for (i = 0; i < n_streams; i++) {
384     GstRTSPSessionStream *sstream;
385     GstRTSPTransport *tr;
386
387     /* get the stream as configured in the session */
388     sstream = gst_rtsp_session_media_get_stream (media, i);
389     /* get the transport, if there is no transport configured, skip this stream */
390     if (!(tr = sstream->trans.transport))
391       continue;
392
393     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
394       /* for TCP, unlink the stream from the TCP connection of the client */
395       unlink_stream (client, session, sstream);
396     }
397   }
398 }
399
400 static void
401 close_connection (GstRTSPClient * client)
402 {
403   const gchar *tunnelid;
404
405   GST_DEBUG ("client %p: closing connection", client);
406
407   if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
408     g_mutex_lock (tunnels_lock);
409     /* remove from tunnelids */
410     g_hash_table_remove (tunnels, tunnelid);
411     g_mutex_unlock (tunnels_lock);
412   }
413
414   gst_rtsp_connection_close (client->connection);
415   if (client->watchid) {
416     g_source_destroy ((GSource *) client->watch);
417     client->watchid = 0;
418     client->watch = NULL;
419   }
420 }
421
422 static gboolean
423 handle_teardown_request (GstRTSPClient * client, GstRTSPUrl * uri,
424     GstRTSPSession * session, GstRTSPMessage * request)
425 {
426   GstRTSPSessionMedia *media;
427   GstRTSPMessage response = { 0 };
428   GstRTSPStatusCode code;
429
430   if (!session)
431     goto no_session;
432
433   /* get a handle to the configuration of the media in the session */
434   media = gst_rtsp_session_get_media (session, uri);
435   if (!media)
436     goto not_found;
437
438   /* unlink the all TCP callbacks */
439   unlink_session_streams (client, session, media);
440
441   /* remove the session from the watched sessions */
442   g_object_weak_unref (G_OBJECT (session),
443       (GWeakNotify) client_session_finalized, client);
444   client->sessions = g_list_remove (client->sessions, session);
445
446   gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
447
448   /* unmanage the media in the session, returns false if all media session
449    * are torn down. */
450   if (!gst_rtsp_session_release_media (session, media)) {
451     /* remove the session */
452     gst_rtsp_session_pool_remove (client->session_pool, session);
453   }
454   /* construct the response now */
455   code = GST_RTSP_STS_OK;
456   gst_rtsp_message_init_response (&response, code,
457       gst_rtsp_status_as_text (code), request);
458
459   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONNECTION, "close");
460
461   send_response (client, session, &response);
462
463   close_connection (client);
464
465   return TRUE;
466
467   /* ERRORS */
468 no_session:
469   {
470     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
471     return FALSE;
472   }
473 not_found:
474   {
475     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
476     return FALSE;
477   }
478 }
479
480 static gboolean
481 handle_get_param_request (GstRTSPClient * client, GstRTSPUrl * uri,
482     GstRTSPSession * session, GstRTSPMessage * request)
483 {
484   GstRTSPResult res;
485   guint8 *data;
486   guint size;
487
488   res = gst_rtsp_message_get_body (request, &data, &size);
489   if (res != GST_RTSP_OK)
490     goto bad_request;
491
492   if (size == 0) {
493     /* no body, keep-alive request */
494     send_generic_response (client, GST_RTSP_STS_OK, request);
495   } else {
496     /* there is a body */
497     GstRTSPMessage response = { 0 };
498
499     /* there is a body, handle the params */
500     res = gst_rtsp_params_get (client, uri, session, request, &response);
501     if (res != GST_RTSP_OK)
502       goto bad_request;
503
504     send_response (client, session, &response);
505   }
506   return TRUE;
507
508   /* ERRORS */
509 bad_request:
510   {
511     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
512     return FALSE;
513   }
514 }
515
516 static gboolean
517 handle_set_param_request (GstRTSPClient * client, GstRTSPUrl * uri,
518     GstRTSPSession * session, GstRTSPMessage * request)
519 {
520   GstRTSPResult res;
521   guint8 *data;
522   guint size;
523
524   res = gst_rtsp_message_get_body (request, &data, &size);
525   if (res != GST_RTSP_OK)
526     goto bad_request;
527
528   if (size == 0) {
529     /* no body, keep-alive request */
530     send_generic_response (client, GST_RTSP_STS_OK, request);
531   } else {
532     GstRTSPMessage response = { 0 };
533
534     /* there is a body, handle the params */
535     res = gst_rtsp_params_set (client, uri, session, request, &response);
536     if (res != GST_RTSP_OK)
537       goto bad_request;
538
539     send_response (client, session, &response);
540   }
541   return TRUE;
542
543   /* ERRORS */
544 bad_request:
545   {
546     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
547     return FALSE;
548   }
549 }
550
551 static gboolean
552 handle_pause_request (GstRTSPClient * client, GstRTSPUrl * uri,
553     GstRTSPSession * session, GstRTSPMessage * request)
554 {
555   GstRTSPSessionMedia *media;
556   GstRTSPMessage response = { 0 };
557   GstRTSPStatusCode code;
558
559   if (!session)
560     goto no_session;
561
562   /* get a handle to the configuration of the media in the session */
563   media = gst_rtsp_session_get_media (session, uri);
564   if (!media)
565     goto not_found;
566
567   /* the session state must be playing or recording */
568   if (media->state != GST_RTSP_STATE_PLAYING &&
569       media->state != GST_RTSP_STATE_RECORDING)
570     goto invalid_state;
571
572   /* unlink the all TCP callbacks */
573   unlink_session_streams (client, session, media);
574
575   /* then pause sending */
576   gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
577
578   /* construct the response now */
579   code = GST_RTSP_STS_OK;
580   gst_rtsp_message_init_response (&response, code,
581       gst_rtsp_status_as_text (code), request);
582
583   send_response (client, session, &response);
584
585   /* the state is now READY */
586   media->state = GST_RTSP_STATE_READY;
587
588   return TRUE;
589
590   /* ERRORS */
591 no_session:
592   {
593     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
594     return FALSE;
595   }
596 not_found:
597   {
598     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
599     return FALSE;
600   }
601 invalid_state:
602   {
603     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
604         request);
605     return FALSE;
606   }
607 }
608
609 static gboolean
610 handle_play_request (GstRTSPClient * client, GstRTSPUrl * uri,
611     GstRTSPSession * session, GstRTSPMessage * request)
612 {
613   GstRTSPSessionMedia *media;
614   GstRTSPMessage response = { 0 };
615   GstRTSPStatusCode code;
616   GString *rtpinfo;
617   guint n_streams, i, infocount;
618   guint timestamp, seqnum;
619   gchar *str;
620   GstRTSPTimeRange *range;
621   GstRTSPResult res;
622
623   if (!session)
624     goto no_session;
625
626   /* get a handle to the configuration of the media in the session */
627   media = gst_rtsp_session_get_media (session, uri);
628   if (!media)
629     goto not_found;
630
631   /* the session state must be playing or ready */
632   if (media->state != GST_RTSP_STATE_PLAYING &&
633       media->state != GST_RTSP_STATE_READY)
634     goto invalid_state;
635
636   /* parse the range header if we have one */
637   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_RANGE, &str, 0);
638   if (res == GST_RTSP_OK) {
639     if (gst_rtsp_range_parse (str, &range) == GST_RTSP_OK) {
640       /* we have a range, seek to the position */
641       gst_rtsp_media_seek (media->media, range);
642       gst_rtsp_range_free (range);
643     }
644   }
645
646   /* grab RTPInfo from the payloaders now */
647   rtpinfo = g_string_new ("");
648
649   n_streams = gst_rtsp_media_n_streams (media->media);
650   for (i = 0, infocount = 0; i < n_streams; i++) {
651     GstRTSPSessionStream *sstream;
652     GstRTSPMediaStream *stream;
653     GstRTSPTransport *tr;
654     GObjectClass *payobjclass;
655     gchar *uristr;
656
657     /* get the stream as configured in the session */
658     sstream = gst_rtsp_session_media_get_stream (media, i);
659     /* get the transport, if there is no transport configured, skip this stream */
660     if (!(tr = sstream->trans.transport)) {
661       GST_INFO ("stream %d is not configured", i);
662       continue;
663     }
664
665     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
666       /* for TCP, link the stream to the TCP connection of the client */
667       link_stream (client, session, sstream);
668     }
669
670     stream = sstream->media_stream;
671
672     payobjclass = G_OBJECT_GET_CLASS (stream->payloader);
673
674     if (g_object_class_find_property (payobjclass, "seqnum") &&
675         g_object_class_find_property (payobjclass, "timestamp")) {
676       GObject *payobj;
677
678       payobj = G_OBJECT (stream->payloader);
679
680       /* only add RTP-Info for streams with seqnum and timestamp */
681       g_object_get (payobj, "seqnum", &seqnum, "timestamp", &timestamp, NULL);
682
683       if (infocount > 0)
684         g_string_append (rtpinfo, ", ");
685
686       uristr = gst_rtsp_url_get_request_uri (uri);
687       g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u",
688           uristr, i, seqnum, timestamp);
689       g_free (uristr);
690
691       infocount++;
692     } else {
693       GST_WARNING ("RTP-Info cannot be determined for stream %d", i);
694     }
695   }
696
697   /* construct the response now */
698   code = GST_RTSP_STS_OK;
699   gst_rtsp_message_init_response (&response, code,
700       gst_rtsp_status_as_text (code), request);
701
702   /* add the RTP-Info header */
703   if (infocount > 0) {
704     str = g_string_free (rtpinfo, FALSE);
705     gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RTP_INFO, str);
706   } else {
707     g_string_free (rtpinfo, TRUE);
708   }
709
710   /* add the range */
711   str = gst_rtsp_range_to_string (&media->media->range);
712   gst_rtsp_message_take_header (&response, GST_RTSP_HDR_RANGE, str);
713
714   send_response (client, session, &response);
715
716   /* start playing after sending the request */
717   gst_rtsp_session_media_set_state (media, GST_STATE_PLAYING);
718
719   media->state = GST_RTSP_STATE_PLAYING;
720
721   return TRUE;
722
723   /* ERRORS */
724 no_session:
725   {
726     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
727     return FALSE;
728   }
729 not_found:
730   {
731     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
732     return FALSE;
733   }
734 invalid_state:
735   {
736     send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
737         request);
738     return FALSE;
739   }
740 }
741
742 static void
743 do_keepalive (GstRTSPSession * session)
744 {
745   GST_INFO ("keep session %p alive", session);
746   gst_rtsp_session_touch (session);
747 }
748
749 static gboolean
750 handle_setup_request (GstRTSPClient * client, GstRTSPUrl * uri,
751     GstRTSPSession * session, GstRTSPMessage * request)
752 {
753   GstRTSPResult res;
754   gchar *transport;
755   gchar **transports;
756   gboolean have_transport;
757   GstRTSPTransport *ct, *st;
758   gint i;
759   GstRTSPLowerTrans supported;
760   GstRTSPMessage response = { 0 };
761   GstRTSPStatusCode code;
762   GstRTSPSessionStream *stream;
763   gchar *trans_str, *pos;
764   guint streamid;
765   GstRTSPSessionMedia *media;
766   gboolean need_session;
767   GstRTSPUrl *url;
768
769   /* the uri contains the stream number we added in the SDP config, which is
770    * always /stream=%d so we need to strip that off 
771    * parse the stream we need to configure, look for the stream in the abspath
772    * first and then in the query. */
773   if (uri->abspath == NULL || !(pos = strstr (uri->abspath, "/stream="))) {
774     if (uri->query == NULL || !(pos = strstr (uri->query, "/stream=")))
775       goto bad_request;
776   }
777
778   /* we can mofify the parse uri in place */
779   *pos = '\0';
780
781   pos += strlen ("/stream=");
782   if (sscanf (pos, "%u", &streamid) != 1)
783     goto bad_request;
784
785   /* parse the transport */
786   res =
787       gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport,
788       0);
789   if (res != GST_RTSP_OK)
790     goto no_transport;
791
792   transports = g_strsplit (transport, ",", 0);
793   gst_rtsp_transport_new (&ct);
794
795   /* init transports */
796   have_transport = FALSE;
797   gst_rtsp_transport_init (ct);
798
799   /* our supported transports */
800   supported = GST_RTSP_LOWER_TRANS_UDP |
801       GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP;
802
803   /* loop through the transports, try to parse */
804   for (i = 0; transports[i]; i++) {
805     res = gst_rtsp_transport_parse (transports[i], ct);
806     if (res != GST_RTSP_OK) {
807       /* no valid transport, search some more */
808       GST_WARNING ("could not parse transport %s", transports[i]);
809       goto next;
810     }
811
812     /* we have a transport, see if it's RTP/AVP */
813     if (ct->trans != GST_RTSP_TRANS_RTP || ct->profile != GST_RTSP_PROFILE_AVP) {
814       GST_WARNING ("invalid transport %s", transports[i]);
815       goto next;
816     }
817
818     if (!(ct->lower_transport & supported)) {
819       GST_WARNING ("unsupported transport %s", transports[i]);
820       goto next;
821     }
822
823     /* we have a valid transport */
824     GST_INFO ("found valid transport %s", transports[i]);
825     have_transport = TRUE;
826     break;
827
828   next:
829     gst_rtsp_transport_init (ct);
830   }
831   g_strfreev (transports);
832
833   /* we have not found anything usable, error out */
834   if (!have_transport)
835     goto unsupported_transports;
836
837   if (client->session_pool == NULL)
838     goto no_pool;
839
840   /* we have a valid transport now, set the destination of the client. */
841   g_free (ct->destination);
842   if (ct->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
843     ct->destination = g_strdup (MCAST_ADDRESS);
844   } else {
845     url = gst_rtsp_connection_get_url (client->connection);
846     ct->destination = g_strdup (url->host);
847   }
848
849   if (session) {
850     g_object_ref (session);
851     /* get a handle to the configuration of the media in the session, this can
852      * return NULL if this is a new url to manage in this session. */
853     media = gst_rtsp_session_get_media (session, uri);
854
855     need_session = FALSE;
856   } else {
857     /* create a session if this fails we probably reached our session limit or
858      * something. */
859     if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
860       goto service_unavailable;
861
862     /* we need a new media configuration in this session */
863     media = NULL;
864
865     need_session = TRUE;
866   }
867
868   /* we have no media, find one and manage it */
869   if (media == NULL) {
870     GstRTSPMedia *m;
871
872     /* get a handle to the configuration of the media in the session */
873     if ((m = find_media (client, uri, request))) {
874       /* manage the media in our session now */
875       media = gst_rtsp_session_manage_media (session, uri, m);
876     }
877   }
878
879   /* if we stil have no media, error */
880   if (media == NULL)
881     goto not_found;
882
883   /* fix the transports */
884   if (ct->lower_transport & GST_RTSP_LOWER_TRANS_TCP) {
885     /* check if the client selected channels for TCP */
886     if (ct->interleaved.min == -1 || ct->interleaved.max == -1) {
887       gst_rtsp_session_media_alloc_channels (media, &ct->interleaved);
888     }
889   }
890
891   /* get a handle to the stream in the media */
892   if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
893     goto no_stream;
894
895   st = gst_rtsp_session_stream_set_transport (stream, ct);
896
897   /* configure keepalive for this transport */
898   gst_rtsp_session_stream_set_keepalive (stream,
899       (GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
900
901   /* serialize the server transport */
902   trans_str = gst_rtsp_transport_as_text (st);
903   gst_rtsp_transport_free (st);
904
905   /* construct the response now */
906   code = GST_RTSP_STS_OK;
907   gst_rtsp_message_init_response (&response, code,
908       gst_rtsp_status_as_text (code), request);
909
910   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
911   g_free (trans_str);
912
913   send_response (client, session, &response);
914
915   /* update the state */
916   switch (media->state) {
917     case GST_RTSP_STATE_PLAYING:
918     case GST_RTSP_STATE_RECORDING:
919     case GST_RTSP_STATE_READY:
920       /* no state change */
921       break;
922     default:
923       media->state = GST_RTSP_STATE_READY;
924       break;
925   }
926   g_object_unref (session);
927
928   return TRUE;
929
930   /* ERRORS */
931 bad_request:
932   {
933     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
934     return FALSE;
935   }
936 not_found:
937   {
938     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
939     g_object_unref (session);
940     return FALSE;
941   }
942 no_stream:
943   {
944     send_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
945     g_object_unref (media);
946     g_object_unref (session);
947     return FALSE;
948   }
949 no_transport:
950   {
951     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
952     return FALSE;
953   }
954 unsupported_transports:
955   {
956     send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
957     gst_rtsp_transport_free (ct);
958     return FALSE;
959   }
960 no_pool:
961   {
962     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
963     return FALSE;
964   }
965 service_unavailable:
966   {
967     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
968     return FALSE;
969   }
970 }
971
972 static GstSDPMessage *
973 create_sdp (GstRTSPClient * client, GstRTSPMedia * media)
974 {
975   GstSDPMessage *sdp;
976   GstSDPInfo info;
977   const gchar *proto;
978
979   gst_sdp_message_new (&sdp);
980
981   /* some standard things first */
982   gst_sdp_message_set_version (sdp, "0");
983
984   if (client->is_ipv6)
985     proto = "IP6";
986   else
987     proto = "IP4";
988
989   gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", proto,
990       client->server_ip);
991
992   gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
993   gst_sdp_message_set_information (sdp, "rtsp-server");
994   gst_sdp_message_add_time (sdp, "0", "0", NULL);
995   gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
996   gst_sdp_message_add_attribute (sdp, "type", "broadcast");
997   gst_sdp_message_add_attribute (sdp, "control", "*");
998
999   info.server_proto = proto;
1000   if (media->protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST)
1001     info.server_ip = MCAST_ADDRESS;
1002   else
1003     info.server_ip = client->server_ip;
1004
1005   /* create an SDP for the media object */
1006   if (!gst_rtsp_sdp_from_media (sdp, &info, media))
1007     goto no_sdp;
1008
1009   return sdp;
1010
1011   /* ERRORS */
1012 no_sdp:
1013   {
1014     gst_sdp_message_free (sdp);
1015     return NULL;
1016   }
1017 }
1018
1019 /* for the describe we must generate an SDP */
1020 static gboolean
1021 handle_describe_request (GstRTSPClient * client, GstRTSPUrl * uri,
1022     GstRTSPSession * session, GstRTSPMessage * request)
1023 {
1024   GstRTSPMessage response = { 0 };
1025   GstRTSPResult res;
1026   GstSDPMessage *sdp;
1027   guint i, str_len;
1028   gchar *str, *content_base;
1029   GstRTSPMedia *media;
1030
1031   /* check what kind of format is accepted, we don't really do anything with it
1032    * and always return SDP for now. */
1033   for (i = 0; i++;) {
1034     gchar *accept;
1035
1036     res =
1037         gst_rtsp_message_get_header (request, GST_RTSP_HDR_ACCEPT, &accept, i);
1038     if (res == GST_RTSP_ENOTIMPL)
1039       break;
1040
1041     if (g_ascii_strcasecmp (accept, "application/sdp") == 0)
1042       break;
1043   }
1044
1045   /* find the media object for the uri */
1046   if (!(media = find_media (client, uri, request)))
1047     goto no_media;
1048
1049   /* create an SDP for the media object on this client */
1050   if (!(sdp = create_sdp (client, media)))
1051     goto no_sdp;
1052
1053   g_object_unref (media);
1054
1055   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
1056       gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
1057
1058   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE,
1059       "application/sdp");
1060
1061   /* content base for some clients that might screw up creating the setup uri */
1062   str = gst_rtsp_url_get_request_uri (uri);
1063   str_len = strlen (str);
1064
1065   /* check for trailing '/' and append one */
1066   if (str[str_len - 1] != '/') {
1067     content_base = g_malloc (str_len + 2);
1068     memcpy (content_base, str, str_len);
1069     content_base[str_len] = '/';
1070     content_base[str_len + 1] = '\0';
1071     g_free (str);
1072   } else {
1073     content_base = str;
1074   }
1075
1076   GST_INFO ("adding content-base: %s", content_base);
1077
1078   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE,
1079       content_base);
1080   g_free (content_base);
1081
1082   /* add SDP to the response body */
1083   str = gst_sdp_message_as_text (sdp);
1084   gst_rtsp_message_take_body (&response, (guint8 *) str, strlen (str));
1085   gst_sdp_message_free (sdp);
1086
1087   send_response (client, session, &response);
1088
1089   return TRUE;
1090
1091   /* ERRORS */
1092 no_media:
1093   {
1094     /* error reply is already sent */
1095     return FALSE;
1096   }
1097 no_sdp:
1098   {
1099     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1100     g_object_unref (media);
1101     return FALSE;
1102   }
1103 }
1104
1105 static gboolean
1106 handle_options_request (GstRTSPClient * client, GstRTSPUrl * uri,
1107     GstRTSPSession * session, GstRTSPMessage * request)
1108 {
1109   GstRTSPMessage response = { 0 };
1110   GstRTSPMethod options;
1111   gchar *str;
1112
1113   options = GST_RTSP_DESCRIBE |
1114       GST_RTSP_OPTIONS |
1115       GST_RTSP_PAUSE |
1116       GST_RTSP_PLAY |
1117       GST_RTSP_SETUP |
1118       GST_RTSP_GET_PARAMETER | GST_RTSP_SET_PARAMETER | GST_RTSP_TEARDOWN;
1119
1120   str = gst_rtsp_options_as_text (options);
1121
1122   gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
1123       gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
1124
1125   gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
1126   g_free (str);
1127
1128   send_response (client, session, &response);
1129
1130   return TRUE;
1131 }
1132
1133 /* remove duplicate and trailing '/' */
1134 static void
1135 santize_uri (GstRTSPUrl * uri)
1136 {
1137   gint i, len;
1138   gchar *s, *d;
1139   gboolean have_slash, prev_slash;
1140
1141   s = d = uri->abspath;
1142   len = strlen (uri->abspath);
1143
1144   prev_slash = FALSE;
1145
1146   for (i = 0; i < len; i++) {
1147     have_slash = s[i] == '/';
1148     *d = s[i];
1149     if (!have_slash || !prev_slash)
1150       d++;
1151     prev_slash = have_slash;
1152   }
1153   len = d - uri->abspath;
1154   /* don't remove the first slash if that's the only thing left */
1155   if (len > 1 && *(d - 1) == '/')
1156     d--;
1157   *d = '\0';
1158 }
1159
1160 static void
1161 client_session_finalized (GstRTSPClient * client, GstRTSPSession * session)
1162 {
1163   GST_INFO ("client %p: session %p finished", client, session);
1164
1165   /* unlink all media managed in this session */
1166   client_unlink_session (client, session);
1167
1168   /* remove the session */
1169   if (!(client->sessions = g_list_remove (client->sessions, session))) {
1170     GST_INFO ("client %p: all sessions finalized, close the connection",
1171         client);
1172     close_connection (client);
1173   }
1174 }
1175
1176 static void
1177 client_watch_session (GstRTSPClient * client, GstRTSPSession * session)
1178 {
1179   GList *walk;
1180
1181   for (walk = client->sessions; walk; walk = g_list_next (walk)) {
1182     GstRTSPSession *msession = (GstRTSPSession *) walk->data;
1183
1184     /* we already know about this session */
1185     if (msession == session)
1186       return;
1187   }
1188
1189   GST_INFO ("watching session %p", session);
1190
1191   g_object_weak_ref (G_OBJECT (session), (GWeakNotify) client_session_finalized,
1192       client);
1193   client->sessions = g_list_prepend (client->sessions, session);
1194 }
1195
1196 static void
1197 handle_request (GstRTSPClient * client, GstRTSPMessage * request)
1198 {
1199   GstRTSPMethod method;
1200   const gchar *uristr;
1201   GstRTSPUrl *uri;
1202   GstRTSPVersion version;
1203   GstRTSPResult res;
1204   GstRTSPSession *session;
1205   gchar *sessid;
1206
1207   if (gst_debug_category_get_threshold (rtsp_client_debug) >= GST_LEVEL_LOG) {
1208     gst_rtsp_message_dump (request);
1209   }
1210
1211   GST_INFO ("client %p: received a request", client);
1212
1213   gst_rtsp_message_parse_request (request, &method, &uristr, &version);
1214
1215   if (version != GST_RTSP_VERSION_1_0) {
1216     /* we can only handle 1.0 requests */
1217     send_generic_response (client, GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
1218         request);
1219     return;
1220   }
1221
1222   /* we always try to parse the url first */
1223   if ((res = gst_rtsp_url_parse (uristr, &uri)) != GST_RTSP_OK) {
1224     send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
1225     return;
1226   }
1227
1228   /* sanitize the uri */
1229   santize_uri (uri);
1230
1231   /* get the session if there is any */
1232   res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
1233   if (res == GST_RTSP_OK) {
1234     if (client->session_pool == NULL)
1235       goto no_pool;
1236
1237     /* we had a session in the request, find it again */
1238     if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
1239       goto session_not_found;
1240
1241     /* we add the session to the client list of watched sessions. When a session
1242      * disappears because it times out, we will be notified. If all sessions are
1243      * gone, we will close the connection */
1244     client_watch_session (client, session);
1245   } else
1246     session = NULL;
1247
1248   /* now see what is asked and dispatch to a dedicated handler */
1249   switch (method) {
1250     case GST_RTSP_OPTIONS:
1251       handle_options_request (client, uri, session, request);
1252       break;
1253     case GST_RTSP_DESCRIBE:
1254       handle_describe_request (client, uri, session, request);
1255       break;
1256     case GST_RTSP_SETUP:
1257       handle_setup_request (client, uri, session, request);
1258       break;
1259     case GST_RTSP_PLAY:
1260       handle_play_request (client, uri, session, request);
1261       break;
1262     case GST_RTSP_PAUSE:
1263       handle_pause_request (client, uri, session, request);
1264       break;
1265     case GST_RTSP_TEARDOWN:
1266       handle_teardown_request (client, uri, session, request);
1267       break;
1268     case GST_RTSP_SET_PARAMETER:
1269       handle_set_param_request (client, uri, session, request);
1270       break;
1271     case GST_RTSP_GET_PARAMETER:
1272       handle_get_param_request (client, uri, session, request);
1273       break;
1274     case GST_RTSP_ANNOUNCE:
1275     case GST_RTSP_RECORD:
1276     case GST_RTSP_REDIRECT:
1277       send_generic_response (client, GST_RTSP_STS_NOT_IMPLEMENTED, request);
1278       break;
1279     case GST_RTSP_INVALID:
1280     default:
1281       send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request);
1282       break;
1283   }
1284   if (session)
1285     g_object_unref (session);
1286
1287   gst_rtsp_url_free (uri);
1288   return;
1289
1290   /* ERRORS */
1291 no_pool:
1292   {
1293     send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
1294     return;
1295   }
1296 session_not_found:
1297   {
1298     send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
1299     return;
1300   }
1301 }
1302
1303 static void
1304 handle_data (GstRTSPClient * client, GstRTSPMessage * message)
1305 {
1306   GstRTSPResult res;
1307   guint8 channel;
1308   GList *walk;
1309   guint8 *data;
1310   guint size;
1311   GstBuffer *buffer;
1312   gboolean handled;
1313
1314   /* find the stream for this message */
1315   res = gst_rtsp_message_parse_data (message, &channel);
1316   if (res != GST_RTSP_OK)
1317     return;
1318
1319   gst_rtsp_message_steal_body (message, &data, &size);
1320
1321   buffer = gst_buffer_new ();
1322   GST_BUFFER_DATA (buffer) = data;
1323   GST_BUFFER_MALLOCDATA (buffer) = data;
1324   GST_BUFFER_SIZE (buffer) = size;
1325
1326   handled = FALSE;
1327   for (walk = client->streams; walk; walk = g_list_next (walk)) {
1328     GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
1329     GstRTSPMediaStream *mstream;
1330     GstRTSPTransport *tr;
1331
1332     /* get the transport, if there is no transport configured, skip this stream */
1333     if (!(tr = stream->trans.transport))
1334       continue;
1335
1336     /* we also need a media stream */
1337     if (!(mstream = stream->media_stream))
1338       continue;
1339
1340     /* check for TCP transport */
1341     if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
1342       /* dispatch to the stream based on the channel number */
1343       if (tr->interleaved.min == channel) {
1344         gst_rtsp_media_stream_rtp (mstream, buffer);
1345         handled = TRUE;
1346         break;
1347       } else if (tr->interleaved.max == channel) {
1348         gst_rtsp_media_stream_rtcp (mstream, buffer);
1349         handled = TRUE;
1350         break;
1351       }
1352     }
1353   }
1354   if (!handled)
1355     gst_buffer_unref (buffer);
1356 }
1357
1358 /**
1359  * gst_rtsp_client_set_session_pool:
1360  * @client: a #GstRTSPClient
1361  * @pool: a #GstRTSPSessionPool
1362  *
1363  * Set @pool as the sessionpool for @client which it will use to find
1364  * or allocate sessions. the sessionpool is usually inherited from the server
1365  * that created the client but can be overridden later.
1366  */
1367 void
1368 gst_rtsp_client_set_session_pool (GstRTSPClient * client,
1369     GstRTSPSessionPool * pool)
1370 {
1371   GstRTSPSessionPool *old;
1372
1373   old = client->session_pool;
1374   if (old != pool) {
1375     if (pool)
1376       g_object_ref (pool);
1377     client->session_pool = pool;
1378     if (old)
1379       g_object_unref (old);
1380   }
1381 }
1382
1383 /**
1384  * gst_rtsp_client_get_session_pool:
1385  * @client: a #GstRTSPClient
1386  *
1387  * Get the #GstRTSPSessionPool object that @client uses to manage its sessions.
1388  *
1389  * Returns: a #GstRTSPSessionPool, unref after usage.
1390  */
1391 GstRTSPSessionPool *
1392 gst_rtsp_client_get_session_pool (GstRTSPClient * client)
1393 {
1394   GstRTSPSessionPool *result;
1395
1396   if ((result = client->session_pool))
1397     g_object_ref (result);
1398
1399   return result;
1400 }
1401
1402 /**
1403  * gst_rtsp_client_set_media_mapping:
1404  * @client: a #GstRTSPClient
1405  * @mapping: a #GstRTSPMediaMapping
1406  *
1407  * Set @mapping as the media mapping for @client which it will use to map urls
1408  * to media streams. These mapping is usually inherited from the server that
1409  * created the client but can be overriden later.
1410  */
1411 void
1412 gst_rtsp_client_set_media_mapping (GstRTSPClient * client,
1413     GstRTSPMediaMapping * mapping)
1414 {
1415   GstRTSPMediaMapping *old;
1416
1417   old = client->media_mapping;
1418
1419   if (old != mapping) {
1420     if (mapping)
1421       g_object_ref (mapping);
1422     client->media_mapping = mapping;
1423     if (old)
1424       g_object_unref (old);
1425   }
1426 }
1427
1428 /**
1429  * gst_rtsp_client_get_media_mapping:
1430  * @client: a #GstRTSPClient
1431  *
1432  * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions.
1433  *
1434  * Returns: a #GstRTSPMediaMapping, unref after usage.
1435  */
1436 GstRTSPMediaMapping *
1437 gst_rtsp_client_get_media_mapping (GstRTSPClient * client)
1438 {
1439   GstRTSPMediaMapping *result;
1440
1441   if ((result = client->media_mapping))
1442     g_object_ref (result);
1443
1444   return result;
1445 }
1446
1447 static GstRTSPResult
1448 message_received (GstRTSPWatch * watch, GstRTSPMessage * message,
1449     gpointer user_data)
1450 {
1451   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1452
1453   switch (message->type) {
1454     case GST_RTSP_MESSAGE_REQUEST:
1455       handle_request (client, message);
1456       break;
1457     case GST_RTSP_MESSAGE_RESPONSE:
1458       break;
1459     case GST_RTSP_MESSAGE_DATA:
1460       handle_data (client, message);
1461       break;
1462     default:
1463       break;
1464   }
1465   return GST_RTSP_OK;
1466 }
1467
1468 static GstRTSPResult
1469 message_sent (GstRTSPWatch * watch, guint cseq, gpointer user_data)
1470 {
1471   GstRTSPClient *client;
1472
1473   client = GST_RTSP_CLIENT (user_data);
1474
1475   /* GST_INFO ("client %p: sent a message with cseq %d", client, cseq); */
1476
1477   return GST_RTSP_OK;
1478 }
1479
1480 static GstRTSPResult
1481 closed (GstRTSPWatch * watch, gpointer user_data)
1482 {
1483   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1484   const gchar *tunnelid;
1485
1486   GST_INFO ("client %p: connection closed", client);
1487
1488   if ((tunnelid = gst_rtsp_connection_get_tunnelid (client->connection))) {
1489     g_mutex_lock (tunnels_lock);
1490     /* remove from tunnelids */
1491     g_hash_table_remove (tunnels, tunnelid);
1492     g_mutex_unlock (tunnels_lock);
1493   }
1494
1495   return GST_RTSP_OK;
1496 }
1497
1498 static GstRTSPResult
1499 error (GstRTSPWatch * watch, GstRTSPResult result, gpointer user_data)
1500 {
1501   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1502   gchar *str;
1503
1504   str = gst_rtsp_strresult (result);
1505   GST_INFO ("client %p: received an error %s", client, str);
1506   g_free (str);
1507
1508   return GST_RTSP_OK;
1509 }
1510
1511 static GstRTSPResult
1512 error_full (GstRTSPWatch * watch, GstRTSPResult result,
1513     GstRTSPMessage * message, guint id, gpointer user_data)
1514 {
1515   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1516   gchar *str;
1517
1518   str = gst_rtsp_strresult (result);
1519   GST_INFO
1520       ("client %p: received an error %s when handling message %p with id %d",
1521       client, str, message, id);
1522   g_free (str);
1523
1524   return GST_RTSP_OK;
1525 }
1526
1527 static gboolean
1528 remember_tunnel (GstRTSPClient * client)
1529 {
1530   const gchar *tunnelid;
1531
1532   /* store client in the pending tunnels */
1533   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1534   if (tunnelid == NULL)
1535     goto no_tunnelid;
1536
1537   GST_INFO ("client %p: inserting tunnel session %s", client, tunnelid);
1538
1539   /* we can't have two clients connecting with the same tunnelid */
1540   g_mutex_lock (tunnels_lock);
1541   if (g_hash_table_lookup (tunnels, tunnelid))
1542     goto tunnel_existed;
1543
1544   g_hash_table_insert (tunnels, g_strdup (tunnelid), g_object_ref (client));
1545   g_mutex_unlock (tunnels_lock);
1546
1547   return TRUE;
1548
1549   /* ERRORS */
1550 no_tunnelid:
1551   {
1552     GST_ERROR ("client %p: no tunnelid provided", client);
1553     return FALSE;
1554   }
1555 tunnel_existed:
1556   {
1557     g_mutex_unlock (tunnels_lock);
1558     GST_ERROR ("client %p: tunnel session %s already existed", client,
1559         tunnelid);
1560     return FALSE;
1561   }
1562 }
1563
1564 static GstRTSPStatusCode
1565 tunnel_start (GstRTSPWatch * watch, gpointer user_data)
1566 {
1567   GstRTSPClient *client;
1568
1569   client = GST_RTSP_CLIENT (user_data);
1570
1571   GST_INFO ("client %p: tunnel start (connection %p)", client,
1572       client->connection);
1573
1574   if (!remember_tunnel (client))
1575     goto tunnel_error;
1576
1577   return GST_RTSP_STS_OK;
1578
1579   /* ERRORS */
1580 tunnel_error:
1581   {
1582     GST_ERROR ("client %p: error starting tunnel", client);
1583     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1584   }
1585 }
1586
1587 static GstRTSPResult
1588 tunnel_lost (GstRTSPWatch * watch, gpointer user_data)
1589 {
1590   GstRTSPClient *client;
1591
1592   client = GST_RTSP_CLIENT (user_data);
1593
1594   GST_INFO ("client %p: tunnel lost (connection %p)", client,
1595       client->connection);
1596
1597   /* ignore error, it'll only be a problem when the client does a POST again */
1598   remember_tunnel (client);
1599
1600   return GST_RTSP_OK;
1601 }
1602
1603 static GstRTSPResult
1604 tunnel_complete (GstRTSPWatch * watch, gpointer user_data)
1605 {
1606   const gchar *tunnelid;
1607   GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
1608   GstRTSPClient *oclient;
1609
1610   GST_INFO ("client %p: tunnel complete", client);
1611
1612   /* find previous tunnel */
1613   tunnelid = gst_rtsp_connection_get_tunnelid (client->connection);
1614   if (tunnelid == NULL)
1615     goto no_tunnelid;
1616
1617   g_mutex_lock (tunnels_lock);
1618   if (!(oclient = g_hash_table_lookup (tunnels, tunnelid)))
1619     goto no_tunnel;
1620
1621   /* remove the old client from the table. ref before because removing it will
1622    * remove the ref to it. */
1623   g_object_ref (oclient);
1624   g_hash_table_remove (tunnels, tunnelid);
1625
1626   if (oclient->watch == NULL)
1627     goto tunnel_closed;
1628   g_mutex_unlock (tunnels_lock);
1629
1630   GST_INFO ("client %p: found tunnel %p (old %p, new %p)", client, oclient,
1631       oclient->connection, client->connection);
1632
1633   /* merge the tunnels into the first client */
1634   gst_rtsp_connection_do_tunnel (oclient->connection, client->connection);
1635   gst_rtsp_watch_reset (oclient->watch);
1636   g_object_unref (oclient);
1637
1638   /* we don't need this watch anymore */
1639   g_source_destroy ((GSource *) client->watch);
1640   client->watchid = 0;
1641   client->watch = NULL;
1642
1643   return GST_RTSP_OK;
1644
1645   /* ERRORS */
1646 no_tunnelid:
1647   {
1648     GST_INFO ("client %p: no tunnelid provided", client);
1649     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1650   }
1651 no_tunnel:
1652   {
1653     g_mutex_unlock (tunnels_lock);
1654     GST_INFO ("client %p: tunnel session %s not found", client, tunnelid);
1655     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1656   }
1657 tunnel_closed:
1658   {
1659     g_mutex_unlock (tunnels_lock);
1660     GST_INFO ("client %p: tunnel session %s was closed", client, tunnelid);
1661     g_object_unref (oclient);
1662     return GST_RTSP_STS_SERVICE_UNAVAILABLE;
1663   }
1664 }
1665
1666 static GstRTSPWatchFuncs watch_funcs = {
1667   message_received,
1668   message_sent,
1669   closed,
1670   error,
1671   tunnel_start,
1672   tunnel_complete,
1673   error_full,
1674   tunnel_lost
1675 };
1676
1677 static void
1678 client_watch_notify (GstRTSPClient * client)
1679 {
1680   GST_INFO ("client %p: watch destroyed", client);
1681   client->watchid = 0;
1682   client->watch = NULL;
1683   g_object_unref (client);
1684 }
1685
1686 /**
1687  * gst_rtsp_client_attach:
1688  * @client: a #GstRTSPClient
1689  * @channel: a #GIOChannel
1690  *
1691  * Accept a new connection for @client on the socket in @channel. 
1692  *
1693  * This function should be called when the client properties and urls are fully
1694  * configured and the client is ready to start.
1695  *
1696  * Returns: %TRUE if the client could be accepted.
1697  */
1698 gboolean
1699 gst_rtsp_client_accept (GstRTSPClient * client, GIOChannel * channel)
1700 {
1701   int sock, fd;
1702   GstRTSPConnection *conn;
1703   GstRTSPResult res;
1704   GSource *source;
1705   GMainContext *context;
1706   GstRTSPUrl *url;
1707   struct sockaddr_storage addr;
1708   socklen_t addrlen;
1709   gchar ip[INET6_ADDRSTRLEN];
1710
1711   /* a new client connected. */
1712   sock = g_io_channel_unix_get_fd (channel);
1713
1714   GST_RTSP_CHECK (gst_rtsp_connection_accept (sock, &conn), accept_failed);
1715
1716   fd = gst_rtsp_connection_get_readfd (conn);
1717
1718   addrlen = sizeof (addr);
1719   if (getsockname (fd, (struct sockaddr *) &addr, &addrlen) < 0)
1720     goto getpeername_failed;
1721
1722   client->is_ipv6 = addr.ss_family == AF_INET6;
1723
1724   addrlen = sizeof (addr);
1725   if (getnameinfo ((struct sockaddr *) &addr, addrlen, ip, sizeof (ip), NULL, 0,
1726           NI_NUMERICHOST) != 0)
1727     goto getnameinfo_failed;
1728
1729   /* keep the original ip that the client connected to */
1730   g_free (client->server_ip);
1731   client->server_ip = g_strndup (ip, sizeof (ip));
1732
1733   GST_INFO ("client %p connected to server ip %s, ipv6 = %d", client,
1734       client->server_ip, client->is_ipv6);
1735
1736   url = gst_rtsp_connection_get_url (conn);
1737   GST_INFO ("added new client %p ip %s:%d", client, url->host, url->port);
1738
1739   client->connection = conn;
1740
1741   /* create watch for the connection and attach */
1742   client->watch = gst_rtsp_watch_new (client->connection, &watch_funcs,
1743       g_object_ref (client), (GDestroyNotify) client_watch_notify);
1744
1745   /* find the context to add the watch */
1746   if ((source = g_main_current_source ()))
1747     context = g_source_get_context (source);
1748   else
1749     context = NULL;
1750
1751   GST_INFO ("attaching to context %p", context);
1752
1753   client->watchid = gst_rtsp_watch_attach (client->watch, context);
1754   gst_rtsp_watch_unref (client->watch);
1755
1756   return TRUE;
1757
1758   /* ERRORS */
1759 accept_failed:
1760   {
1761     gchar *str = gst_rtsp_strresult (res);
1762
1763     GST_ERROR ("Could not accept client on server socket %d: %s", sock, str);
1764     g_free (str);
1765     return FALSE;
1766   }
1767 getpeername_failed:
1768   {
1769     GST_ERROR ("getpeername failed: %s", g_strerror (errno));
1770     return FALSE;
1771   }
1772 getnameinfo_failed:
1773   {
1774     GST_ERROR ("getnameinfo failed: %s", g_strerror (errno));
1775     return FALSE;
1776   }
1777 }