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