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