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