22f3d0b5b954a3a5895b037e82a05f99afc42c00
[platform/upstream/gstreamer.git] / gst-libs / gst / rtsp / gstrtspconnection.c
1 /* GStreamer
2  * Copyright (C) <2005-2009> Wim Taymans <wim.taymans@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  * Unless otherwise indicated, Source Code is licensed under MIT license.
21  * See further explanation attached in License Statement (distributed in the file
22  * LICENSE).
23  *
24  * Permission is hereby granted, free of charge, to any person obtaining a copy of
25  * this software and associated documentation files (the "Software"), to deal in
26  * the Software without restriction, including without limitation the rights to
27  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28  * of the Software, and to permit persons to whom the Software is furnished to do
29  * so, subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be included in all
32  * copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40  * SOFTWARE.
41  */
42
43 /**
44  * SECTION:gstrtspconnection
45  * @short_description: manage RTSP connections
46  * @see_also: gstrtspurl
47  *  
48  * This object manages the RTSP connection to the server. It provides function
49  * to receive and send bytes and messages.
50  *  
51  * Last reviewed on 2007-07-24 (0.10.14)
52  */
53
54 #ifdef HAVE_CONFIG_H
55 #  include <config.h>
56 #endif
57
58 #include <stdio.h>
59 #include <errno.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <time.h>
63
64 /* we include this here to get the G_OS_* defines */
65 #include <glib.h>
66 #include <gst/gst.h>
67
68 #include "gstrtspconnection.h"
69
70 #include "gst/glib-compat-private.h"
71
72 #ifdef IP_TOS
73 union gst_sockaddr
74 {
75   struct sockaddr sa;
76   struct sockaddr_in sa_in;
77   struct sockaddr_in6 sa_in6;
78   struct sockaddr_storage sa_stor;
79 };
80 #endif
81
82 typedef struct
83 {
84   gint state;
85   guint save;
86   guchar out[3];                /* the size must be evenly divisible by 3 */
87   guint cout;
88   guint coutl;
89 } DecodeCtx;
90
91 #ifdef MSG_NOSIGNAL
92 #define SEND_FLAGS MSG_NOSIGNAL
93 #else
94 #define SEND_FLAGS 0
95 #endif
96
97 typedef enum
98 {
99   TUNNEL_STATE_NONE,
100   TUNNEL_STATE_GET,
101   TUNNEL_STATE_POST,
102   TUNNEL_STATE_COMPLETE
103 } GstRTSPTunnelState;
104
105 #define TUNNELID_LEN   24
106
107 struct _GstRTSPConnection
108 {
109   /*< private > */
110   /* URL for the connection */
111   GstRTSPUrl *url;
112
113   /* connection state */
114   GSocket *read_socket;
115   GSocket *write_socket;
116   gboolean manual_http;
117   GSocket *socket0, *socket1;
118   GCancellable *cancellable;
119
120   gchar tunnelid[TUNNELID_LEN];
121   gboolean tunneled;
122   GstRTSPTunnelState tstate;
123
124   gchar *ip;
125
126   gint read_ahead;
127
128   gchar *initial_buffer;
129   gsize initial_buffer_offset;
130
131   /* Session state */
132   gint cseq;                    /* sequence number */
133   gchar session_id[512];        /* session id */
134   gint timeout;                 /* session timeout in seconds */
135   GTimer *timer;                /* timeout timer */
136
137   /* Authentication */
138   GstRTSPAuthMethod auth_method;
139   gchar *username;
140   gchar *passwd;
141   GHashTable *auth_params;
142
143   DecodeCtx ctx;
144   DecodeCtx *ctxp;
145
146   gchar *proxy_host;
147   guint proxy_port;
148 };
149
150 enum
151 {
152   STATE_START = 0,
153   STATE_DATA_HEADER,
154   STATE_DATA_BODY,
155   STATE_READ_LINES,
156   STATE_END,
157   STATE_LAST
158 };
159
160 enum
161 {
162   READ_AHEAD_EOH = -1,          /* end of headers */
163   READ_AHEAD_CRLF = -2,
164   READ_AHEAD_CRLFCR = -3
165 };
166
167 /* a structure for constructing RTSPMessages */
168 typedef struct
169 {
170   gint state;
171   GstRTSPResult status;
172   guint8 buffer[4096];
173   guint offset;
174
175   guint line;
176   guint8 *body_data;
177   glong body_len;
178 } GstRTSPBuilder;
179
180 static void
181 build_reset (GstRTSPBuilder * builder)
182 {
183   g_free (builder->body_data);
184   memset (builder, 0, sizeof (GstRTSPBuilder));
185 }
186
187 /**
188  * gst_rtsp_connection_create:
189  * @url: a #GstRTSPUrl 
190  * @conn: storage for a #GstRTSPConnection
191  *
192  * Create a newly allocated #GstRTSPConnection from @url and store it in @conn.
193  * The connection will not yet attempt to connect to @url, use
194  * gst_rtsp_connection_connect().
195  *
196  * A copy of @url will be made.
197  *
198  * Returns: #GST_RTSP_OK when @conn contains a valid connection.
199  */
200 GstRTSPResult
201 gst_rtsp_connection_create (const GstRTSPUrl * url, GstRTSPConnection ** conn)
202 {
203   GstRTSPConnection *newconn;
204
205   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
206
207   newconn = g_new0 (GstRTSPConnection, 1);
208
209   newconn->cancellable = g_cancellable_new ();
210
211   newconn->url = gst_rtsp_url_copy (url);
212   newconn->timer = g_timer_new ();
213   newconn->timeout = 60;
214   newconn->cseq = 1;
215
216   newconn->auth_method = GST_RTSP_AUTH_NONE;
217   newconn->username = NULL;
218   newconn->passwd = NULL;
219   newconn->auth_params = NULL;
220
221   *conn = newconn;
222
223   return GST_RTSP_OK;
224 }
225
226 /**
227  * gst_rtsp_connection_create_from_socket:
228  * @socket: a #GSocket
229  * @ip: the IP address of the other end
230  * @port: the port used by the other end
231  * @initial_buffer: data already read from @fd
232  * @conn: storage for a #GstRTSPConnection
233  *
234  * Create a new #GstRTSPConnection for handling communication on the existing
235  * socket @socket. The @initial_buffer contains any data already read from
236  * @socket which should be used before starting to read new data.
237  *
238  * Returns: #GST_RTSP_OK when @conn contains a valid connection.
239  *
240  * Since: 0.10.25
241  */
242 GstRTSPResult
243 gst_rtsp_connection_create_from_socket (GSocket * socket, const gchar * ip,
244     guint16 port, const gchar * initial_buffer, GstRTSPConnection ** conn)
245 {
246   GstRTSPConnection *newconn = NULL;
247   GstRTSPUrl *url;
248   GstRTSPResult res;
249
250   g_return_val_if_fail (G_IS_SOCKET (socket), GST_RTSP_EINVAL);
251   g_return_val_if_fail (ip != NULL, GST_RTSP_EINVAL);
252   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
253
254   /* set to non-blocking mode so that we can cancel the communication */
255   g_socket_set_blocking (socket, FALSE);
256
257   /* create a url for the client address */
258   url = g_new0 (GstRTSPUrl, 1);
259   url->host = g_strdup (ip);
260   url->port = port;
261
262   /* now create the connection object */
263   GST_RTSP_CHECK (gst_rtsp_connection_create (url, &newconn), newconn_failed);
264   gst_rtsp_url_free (url);
265
266   /* both read and write initially */
267   newconn->socket0 = G_SOCKET (g_object_ref (socket));
268   newconn->socket1 = G_SOCKET (g_object_ref (socket));
269   newconn->write_socket = newconn->read_socket = newconn->socket0;
270
271   newconn->ip = g_strdup (ip);
272
273   newconn->initial_buffer = g_strdup (initial_buffer);
274
275   *conn = newconn;
276
277   return GST_RTSP_OK;
278
279   /* ERRORS */
280 newconn_failed:
281   {
282     gst_rtsp_url_free (url);
283     return res;
284   }
285 }
286
287 /**
288  * gst_rtsp_connection_accept:
289  * @socket: a socket
290  * @conn: storage for a #GstRTSPConnection
291  * @cancellable: a #GCancellable to cancel the operation
292  *
293  * Accept a new connection on @socket and create a new #GstRTSPConnection for
294  * handling communication on new socket.
295  *
296  * Returns: #GST_RTSP_OK when @conn contains a valid connection.
297  *
298  * Since: 0.10.23
299  */
300 GstRTSPResult
301 gst_rtsp_connection_accept (GSocket * socket, GstRTSPConnection ** conn,
302     GCancellable * cancellable)
303 {
304   GError *err = NULL;
305   gchar *ip;
306   guint16 port;
307   GSocket *client_sock;
308   GSocketAddress *addr;
309   GstRTSPResult ret;
310
311   g_return_val_if_fail (G_IS_SOCKET (socket), GST_RTSP_EINVAL);
312   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
313
314   client_sock = g_socket_accept (socket, cancellable, &err);
315   if (!client_sock)
316     goto accept_failed;
317
318   addr = g_socket_get_remote_address (client_sock, &err);
319   if (!addr)
320     goto getnameinfo_failed;
321
322   ip = g_inet_address_to_string (g_inet_socket_address_get_address
323       (G_INET_SOCKET_ADDRESS (addr)));
324   port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
325
326   ret =
327       gst_rtsp_connection_create_from_socket (client_sock, ip, port, NULL,
328       conn);
329   g_free (ip);
330
331   return ret;
332
333   /* ERRORS */
334 accept_failed:
335   {
336     GST_DEBUG ("Accepting client failed: %s", err->message);
337     g_clear_error (&err);
338     return GST_RTSP_ESYS;
339   }
340 getnameinfo_failed:
341   {
342     if (!g_socket_close (client_sock, &err)) {
343       GST_DEBUG ("Closing socket failed: %s", err->message);
344       g_clear_error (&err);
345     }
346     g_object_unref (client_sock);
347     return GST_RTSP_ERROR;
348   }
349 }
350
351 static gchar *
352 do_resolve (const gchar * host, GCancellable * cancellable)
353 {
354   GResolver *resolver;
355   GInetAddress *addr;
356   GError *err = NULL;
357   gchar *ip;
358
359   addr = g_inet_address_new_from_string (host);
360   if (!addr) {
361     GList *results, *l;
362
363     resolver = g_resolver_get_default ();
364
365     results = g_resolver_lookup_by_name (resolver, host, cancellable, &err);
366     if (!results)
367       goto name_resolve;
368
369     for (l = results; l; l = l->next) {
370       GInetAddress *tmp = l->data;
371
372       if (g_inet_address_get_family (tmp) == G_SOCKET_FAMILY_IPV4 ||
373           g_inet_address_get_family (tmp) == G_SOCKET_FAMILY_IPV6) {
374         addr = G_INET_ADDRESS (g_object_ref (tmp));
375         break;
376       }
377     }
378
379     g_resolver_free_addresses (results);
380     g_object_unref (resolver);
381   }
382
383   if (!addr)
384     return NULL;
385
386   ip = g_inet_address_to_string (addr);
387   g_object_unref (addr);
388
389   return ip;
390
391   /* ERRORS */
392 name_resolve:
393   {
394     GST_ERROR ("failed to resolve %s: %s", host, err->message);
395     g_clear_error (&err);
396     g_object_unref (resolver);
397     return NULL;
398   }
399 }
400
401 static GstRTSPResult
402 do_connect (const gchar * ip, guint16 port, GSocket ** socket_out,
403     GTimeVal * timeout, GCancellable * cancellable)
404 {
405   GSocket *socket;
406   GstClockTime to;
407   GInetAddress *addr;
408   GSocketAddress *saddr;
409   GError *err = NULL;
410
411   addr = g_inet_address_new_from_string (ip);
412   g_assert (addr);
413   saddr = g_inet_socket_address_new (addr, port);
414   g_object_unref (addr);
415
416   socket =
417       g_socket_new (g_socket_address_get_family (saddr), G_SOCKET_TYPE_STREAM,
418       G_SOCKET_PROTOCOL_TCP, &err);
419   if (socket == NULL)
420     goto no_socket;
421
422   /* set to non-blocking mode so that we can cancel the connect */
423   g_socket_set_blocking (socket, FALSE);
424
425   /* we are going to connect ASYNC now */
426   if (!g_socket_connect (socket, saddr, cancellable, &err)) {
427     if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_PENDING))
428       goto sys_error;
429     g_clear_error (&err);
430   } else {
431     goto done;
432   }
433
434   /* wait for connect to complete up to the specified timeout or until we got
435    * interrupted. */
436   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
437
438   g_socket_set_timeout (socket, (to + GST_SECOND - 1) / GST_SECOND);
439   if (!g_socket_condition_wait (socket, G_IO_OUT, cancellable, &err)) {
440     g_socket_set_timeout (socket, 0);
441     if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
442       goto timeout;
443     else
444       goto sys_error;
445   }
446   g_socket_set_timeout (socket, 0);
447
448   if (!g_socket_check_connect_result (socket, &err))
449     goto sys_error;
450
451 done:
452   g_object_unref (saddr);
453
454   *socket_out = socket;
455
456   return GST_RTSP_OK;
457
458   /* ERRORS */
459 no_socket:
460   {
461     GST_ERROR ("no socket: %s", err->message);
462     g_clear_error (&err);
463     g_object_unref (saddr);
464     return GST_RTSP_ESYS;
465   }
466 sys_error:
467   {
468     GST_ERROR ("system error: %s", err->message);
469     g_clear_error (&err);
470     g_object_unref (saddr);
471     g_object_unref (socket);
472     return GST_RTSP_ESYS;
473   }
474 timeout:
475   {
476     GST_ERROR ("timeout");
477     g_clear_error (&err);
478     g_object_unref (saddr);
479     g_object_unref (socket);
480     return GST_RTSP_ETIMEOUT;
481   }
482 }
483
484 static GstRTSPResult
485 setup_tunneling (GstRTSPConnection * conn, GTimeVal * timeout)
486 {
487   gint i;
488   GstRTSPResult res;
489   gchar *ip;
490   gchar *uri;
491   gchar *value;
492   guint16 port, url_port;
493   GstRTSPUrl *url;
494   gchar *hostparam;
495   GstRTSPMessage *msg;
496   GstRTSPMessage response;
497   gboolean old_http;
498
499   memset (&response, 0, sizeof (response));
500   gst_rtsp_message_init (&response);
501
502   /* create a random sessionid */
503   for (i = 0; i < TUNNELID_LEN; i++)
504     conn->tunnelid[i] = g_random_int_range ('a', 'z');
505   conn->tunnelid[TUNNELID_LEN - 1] = '\0';
506
507   url = conn->url;
508   /* get the port from the url */
509   gst_rtsp_url_get_port (url, &url_port);
510
511   if (conn->proxy_host) {
512     uri = g_strdup_printf ("http://%s:%d%s%s%s", url->host, url_port,
513         url->abspath, url->query ? "?" : "", url->query ? url->query : "");
514     hostparam = g_strdup_printf ("%s:%d", url->host, url_port);
515     ip = conn->proxy_host;
516     port = conn->proxy_port;
517   } else {
518     uri = g_strdup_printf ("%s%s%s", url->abspath, url->query ? "?" : "",
519         url->query ? url->query : "");
520     hostparam = NULL;
521     ip = conn->ip;
522     port = url_port;
523   }
524
525   /* create the GET request for the read connection */
526   GST_RTSP_CHECK (gst_rtsp_message_new_request (&msg, GST_RTSP_GET, uri),
527       no_message);
528   msg->type = GST_RTSP_MESSAGE_HTTP_REQUEST;
529
530   if (hostparam != NULL)
531     gst_rtsp_message_add_header (msg, GST_RTSP_HDR_HOST, hostparam);
532   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SESSIONCOOKIE,
533       conn->tunnelid);
534   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_ACCEPT,
535       "application/x-rtsp-tunnelled");
536   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-cache");
537   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
538
539   /* we start by writing to this fd */
540   conn->write_socket = conn->socket0;
541
542   /* we need to temporarily set conn->tunneled to FALSE to prevent the HTTP
543    * request from being base64 encoded */
544   conn->tunneled = FALSE;
545   GST_RTSP_CHECK (gst_rtsp_connection_send (conn, msg, timeout), write_failed);
546   gst_rtsp_message_free (msg);
547   conn->tunneled = TRUE;
548
549   /* receive the response to the GET request */
550   /* we need to temporarily set manual_http to TRUE since
551    * gst_rtsp_connection_receive() will treat the HTTP response as a parsing
552    * failure otherwise */
553   old_http = conn->manual_http;
554   conn->manual_http = TRUE;
555   GST_RTSP_CHECK (gst_rtsp_connection_receive (conn, &response, timeout),
556       read_failed);
557   conn->manual_http = old_http;
558
559   if (response.type != GST_RTSP_MESSAGE_HTTP_RESPONSE ||
560       response.type_data.response.code != GST_RTSP_STS_OK)
561     goto wrong_result;
562
563   if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
564           &value, 0) == GST_RTSP_OK) {
565     if (conn->proxy_host) {
566       /* if we use a proxy we need to change the destination url */
567       g_free (url->host);
568       url->host = g_strdup (value);
569       g_free (hostparam);
570       hostparam = g_strdup_printf ("%s:%d", url->host, url_port);
571     } else {
572       /* and resolve the new ip address */
573       if (!(ip = do_resolve (value, conn->cancellable)))
574         goto not_resolved;
575       g_free (conn->ip);
576       conn->ip = ip;
577     }
578   }
579
580   /* connect to the host/port */
581   res = do_connect (ip, port, &conn->socket1, timeout, conn->cancellable);
582   if (res != GST_RTSP_OK)
583     goto connect_failed;
584
585   /* this is now our writing socket */
586   conn->write_socket = conn->socket1;
587
588   /* create the POST request for the write connection */
589   GST_RTSP_CHECK (gst_rtsp_message_new_request (&msg, GST_RTSP_POST, uri),
590       no_message);
591   msg->type = GST_RTSP_MESSAGE_HTTP_REQUEST;
592
593   if (hostparam != NULL)
594     gst_rtsp_message_add_header (msg, GST_RTSP_HDR_HOST, hostparam);
595   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SESSIONCOOKIE,
596       conn->tunnelid);
597   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_ACCEPT,
598       "application/x-rtsp-tunnelled");
599   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-cache");
600   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
601   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_EXPIRES,
602       "Sun, 9 Jan 1972 00:00:00 GMT");
603   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_LENGTH, "32767");
604
605   /* we need to temporarily set conn->tunneled to FALSE to prevent the HTTP
606    * request from being base64 encoded */
607   conn->tunneled = FALSE;
608   GST_RTSP_CHECK (gst_rtsp_connection_send (conn, msg, timeout), write_failed);
609   gst_rtsp_message_free (msg);
610   conn->tunneled = TRUE;
611
612 exit:
613   gst_rtsp_message_unset (&response);
614   g_free (hostparam);
615   g_free (uri);
616
617   return res;
618
619   /* ERRORS */
620 no_message:
621   {
622     GST_ERROR ("failed to create request (%d)", res);
623     goto exit;
624   }
625 write_failed:
626   {
627     GST_ERROR ("write failed (%d)", res);
628     gst_rtsp_message_free (msg);
629     conn->tunneled = TRUE;
630     goto exit;
631   }
632 read_failed:
633   {
634     GST_ERROR ("read failed (%d)", res);
635     conn->manual_http = FALSE;
636     goto exit;
637   }
638 wrong_result:
639   {
640     GST_ERROR ("got failure response %d %s", response.type_data.response.code,
641         response.type_data.response.reason);
642     res = GST_RTSP_ERROR;
643     goto exit;
644   }
645 not_resolved:
646   {
647     GST_ERROR ("could not resolve %s", conn->ip);
648     res = GST_RTSP_ENET;
649     goto exit;
650   }
651 connect_failed:
652   {
653     GST_ERROR ("failed to connect");
654     goto exit;
655   }
656 }
657
658 /**
659  * gst_rtsp_connection_connect:
660  * @conn: a #GstRTSPConnection 
661  * @timeout: a #GTimeVal timeout
662  *
663  * Attempt to connect to the url of @conn made with
664  * gst_rtsp_connection_create(). If @timeout is #NULL this function can block
665  * forever. If @timeout contains a valid timeout, this function will return
666  * #GST_RTSP_ETIMEOUT after the timeout expired.
667  *
668  * This function can be cancelled with gst_rtsp_connection_flush().
669  *
670  * Returns: #GST_RTSP_OK when a connection could be made.
671  */
672 GstRTSPResult
673 gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
674 {
675   GstRTSPResult res;
676   gchar *ip;
677   guint16 port;
678   GstRTSPUrl *url;
679
680   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
681   g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
682   g_return_val_if_fail (conn->socket0 == NULL, GST_RTSP_EINVAL);
683
684   url = conn->url;
685
686   if (conn->proxy_host && conn->tunneled) {
687     if (!(ip = do_resolve (conn->proxy_host, conn->cancellable))) {
688       GST_ERROR ("could not resolve %s", conn->proxy_host);
689       goto not_resolved;
690     }
691     port = conn->proxy_port;
692     g_free (conn->proxy_host);
693     conn->proxy_host = ip;
694   } else {
695     if (!(ip = do_resolve (url->host, conn->cancellable))) {
696       GST_ERROR ("could not resolve %s", url->host);
697       goto not_resolved;
698     }
699     /* get the port from the url */
700     gst_rtsp_url_get_port (url, &port);
701
702     g_free (conn->ip);
703     conn->ip = ip;
704   }
705
706   /* connect to the host/port */
707   res = do_connect (ip, port, &conn->socket0, timeout, conn->cancellable);
708   if (res != GST_RTSP_OK)
709     goto connect_failed;
710
711   /* this is our read URL */
712   conn->read_socket = conn->socket0;
713
714   if (conn->tunneled) {
715     res = setup_tunneling (conn, timeout);
716     if (res != GST_RTSP_OK)
717       goto tunneling_failed;
718   } else {
719     conn->write_socket = conn->socket0;
720   }
721
722   return GST_RTSP_OK;
723
724 not_resolved:
725   {
726     return GST_RTSP_ENET;
727   }
728 connect_failed:
729   {
730     GST_ERROR ("failed to connect");
731     return res;
732   }
733 tunneling_failed:
734   {
735     GST_ERROR ("failed to setup tunneling");
736     return res;
737   }
738 }
739
740 static void
741 auth_digest_compute_hex_urp (const gchar * username,
742     const gchar * realm, const gchar * password, gchar hex_urp[33])
743 {
744   GChecksum *md5_context = g_checksum_new (G_CHECKSUM_MD5);
745   const gchar *digest_string;
746
747   g_checksum_update (md5_context, (const guchar *) username, strlen (username));
748   g_checksum_update (md5_context, (const guchar *) ":", 1);
749   g_checksum_update (md5_context, (const guchar *) realm, strlen (realm));
750   g_checksum_update (md5_context, (const guchar *) ":", 1);
751   g_checksum_update (md5_context, (const guchar *) password, strlen (password));
752   digest_string = g_checksum_get_string (md5_context);
753
754   memset (hex_urp, 0, 33);
755   memcpy (hex_urp, digest_string, strlen (digest_string));
756
757   g_checksum_free (md5_context);
758 }
759
760 static void
761 auth_digest_compute_response (const gchar * method,
762     const gchar * uri, const gchar * hex_a1, const gchar * nonce,
763     gchar response[33])
764 {
765   char hex_a2[33] = { 0, };
766   GChecksum *md5_context = g_checksum_new (G_CHECKSUM_MD5);
767   const gchar *digest_string;
768
769   /* compute A2 */
770   g_checksum_update (md5_context, (const guchar *) method, strlen (method));
771   g_checksum_update (md5_context, (const guchar *) ":", 1);
772   g_checksum_update (md5_context, (const guchar *) uri, strlen (uri));
773   digest_string = g_checksum_get_string (md5_context);
774   memcpy (hex_a2, digest_string, strlen (digest_string));
775
776   /* compute KD */
777   g_checksum_reset (md5_context);
778   g_checksum_update (md5_context, (const guchar *) hex_a1, strlen (hex_a1));
779   g_checksum_update (md5_context, (const guchar *) ":", 1);
780   g_checksum_update (md5_context, (const guchar *) nonce, strlen (nonce));
781   g_checksum_update (md5_context, (const guchar *) ":", 1);
782
783   g_checksum_update (md5_context, (const guchar *) hex_a2, 32);
784   digest_string = g_checksum_get_string (md5_context);
785   memset (response, 0, 33);
786   memcpy (response, digest_string, strlen (digest_string));
787
788   g_checksum_free (md5_context);
789 }
790
791 static void
792 add_auth_header (GstRTSPConnection * conn, GstRTSPMessage * message)
793 {
794   switch (conn->auth_method) {
795     case GST_RTSP_AUTH_BASIC:{
796       gchar *user_pass;
797       gchar *user_pass64;
798       gchar *auth_string;
799
800       if (conn->username == NULL || conn->passwd == NULL)
801         break;
802
803       user_pass = g_strdup_printf ("%s:%s", conn->username, conn->passwd);
804       user_pass64 = g_base64_encode ((guchar *) user_pass, strlen (user_pass));
805       auth_string = g_strdup_printf ("Basic %s", user_pass64);
806
807       gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
808           auth_string);
809
810       g_free (user_pass);
811       g_free (user_pass64);
812       break;
813     }
814     case GST_RTSP_AUTH_DIGEST:{
815       gchar response[33], hex_urp[33];
816       gchar *auth_string, *auth_string2;
817       gchar *realm;
818       gchar *nonce;
819       gchar *opaque;
820       const gchar *uri;
821       const gchar *method;
822
823       /* we need to have some params set */
824       if (conn->auth_params == NULL || conn->username == NULL ||
825           conn->passwd == NULL)
826         break;
827
828       /* we need the realm and nonce */
829       realm = (gchar *) g_hash_table_lookup (conn->auth_params, "realm");
830       nonce = (gchar *) g_hash_table_lookup (conn->auth_params, "nonce");
831       if (realm == NULL || nonce == NULL)
832         break;
833
834       auth_digest_compute_hex_urp (conn->username, realm, conn->passwd,
835           hex_urp);
836
837       method = gst_rtsp_method_as_text (message->type_data.request.method);
838       uri = message->type_data.request.uri;
839
840       /* Assume no qop, algorithm=md5, stale=false */
841       /* For algorithm MD5, a1 = urp. */
842       auth_digest_compute_response (method, uri, hex_urp, nonce, response);
843       auth_string = g_strdup_printf ("Digest username=\"%s\", "
844           "realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
845           conn->username, realm, nonce, uri, response);
846
847       opaque = (gchar *) g_hash_table_lookup (conn->auth_params, "opaque");
848       if (opaque) {
849         auth_string2 = g_strdup_printf ("%s, opaque=\"%s\"", auth_string,
850             opaque);
851         g_free (auth_string);
852         auth_string = auth_string2;
853       }
854       gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
855           auth_string);
856       break;
857     }
858     default:
859       /* Nothing to do */
860       break;
861   }
862 }
863
864 static void
865 gen_date_string (gchar * date_string, guint len)
866 {
867   static const char wkdays[7][4] =
868       { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
869   static const char months[12][4] =
870       { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
871     "Nov", "Dec"
872   };
873   struct tm tm;
874   time_t t;
875
876   time (&t);
877
878 #ifdef HAVE_GMTIME_R
879   gmtime_r (&t, &tm);
880 #else
881   tm = *gmtime (&t);
882 #endif
883
884   g_snprintf (date_string, len, "%s, %02d %s %04d %02d:%02d:%02d GMT",
885       wkdays[tm.tm_wday], tm.tm_mday, months[tm.tm_mon], tm.tm_year + 1900,
886       tm.tm_hour, tm.tm_min, tm.tm_sec);
887 }
888
889 static GstRTSPResult
890 write_bytes (GSocket * socket, const guint8 * buffer, guint * idx, guint size,
891     GCancellable * cancellable)
892 {
893   guint left;
894
895   if (G_UNLIKELY (*idx > size))
896     return GST_RTSP_ERROR;
897
898   left = size - *idx;
899
900   while (left) {
901     GError *err = NULL;
902     gssize r;
903
904     r = g_socket_send (socket, (gchar *) & buffer[*idx], left, cancellable,
905         &err);
906     if (G_UNLIKELY (r == 0)) {
907       return GST_RTSP_EINTR;
908     } else if (G_UNLIKELY (r < 0)) {
909       if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
910         g_clear_error (&err);
911         return GST_RTSP_EINTR;
912       }
913       g_clear_error (&err);
914       return GST_RTSP_ESYS;
915     } else {
916       left -= r;
917       *idx += r;
918     }
919   }
920   return GST_RTSP_OK;
921 }
922
923 static gint
924 fill_raw_bytes (GstRTSPConnection * conn, guint8 * buffer, guint size,
925     GError ** err)
926 {
927   gint out = 0;
928
929   if (G_UNLIKELY (conn->initial_buffer != NULL)) {
930     gsize left = strlen (&conn->initial_buffer[conn->initial_buffer_offset]);
931
932     out = MIN (left, size);
933     memcpy (buffer, &conn->initial_buffer[conn->initial_buffer_offset], out);
934
935     if (left == (gsize) out) {
936       g_free (conn->initial_buffer);
937       conn->initial_buffer = NULL;
938       conn->initial_buffer_offset = 0;
939     } else
940       conn->initial_buffer_offset += out;
941   }
942
943   if (G_LIKELY (size > (guint) out)) {
944     gssize r;
945
946     r = g_socket_receive (conn->read_socket, (gchar *) & buffer[out],
947         size - out, conn->cancellable, err);
948     if (r <= 0) {
949       if (out == 0)
950         out = r;
951     } else
952       out += r;
953   }
954
955   return out;
956 }
957
958 static gint
959 fill_bytes (GstRTSPConnection * conn, guint8 * buffer, guint size,
960     GError ** err)
961 {
962   DecodeCtx *ctx = conn->ctxp;
963   gint out = 0;
964
965   if (ctx) {
966     while (size > 0) {
967       guint8 in[sizeof (ctx->out) * 4 / 3];
968       gint r;
969
970       while (size > 0 && ctx->cout < ctx->coutl) {
971         /* we have some leftover bytes */
972         *buffer++ = ctx->out[ctx->cout++];
973         size--;
974         out++;
975       }
976
977       /* got what we needed? */
978       if (size == 0)
979         break;
980
981       /* try to read more bytes */
982       r = fill_raw_bytes (conn, in, sizeof (in), err);
983       if (r <= 0) {
984         if (out == 0)
985           out = r;
986         break;
987       }
988
989       ctx->cout = 0;
990       ctx->coutl =
991           g_base64_decode_step ((gchar *) in, r, ctx->out, &ctx->state,
992           &ctx->save);
993     }
994   } else {
995     out = fill_raw_bytes (conn, buffer, size, err);
996   }
997
998   return out;
999 }
1000
1001 static GstRTSPResult
1002 read_bytes (GstRTSPConnection * conn, guint8 * buffer, guint * idx, guint size)
1003 {
1004   guint left;
1005   GError *err = NULL;
1006
1007   if (G_UNLIKELY (*idx > size))
1008     return GST_RTSP_ERROR;
1009
1010   left = size - *idx;
1011
1012   while (left) {
1013     gint r;
1014
1015     r = fill_bytes (conn, &buffer[*idx], left, &err);
1016     if (G_UNLIKELY (r == 0)) {
1017       return GST_RTSP_EEOF;
1018     } else if (G_UNLIKELY (r < 0)) {
1019       if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1020         g_clear_error (&err);
1021         return GST_RTSP_EINTR;
1022       }
1023       g_clear_error (&err);
1024       return GST_RTSP_ESYS;
1025     } else {
1026       left -= r;
1027       *idx += r;
1028     }
1029   }
1030   return GST_RTSP_OK;
1031 }
1032
1033 /* The code below tries to handle clients using \r, \n or \r\n to indicate the
1034  * end of a line. It even does its best to handle clients which mix them (even
1035  * though this is a really stupid idea (tm).) It also handles Line White Space
1036  * (LWS), where a line end followed by whitespace is considered LWS. This is
1037  * the method used in RTSP (and HTTP) to break long lines.
1038  */
1039 static GstRTSPResult
1040 read_line (GstRTSPConnection * conn, guint8 * buffer, guint * idx, guint size)
1041 {
1042   GError *err = NULL;
1043
1044   while (TRUE) {
1045     guint8 c;
1046     gint r;
1047
1048     if (conn->read_ahead == READ_AHEAD_EOH) {
1049       /* the last call to read_line() already determined that we have reached
1050        * the end of the headers, so convey that information now */
1051       conn->read_ahead = 0;
1052       break;
1053     } else if (conn->read_ahead == READ_AHEAD_CRLF) {
1054       /* the last call to read_line() left off after having read \r\n */
1055       c = '\n';
1056     } else if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1057       /* the last call to read_line() left off after having read \r\n\r */
1058       c = '\r';
1059     } else if (conn->read_ahead != 0) {
1060       /* the last call to read_line() left us with a character to start with */
1061       c = (guint8) conn->read_ahead;
1062       conn->read_ahead = 0;
1063     } else {
1064       /* read the next character */
1065       r = fill_bytes (conn, &c, 1, &err);
1066       if (G_UNLIKELY (r == 0)) {
1067         return GST_RTSP_EEOF;
1068       } else if (G_UNLIKELY (r < 0)) {
1069         if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1070           g_clear_error (&err);
1071           return GST_RTSP_EINTR;
1072         }
1073
1074         g_clear_error (&err);
1075         return GST_RTSP_ESYS;
1076       }
1077     }
1078
1079     /* special treatment of line endings */
1080     if (c == '\r' || c == '\n') {
1081       guint8 read_ahead;
1082
1083     retry:
1084       /* need to read ahead one more character to know what to do... */
1085       r = fill_bytes (conn, &read_ahead, 1, &err);
1086       if (G_UNLIKELY (r == 0)) {
1087         return GST_RTSP_EEOF;
1088       } else if (G_UNLIKELY (r < 0)) {
1089         if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1090           /* remember the original character we read and try again next time */
1091           if (conn->read_ahead == 0)
1092             conn->read_ahead = c;
1093           return GST_RTSP_EINTR;
1094           g_clear_error (&err);
1095           return GST_RTSP_EINTR;
1096         }
1097
1098         g_clear_error (&err);
1099         return GST_RTSP_ESYS;
1100       }
1101
1102       if (read_ahead == ' ' || read_ahead == '\t') {
1103         if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1104           /* got \r\n\r followed by whitespace, treat it as a normal line
1105            * followed by one starting with LWS */
1106           conn->read_ahead = read_ahead;
1107           break;
1108         } else {
1109           /* got LWS, change the line ending to a space and continue */
1110           c = ' ';
1111           conn->read_ahead = read_ahead;
1112         }
1113       } else if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1114         if (read_ahead == '\r' || read_ahead == '\n') {
1115           /* got \r\n\r\r or \r\n\r\n, treat it as the end of the headers */
1116           conn->read_ahead = READ_AHEAD_EOH;
1117           break;
1118         } else {
1119           /* got \r\n\r followed by something else, this is not really
1120            * supported since we have probably just eaten the first character
1121            * of the body or the next message, so just ignore the second \r
1122            * and live with it... */
1123           conn->read_ahead = read_ahead;
1124           break;
1125         }
1126       } else if (conn->read_ahead == READ_AHEAD_CRLF) {
1127         if (read_ahead == '\r') {
1128           /* got \r\n\r so far, need one more character... */
1129           conn->read_ahead = READ_AHEAD_CRLFCR;
1130           goto retry;
1131         } else if (read_ahead == '\n') {
1132           /* got \r\n\n, treat it as the end of the headers */
1133           conn->read_ahead = READ_AHEAD_EOH;
1134           break;
1135         } else {
1136           /* found the end of a line, keep read_ahead for the next line */
1137           conn->read_ahead = read_ahead;
1138           break;
1139         }
1140       } else if (c == read_ahead) {
1141         /* got double \r or \n, treat it as the end of the headers */
1142         conn->read_ahead = READ_AHEAD_EOH;
1143         break;
1144       } else if (c == '\r' && read_ahead == '\n') {
1145         /* got \r\n so far, still need more to know what to do... */
1146         conn->read_ahead = READ_AHEAD_CRLF;
1147         goto retry;
1148       } else {
1149         /* found the end of a line, keep read_ahead for the next line */
1150         conn->read_ahead = read_ahead;
1151         break;
1152       }
1153     }
1154
1155     if (G_LIKELY (*idx < size - 1))
1156       buffer[(*idx)++] = c;
1157   }
1158   buffer[*idx] = '\0';
1159
1160   return GST_RTSP_OK;
1161 }
1162
1163 /**
1164  * gst_rtsp_connection_write:
1165  * @conn: a #GstRTSPConnection
1166  * @data: the data to write
1167  * @size: the size of @data
1168  * @timeout: a timeout value or #NULL
1169  *
1170  * Attempt to write @size bytes of @data to the connected @conn, blocking up to
1171  * the specified @timeout. @timeout can be #NULL, in which case this function
1172  * might block forever.
1173  * 
1174  * This function can be cancelled with gst_rtsp_connection_flush().
1175  *
1176  * Returns: #GST_RTSP_OK on success.
1177  */
1178 GstRTSPResult
1179 gst_rtsp_connection_write (GstRTSPConnection * conn, const guint8 * data,
1180     guint size, GTimeVal * timeout)
1181 {
1182   guint offset;
1183   GstClockTime to;
1184   GstRTSPResult res;
1185   GError *err = NULL;
1186
1187   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1188   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
1189   g_return_val_if_fail (conn->write_socket != NULL, GST_RTSP_EINVAL);
1190
1191   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
1192
1193   offset = 0;
1194
1195   while (TRUE) {
1196     /* try to write */
1197     res =
1198         write_bytes (conn->write_socket, data, &offset, size,
1199         conn->cancellable);
1200     if (G_LIKELY (res == GST_RTSP_OK))
1201       break;
1202     if (G_UNLIKELY (res != GST_RTSP_EINTR))
1203       goto write_error;
1204
1205     /* not all is written, wait until we can write more */
1206     g_socket_set_timeout (conn->write_socket,
1207         (to + GST_SECOND - 1) / GST_SECOND);
1208     if (!g_socket_condition_wait (conn->write_socket,
1209             G_IO_OUT | G_IO_ERR | G_IO_HUP, conn->cancellable, &err)) {
1210       g_socket_set_timeout (conn->write_socket, 0);
1211       if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY)) {
1212         g_clear_error (&err);
1213         goto stopped;
1214       } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
1215         g_clear_error (&err);
1216         goto timeout;
1217       }
1218       g_clear_error (&err);
1219       goto select_error;
1220     }
1221     g_socket_set_timeout (conn->write_socket, 0);
1222   }
1223   return GST_RTSP_OK;
1224
1225   /* ERRORS */
1226 timeout:
1227   {
1228     return GST_RTSP_ETIMEOUT;
1229   }
1230 select_error:
1231   {
1232     return GST_RTSP_ESYS;
1233   }
1234 stopped:
1235   {
1236     return GST_RTSP_EINTR;
1237   }
1238 write_error:
1239   {
1240     return res;
1241   }
1242 }
1243
1244 static GString *
1245 message_to_string (GstRTSPConnection * conn, GstRTSPMessage * message)
1246 {
1247   GString *str = NULL;
1248
1249   str = g_string_new ("");
1250
1251   switch (message->type) {
1252     case GST_RTSP_MESSAGE_REQUEST:
1253       /* create request string, add CSeq */
1254       g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
1255           "CSeq: %d\r\n",
1256           gst_rtsp_method_as_text (message->type_data.request.method),
1257           message->type_data.request.uri, conn->cseq++);
1258       /* add session id if we have one */
1259       if (conn->session_id[0] != '\0') {
1260         gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
1261         gst_rtsp_message_add_header (message, GST_RTSP_HDR_SESSION,
1262             conn->session_id);
1263       }
1264       /* add any authentication headers */
1265       add_auth_header (conn, message);
1266       break;
1267     case GST_RTSP_MESSAGE_RESPONSE:
1268       /* create response string */
1269       g_string_append_printf (str, "RTSP/1.0 %d %s\r\n",
1270           message->type_data.response.code, message->type_data.response.reason);
1271       break;
1272     case GST_RTSP_MESSAGE_HTTP_REQUEST:
1273       /* create request string */
1274       g_string_append_printf (str, "%s %s HTTP/%s\r\n",
1275           gst_rtsp_method_as_text (message->type_data.request.method),
1276           message->type_data.request.uri,
1277           gst_rtsp_version_as_text (message->type_data.request.version));
1278       /* add any authentication headers */
1279       add_auth_header (conn, message);
1280       break;
1281     case GST_RTSP_MESSAGE_HTTP_RESPONSE:
1282       /* create response string */
1283       g_string_append_printf (str, "HTTP/%s %d %s\r\n",
1284           gst_rtsp_version_as_text (message->type_data.request.version),
1285           message->type_data.response.code, message->type_data.response.reason);
1286       break;
1287     case GST_RTSP_MESSAGE_DATA:
1288     {
1289       guint8 data_header[4];
1290
1291       /* prepare data header */
1292       data_header[0] = '$';
1293       data_header[1] = message->type_data.data.channel;
1294       data_header[2] = (message->body_size >> 8) & 0xff;
1295       data_header[3] = message->body_size & 0xff;
1296
1297       /* create string with header and data */
1298       str = g_string_append_len (str, (gchar *) data_header, 4);
1299       str =
1300           g_string_append_len (str, (gchar *) message->body,
1301           message->body_size);
1302       break;
1303     }
1304     default:
1305       g_string_free (str, TRUE);
1306       g_return_val_if_reached (NULL);
1307       break;
1308   }
1309
1310   /* append headers and body */
1311   if (message->type != GST_RTSP_MESSAGE_DATA) {
1312     gchar date_string[100];
1313
1314     gen_date_string (date_string, sizeof (date_string));
1315
1316     /* add date header */
1317     gst_rtsp_message_remove_header (message, GST_RTSP_HDR_DATE, -1);
1318     gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string);
1319
1320     /* append headers */
1321     gst_rtsp_message_append_headers (message, str);
1322
1323     /* append Content-Length and body if needed */
1324     if (message->body != NULL && message->body_size > 0) {
1325       gchar *len;
1326
1327       len = g_strdup_printf ("%d", message->body_size);
1328       g_string_append_printf (str, "%s: %s\r\n",
1329           gst_rtsp_header_as_text (GST_RTSP_HDR_CONTENT_LENGTH), len);
1330       g_free (len);
1331       /* header ends here */
1332       g_string_append (str, "\r\n");
1333       str =
1334           g_string_append_len (str, (gchar *) message->body,
1335           message->body_size);
1336     } else {
1337       /* just end headers */
1338       g_string_append (str, "\r\n");
1339     }
1340   }
1341
1342   return str;
1343 }
1344
1345 /**
1346  * gst_rtsp_connection_send:
1347  * @conn: a #GstRTSPConnection
1348  * @message: the message to send
1349  * @timeout: a timeout value or #NULL
1350  *
1351  * Attempt to send @message to the connected @conn, blocking up to
1352  * the specified @timeout. @timeout can be #NULL, in which case this function
1353  * might block forever.
1354  * 
1355  * This function can be cancelled with gst_rtsp_connection_flush().
1356  *
1357  * Returns: #GST_RTSP_OK on success.
1358  */
1359 GstRTSPResult
1360 gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
1361     GTimeVal * timeout)
1362 {
1363   GString *string = NULL;
1364   GstRTSPResult res;
1365   gchar *str;
1366   gsize len;
1367
1368   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1369   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
1370
1371   if (G_UNLIKELY (!(string = message_to_string (conn, message))))
1372     goto no_message;
1373
1374   if (conn->tunneled) {
1375     str = g_base64_encode ((const guchar *) string->str, string->len);
1376     g_string_free (string, TRUE);
1377     len = strlen (str);
1378   } else {
1379     str = string->str;
1380     len = string->len;
1381     g_string_free (string, FALSE);
1382   }
1383
1384   /* write request */
1385   res = gst_rtsp_connection_write (conn, (guint8 *) str, len, timeout);
1386
1387   g_free (str);
1388
1389   return res;
1390
1391 no_message:
1392   {
1393     g_warning ("Wrong message");
1394     return GST_RTSP_EINVAL;
1395   }
1396 }
1397
1398 static GstRTSPResult
1399 parse_string (gchar * dest, gint size, gchar ** src)
1400 {
1401   GstRTSPResult res = GST_RTSP_OK;
1402   gint idx;
1403
1404   idx = 0;
1405   /* skip spaces */
1406   while (g_ascii_isspace (**src))
1407     (*src)++;
1408
1409   while (!g_ascii_isspace (**src) && **src != '\0') {
1410     if (idx < size - 1)
1411       dest[idx++] = **src;
1412     else
1413       res = GST_RTSP_EPARSE;
1414     (*src)++;
1415   }
1416   if (size > 0)
1417     dest[idx] = '\0';
1418
1419   return res;
1420 }
1421
1422 static GstRTSPResult
1423 parse_protocol_version (gchar * protocol, GstRTSPMsgType * type,
1424     GstRTSPVersion * version)
1425 {
1426   GstRTSPResult res = GST_RTSP_OK;
1427   gchar *ver;
1428
1429   if (G_LIKELY ((ver = strchr (protocol, '/')) != NULL)) {
1430     guint major;
1431     guint minor;
1432     gchar dummychar;
1433
1434     *ver++ = '\0';
1435
1436     /* the version number must be formatted as X.Y with nothing following */
1437     if (sscanf (ver, "%u.%u%c", &major, &minor, &dummychar) != 2)
1438       res = GST_RTSP_EPARSE;
1439
1440     if (g_ascii_strcasecmp (protocol, "RTSP") == 0) {
1441       if (major != 1 || minor != 0) {
1442         *version = GST_RTSP_VERSION_INVALID;
1443         res = GST_RTSP_ERROR;
1444       }
1445     } else if (g_ascii_strcasecmp (protocol, "HTTP") == 0) {
1446       if (*type == GST_RTSP_MESSAGE_REQUEST)
1447         *type = GST_RTSP_MESSAGE_HTTP_REQUEST;
1448       else if (*type == GST_RTSP_MESSAGE_RESPONSE)
1449         *type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
1450
1451       if (major == 1 && minor == 1) {
1452         *version = GST_RTSP_VERSION_1_1;
1453       } else if (major != 1 || minor != 0) {
1454         *version = GST_RTSP_VERSION_INVALID;
1455         res = GST_RTSP_ERROR;
1456       }
1457     } else
1458       res = GST_RTSP_EPARSE;
1459   } else
1460     res = GST_RTSP_EPARSE;
1461
1462   return res;
1463 }
1464
1465 static GstRTSPResult
1466 parse_response_status (guint8 * buffer, GstRTSPMessage * msg)
1467 {
1468   GstRTSPResult res = GST_RTSP_OK;
1469   GstRTSPResult res2;
1470   gchar versionstr[20];
1471   gchar codestr[4];
1472   gint code;
1473   gchar *bptr;
1474
1475   bptr = (gchar *) buffer;
1476
1477   if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
1478     res = GST_RTSP_EPARSE;
1479
1480   if (parse_string (codestr, sizeof (codestr), &bptr) != GST_RTSP_OK)
1481     res = GST_RTSP_EPARSE;
1482   code = atoi (codestr);
1483   if (G_UNLIKELY (*codestr == '\0' || code < 0 || code >= 600))
1484     res = GST_RTSP_EPARSE;
1485
1486   while (g_ascii_isspace (*bptr))
1487     bptr++;
1488
1489   if (G_UNLIKELY (gst_rtsp_message_init_response (msg, code, bptr,
1490               NULL) != GST_RTSP_OK))
1491     res = GST_RTSP_EPARSE;
1492
1493   res2 = parse_protocol_version (versionstr, &msg->type,
1494       &msg->type_data.response.version);
1495   if (G_LIKELY (res == GST_RTSP_OK))
1496     res = res2;
1497
1498   return res;
1499 }
1500
1501 static GstRTSPResult
1502 parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
1503 {
1504   GstRTSPResult res = GST_RTSP_OK;
1505   GstRTSPResult res2;
1506   gchar versionstr[20];
1507   gchar methodstr[20];
1508   gchar urlstr[4096];
1509   gchar *bptr;
1510   GstRTSPMethod method;
1511
1512   bptr = (gchar *) buffer;
1513
1514   if (parse_string (methodstr, sizeof (methodstr), &bptr) != GST_RTSP_OK)
1515     res = GST_RTSP_EPARSE;
1516   method = gst_rtsp_find_method (methodstr);
1517
1518   if (parse_string (urlstr, sizeof (urlstr), &bptr) != GST_RTSP_OK)
1519     res = GST_RTSP_EPARSE;
1520   if (G_UNLIKELY (*urlstr == '\0'))
1521     res = GST_RTSP_EPARSE;
1522
1523   if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
1524     res = GST_RTSP_EPARSE;
1525
1526   if (G_UNLIKELY (*bptr != '\0'))
1527     res = GST_RTSP_EPARSE;
1528
1529   if (G_UNLIKELY (gst_rtsp_message_init_request (msg, method,
1530               urlstr) != GST_RTSP_OK))
1531     res = GST_RTSP_EPARSE;
1532
1533   res2 = parse_protocol_version (versionstr, &msg->type,
1534       &msg->type_data.request.version);
1535   if (G_LIKELY (res == GST_RTSP_OK))
1536     res = res2;
1537
1538   if (G_LIKELY (msg->type == GST_RTSP_MESSAGE_REQUEST)) {
1539     /* GET and POST are not allowed as RTSP methods */
1540     if (msg->type_data.request.method == GST_RTSP_GET ||
1541         msg->type_data.request.method == GST_RTSP_POST) {
1542       msg->type_data.request.method = GST_RTSP_INVALID;
1543       if (res == GST_RTSP_OK)
1544         res = GST_RTSP_ERROR;
1545     }
1546   } else if (msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
1547     /* only GET and POST are allowed as HTTP methods */
1548     if (msg->type_data.request.method != GST_RTSP_GET &&
1549         msg->type_data.request.method != GST_RTSP_POST) {
1550       msg->type_data.request.method = GST_RTSP_INVALID;
1551       if (res == GST_RTSP_OK)
1552         res = GST_RTSP_ERROR;
1553     }
1554   }
1555
1556   return res;
1557 }
1558
1559 /* parsing lines means reading a Key: Value pair */
1560 static GstRTSPResult
1561 parse_line (guint8 * buffer, GstRTSPMessage * msg)
1562 {
1563   GstRTSPHeaderField field;
1564   gchar *line = (gchar *) buffer;
1565   gchar *value;
1566
1567   if ((value = strchr (line, ':')) == NULL || value == line)
1568     goto parse_error;
1569
1570   /* trim space before the colon */
1571   if (value[-1] == ' ')
1572     value[-1] = '\0';
1573
1574   /* replace the colon with a NUL */
1575   *value++ = '\0';
1576
1577   /* find the header */
1578   field = gst_rtsp_find_header_field (line);
1579   if (field == GST_RTSP_HDR_INVALID)
1580     goto done;
1581
1582   /* split up the value in multiple key:value pairs if it contains comma(s) */
1583   while (*value != '\0') {
1584     gchar *next_value;
1585     gchar *comma = NULL;
1586     gboolean quoted = FALSE;
1587     guint comment = 0;
1588
1589     /* trim leading space */
1590     if (*value == ' ')
1591       value++;
1592
1593     /* for headers which may not appear multiple times, and thus may not
1594      * contain multiple values on the same line, we can short-circuit the loop
1595      * below and the entire value results in just one key:value pair*/
1596     if (!gst_rtsp_header_allow_multiple (field))
1597       next_value = value + strlen (value);
1598     else
1599       next_value = value;
1600
1601     /* find the next value, taking special care of quotes and comments */
1602     while (*next_value != '\0') {
1603       if ((quoted || comment != 0) && *next_value == '\\' &&
1604           next_value[1] != '\0')
1605         next_value++;
1606       else if (comment == 0 && *next_value == '"')
1607         quoted = !quoted;
1608       else if (!quoted && *next_value == '(')
1609         comment++;
1610       else if (comment != 0 && *next_value == ')')
1611         comment--;
1612       else if (!quoted && comment == 0) {
1613         /* To quote RFC 2068: "User agents MUST take special care in parsing
1614          * the WWW-Authenticate field value if it contains more than one
1615          * challenge, or if more than one WWW-Authenticate header field is
1616          * provided, since the contents of a challenge may itself contain a
1617          * comma-separated list of authentication parameters."
1618          *
1619          * What this means is that we cannot just look for an unquoted comma
1620          * when looking for multiple values in Proxy-Authenticate and
1621          * WWW-Authenticate headers. Instead we need to look for the sequence
1622          * "comma [space] token space token" before we can split after the
1623          * comma...
1624          */
1625         if (field == GST_RTSP_HDR_PROXY_AUTHENTICATE ||
1626             field == GST_RTSP_HDR_WWW_AUTHENTICATE) {
1627           if (*next_value == ',') {
1628             if (next_value[1] == ' ') {
1629               /* skip any space following the comma so we do not mistake it for
1630                * separating between two tokens */
1631               next_value++;
1632             }
1633             comma = next_value;
1634           } else if (*next_value == ' ' && next_value[1] != ',' &&
1635               next_value[1] != '=' && comma != NULL) {
1636             next_value = comma;
1637             comma = NULL;
1638             break;
1639           }
1640         } else if (*next_value == ',')
1641           break;
1642       }
1643
1644       next_value++;
1645     }
1646
1647     /* trim space */
1648     if (value != next_value && next_value[-1] == ' ')
1649       next_value[-1] = '\0';
1650
1651     if (*next_value != '\0')
1652       *next_value++ = '\0';
1653
1654     /* add the key:value pair */
1655     if (*value != '\0')
1656       gst_rtsp_message_add_header (msg, field, value);
1657
1658     value = next_value;
1659   }
1660
1661 done:
1662   return GST_RTSP_OK;
1663
1664   /* ERRORS */
1665 parse_error:
1666   {
1667     return GST_RTSP_EPARSE;
1668   }
1669 }
1670
1671 /* convert all consecutive whitespace to a single space */
1672 static void
1673 normalize_line (guint8 * buffer)
1674 {
1675   while (*buffer) {
1676     if (g_ascii_isspace (*buffer)) {
1677       guint8 *tmp;
1678
1679       *buffer++ = ' ';
1680       for (tmp = buffer; g_ascii_isspace (*tmp); tmp++) {
1681       }
1682       if (buffer != tmp)
1683         memmove (buffer, tmp, strlen ((gchar *) tmp) + 1);
1684     } else {
1685       buffer++;
1686     }
1687   }
1688 }
1689
1690 /* returns:
1691  *  GST_RTSP_OK when a complete message was read.
1692  *  GST_RTSP_EEOF: when the read socket is closed
1693  *  GST_RTSP_EINTR: when more data is needed.
1694  *  GST_RTSP_..: some other error occured.
1695  */
1696 static GstRTSPResult
1697 build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
1698     GstRTSPConnection * conn)
1699 {
1700   GstRTSPResult res;
1701
1702   while (TRUE) {
1703     switch (builder->state) {
1704       case STATE_START:
1705       {
1706         guint8 c;
1707
1708         builder->offset = 0;
1709         res =
1710             read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 1);
1711         if (res != GST_RTSP_OK)
1712           goto done;
1713
1714         c = builder->buffer[0];
1715
1716         /* we have 1 bytes now and we can see if this is a data message or
1717          * not */
1718         if (c == '$') {
1719           /* data message, prepare for the header */
1720           builder->state = STATE_DATA_HEADER;
1721         } else if (c == '\n' || c == '\r') {
1722           /* skip \n and \r */
1723           builder->offset = 0;
1724         } else {
1725           builder->line = 0;
1726           builder->state = STATE_READ_LINES;
1727         }
1728         break;
1729       }
1730       case STATE_DATA_HEADER:
1731       {
1732         res =
1733             read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 4);
1734         if (res != GST_RTSP_OK)
1735           goto done;
1736
1737         gst_rtsp_message_init_data (message, builder->buffer[1]);
1738
1739         builder->body_len = (builder->buffer[2] << 8) | builder->buffer[3];
1740         builder->body_data = g_malloc (builder->body_len + 1);
1741         builder->body_data[builder->body_len] = '\0';
1742         builder->offset = 0;
1743         builder->state = STATE_DATA_BODY;
1744         break;
1745       }
1746       case STATE_DATA_BODY:
1747       {
1748         res =
1749             read_bytes (conn, builder->body_data, &builder->offset,
1750             builder->body_len);
1751         if (res != GST_RTSP_OK)
1752           goto done;
1753
1754         /* we have the complete body now, store in the message adjusting the
1755          * length to include the trailing '\0' */
1756         gst_rtsp_message_take_body (message,
1757             (guint8 *) builder->body_data, builder->body_len + 1);
1758         builder->body_data = NULL;
1759         builder->body_len = 0;
1760
1761         builder->state = STATE_END;
1762         break;
1763       }
1764       case STATE_READ_LINES:
1765       {
1766         res = read_line (conn, builder->buffer, &builder->offset,
1767             sizeof (builder->buffer));
1768         if (res != GST_RTSP_OK)
1769           goto done;
1770
1771         /* we have a regular response */
1772         if (builder->buffer[0] == '\0') {
1773           gchar *hdrval;
1774
1775           /* empty line, end of message header */
1776           /* see if there is a Content-Length header, but ignore it if this
1777            * is a POST request with an x-sessioncookie header */
1778           if (gst_rtsp_message_get_header (message,
1779                   GST_RTSP_HDR_CONTENT_LENGTH, &hdrval, 0) == GST_RTSP_OK &&
1780               (message->type != GST_RTSP_MESSAGE_HTTP_REQUEST ||
1781                   message->type_data.request.method != GST_RTSP_POST ||
1782                   gst_rtsp_message_get_header (message,
1783                       GST_RTSP_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) {
1784             /* there is, prepare to read the body */
1785             builder->body_len = atol (hdrval);
1786             builder->body_data = g_try_malloc (builder->body_len + 1);
1787             /* we can't do much here, we need the length to know how many bytes
1788              * we need to read next and when allocation fails, something is
1789              * probably wrong with the length. */
1790             if (builder->body_data == NULL)
1791               goto invalid_body_len;
1792
1793             builder->body_data[builder->body_len] = '\0';
1794             builder->offset = 0;
1795             builder->state = STATE_DATA_BODY;
1796           } else {
1797             builder->state = STATE_END;
1798           }
1799           break;
1800         }
1801
1802         /* we have a line */
1803         normalize_line (builder->buffer);
1804         if (builder->line == 0) {
1805           /* first line, check for response status */
1806           if (memcmp (builder->buffer, "RTSP", 4) == 0 ||
1807               memcmp (builder->buffer, "HTTP", 4) == 0) {
1808             builder->status = parse_response_status (builder->buffer, message);
1809           } else {
1810             builder->status = parse_request_line (builder->buffer, message);
1811           }
1812         } else {
1813           /* else just parse the line */
1814           res = parse_line (builder->buffer, message);
1815           if (res != GST_RTSP_OK)
1816             builder->status = res;
1817         }
1818         builder->line++;
1819         builder->offset = 0;
1820         break;
1821       }
1822       case STATE_END:
1823       {
1824         gchar *session_cookie;
1825         gchar *session_id;
1826
1827         if (message->type == GST_RTSP_MESSAGE_DATA) {
1828           /* data messages don't have headers */
1829           res = GST_RTSP_OK;
1830           goto done;
1831         }
1832
1833         /* save the tunnel session in the connection */
1834         if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST &&
1835             !conn->manual_http &&
1836             conn->tstate == TUNNEL_STATE_NONE &&
1837             gst_rtsp_message_get_header (message, GST_RTSP_HDR_X_SESSIONCOOKIE,
1838                 &session_cookie, 0) == GST_RTSP_OK) {
1839           strncpy (conn->tunnelid, session_cookie, TUNNELID_LEN);
1840           conn->tunnelid[TUNNELID_LEN - 1] = '\0';
1841           conn->tunneled = TRUE;
1842         }
1843
1844         /* save session id in the connection for further use */
1845         if (message->type == GST_RTSP_MESSAGE_RESPONSE &&
1846             gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
1847                 &session_id, 0) == GST_RTSP_OK) {
1848           gint maxlen, i;
1849
1850           maxlen = sizeof (conn->session_id) - 1;
1851           /* the sessionid can have attributes marked with ;
1852            * Make sure we strip them */
1853           for (i = 0; session_id[i] != '\0'; i++) {
1854             if (session_id[i] == ';') {
1855               maxlen = i;
1856               /* parse timeout */
1857               do {
1858                 i++;
1859               } while (g_ascii_isspace (session_id[i]));
1860               if (g_str_has_prefix (&session_id[i], "timeout=")) {
1861                 gint to;
1862
1863                 /* if we parsed something valid, configure */
1864                 if ((to = atoi (&session_id[i + 8])) > 0)
1865                   conn->timeout = to;
1866               }
1867               break;
1868             }
1869           }
1870
1871           /* make sure to not overflow */
1872           strncpy (conn->session_id, session_id, maxlen);
1873           conn->session_id[maxlen] = '\0';
1874         }
1875         res = builder->status;
1876         goto done;
1877       }
1878       default:
1879         res = GST_RTSP_ERROR;
1880         break;
1881     }
1882   }
1883 done:
1884   return res;
1885
1886   /* ERRORS */
1887 invalid_body_len:
1888   {
1889     GST_DEBUG ("could not allocate body");
1890     return GST_RTSP_ERROR;
1891   }
1892 }
1893
1894 /**
1895  * gst_rtsp_connection_read:
1896  * @conn: a #GstRTSPConnection
1897  * @data: the data to read
1898  * @size: the size of @data
1899  * @timeout: a timeout value or #NULL
1900  *
1901  * Attempt to read @size bytes into @data from the connected @conn, blocking up to
1902  * the specified @timeout. @timeout can be #NULL, in which case this function
1903  * might block forever.
1904  *
1905  * This function can be cancelled with gst_rtsp_connection_flush().
1906  *
1907  * Returns: #GST_RTSP_OK on success.
1908  */
1909 GstRTSPResult
1910 gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size,
1911     GTimeVal * timeout)
1912 {
1913   guint offset;
1914   GstClockTime to;
1915   GstRTSPResult res;
1916   GError *err = NULL;
1917
1918   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1919   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
1920   g_return_val_if_fail (conn->read_socket != NULL, GST_RTSP_EINVAL);
1921
1922   if (G_UNLIKELY (size == 0))
1923     return GST_RTSP_OK;
1924
1925   offset = 0;
1926
1927   /* configure timeout if any */
1928   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
1929
1930   while (TRUE) {
1931     res = read_bytes (conn, data, &offset, size);
1932     if (G_UNLIKELY (res == GST_RTSP_EEOF))
1933       goto eof;
1934     if (G_LIKELY (res == GST_RTSP_OK))
1935       break;
1936     if (G_UNLIKELY (res != GST_RTSP_EINTR))
1937       goto read_error;
1938
1939     g_socket_set_timeout (conn->read_socket,
1940         (to + GST_SECOND - 1) / GST_SECOND);
1941     if (!g_socket_condition_wait (conn->read_socket,
1942             G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, conn->cancellable,
1943             &err)) {
1944       g_socket_set_timeout (conn->read_socket, 0);
1945       if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY)) {
1946         g_clear_error (&err);
1947         goto stopped;
1948       } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
1949         g_clear_error (&err);
1950         goto select_timeout;
1951       }
1952       g_clear_error (&err);
1953       goto select_error;
1954     }
1955     g_socket_set_timeout (conn->read_socket, 0);
1956   }
1957   return GST_RTSP_OK;
1958
1959   /* ERRORS */
1960 select_error:
1961   {
1962     return GST_RTSP_ESYS;
1963   }
1964 select_timeout:
1965   {
1966     return GST_RTSP_ETIMEOUT;
1967   }
1968 stopped:
1969   {
1970     return GST_RTSP_EINTR;
1971   }
1972 eof:
1973   {
1974     return GST_RTSP_EEOF;
1975   }
1976 read_error:
1977   {
1978     return res;
1979   }
1980 }
1981
1982 static GstRTSPMessage *
1983 gen_tunnel_reply (GstRTSPConnection * conn, GstRTSPStatusCode code,
1984     const GstRTSPMessage * request)
1985 {
1986   GstRTSPMessage *msg;
1987   GstRTSPResult res;
1988
1989   if (gst_rtsp_status_as_text (code) == NULL)
1990     code = GST_RTSP_STS_INTERNAL_SERVER_ERROR;
1991
1992   GST_RTSP_CHECK (gst_rtsp_message_new_response (&msg, code, NULL, request),
1993       no_message);
1994
1995   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_SERVER,
1996       "GStreamer RTSP Server");
1997   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONNECTION, "close");
1998   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-store");
1999   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
2000
2001   if (code == GST_RTSP_STS_OK) {
2002     if (conn->ip)
2003       gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
2004           conn->ip);
2005     gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_TYPE,
2006         "application/x-rtsp-tunnelled");
2007   }
2008
2009   return msg;
2010
2011   /* ERRORS */
2012 no_message:
2013   {
2014     return NULL;
2015   }
2016 }
2017
2018 /**
2019  * gst_rtsp_connection_receive:
2020  * @conn: a #GstRTSPConnection
2021  * @message: the message to read
2022  * @timeout: a timeout value or #NULL
2023  *
2024  * Attempt to read into @message from the connected @conn, blocking up to
2025  * the specified @timeout. @timeout can be #NULL, in which case this function
2026  * might block forever.
2027  * 
2028  * This function can be cancelled with gst_rtsp_connection_flush().
2029  *
2030  * Returns: #GST_RTSP_OK on success.
2031  */
2032 GstRTSPResult
2033 gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message,
2034     GTimeVal * timeout)
2035 {
2036   GstRTSPResult res;
2037   GstRTSPBuilder builder;
2038   GstClockTime to;
2039   GError *err = NULL;
2040
2041   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2042   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2043   g_return_val_if_fail (conn->read_socket != NULL, GST_RTSP_EINVAL);
2044
2045   /* configure timeout if any */
2046   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2047
2048   memset (&builder, 0, sizeof (GstRTSPBuilder));
2049   while (TRUE) {
2050     res = build_next (&builder, message, conn);
2051     if (G_UNLIKELY (res == GST_RTSP_EEOF))
2052       goto eof;
2053     else if (G_LIKELY (res == GST_RTSP_OK)) {
2054       if (!conn->manual_http) {
2055         if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
2056           if (conn->tstate == TUNNEL_STATE_NONE &&
2057               message->type_data.request.method == GST_RTSP_GET) {
2058             GstRTSPMessage *response;
2059
2060             conn->tstate = TUNNEL_STATE_GET;
2061
2062             /* tunnel GET request, we can reply now */
2063             response = gen_tunnel_reply (conn, GST_RTSP_STS_OK, message);
2064             res = gst_rtsp_connection_send (conn, response, timeout);
2065             gst_rtsp_message_free (response);
2066             if (res == GST_RTSP_OK)
2067               res = GST_RTSP_ETGET;
2068             goto cleanup;
2069           } else if (conn->tstate == TUNNEL_STATE_NONE &&
2070               message->type_data.request.method == GST_RTSP_POST) {
2071             conn->tstate = TUNNEL_STATE_POST;
2072
2073             /* tunnel POST request, the caller now has to link the two
2074              * connections. */
2075             res = GST_RTSP_ETPOST;
2076             goto cleanup;
2077           } else {
2078             res = GST_RTSP_EPARSE;
2079             goto cleanup;
2080           }
2081         } else if (message->type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
2082           res = GST_RTSP_EPARSE;
2083           goto cleanup;
2084         }
2085       }
2086
2087       break;
2088     } else if (G_UNLIKELY (res != GST_RTSP_EINTR))
2089       goto read_error;
2090
2091     g_socket_set_timeout (conn->read_socket,
2092         (to + GST_SECOND - 1) / GST_SECOND);
2093     if (!g_socket_condition_wait (conn->read_socket,
2094             G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, conn->cancellable,
2095             &err)) {
2096       g_socket_set_timeout (conn->read_socket, 0);
2097       if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_BUSY)) {
2098         g_clear_error (&err);
2099         goto stopped;
2100       } else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
2101         g_clear_error (&err);
2102         goto select_timeout;
2103       }
2104       g_clear_error (&err);
2105       goto select_error;
2106     }
2107     g_socket_set_timeout (conn->read_socket, 0);
2108   }
2109
2110   /* we have a message here */
2111   build_reset (&builder);
2112
2113   return GST_RTSP_OK;
2114
2115   /* ERRORS */
2116 select_error:
2117   {
2118     res = GST_RTSP_ESYS;
2119     goto cleanup;
2120   }
2121 select_timeout:
2122   {
2123     res = GST_RTSP_ETIMEOUT;
2124     goto cleanup;
2125   }
2126 stopped:
2127   {
2128     res = GST_RTSP_EINTR;
2129     goto cleanup;
2130   }
2131 eof:
2132   {
2133     res = GST_RTSP_EEOF;
2134     goto cleanup;
2135   }
2136 read_error:
2137 cleanup:
2138   {
2139     build_reset (&builder);
2140     gst_rtsp_message_unset (message);
2141     return res;
2142   }
2143 }
2144
2145 /**
2146  * gst_rtsp_connection_close:
2147  * @conn: a #GstRTSPConnection
2148  *
2149  * Close the connected @conn. After this call, the connection is in the same
2150  * state as when it was first created.
2151  * 
2152  * Returns: #GST_RTSP_OK on success.
2153  */
2154 GstRTSPResult
2155 gst_rtsp_connection_close (GstRTSPConnection * conn)
2156 {
2157   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2158
2159   /* last unref closes the connection we don't want to explicitly close here
2160    * because these sockets might have been provided at construction */
2161   if (conn->socket0) {
2162     g_object_unref (conn->socket0);
2163     conn->socket0 = NULL;
2164   }
2165   if (conn->socket1) {
2166     g_object_unref (conn->socket1);
2167     conn->socket1 = NULL;
2168   }
2169
2170   g_free (conn->ip);
2171   conn->ip = NULL;
2172
2173   conn->read_ahead = 0;
2174
2175   g_free (conn->initial_buffer);
2176   conn->initial_buffer = NULL;
2177   conn->initial_buffer_offset = 0;
2178
2179   conn->write_socket = NULL;
2180   conn->read_socket = NULL;
2181   conn->tunneled = FALSE;
2182   conn->tstate = TUNNEL_STATE_NONE;
2183   conn->ctxp = NULL;
2184   g_free (conn->username);
2185   conn->username = NULL;
2186   g_free (conn->passwd);
2187   conn->passwd = NULL;
2188   gst_rtsp_connection_clear_auth_params (conn);
2189   conn->timeout = 60;
2190   conn->cseq = 0;
2191   conn->session_id[0] = '\0';
2192
2193   return GST_RTSP_OK;
2194 }
2195
2196 /**
2197  * gst_rtsp_connection_free:
2198  * @conn: a #GstRTSPConnection
2199  *
2200  * Close and free @conn.
2201  * 
2202  * Returns: #GST_RTSP_OK on success.
2203  */
2204 GstRTSPResult
2205 gst_rtsp_connection_free (GstRTSPConnection * conn)
2206 {
2207   GstRTSPResult res;
2208
2209   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2210
2211   res = gst_rtsp_connection_close (conn);
2212
2213   if (conn->cancellable)
2214     g_object_unref (conn->cancellable);
2215
2216   g_timer_destroy (conn->timer);
2217   gst_rtsp_url_free (conn->url);
2218   g_free (conn->proxy_host);
2219   g_free (conn);
2220
2221   return res;
2222 }
2223
2224 /**
2225  * gst_rtsp_connection_poll:
2226  * @conn: a #GstRTSPConnection
2227  * @events: a bitmask of #GstRTSPEvent flags to check
2228  * @revents: location for result flags 
2229  * @timeout: a timeout
2230  *
2231  * Wait up to the specified @timeout for the connection to become available for
2232  * at least one of the operations specified in @events. When the function returns
2233  * with #GST_RTSP_OK, @revents will contain a bitmask of available operations on
2234  * @conn.
2235  *
2236  * @timeout can be #NULL, in which case this function might block forever.
2237  *
2238  * This function can be cancelled with gst_rtsp_connection_flush().
2239  * 
2240  * Returns: #GST_RTSP_OK on success.
2241  *
2242  * Since: 0.10.15
2243  */
2244 GstRTSPResult
2245 gst_rtsp_connection_poll (GstRTSPConnection * conn, GstRTSPEvent events,
2246     GstRTSPEvent * revents, GTimeVal * timeout)
2247 {
2248   GstClockTime to;
2249   GMainContext *ctx;
2250   GSource *rs, *ws, *ts;
2251   GIOCondition condition;
2252
2253   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2254   g_return_val_if_fail (events != 0, GST_RTSP_EINVAL);
2255   g_return_val_if_fail (revents != NULL, GST_RTSP_EINVAL);
2256   g_return_val_if_fail (conn->read_socket != NULL, GST_RTSP_EINVAL);
2257   g_return_val_if_fail (conn->write_socket != NULL, GST_RTSP_EINVAL);
2258
2259   ctx = g_main_context_new ();
2260
2261   /* configure timeout if any */
2262   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2263
2264   if (timeout) {
2265     ts = g_timeout_source_new (to / GST_MSECOND);
2266     g_source_set_dummy_callback (ts);
2267     g_source_attach (ts, ctx);
2268     g_source_unref (ts);
2269   }
2270
2271   rs = g_socket_create_source (conn->read_socket,
2272       G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, conn->cancellable);
2273   g_source_set_dummy_callback (rs);
2274   g_source_attach (rs, ctx);
2275   g_source_unref (rs);
2276
2277   ws = g_socket_create_source (conn->write_socket,
2278       G_IO_OUT | G_IO_ERR | G_IO_HUP, conn->cancellable);
2279   g_source_set_dummy_callback (ws);
2280   g_source_attach (ws, ctx);
2281   g_source_unref (ws);
2282
2283   /* Returns after handling all pending events */
2284   g_main_context_iteration (ctx, TRUE);
2285
2286   g_main_context_unref (ctx);
2287
2288   condition =
2289       g_socket_condition_check (conn->read_socket,
2290       G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP);
2291   condition |=
2292       g_socket_condition_check (conn->write_socket,
2293       G_IO_OUT | G_IO_ERR | G_IO_HUP);
2294
2295   *revents = 0;
2296   if (events & GST_RTSP_EV_READ) {
2297     if ((condition & G_IO_IN) || (condition & G_IO_PRI))
2298       *revents |= GST_RTSP_EV_READ;
2299   }
2300   if (events & GST_RTSP_EV_WRITE) {
2301     if ((condition & G_IO_OUT))
2302       *revents |= GST_RTSP_EV_WRITE;
2303   }
2304
2305   if (*revents == 0)
2306     return GST_RTSP_ETIMEOUT;
2307
2308   return GST_RTSP_OK;
2309 }
2310
2311 /**
2312  * gst_rtsp_connection_next_timeout:
2313  * @conn: a #GstRTSPConnection
2314  * @timeout: a timeout
2315  *
2316  * Calculate the next timeout for @conn, storing the result in @timeout.
2317  *
2318  * Returns: #GST_RTSP_OK.
2319  */
2320 GstRTSPResult
2321 gst_rtsp_connection_next_timeout (GstRTSPConnection * conn, GTimeVal * timeout)
2322 {
2323   gdouble elapsed;
2324   glong sec;
2325   gulong usec;
2326   gint ctimeout;
2327
2328   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2329   g_return_val_if_fail (timeout != NULL, GST_RTSP_EINVAL);
2330
2331   ctimeout = conn->timeout;
2332   if (ctimeout >= 20) {
2333     /* Because we should act before the timeout we timeout 5
2334      * seconds in advance. */
2335     ctimeout -= 5;
2336   } else if (ctimeout >= 5) {
2337     /* else timeout 20% earlier */
2338     ctimeout -= ctimeout / 5;
2339   } else if (ctimeout >= 1) {
2340     /* else timeout 1 second earlier */
2341     ctimeout -= 1;
2342   }
2343
2344   elapsed = g_timer_elapsed (conn->timer, &usec);
2345   if (elapsed >= ctimeout) {
2346     sec = 0;
2347     usec = 0;
2348   } else {
2349     sec = ctimeout - elapsed;
2350     if (usec <= G_USEC_PER_SEC)
2351       usec = G_USEC_PER_SEC - usec;
2352     else
2353       usec = 0;
2354   }
2355
2356   timeout->tv_sec = sec;
2357   timeout->tv_usec = usec;
2358
2359   return GST_RTSP_OK;
2360 }
2361
2362 /**
2363  * gst_rtsp_connection_reset_timeout:
2364  * @conn: a #GstRTSPConnection
2365  *
2366  * Reset the timeout of @conn.
2367  *
2368  * Returns: #GST_RTSP_OK.
2369  */
2370 GstRTSPResult
2371 gst_rtsp_connection_reset_timeout (GstRTSPConnection * conn)
2372 {
2373   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2374
2375   g_timer_start (conn->timer);
2376
2377   return GST_RTSP_OK;
2378 }
2379
2380 /**
2381  * gst_rtsp_connection_flush:
2382  * @conn: a #GstRTSPConnection
2383  * @flush: start or stop the flush
2384  *
2385  * Start or stop the flushing action on @conn. When flushing, all current
2386  * and future actions on @conn will return #GST_RTSP_EINTR until the connection
2387  * is set to non-flushing mode again.
2388  * 
2389  * Returns: #GST_RTSP_OK.
2390  */
2391 GstRTSPResult
2392 gst_rtsp_connection_flush (GstRTSPConnection * conn, gboolean flush)
2393 {
2394   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2395
2396   if (flush)
2397     g_cancellable_cancel (conn->cancellable);
2398   else
2399     g_cancellable_reset (conn->cancellable);
2400
2401   return GST_RTSP_OK;
2402 }
2403
2404 /**
2405  * gst_rtsp_connection_set_proxy:
2406  * @conn: a #GstRTSPConnection
2407  * @host: the proxy host
2408  * @port: the proxy port
2409  *
2410  * Set the proxy host and port.
2411  * 
2412  * Returns: #GST_RTSP_OK.
2413  *
2414  * Since: 0.10.23
2415  */
2416 GstRTSPResult
2417 gst_rtsp_connection_set_proxy (GstRTSPConnection * conn,
2418     const gchar * host, guint port)
2419 {
2420   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2421
2422   g_free (conn->proxy_host);
2423   conn->proxy_host = g_strdup (host);
2424   conn->proxy_port = port;
2425
2426   return GST_RTSP_OK;
2427 }
2428
2429 /**
2430  * gst_rtsp_connection_set_auth:
2431  * @conn: a #GstRTSPConnection
2432  * @method: authentication method
2433  * @user: the user
2434  * @pass: the password
2435  *
2436  * Configure @conn for authentication mode @method with @user and @pass as the
2437  * user and password respectively.
2438  * 
2439  * Returns: #GST_RTSP_OK.
2440  */
2441 GstRTSPResult
2442 gst_rtsp_connection_set_auth (GstRTSPConnection * conn,
2443     GstRTSPAuthMethod method, const gchar * user, const gchar * pass)
2444 {
2445   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2446
2447   if (method == GST_RTSP_AUTH_DIGEST && ((user == NULL || pass == NULL)
2448           || g_strrstr (user, ":") != NULL))
2449     return GST_RTSP_EINVAL;
2450
2451   /* Make sure the username and passwd are being set for authentication */
2452   if (method == GST_RTSP_AUTH_NONE && (user == NULL || pass == NULL))
2453     return GST_RTSP_EINVAL;
2454
2455   /* ":" chars are not allowed in usernames for basic auth */
2456   if (method == GST_RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL)
2457     return GST_RTSP_EINVAL;
2458
2459   g_free (conn->username);
2460   g_free (conn->passwd);
2461
2462   conn->auth_method = method;
2463   conn->username = g_strdup (user);
2464   conn->passwd = g_strdup (pass);
2465
2466   return GST_RTSP_OK;
2467 }
2468
2469 /**
2470  * str_case_hash:
2471  * @key: ASCII string to hash
2472  *
2473  * Hashes @key in a case-insensitive manner.
2474  *
2475  * Returns: the hash code.
2476  **/
2477 static guint
2478 str_case_hash (gconstpointer key)
2479 {
2480   const char *p = key;
2481   guint h = g_ascii_toupper (*p);
2482
2483   if (h)
2484     for (p += 1; *p != '\0'; p++)
2485       h = (h << 5) - h + g_ascii_toupper (*p);
2486
2487   return h;
2488 }
2489
2490 /**
2491  * str_case_equal:
2492  * @v1: an ASCII string
2493  * @v2: another ASCII string
2494  *
2495  * Compares @v1 and @v2 in a case-insensitive manner
2496  *
2497  * Returns: %TRUE if they are equal (modulo case)
2498  **/
2499 static gboolean
2500 str_case_equal (gconstpointer v1, gconstpointer v2)
2501 {
2502   const char *string1 = v1;
2503   const char *string2 = v2;
2504
2505   return g_ascii_strcasecmp (string1, string2) == 0;
2506 }
2507
2508 /**
2509  * gst_rtsp_connection_set_auth_param:
2510  * @conn: a #GstRTSPConnection
2511  * @param: authentication directive
2512  * @value: value
2513  *
2514  * Setup @conn with authentication directives. This is not necesary for
2515  * methods #GST_RTSP_AUTH_NONE and #GST_RTSP_AUTH_BASIC. For
2516  * #GST_RTSP_AUTH_DIGEST, directives should be taken from the digest challenge
2517  * in the WWW-Authenticate response header and can include realm, domain,
2518  * nonce, opaque, stale, algorithm, qop as per RFC2617.
2519  * 
2520  * Since: 0.10.20
2521  */
2522 void
2523 gst_rtsp_connection_set_auth_param (GstRTSPConnection * conn,
2524     const gchar * param, const gchar * value)
2525 {
2526   g_return_if_fail (conn != NULL);
2527   g_return_if_fail (param != NULL);
2528
2529   if (conn->auth_params == NULL) {
2530     conn->auth_params =
2531         g_hash_table_new_full (str_case_hash, str_case_equal, g_free, g_free);
2532   }
2533   g_hash_table_insert (conn->auth_params, g_strdup (param), g_strdup (value));
2534 }
2535
2536 /**
2537  * gst_rtsp_connection_clear_auth_params:
2538  * @conn: a #GstRTSPConnection
2539  *
2540  * Clear the list of authentication directives stored in @conn.
2541  *
2542  * Since: 0.10.20
2543  */
2544 void
2545 gst_rtsp_connection_clear_auth_params (GstRTSPConnection * conn)
2546 {
2547   g_return_if_fail (conn != NULL);
2548
2549   if (conn->auth_params != NULL) {
2550     g_hash_table_destroy (conn->auth_params);
2551     conn->auth_params = NULL;
2552   }
2553 }
2554
2555 static GstRTSPResult
2556 set_qos_dscp (GSocket * socket, guint qos_dscp)
2557 {
2558 #ifndef IP_TOS
2559   return GST_RTSP_OK;
2560 #else
2561   gint fd;
2562   union gst_sockaddr sa;
2563   socklen_t slen = sizeof (sa);
2564   gint af;
2565   gint tos;
2566
2567   if (!socket)
2568     return GST_RTSP_OK;
2569
2570   fd = g_socket_get_fd (socket);
2571   if (getsockname (fd, &sa.sa, &slen) < 0)
2572     goto no_getsockname;
2573
2574   af = sa.sa.sa_family;
2575
2576   /* if this is an IPv4-mapped address then do IPv4 QoS */
2577   if (af == AF_INET6) {
2578     if (IN6_IS_ADDR_V4MAPPED (&sa.sa_in6.sin6_addr))
2579       af = AF_INET;
2580   }
2581
2582   /* extract and shift 6 bits of the DSCP */
2583   tos = (qos_dscp & 0x3f) << 2;
2584
2585   switch (af) {
2586     case AF_INET:
2587       if (setsockopt (fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0)
2588         goto no_setsockopt;
2589       break;
2590     case AF_INET6:
2591 #ifdef IPV6_TCLASS
2592       if (setsockopt (fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof (tos)) < 0)
2593         goto no_setsockopt;
2594       break;
2595 #endif
2596     default:
2597       goto wrong_family;
2598   }
2599
2600   return GST_RTSP_OK;
2601
2602   /* ERRORS */
2603 no_getsockname:
2604 no_setsockopt:
2605   {
2606     return GST_RTSP_ESYS;
2607   }
2608 wrong_family:
2609   {
2610     return GST_RTSP_ERROR;
2611   }
2612 #endif
2613 }
2614
2615 /**
2616  * gst_rtsp_connection_set_qos_dscp:
2617  * @conn: a #GstRTSPConnection
2618  * @qos_dscp: DSCP value
2619  *
2620  * Configure @conn to use the specified DSCP value.
2621  *
2622  * Returns: #GST_RTSP_OK on success.
2623  *
2624  * Since: 0.10.20
2625  */
2626 GstRTSPResult
2627 gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
2628 {
2629   GstRTSPResult res;
2630
2631   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2632   g_return_val_if_fail (conn->read_socket != NULL, GST_RTSP_EINVAL);
2633   g_return_val_if_fail (conn->write_socket != NULL, GST_RTSP_EINVAL);
2634
2635   res = set_qos_dscp (conn->socket0, qos_dscp);
2636   if (res == GST_RTSP_OK)
2637     res = set_qos_dscp (conn->socket1, qos_dscp);
2638
2639   return res;
2640 }
2641
2642
2643 /**
2644  * gst_rtsp_connection_get_url:
2645  * @conn: a #GstRTSPConnection
2646  *
2647  * Retrieve the URL of the other end of @conn.
2648  *
2649  * Returns: The URL. This value remains valid until the
2650  * connection is freed.
2651  *
2652  * Since: 0.10.23
2653  */
2654 GstRTSPUrl *
2655 gst_rtsp_connection_get_url (const GstRTSPConnection * conn)
2656 {
2657   g_return_val_if_fail (conn != NULL, NULL);
2658
2659   return conn->url;
2660 }
2661
2662 /**
2663  * gst_rtsp_connection_get_ip:
2664  * @conn: a #GstRTSPConnection
2665  *
2666  * Retrieve the IP address of the other end of @conn.
2667  *
2668  * Returns: The IP address as a string. this value remains valid until the
2669  * connection is closed.
2670  *
2671  * Since: 0.10.20
2672  */
2673 const gchar *
2674 gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
2675 {
2676   g_return_val_if_fail (conn != NULL, NULL);
2677
2678   return conn->ip;
2679 }
2680
2681 /**
2682  * gst_rtsp_connection_set_ip:
2683  * @conn: a #GstRTSPConnection
2684  * @ip: an ip address
2685  *
2686  * Set the IP address of the server.
2687  *
2688  * Since: 0.10.23
2689  */
2690 void
2691 gst_rtsp_connection_set_ip (GstRTSPConnection * conn, const gchar * ip)
2692 {
2693   g_return_if_fail (conn != NULL);
2694
2695   g_free (conn->ip);
2696   conn->ip = g_strdup (ip);
2697 }
2698
2699 /**
2700  * gst_rtsp_connection_get_readfd:
2701  * @conn: a #GstRTSPConnection
2702  *
2703  * Get the file descriptor for reading.
2704  *
2705  * Returns: the file descriptor used for reading or %NULL on error. The file
2706  * descriptor remains valid until the connection is closed.
2707  *
2708  * Since: 0.10.23
2709  */
2710 GSocket *
2711 gst_rtsp_connection_get_read_socket (const GstRTSPConnection * conn)
2712 {
2713   g_return_val_if_fail (conn != NULL, NULL);
2714   g_return_val_if_fail (conn->read_socket != NULL, NULL);
2715
2716   return conn->read_socket;
2717 }
2718
2719 /**
2720  * gst_rtsp_connection_get_write_socket:
2721  * @conn: a #GstRTSPConnection
2722  *
2723  * Get the file descriptor for writing.
2724  *
2725  * Returns: the file descriptor used for writing or NULL on error. The file
2726  * descriptor remains valid until the connection is closed.
2727  *
2728  * Since: 0.10.23
2729  */
2730 GSocket *
2731 gst_rtsp_connection_get_write_socket (const GstRTSPConnection * conn)
2732 {
2733   g_return_val_if_fail (conn != NULL, NULL);
2734   g_return_val_if_fail (conn->write_socket != NULL, NULL);
2735
2736   return conn->write_socket;
2737 }
2738
2739 /**
2740  * gst_rtsp_connection_set_http_mode:
2741  * @conn: a #GstRTSPConnection
2742  * @enable: %TRUE to enable manual HTTP mode
2743  *
2744  * By setting the HTTP mode to %TRUE the message parsing will support HTTP
2745  * messages in addition to the RTSP messages. It will also disable the
2746  * automatic handling of setting up an HTTP tunnel.
2747  *
2748  * Since: 0.10.25
2749  */
2750 void
2751 gst_rtsp_connection_set_http_mode (GstRTSPConnection * conn, gboolean enable)
2752 {
2753   g_return_if_fail (conn != NULL);
2754
2755   conn->manual_http = enable;
2756 }
2757
2758 /**
2759  * gst_rtsp_connection_set_tunneled:
2760  * @conn: a #GstRTSPConnection
2761  * @tunneled: the new state
2762  *
2763  * Set the HTTP tunneling state of the connection. This must be configured before
2764  * the @conn is connected.
2765  *
2766  * Since: 0.10.23
2767  */
2768 void
2769 gst_rtsp_connection_set_tunneled (GstRTSPConnection * conn, gboolean tunneled)
2770 {
2771   g_return_if_fail (conn != NULL);
2772   g_return_if_fail (conn->read_socket == NULL);
2773   g_return_if_fail (conn->write_socket == NULL);
2774
2775   conn->tunneled = tunneled;
2776 }
2777
2778 /**
2779  * gst_rtsp_connection_is_tunneled:
2780  * @conn: a #GstRTSPConnection
2781  *
2782  * Get the tunneling state of the connection. 
2783  *
2784  * Returns: if @conn is using HTTP tunneling.
2785  *
2786  * Since: 0.10.23
2787  */
2788 gboolean
2789 gst_rtsp_connection_is_tunneled (const GstRTSPConnection * conn)
2790 {
2791   g_return_val_if_fail (conn != NULL, FALSE);
2792
2793   return conn->tunneled;
2794 }
2795
2796 /**
2797  * gst_rtsp_connection_get_tunnelid:
2798  * @conn: a #GstRTSPConnection
2799  *
2800  * Get the tunnel session id the connection. 
2801  *
2802  * Returns: returns a non-empty string if @conn is being tunneled over HTTP.
2803  *
2804  * Since: 0.10.23
2805  */
2806 const gchar *
2807 gst_rtsp_connection_get_tunnelid (const GstRTSPConnection * conn)
2808 {
2809   g_return_val_if_fail (conn != NULL, NULL);
2810
2811   if (!conn->tunneled)
2812     return NULL;
2813
2814   return conn->tunnelid;
2815 }
2816
2817 /**
2818  * gst_rtsp_connection_do_tunnel:
2819  * @conn: a #GstRTSPConnection
2820  * @conn2: a #GstRTSPConnection or %NULL
2821  *
2822  * If @conn received the first tunnel connection and @conn2 received
2823  * the second tunnel connection, link the two connections together so that
2824  * @conn manages the tunneled connection.
2825  *
2826  * After this call, @conn2 cannot be used anymore and must be freed with
2827  * gst_rtsp_connection_free().
2828  *
2829  * If @conn2 is %NULL then only the base64 decoding context will be setup for
2830  * @conn.
2831  *
2832  * Returns: return GST_RTSP_OK on success.
2833  *
2834  * Since: 0.10.23
2835  */
2836 GstRTSPResult
2837 gst_rtsp_connection_do_tunnel (GstRTSPConnection * conn,
2838     GstRTSPConnection * conn2)
2839 {
2840   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2841
2842   if (conn2 != NULL) {
2843     g_return_val_if_fail (conn->tstate == TUNNEL_STATE_GET, GST_RTSP_EINVAL);
2844     g_return_val_if_fail (conn2->tstate == TUNNEL_STATE_POST, GST_RTSP_EINVAL);
2845     g_return_val_if_fail (!memcmp (conn2->tunnelid, conn->tunnelid,
2846             TUNNELID_LEN), GST_RTSP_EINVAL);
2847
2848     /* both connections have socket0 as the read/write socket. start by taking the
2849      * socket from conn2 and set it as the socket in conn */
2850     conn->socket1 = conn2->socket0;
2851
2852     /* clean up some of the state of conn2 */
2853     g_cancellable_cancel (conn2->cancellable);
2854     conn2->socket0 = 0;
2855     g_object_unref (conn2->socket1);
2856     conn2->socket1 = NULL;
2857     conn2->write_socket = conn2->read_socket = NULL;
2858     g_cancellable_reset (conn2->cancellable);
2859
2860     /* We make socket0 the write socket and socket1 the read socket. */
2861     conn->write_socket = conn->socket0;
2862     conn->read_socket = conn->socket1;
2863
2864     conn->tstate = TUNNEL_STATE_COMPLETE;
2865   }
2866
2867   /* we need base64 decoding for the readfd */
2868   conn->ctx.state = 0;
2869   conn->ctx.save = 0;
2870   conn->ctx.cout = 0;
2871   conn->ctx.coutl = 0;
2872   conn->ctxp = &conn->ctx;
2873
2874   return GST_RTSP_OK;
2875 }
2876
2877 #define READ_ERR    (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
2878 #define READ_COND   (G_IO_IN | READ_ERR)
2879 #define WRITE_ERR   (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
2880 #define WRITE_COND  (G_IO_OUT | WRITE_ERR)
2881
2882 typedef struct
2883 {
2884   guint8 *data;
2885   guint size;
2886   guint id;
2887 } GstRTSPRec;
2888
2889 /* async functions */
2890 struct _GstRTSPWatch
2891 {
2892   GSource source;
2893
2894   GstRTSPConnection *conn;
2895
2896   GstRTSPBuilder builder;
2897   GstRTSPMessage message;
2898
2899   GPollFD readfd;
2900   GPollFD writefd;
2901
2902   /* queued message for transmission */
2903   guint id;
2904   GMutex *mutex;
2905   GQueue *messages;
2906   guint8 *write_data;
2907   guint write_off;
2908   guint write_size;
2909   guint write_id;
2910
2911   GstRTSPWatchFuncs funcs;
2912
2913   gpointer user_data;
2914   GDestroyNotify notify;
2915 };
2916
2917 static gboolean
2918 gst_rtsp_source_prepare (GSource * source, gint * timeout)
2919 {
2920   GstRTSPWatch *watch = (GstRTSPWatch *) source;
2921
2922   if (watch->conn->initial_buffer != NULL)
2923     return TRUE;
2924
2925   *timeout = (watch->conn->timeout * 1000);
2926
2927   return FALSE;
2928 }
2929
2930 static gboolean
2931 gst_rtsp_source_check (GSource * source)
2932 {
2933   GstRTSPWatch *watch = (GstRTSPWatch *) source;
2934
2935   if (watch->readfd.revents & READ_COND)
2936     return TRUE;
2937
2938   if (watch->writefd.revents & WRITE_COND)
2939     return TRUE;
2940
2941   return FALSE;
2942 }
2943
2944 static gboolean
2945 gst_rtsp_source_dispatch (GSource * source, GSourceFunc callback G_GNUC_UNUSED,
2946     gpointer user_data G_GNUC_UNUSED)
2947 {
2948   GstRTSPWatch *watch = (GstRTSPWatch *) source;
2949   GstRTSPResult res = GST_RTSP_ERROR;
2950   gboolean keep_running = TRUE;
2951
2952   /* first read as much as we can */
2953   if (watch->readfd.revents & READ_COND || watch->conn->initial_buffer != NULL) {
2954     do {
2955       if (watch->readfd.revents & READ_ERR)
2956         goto read_error;
2957
2958       res = build_next (&watch->builder, &watch->message, watch->conn);
2959       if (res == GST_RTSP_EINTR)
2960         break;
2961       else if (G_UNLIKELY (res == GST_RTSP_EEOF)) {
2962         watch->readfd.events = 0;
2963         watch->readfd.revents = 0;
2964         g_source_remove_poll ((GSource *) watch, &watch->readfd);
2965         /* When we are in tunnelled mode, the read socket can be closed and we
2966          * should be prepared for a new POST method to reopen it */
2967         if (watch->conn->tstate == TUNNEL_STATE_COMPLETE) {
2968           /* remove the read connection for the tunnel */
2969           /* we accept a new POST request */
2970           watch->conn->tstate = TUNNEL_STATE_GET;
2971           /* and signal that we lost our tunnel */
2972           if (watch->funcs.tunnel_lost)
2973             res = watch->funcs.tunnel_lost (watch, watch->user_data);
2974           goto read_done;
2975         } else
2976           goto eof;
2977       } else if (G_LIKELY (res == GST_RTSP_OK)) {
2978         if (!watch->conn->manual_http &&
2979             watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
2980           if (watch->conn->tstate == TUNNEL_STATE_NONE &&
2981               watch->message.type_data.request.method == GST_RTSP_GET) {
2982             GstRTSPMessage *response;
2983             GstRTSPStatusCode code;
2984
2985             watch->conn->tstate = TUNNEL_STATE_GET;
2986
2987             if (watch->funcs.tunnel_start)
2988               code = watch->funcs.tunnel_start (watch, watch->user_data);
2989             else
2990               code = GST_RTSP_STS_OK;
2991
2992             /* queue the response */
2993             response = gen_tunnel_reply (watch->conn, code, &watch->message);
2994             gst_rtsp_watch_send_message (watch, response, NULL);
2995             gst_rtsp_message_free (response);
2996             goto read_done;
2997           } else if (watch->conn->tstate == TUNNEL_STATE_NONE &&
2998               watch->message.type_data.request.method == GST_RTSP_POST) {
2999             watch->conn->tstate = TUNNEL_STATE_POST;
3000
3001             /* in the callback the connection should be tunneled with the
3002              * GET connection */
3003             if (watch->funcs.tunnel_complete)
3004               watch->funcs.tunnel_complete (watch, watch->user_data);
3005             goto read_done;
3006           }
3007         }
3008       }
3009
3010       if (!watch->conn->manual_http) {
3011         /* if manual HTTP support is not enabled, then restore the message to
3012          * what it would have looked like without the support for parsing HTTP
3013          * messages being present */
3014         if (watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
3015           watch->message.type = GST_RTSP_MESSAGE_REQUEST;
3016           watch->message.type_data.request.method = GST_RTSP_INVALID;
3017           if (watch->message.type_data.request.version != GST_RTSP_VERSION_1_0)
3018             watch->message.type_data.request.version = GST_RTSP_VERSION_INVALID;
3019           res = GST_RTSP_EPARSE;
3020         } else if (watch->message.type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
3021           watch->message.type = GST_RTSP_MESSAGE_RESPONSE;
3022           if (watch->message.type_data.response.version != GST_RTSP_VERSION_1_0)
3023             watch->message.type_data.response.version =
3024                 GST_RTSP_VERSION_INVALID;
3025           res = GST_RTSP_EPARSE;
3026         }
3027       }
3028
3029       if (G_LIKELY (res == GST_RTSP_OK)) {
3030         if (watch->funcs.message_received)
3031           watch->funcs.message_received (watch, &watch->message,
3032               watch->user_data);
3033       } else {
3034         goto read_error;
3035       }
3036
3037     read_done:
3038       gst_rtsp_message_unset (&watch->message);
3039       build_reset (&watch->builder);
3040     } while (FALSE);
3041   }
3042
3043   if (watch->writefd.revents & WRITE_COND) {
3044     if (watch->writefd.revents & WRITE_ERR)
3045       goto write_error;
3046
3047     g_mutex_lock (watch->mutex);
3048     do {
3049       if (watch->write_data == NULL) {
3050         GstRTSPRec *rec;
3051
3052         /* get a new message from the queue */
3053         rec = g_queue_pop_tail (watch->messages);
3054         if (rec == NULL)
3055           break;
3056
3057         watch->write_off = 0;
3058         watch->write_data = rec->data;
3059         watch->write_size = rec->size;
3060         watch->write_id = rec->id;
3061
3062         g_slice_free (GstRTSPRec, rec);
3063       }
3064
3065       res = write_bytes (watch->conn->write_socket, watch->write_data,
3066           &watch->write_off, watch->write_size, watch->conn->cancellable);
3067       g_mutex_unlock (watch->mutex);
3068
3069       if (res == GST_RTSP_EINTR)
3070         goto write_blocked;
3071       else if (G_LIKELY (res == GST_RTSP_OK)) {
3072         if (watch->funcs.message_sent)
3073           watch->funcs.message_sent (watch, watch->write_id, watch->user_data);
3074       } else {
3075         goto write_error;
3076       }
3077       g_mutex_lock (watch->mutex);
3078
3079       g_free (watch->write_data);
3080       watch->write_data = NULL;
3081     } while (TRUE);
3082
3083     watch->writefd.events = WRITE_ERR;
3084
3085     g_mutex_unlock (watch->mutex);
3086   }
3087
3088 write_blocked:
3089   return keep_running;
3090
3091   /* ERRORS */
3092 eof:
3093   {
3094     if (watch->funcs.closed)
3095       watch->funcs.closed (watch, watch->user_data);
3096
3097     /* always stop when the readfd returns EOF in non-tunneled mode */
3098     return FALSE;
3099   }
3100 read_error:
3101   {
3102     watch->readfd.events = 0;
3103     watch->readfd.revents = 0;
3104     g_source_remove_poll ((GSource *) watch, &watch->readfd);
3105     keep_running = (watch->writefd.events != 0);
3106
3107     if (keep_running) {
3108       if (watch->funcs.error_full)
3109         GST_RTSP_CHECK (watch->funcs.error_full (watch, res, &watch->message,
3110                 0, watch->user_data), error);
3111       else
3112         goto error;
3113     } else
3114       goto eof;
3115   }
3116 write_error:
3117   {
3118     watch->writefd.events = 0;
3119     watch->writefd.revents = 0;
3120     g_source_remove_poll ((GSource *) watch, &watch->writefd);
3121     keep_running = (watch->readfd.events != 0);
3122
3123     if (keep_running) {
3124       if (watch->funcs.error_full)
3125         GST_RTSP_CHECK (watch->funcs.error_full (watch, res, NULL,
3126                 watch->write_id, watch->user_data), error);
3127       else
3128         goto error;
3129     } else
3130       goto eof;
3131   }
3132 error:
3133   {
3134     if (watch->funcs.error)
3135       watch->funcs.error (watch, res, watch->user_data);
3136
3137     return keep_running;
3138   }
3139 }
3140
3141 static void
3142 gst_rtsp_rec_free (gpointer data)
3143 {
3144   GstRTSPRec *rec = data;
3145
3146   g_free (rec->data);
3147   g_slice_free (GstRTSPRec, rec);
3148 }
3149
3150 static void
3151 gst_rtsp_source_finalize (GSource * source)
3152 {
3153   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3154
3155   build_reset (&watch->builder);
3156   gst_rtsp_message_unset (&watch->message);
3157
3158   g_queue_foreach (watch->messages, (GFunc) gst_rtsp_rec_free, NULL);
3159   g_queue_free (watch->messages);
3160   watch->messages = NULL;
3161   g_free (watch->write_data);
3162
3163   g_mutex_free (watch->mutex);
3164
3165   if (watch->notify)
3166     watch->notify (watch->user_data);
3167 }
3168
3169 static GSourceFuncs gst_rtsp_source_funcs = {
3170   gst_rtsp_source_prepare,
3171   gst_rtsp_source_check,
3172   gst_rtsp_source_dispatch,
3173   gst_rtsp_source_finalize,
3174   NULL,
3175   NULL
3176 };
3177
3178 /**
3179  * gst_rtsp_watch_new:
3180  * @conn: a #GstRTSPConnection
3181  * @funcs: watch functions
3182  * @user_data: user data to pass to @funcs
3183  * @notify: notify when @user_data is not referenced anymore
3184  *
3185  * Create a watch object for @conn. The functions provided in @funcs will be
3186  * called with @user_data when activity happened on the watch.
3187  *
3188  * The new watch is usually created so that it can be attached to a
3189  * maincontext with gst_rtsp_watch_attach(). 
3190  *
3191  * @conn must exist for the entire lifetime of the watch.
3192  *
3193  * Returns: a #GstRTSPWatch that can be used for asynchronous RTSP
3194  * communication. Free with gst_rtsp_watch_unref () after usage.
3195  *
3196  * Since: 0.10.23
3197  */
3198 GstRTSPWatch *
3199 gst_rtsp_watch_new (GstRTSPConnection * conn,
3200     GstRTSPWatchFuncs * funcs, gpointer user_data, GDestroyNotify notify)
3201 {
3202   GstRTSPWatch *result;
3203
3204   g_return_val_if_fail (conn != NULL, NULL);
3205   g_return_val_if_fail (funcs != NULL, NULL);
3206   g_return_val_if_fail (conn->read_socket != NULL, NULL);
3207   g_return_val_if_fail (conn->write_socket != NULL, NULL);
3208
3209   result = (GstRTSPWatch *) g_source_new (&gst_rtsp_source_funcs,
3210       sizeof (GstRTSPWatch));
3211
3212   result->conn = conn;
3213   result->builder.state = STATE_START;
3214
3215   result->mutex = g_mutex_new ();
3216   result->messages = g_queue_new ();
3217
3218   result->readfd.fd = -1;
3219   result->writefd.fd = -1;
3220
3221   gst_rtsp_watch_reset (result);
3222
3223   result->funcs = *funcs;
3224   result->user_data = user_data;
3225   result->notify = notify;
3226
3227   return result;
3228 }
3229
3230 /**
3231  * gst_rtsp_watch_reset:
3232  * @watch: a #GstRTSPWatch
3233  *
3234  * Reset @watch, this is usually called after gst_rtsp_connection_do_tunnel()
3235  * when the file descriptors of the connection might have changed.
3236  *
3237  * Since: 0.10.23
3238  */
3239 void
3240 gst_rtsp_watch_reset (GstRTSPWatch * watch)
3241 {
3242   if (watch->readfd.fd != -1)
3243     g_source_remove_poll ((GSource *) watch, &watch->readfd);
3244   if (watch->writefd.fd != -1)
3245     g_source_remove_poll ((GSource *) watch, &watch->writefd);
3246
3247   watch->readfd.fd = g_socket_get_fd (watch->conn->read_socket);
3248   watch->readfd.events = READ_COND;
3249   watch->readfd.revents = 0;
3250
3251   watch->writefd.fd = g_socket_get_fd (watch->conn->write_socket);
3252   watch->writefd.events = WRITE_ERR;
3253   watch->writefd.revents = 0;
3254
3255   if (watch->readfd.fd != -1)
3256     g_source_add_poll ((GSource *) watch, &watch->readfd);
3257   if (watch->writefd.fd != -1)
3258     g_source_add_poll ((GSource *) watch, &watch->writefd);
3259 }
3260
3261 /**
3262  * gst_rtsp_watch_attach:
3263  * @watch: a #GstRTSPWatch
3264  * @context: a GMainContext (if NULL, the default context will be used)
3265  *
3266  * Adds a #GstRTSPWatch to a context so that it will be executed within that context.
3267  *
3268  * Returns: the ID (greater than 0) for the watch within the GMainContext. 
3269  *
3270  * Since: 0.10.23
3271  */
3272 guint
3273 gst_rtsp_watch_attach (GstRTSPWatch * watch, GMainContext * context)
3274 {
3275   g_return_val_if_fail (watch != NULL, 0);
3276
3277   return g_source_attach ((GSource *) watch, context);
3278 }
3279
3280 /**
3281  * gst_rtsp_watch_unref:
3282  * @watch: a #GstRTSPWatch
3283  *
3284  * Decreases the reference count of @watch by one. If the resulting reference
3285  * count is zero the watch and associated memory will be destroyed.
3286  *
3287  * Since: 0.10.23
3288  */
3289 void
3290 gst_rtsp_watch_unref (GstRTSPWatch * watch)
3291 {
3292   g_return_if_fail (watch != NULL);
3293
3294   g_source_unref ((GSource *) watch);
3295 }
3296
3297 /**
3298  * gst_rtsp_watch_write_data:
3299  * @watch: a #GstRTSPWatch
3300  * @data: the data to queue
3301  * @size: the size of @data
3302  * @id: location for a message ID or %NULL
3303  *
3304  * Write @data using the connection of the @watch. If it cannot be sent
3305  * immediately, it will be queued for transmission in @watch. The contents of
3306  * @message will then be serialized and transmitted when the connection of the
3307  * @watch becomes writable. In case the @message is queued, the ID returned in
3308  * @id will be non-zero and used as the ID argument in the message_sent
3309  * callback.
3310  *
3311  * This function will take ownership of @data and g_free() it after use.
3312  *
3313  * Returns: #GST_RTSP_OK on success.
3314  *
3315  * Since: 0.10.25
3316  */
3317 GstRTSPResult
3318 gst_rtsp_watch_write_data (GstRTSPWatch * watch, const guint8 * data,
3319     guint size, guint * id)
3320 {
3321   GstRTSPResult res;
3322   GstRTSPRec *rec;
3323   guint off = 0;
3324   GMainContext *context = NULL;
3325
3326   g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3327   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
3328   g_return_val_if_fail (size != 0, GST_RTSP_EINVAL);
3329
3330   g_mutex_lock (watch->mutex);
3331
3332   /* try to send the message synchronously first */
3333   if (watch->messages->length == 0 && watch->write_data == NULL) {
3334     res =
3335         write_bytes (watch->conn->write_socket, data, &off, size,
3336         watch->conn->cancellable);
3337     if (res != GST_RTSP_EINTR) {
3338       if (id != NULL)
3339         *id = 0;
3340       g_free ((gpointer) data);
3341       goto done;
3342     }
3343   }
3344
3345   /* make a record with the data and id for sending async */
3346   rec = g_slice_new (GstRTSPRec);
3347   if (off == 0) {
3348     rec->data = (guint8 *) data;
3349     rec->size = size;
3350   } else {
3351     rec->data = g_memdup (data + off, size - off);
3352     rec->size = size - off;
3353     g_free ((gpointer) data);
3354   }
3355
3356   do {
3357     /* make sure rec->id is never 0 */
3358     rec->id = ++watch->id;
3359   } while (G_UNLIKELY (rec->id == 0));
3360
3361   /* add the record to a queue. FIXME we would like to have an upper limit here */
3362   g_queue_push_head (watch->messages, rec);
3363
3364   /* make sure the main context will now also check for writability on the
3365    * socket */
3366   if (watch->writefd.events != WRITE_COND) {
3367     watch->writefd.events = WRITE_COND;
3368     context = ((GSource *) watch)->context;
3369   }
3370
3371   if (id != NULL)
3372     *id = rec->id;
3373   res = GST_RTSP_OK;
3374
3375 done:
3376   g_mutex_unlock (watch->mutex);
3377
3378   if (context)
3379     g_main_context_wakeup (context);
3380
3381   return res;
3382 }
3383
3384 /**
3385  * gst_rtsp_watch_send_message:
3386  * @watch: a #GstRTSPWatch
3387  * @message: a #GstRTSPMessage
3388  * @id: location for a message ID or %NULL
3389  *
3390  * Send a @message using the connection of the @watch. If it cannot be sent
3391  * immediately, it will be queued for transmission in @watch. The contents of
3392  * @message will then be serialized and transmitted when the connection of the
3393  * @watch becomes writable. In case the @message is queued, the ID returned in
3394  * @id will be non-zero and used as the ID argument in the message_sent
3395  * callback.
3396  *
3397  * Returns: #GST_RTSP_OK on success.
3398  *
3399  * Since: 0.10.25
3400  */
3401 GstRTSPResult
3402 gst_rtsp_watch_send_message (GstRTSPWatch * watch, GstRTSPMessage * message,
3403     guint * id)
3404 {
3405   GString *str;
3406   guint size;
3407
3408   g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3409   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3410
3411   /* make a record with the message as a string and id */
3412   str = message_to_string (watch->conn, message);
3413   size = str->len;
3414   return gst_rtsp_watch_write_data (watch,
3415       (guint8 *) g_string_free (str, FALSE), size, id);
3416 }