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