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