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