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