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