Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst-libs / gst / rtsp / gstrtspconnection.c
1 /* GStreamer
2  * Copyright (C) <2005-2009> Wim Taymans <wim.taymans@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 /*
20  * Unless otherwise indicated, Source Code is licensed under MIT license.
21  * See further explanation attached in License Statement (distributed in the file
22  * LICENSE).
23  *
24  * Permission is hereby granted, free of charge, to any person obtaining a copy of
25  * this software and associated documentation files (the "Software"), to deal in
26  * the Software without restriction, including without limitation the rights to
27  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28  * of the Software, and to permit persons to whom the Software is furnished to do
29  * so, subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be included in all
32  * copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40  * SOFTWARE.
41  */
42
43 /**
44  * SECTION:gstrtspconnection
45  * @short_description: manage RTSP connections
46  * @see_also: gstrtspurl
47  *  
48  * This object manages the RTSP connection to the server. It provides function
49  * to receive and send bytes and messages.
50  *  
51  * Last reviewed on 2007-07-24 (0.10.14)
52  */
53
54 #ifdef HAVE_CONFIG_H
55 #  include <config.h>
56 #endif
57
58 #include <stdio.h>
59 #include <errno.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <time.h>
63
64 #ifdef HAVE_UNISTD_H
65 #include <unistd.h>
66 #endif
67
68 /* we include this here to get the G_OS_* defines */
69 #include <glib.h>
70 #include <gst/gst.h>
71
72 #ifdef G_OS_WIN32
73 /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
74  * minwg32 headers check WINVER before allowing the use of these */
75 #ifndef WINVER
76 #define WINVER 0x0501
77 #endif
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 <fcntl.h>
86 #include <netinet/in.h>
87 #endif
88
89 #ifdef HAVE_FIONREAD_IN_SYS_FILIO
90 #include <sys/filio.h>
91 #endif
92
93 #include "gstrtspconnection.h"
94
95 #include "gst/glib-compat-private.h"
96
97 union gst_sockaddr
98 {
99   struct sockaddr sa;
100   struct sockaddr_in sa_in;
101   struct sockaddr_in6 sa_in6;
102   struct sockaddr_storage sa_stor;
103 };
104
105 typedef struct
106 {
107   gint state;
108   guint save;
109   guchar out[3];                /* the size must be evenly divisible by 3 */
110   guint cout;
111   guint coutl;
112 } DecodeCtx;
113
114 #ifdef MSG_NOSIGNAL
115 #define SEND_FLAGS MSG_NOSIGNAL
116 #else
117 #define SEND_FLAGS 0
118 #endif
119
120 #ifdef G_OS_WIN32
121 #define READ_SOCKET(fd, buf, len) recv (fd, (char *)buf, len, 0)
122 #define WRITE_SOCKET(fd, buf, len) send (fd, (const char *)buf, len, SEND_FLAGS)
123 #define SETSOCKOPT(sock, level, name, val, len) setsockopt (sock, level, name, (const char *)val, len)
124 #define CLOSE_SOCKET(sock) closesocket (sock)
125 #define ERRNO_IS_EAGAIN (WSAGetLastError () == WSAEWOULDBLOCK)
126 #define ERRNO_IS_EINTR (WSAGetLastError () == WSAEINTR)
127 /* According to Microsoft's connect() documentation this one returns
128  * WSAEWOULDBLOCK and not WSAEINPROGRESS. */
129 #define ERRNO_IS_EINPROGRESS (WSAGetLastError () == WSAEWOULDBLOCK)
130 #else
131 #define READ_SOCKET(fd, buf, len) read (fd, buf, len)
132 #define WRITE_SOCKET(fd, buf, len) send (fd, buf, len, SEND_FLAGS)
133 #define SETSOCKOPT(sock, level, name, val, len) setsockopt (sock, level, name, val, len)
134 #define CLOSE_SOCKET(sock) close (sock)
135 #define ERRNO_IS_EAGAIN (errno == EAGAIN)
136 #define ERRNO_IS_EINTR (errno == EINTR)
137 #define ERRNO_IS_EINPROGRESS (errno == EINPROGRESS)
138 #endif
139
140 #define ADD_POLLFD(fdset, pfd, fd)        \
141 G_STMT_START {                            \
142   (pfd)->fd = fd;                         \
143   gst_poll_add_fd (fdset, pfd);           \
144 } G_STMT_END
145
146 #define REMOVE_POLLFD(fdset, pfd)          \
147 G_STMT_START {                             \
148   if ((pfd)->fd != -1) {                   \
149     GST_DEBUG ("remove fd %d", (pfd)->fd); \
150     gst_poll_remove_fd (fdset, pfd);       \
151     CLOSE_SOCKET ((pfd)->fd);              \
152     (pfd)->fd = -1;                        \
153   }                                        \
154 } G_STMT_END
155
156 typedef enum
157 {
158   TUNNEL_STATE_NONE,
159   TUNNEL_STATE_GET,
160   TUNNEL_STATE_POST,
161   TUNNEL_STATE_COMPLETE
162 } GstRTSPTunnelState;
163
164 #define TUNNELID_LEN   24
165
166 struct _GstRTSPConnection
167 {
168   /*< private > */
169   /* URL for the connection */
170   GstRTSPUrl *url;
171
172   /* connection state */
173   GstPollFD fd0;
174   GstPollFD fd1;
175
176   GstPollFD *readfd;
177   GstPollFD *writefd;
178
179   gboolean manual_http;
180
181   gchar tunnelid[TUNNELID_LEN];
182   gboolean tunneled;
183   GstRTSPTunnelState tstate;
184
185   GstPoll *fdset;
186   gchar *ip;
187
188   gint read_ahead;
189
190   gchar *initial_buffer;
191   gsize initial_buffer_offset;
192
193   /* Session state */
194   gint cseq;                    /* sequence number */
195   gchar session_id[512];        /* session id */
196   gint timeout;                 /* session timeout in seconds */
197   GTimer *timer;                /* timeout timer */
198
199   /* Authentication */
200   GstRTSPAuthMethod auth_method;
201   gchar *username;
202   gchar *passwd;
203   GHashTable *auth_params;
204
205   DecodeCtx ctx;
206   DecodeCtx *ctxp;
207
208   gchar *proxy_host;
209   guint proxy_port;
210 };
211
212 enum
213 {
214   STATE_START = 0,
215   STATE_DATA_HEADER,
216   STATE_DATA_BODY,
217   STATE_READ_LINES,
218   STATE_END,
219   STATE_LAST
220 };
221
222 enum
223 {
224   READ_AHEAD_EOH = -1,          /* end of headers */
225   READ_AHEAD_CRLF = -2,
226   READ_AHEAD_CRLFCR = -3
227 };
228
229 /* a structure for constructing RTSPMessages */
230 typedef struct
231 {
232   gint state;
233   GstRTSPResult status;
234   guint8 buffer[4096];
235   guint offset;
236
237   guint line;
238   guint8 *body_data;
239   glong body_len;
240 } GstRTSPBuilder;
241
242 static void
243 build_reset (GstRTSPBuilder * builder)
244 {
245   g_free (builder->body_data);
246   memset (builder, 0, sizeof (GstRTSPBuilder));
247 }
248
249 /**
250  * gst_rtsp_connection_create:
251  * @url: a #GstRTSPUrl 
252  * @conn: storage for a #GstRTSPConnection
253  *
254  * Create a newly allocated #GstRTSPConnection from @url and store it in @conn.
255  * The connection will not yet attempt to connect to @url, use
256  * gst_rtsp_connection_connect().
257  *
258  * A copy of @url will be made.
259  *
260  * Returns: #GST_RTSP_OK when @conn contains a valid connection.
261  */
262 GstRTSPResult
263 gst_rtsp_connection_create (const GstRTSPUrl * url, GstRTSPConnection ** conn)
264 {
265   GstRTSPConnection *newconn;
266 #ifdef G_OS_WIN32
267   WSADATA w;
268   int error;
269 #endif
270
271   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
272
273 #ifdef G_OS_WIN32
274   error = WSAStartup (0x0202, &w);
275
276   if (error)
277     goto startup_error;
278
279   if (w.wVersion != 0x0202)
280     goto version_error;
281 #endif
282
283   newconn = g_new0 (GstRTSPConnection, 1);
284
285   if ((newconn->fdset = gst_poll_new (TRUE)) == NULL)
286     goto no_fdset;
287
288   newconn->url = gst_rtsp_url_copy (url);
289   newconn->fd0.fd = -1;
290   newconn->fd1.fd = -1;
291   newconn->timer = g_timer_new ();
292   newconn->timeout = 60;
293   newconn->cseq = 1;
294
295   newconn->auth_method = GST_RTSP_AUTH_NONE;
296   newconn->username = NULL;
297   newconn->passwd = NULL;
298   newconn->auth_params = NULL;
299
300   *conn = newconn;
301
302   return GST_RTSP_OK;
303
304   /* ERRORS */
305 #ifdef G_OS_WIN32
306 startup_error:
307   {
308     g_warning ("Error %d on WSAStartup", error);
309     return GST_RTSP_EWSASTART;
310   }
311 version_error:
312   {
313     g_warning ("Windows sockets are not version 0x202 (current 0x%x)",
314         w.wVersion);
315     WSACleanup ();
316     return GST_RTSP_EWSAVERSION;
317   }
318 #endif
319 no_fdset:
320   {
321     g_free (newconn);
322 #ifdef G_OS_WIN32
323     WSACleanup ();
324 #endif
325     return GST_RTSP_ESYS;
326   }
327 }
328
329 /**
330  * gst_rtsp_connection_create_from_fd:
331  * @fd: a file descriptor
332  * @ip: the IP address of the other end
333  * @port: the port used by the other end
334  * @initial_buffer: data already read from @fd
335  * @conn: storage for a #GstRTSPConnection
336  *
337  * Create a new #GstRTSPConnection for handling communication on the existing
338  * file descriptor @fd. The @initial_buffer contains any data already read from
339  * @fd which should be used before starting to read new data.
340  *
341  * Returns: #GST_RTSP_OK when @conn contains a valid connection.
342  *
343  * Since: 0.10.25
344  */
345 GstRTSPResult
346 gst_rtsp_connection_create_from_fd (gint fd, const gchar * ip, guint16 port,
347     const gchar * initial_buffer, GstRTSPConnection ** conn)
348 {
349   GstRTSPConnection *newconn = NULL;
350   GstRTSPUrl *url;
351 #ifdef G_OS_WIN32
352   gulong flags = 1;
353 #endif
354   GstRTSPResult res;
355
356   g_return_val_if_fail (fd >= 0, GST_RTSP_EINVAL);
357   g_return_val_if_fail (ip != NULL, GST_RTSP_EINVAL);
358   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
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 (ip);
370   url->port = port;
371
372   /* now create the connection object */
373   GST_RTSP_CHECK (gst_rtsp_connection_create (url, &newconn), newconn_failed);
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   newconn->ip = g_strdup (ip);
383
384   newconn->initial_buffer = g_strdup (initial_buffer);
385
386   *conn = newconn;
387
388   return GST_RTSP_OK;
389
390   /* ERRORS */
391 newconn_failed:
392   {
393     gst_rtsp_url_free (url);
394     return res;
395   }
396 }
397
398 /**
399  * gst_rtsp_connection_accept:
400  * @sock: a socket
401  * @conn: storage for a #GstRTSPConnection
402  *
403  * Accept a new connection on @sock and create a new #GstRTSPConnection for
404  * handling communication on new socket.
405  *
406  * Returns: #GST_RTSP_OK when @conn contains a valid connection.
407  *
408  * Since: 0.10.23
409  */
410 GstRTSPResult
411 gst_rtsp_connection_accept (gint sock, GstRTSPConnection ** conn)
412 {
413   int fd;
414   union gst_sockaddr sa;
415   socklen_t slen = sizeof (sa);
416   gchar ip[INET6_ADDRSTRLEN];
417   guint16 port;
418
419   g_return_val_if_fail (sock >= 0, GST_RTSP_EINVAL);
420   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
421
422   memset (&sa, 0, slen);
423
424 #ifndef G_OS_WIN32
425   fd = accept (sock, &sa.sa, &slen);
426 #else
427   fd = accept (sock, &sa.sa, (gint *) & slen);
428 #endif /* G_OS_WIN32 */
429   if (fd == -1)
430     goto accept_failed;
431
432   if (getnameinfo (&sa.sa, slen, ip, sizeof (ip), NULL, 0, NI_NUMERICHOST) != 0)
433     goto getnameinfo_failed;
434
435   if (sa.sa.sa_family == AF_INET)
436     port = sa.sa_in.sin_port;
437   else if (sa.sa.sa_family == AF_INET6)
438     port = sa.sa_in6.sin6_port;
439   else
440     goto wrong_family;
441
442   return gst_rtsp_connection_create_from_fd (fd, ip, port, NULL, conn);
443
444   /* ERRORS */
445 accept_failed:
446   {
447     return GST_RTSP_ESYS;
448   }
449 getnameinfo_failed:
450 wrong_family:
451   {
452     CLOSE_SOCKET (fd);
453     return GST_RTSP_ERROR;
454   }
455 }
456
457 static gchar *
458 do_resolve (const gchar * host)
459 {
460   gchar ip[INET6_ADDRSTRLEN];
461   struct addrinfo *aires, hints;
462   struct addrinfo *ai;
463   gint aierr;
464
465   memset (&hints, 0, sizeof (struct addrinfo));
466   hints.ai_family = AF_UNSPEC;  /* Allow IPv4 or IPv6 */
467   hints.ai_socktype = SOCK_DGRAM;       /* Datagram socket */
468   hints.ai_flags = AI_PASSIVE;  /* For wildcard IP address */
469   hints.ai_protocol = 0;        /* Any protocol */
470   hints.ai_canonname = NULL;
471   hints.ai_addr = NULL;
472   hints.ai_next = NULL;
473
474   aierr = getaddrinfo (host, NULL, &hints, &aires);
475   if (aierr != 0)
476     goto no_addrinfo;
477
478   for (ai = aires; ai; ai = ai->ai_next) {
479     if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
480       break;
481     }
482   }
483   if (ai == NULL)
484     goto no_family;
485
486   aierr = getnameinfo (ai->ai_addr, ai->ai_addrlen, ip, sizeof (ip), NULL, 0,
487       NI_NUMERICHOST | NI_NUMERICSERV);
488   if (aierr != 0)
489     goto no_address;
490
491   freeaddrinfo (aires);
492
493   return g_strdup (ip);
494
495   /* ERRORS */
496 no_addrinfo:
497   {
498     GST_ERROR ("no addrinfo found for %s: %s", host, gai_strerror (aierr));
499     return NULL;
500   }
501 no_family:
502   {
503     GST_ERROR ("no family found for %s", host);
504     freeaddrinfo (aires);
505     return NULL;
506   }
507 no_address:
508   {
509     GST_ERROR ("no address found for %s: %s", host, gai_strerror (aierr));
510     freeaddrinfo (aires);
511     return NULL;
512   }
513 }
514
515 static GstRTSPResult
516 do_connect (const gchar * ip, guint16 port, GstPollFD * fdout,
517     GstPoll * fdset, GTimeVal * timeout)
518 {
519   gint fd;
520   struct addrinfo hints;
521   struct addrinfo *aires;
522   struct addrinfo *ai;
523   gint aierr;
524   gchar service[NI_MAXSERV];
525   gint ret;
526 #ifdef G_OS_WIN32
527   unsigned long flags = 1;
528 #endif /* G_OS_WIN32 */
529   GstClockTime to;
530   gint retval;
531
532   memset (&hints, 0, sizeof hints);
533   hints.ai_flags = AI_NUMERICHOST;
534   hints.ai_family = AF_UNSPEC;
535   hints.ai_socktype = SOCK_STREAM;
536   g_snprintf (service, sizeof (service) - 1, "%hu", port);
537   service[sizeof (service) - 1] = '\0';
538
539   aierr = getaddrinfo (ip, service, &hints, &aires);
540   if (aierr != 0)
541     goto no_addrinfo;
542
543   for (ai = aires; ai; ai = ai->ai_next) {
544     if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
545       break;
546     }
547   }
548   if (ai == NULL)
549     goto no_family;
550
551   fd = socket (ai->ai_family, SOCK_STREAM, 0);
552   if (fd == -1)
553     goto no_socket;
554
555   /* set to non-blocking mode so that we can cancel the connect */
556 #ifndef G_OS_WIN32
557   fcntl (fd, F_SETFL, O_NONBLOCK);
558 #else
559   ioctlsocket (fd, FIONBIO, &flags);
560 #endif /* G_OS_WIN32 */
561
562   /* add the socket to our fdset */
563   ADD_POLLFD (fdset, fdout, fd);
564
565   /* we are going to connect ASYNC now */
566   ret = connect (fd, ai->ai_addr, ai->ai_addrlen);
567   if (ret == 0)
568     goto done;
569   if (!ERRNO_IS_EINPROGRESS)
570     goto sys_error;
571
572   /* wait for connect to complete up to the specified timeout or until we got
573    * interrupted. */
574   gst_poll_fd_ctl_write (fdset, fdout, TRUE);
575
576   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
577
578   do {
579     retval = gst_poll_wait (fdset, to);
580   } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
581
582   if (retval == 0)
583     goto timeout;
584   else if (retval == -1)
585     goto sys_error;
586
587   /* we can still have an error connecting on windows */
588   if (gst_poll_fd_has_error (fdset, fdout)) {
589     socklen_t len = sizeof (errno);
590 #ifndef G_OS_WIN32
591     getsockopt (fd, SOL_SOCKET, SO_ERROR, &errno, &len);
592 #else
593     getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &errno, &len);
594 #endif
595     goto sys_error;
596   } else {
597 #ifdef __APPLE__
598     /* osx wakes up select with POLLOUT if the connection is refused... */
599     socklen_t len = sizeof (errno);
600     getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &errno, &len);
601     if (errno != 0)
602       goto sys_error;
603 #endif
604   }
605
606   gst_poll_fd_ignored (fdset, fdout);
607
608 done:
609   freeaddrinfo (aires);
610
611   return GST_RTSP_OK;
612
613   /* ERRORS */
614 no_addrinfo:
615   {
616     GST_ERROR ("no addrinfo found for %s: %s", ip, gai_strerror (aierr));
617     return GST_RTSP_ERROR;
618   }
619 no_family:
620   {
621     GST_ERROR ("no family found for %s", ip);
622     freeaddrinfo (aires);
623     return GST_RTSP_ERROR;
624   }
625 no_socket:
626   {
627     GST_ERROR ("no socket %d (%s)", errno, g_strerror (errno));
628     freeaddrinfo (aires);
629     return GST_RTSP_ESYS;
630   }
631 sys_error:
632   {
633     GST_ERROR ("system error %d (%s)", errno, g_strerror (errno));
634     REMOVE_POLLFD (fdset, fdout);
635     freeaddrinfo (aires);
636     return GST_RTSP_ESYS;
637   }
638 timeout:
639   {
640     GST_ERROR ("timeout");
641     REMOVE_POLLFD (fdset, fdout);
642     freeaddrinfo (aires);
643     return GST_RTSP_ETIMEOUT;
644   }
645 }
646
647 static GstRTSPResult
648 setup_tunneling (GstRTSPConnection * conn, GTimeVal * timeout)
649 {
650   gint i;
651   GstRTSPResult res;
652   gchar *ip;
653   gchar *uri;
654   gchar *value;
655   guint16 port, url_port;
656   GstRTSPUrl *url;
657   gchar *hostparam;
658   GstRTSPMessage *msg;
659   GstRTSPMessage response;
660   gboolean old_http;
661
662   memset (&response, 0, sizeof (response));
663   gst_rtsp_message_init (&response);
664
665   /* create a random sessionid */
666   for (i = 0; i < TUNNELID_LEN; i++)
667     conn->tunnelid[i] = g_random_int_range ('a', 'z');
668   conn->tunnelid[TUNNELID_LEN - 1] = '\0';
669
670   url = conn->url;
671   /* get the port from the url */
672   gst_rtsp_url_get_port (url, &url_port);
673
674   if (conn->proxy_host) {
675     uri = g_strdup_printf ("http://%s:%d%s%s%s", url->host, url_port,
676         url->abspath, url->query ? "?" : "", url->query ? url->query : "");
677     hostparam = g_strdup_printf ("%s:%d", url->host, url_port);
678     ip = conn->proxy_host;
679     port = conn->proxy_port;
680   } else {
681     uri = g_strdup_printf ("%s%s%s", url->abspath, url->query ? "?" : "",
682         url->query ? url->query : "");
683     hostparam = NULL;
684     ip = conn->ip;
685     port = url_port;
686   }
687
688   /* create the GET request for the read connection */
689   GST_RTSP_CHECK (gst_rtsp_message_new_request (&msg, GST_RTSP_GET, uri),
690       no_message);
691   msg->type = GST_RTSP_MESSAGE_HTTP_REQUEST;
692
693   if (hostparam != NULL)
694     gst_rtsp_message_add_header (msg, GST_RTSP_HDR_HOST, hostparam);
695   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SESSIONCOOKIE,
696       conn->tunnelid);
697   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_ACCEPT,
698       "application/x-rtsp-tunnelled");
699   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-cache");
700   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
701
702   /* we start by writing to this fd */
703   conn->writefd = &conn->fd0;
704
705   /* we need to temporarily set conn->tunneled to FALSE to prevent the HTTP
706    * request from being base64 encoded */
707   conn->tunneled = FALSE;
708   GST_RTSP_CHECK (gst_rtsp_connection_send (conn, msg, timeout), write_failed);
709   gst_rtsp_message_free (msg);
710   conn->tunneled = TRUE;
711
712   /* receive the response to the GET request */
713   /* we need to temporarily set manual_http to TRUE since
714    * gst_rtsp_connection_receive() will treat the HTTP response as a parsing
715    * failure otherwise */
716   old_http = conn->manual_http;
717   conn->manual_http = TRUE;
718   GST_RTSP_CHECK (gst_rtsp_connection_receive (conn, &response, timeout),
719       read_failed);
720   conn->manual_http = old_http;
721
722   if (response.type != GST_RTSP_MESSAGE_HTTP_RESPONSE ||
723       response.type_data.response.code != GST_RTSP_STS_OK)
724     goto wrong_result;
725
726   if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
727           &value, 0) == GST_RTSP_OK) {
728     if (conn->proxy_host) {
729       /* if we use a proxy we need to change the destination url */
730       g_free (url->host);
731       url->host = g_strdup (value);
732       g_free (hostparam);
733       hostparam = g_strdup_printf ("%s:%d", url->host, url_port);
734     } else {
735       /* and resolve the new ip address */
736       if (!(ip = do_resolve (value)))
737         goto not_resolved;
738       g_free (conn->ip);
739       conn->ip = ip;
740     }
741   }
742
743   /* connect to the host/port */
744   res = do_connect (ip, port, &conn->fd1, conn->fdset, timeout);
745   if (res != GST_RTSP_OK)
746     goto connect_failed;
747
748   /* this is now our writing socket */
749   conn->writefd = &conn->fd1;
750
751   /* create the POST request for the write connection */
752   GST_RTSP_CHECK (gst_rtsp_message_new_request (&msg, GST_RTSP_POST, uri),
753       no_message);
754   msg->type = GST_RTSP_MESSAGE_HTTP_REQUEST;
755
756   if (hostparam != NULL)
757     gst_rtsp_message_add_header (msg, GST_RTSP_HDR_HOST, hostparam);
758   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SESSIONCOOKIE,
759       conn->tunnelid);
760   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_ACCEPT,
761       "application/x-rtsp-tunnelled");
762   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-cache");
763   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
764   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_EXPIRES,
765       "Sun, 9 Jan 1972 00:00:00 GMT");
766   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_LENGTH, "32767");
767
768   /* we need to temporarily set conn->tunneled to FALSE to prevent the HTTP
769    * request from being base64 encoded */
770   conn->tunneled = FALSE;
771   GST_RTSP_CHECK (gst_rtsp_connection_send (conn, msg, timeout), write_failed);
772   gst_rtsp_message_free (msg);
773   conn->tunneled = TRUE;
774
775 exit:
776   gst_rtsp_message_unset (&response);
777   g_free (hostparam);
778   g_free (uri);
779
780   return res;
781
782   /* ERRORS */
783 no_message:
784   {
785     GST_ERROR ("failed to create request (%d)", res);
786     goto exit;
787   }
788 write_failed:
789   {
790     GST_ERROR ("write failed (%d)", res);
791     gst_rtsp_message_free (msg);
792     conn->tunneled = TRUE;
793     goto exit;
794   }
795 read_failed:
796   {
797     GST_ERROR ("read failed (%d)", res);
798     conn->manual_http = FALSE;
799     goto exit;
800   }
801 wrong_result:
802   {
803     GST_ERROR ("got failure response %d %s", response.type_data.response.code,
804         response.type_data.response.reason);
805     res = GST_RTSP_ERROR;
806     goto exit;
807   }
808 not_resolved:
809   {
810     GST_ERROR ("could not resolve %s", conn->ip);
811     res = GST_RTSP_ENET;
812     goto exit;
813   }
814 connect_failed:
815   {
816     GST_ERROR ("failed to connect");
817     goto exit;
818   }
819 }
820
821 /**
822  * gst_rtsp_connection_connect:
823  * @conn: a #GstRTSPConnection 
824  * @timeout: a #GTimeVal timeout
825  *
826  * Attempt to connect to the url of @conn made with
827  * gst_rtsp_connection_create(). If @timeout is #NULL this function can block
828  * forever. If @timeout contains a valid timeout, this function will return
829  * #GST_RTSP_ETIMEOUT after the timeout expired.
830  *
831  * This function can be cancelled with gst_rtsp_connection_flush().
832  *
833  * Returns: #GST_RTSP_OK when a connection could be made.
834  */
835 GstRTSPResult
836 gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
837 {
838   GstRTSPResult res;
839   gchar *ip;
840   guint16 port;
841   GstRTSPUrl *url;
842
843   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
844   g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
845   g_return_val_if_fail (conn->fd0.fd < 0, GST_RTSP_EINVAL);
846
847   url = conn->url;
848
849   if (conn->proxy_host && conn->tunneled) {
850     if (!(ip = do_resolve (conn->proxy_host))) {
851       GST_ERROR ("could not resolve %s", conn->proxy_host);
852       goto not_resolved;
853     }
854     port = conn->proxy_port;
855     g_free (conn->proxy_host);
856     conn->proxy_host = ip;
857   } else {
858     if (!(ip = do_resolve (url->host))) {
859       GST_ERROR ("could not resolve %s", url->host);
860       goto not_resolved;
861     }
862     /* get the port from the url */
863     gst_rtsp_url_get_port (url, &port);
864
865     g_free (conn->ip);
866     conn->ip = ip;
867   }
868
869   /* connect to the host/port */
870   res = do_connect (ip, port, &conn->fd0, conn->fdset, timeout);
871   if (res != GST_RTSP_OK)
872     goto connect_failed;
873
874   /* this is our read URL */
875   conn->readfd = &conn->fd0;
876
877   if (conn->tunneled) {
878     res = setup_tunneling (conn, timeout);
879     if (res != GST_RTSP_OK)
880       goto tunneling_failed;
881   } else {
882     conn->writefd = &conn->fd0;
883   }
884
885   return GST_RTSP_OK;
886
887 not_resolved:
888   {
889     return GST_RTSP_ENET;
890   }
891 connect_failed:
892   {
893     GST_ERROR ("failed to connect");
894     return res;
895   }
896 tunneling_failed:
897   {
898     GST_ERROR ("failed to setup tunneling");
899     return res;
900   }
901 }
902
903 static void
904 auth_digest_compute_hex_urp (const gchar * username,
905     const gchar * realm, const gchar * password, gchar hex_urp[33])
906 {
907   GChecksum *md5_context = g_checksum_new (G_CHECKSUM_MD5);
908   const gchar *digest_string;
909
910   g_checksum_update (md5_context, (const guchar *) username, strlen (username));
911   g_checksum_update (md5_context, (const guchar *) ":", 1);
912   g_checksum_update (md5_context, (const guchar *) realm, strlen (realm));
913   g_checksum_update (md5_context, (const guchar *) ":", 1);
914   g_checksum_update (md5_context, (const guchar *) password, strlen (password));
915   digest_string = g_checksum_get_string (md5_context);
916
917   memset (hex_urp, 0, 33);
918   memcpy (hex_urp, digest_string, strlen (digest_string));
919
920   g_checksum_free (md5_context);
921 }
922
923 static void
924 auth_digest_compute_response (const gchar * method,
925     const gchar * uri, const gchar * hex_a1, const gchar * nonce,
926     gchar response[33])
927 {
928   char hex_a2[33] = { 0, };
929   GChecksum *md5_context = g_checksum_new (G_CHECKSUM_MD5);
930   const gchar *digest_string;
931
932   /* compute A2 */
933   g_checksum_update (md5_context, (const guchar *) method, strlen (method));
934   g_checksum_update (md5_context, (const guchar *) ":", 1);
935   g_checksum_update (md5_context, (const guchar *) uri, strlen (uri));
936   digest_string = g_checksum_get_string (md5_context);
937   memcpy (hex_a2, digest_string, strlen (digest_string));
938
939   /* compute KD */
940   g_checksum_reset (md5_context);
941   g_checksum_update (md5_context, (const guchar *) hex_a1, strlen (hex_a1));
942   g_checksum_update (md5_context, (const guchar *) ":", 1);
943   g_checksum_update (md5_context, (const guchar *) nonce, strlen (nonce));
944   g_checksum_update (md5_context, (const guchar *) ":", 1);
945
946   g_checksum_update (md5_context, (const guchar *) hex_a2, 32);
947   digest_string = g_checksum_get_string (md5_context);
948   memset (response, 0, 33);
949   memcpy (response, digest_string, strlen (digest_string));
950
951   g_checksum_free (md5_context);
952 }
953
954 static void
955 add_auth_header (GstRTSPConnection * conn, GstRTSPMessage * message)
956 {
957   switch (conn->auth_method) {
958     case GST_RTSP_AUTH_BASIC:{
959       gchar *user_pass;
960       gchar *user_pass64;
961       gchar *auth_string;
962
963       if (conn->username == NULL || conn->passwd == NULL)
964         break;
965
966       user_pass = g_strdup_printf ("%s:%s", conn->username, conn->passwd);
967       user_pass64 = g_base64_encode ((guchar *) user_pass, strlen (user_pass));
968       auth_string = g_strdup_printf ("Basic %s", user_pass64);
969
970       gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
971           auth_string);
972
973       g_free (user_pass);
974       g_free (user_pass64);
975       break;
976     }
977     case GST_RTSP_AUTH_DIGEST:{
978       gchar response[33], hex_urp[33];
979       gchar *auth_string, *auth_string2;
980       gchar *realm;
981       gchar *nonce;
982       gchar *opaque;
983       const gchar *uri;
984       const gchar *method;
985
986       /* we need to have some params set */
987       if (conn->auth_params == NULL || conn->username == NULL ||
988           conn->passwd == NULL)
989         break;
990
991       /* we need the realm and nonce */
992       realm = (gchar *) g_hash_table_lookup (conn->auth_params, "realm");
993       nonce = (gchar *) g_hash_table_lookup (conn->auth_params, "nonce");
994       if (realm == NULL || nonce == NULL)
995         break;
996
997       auth_digest_compute_hex_urp (conn->username, realm, conn->passwd,
998           hex_urp);
999
1000       method = gst_rtsp_method_as_text (message->type_data.request.method);
1001       uri = message->type_data.request.uri;
1002
1003       /* Assume no qop, algorithm=md5, stale=false */
1004       /* For algorithm MD5, a1 = urp. */
1005       auth_digest_compute_response (method, uri, hex_urp, nonce, response);
1006       auth_string = g_strdup_printf ("Digest username=\"%s\", "
1007           "realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
1008           conn->username, realm, nonce, uri, response);
1009
1010       opaque = (gchar *) g_hash_table_lookup (conn->auth_params, "opaque");
1011       if (opaque) {
1012         auth_string2 = g_strdup_printf ("%s, opaque=\"%s\"", auth_string,
1013             opaque);
1014         g_free (auth_string);
1015         auth_string = auth_string2;
1016       }
1017       gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
1018           auth_string);
1019       break;
1020     }
1021     default:
1022       /* Nothing to do */
1023       break;
1024   }
1025 }
1026
1027 static void
1028 gen_date_string (gchar * date_string, guint len)
1029 {
1030   static const char wkdays[7][4] =
1031       { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
1032   static const char months[12][4] =
1033       { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
1034     "Nov", "Dec"
1035   };
1036   struct tm tm;
1037   time_t t;
1038
1039   time (&t);
1040
1041 #ifdef HAVE_GMTIME_R
1042   gmtime_r (&t, &tm);
1043 #else
1044   tm = *gmtime (&t);
1045 #endif
1046
1047   g_snprintf (date_string, len, "%s, %02d %s %04d %02d:%02d:%02d GMT",
1048       wkdays[tm.tm_wday], tm.tm_mday, months[tm.tm_mon], tm.tm_year + 1900,
1049       tm.tm_hour, tm.tm_min, tm.tm_sec);
1050 }
1051
1052 static GstRTSPResult
1053 write_bytes (gint fd, const guint8 * buffer, guint * idx, guint size)
1054 {
1055   guint left;
1056
1057   if (G_UNLIKELY (*idx > size))
1058     return GST_RTSP_ERROR;
1059
1060   left = size - *idx;
1061
1062   while (left) {
1063     gint r;
1064
1065     r = WRITE_SOCKET (fd, &buffer[*idx], left);
1066     if (G_UNLIKELY (r == 0)) {
1067       return GST_RTSP_EINTR;
1068     } else if (G_UNLIKELY (r < 0)) {
1069       if (ERRNO_IS_EAGAIN)
1070         return GST_RTSP_EINTR;
1071       if (!ERRNO_IS_EINTR)
1072         return GST_RTSP_ESYS;
1073     } else {
1074       left -= r;
1075       *idx += r;
1076     }
1077   }
1078   return GST_RTSP_OK;
1079 }
1080
1081 static gint
1082 fill_raw_bytes (GstRTSPConnection * conn, guint8 * buffer, guint size)
1083 {
1084   gint out = 0;
1085
1086   if (G_UNLIKELY (conn->initial_buffer != NULL)) {
1087     gsize left = strlen (&conn->initial_buffer[conn->initial_buffer_offset]);
1088
1089     out = MIN (left, size);
1090     memcpy (buffer, &conn->initial_buffer[conn->initial_buffer_offset], out);
1091
1092     if (left == (gsize) out) {
1093       g_free (conn->initial_buffer);
1094       conn->initial_buffer = NULL;
1095       conn->initial_buffer_offset = 0;
1096     } else
1097       conn->initial_buffer_offset += out;
1098   }
1099
1100   if (G_LIKELY (size > (guint) out)) {
1101     gint r;
1102
1103     r = READ_SOCKET (conn->readfd->fd, &buffer[out], size - out);
1104     if (r <= 0) {
1105       if (out == 0)
1106         out = r;
1107     } else
1108       out += r;
1109   }
1110
1111   return out;
1112 }
1113
1114 static gint
1115 fill_bytes (GstRTSPConnection * conn, guint8 * buffer, guint size)
1116 {
1117   DecodeCtx *ctx = conn->ctxp;
1118   gint out = 0;
1119
1120   if (ctx) {
1121     while (size > 0) {
1122       guint8 in[sizeof (ctx->out) * 4 / 3];
1123       gint r;
1124
1125       while (size > 0 && ctx->cout < ctx->coutl) {
1126         /* we have some leftover bytes */
1127         *buffer++ = ctx->out[ctx->cout++];
1128         size--;
1129         out++;
1130       }
1131
1132       /* got what we needed? */
1133       if (size == 0)
1134         break;
1135
1136       /* try to read more bytes */
1137       r = fill_raw_bytes (conn, in, sizeof (in));
1138       if (r <= 0) {
1139         if (out == 0)
1140           out = r;
1141         break;
1142       }
1143
1144       ctx->cout = 0;
1145       ctx->coutl =
1146           g_base64_decode_step ((gchar *) in, r, ctx->out, &ctx->state,
1147           &ctx->save);
1148     }
1149   } else {
1150     out = fill_raw_bytes (conn, buffer, size);
1151   }
1152
1153   return out;
1154 }
1155
1156 static GstRTSPResult
1157 read_bytes (GstRTSPConnection * conn, guint8 * buffer, guint * idx, guint size)
1158 {
1159   guint left;
1160
1161   if (G_UNLIKELY (*idx > size))
1162     return GST_RTSP_ERROR;
1163
1164   left = size - *idx;
1165
1166   while (left) {
1167     gint r;
1168
1169     r = fill_bytes (conn, &buffer[*idx], left);
1170     if (G_UNLIKELY (r == 0)) {
1171       return GST_RTSP_EEOF;
1172     } else if (G_UNLIKELY (r < 0)) {
1173       if (ERRNO_IS_EAGAIN)
1174         return GST_RTSP_EINTR;
1175       if (!ERRNO_IS_EINTR)
1176         return GST_RTSP_ESYS;
1177     } else {
1178       left -= r;
1179       *idx += r;
1180     }
1181   }
1182   return GST_RTSP_OK;
1183 }
1184
1185 /* The code below tries to handle clients using \r, \n or \r\n to indicate the
1186  * end of a line. It even does its best to handle clients which mix them (even
1187  * though this is a really stupid idea (tm).) It also handles Line White Space
1188  * (LWS), where a line end followed by whitespace is considered LWS. This is
1189  * the method used in RTSP (and HTTP) to break long lines.
1190  */
1191 static GstRTSPResult
1192 read_line (GstRTSPConnection * conn, guint8 * buffer, guint * idx, guint size)
1193 {
1194   while (TRUE) {
1195     guint8 c;
1196     gint r;
1197
1198     if (conn->read_ahead == READ_AHEAD_EOH) {
1199       /* the last call to read_line() already determined that we have reached
1200        * the end of the headers, so convey that information now */
1201       conn->read_ahead = 0;
1202       break;
1203     } else if (conn->read_ahead == READ_AHEAD_CRLF) {
1204       /* the last call to read_line() left off after having read \r\n */
1205       c = '\n';
1206     } else if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1207       /* the last call to read_line() left off after having read \r\n\r */
1208       c = '\r';
1209     } else if (conn->read_ahead != 0) {
1210       /* the last call to read_line() left us with a character to start with */
1211       c = (guint8) conn->read_ahead;
1212       conn->read_ahead = 0;
1213     } else {
1214       /* read the next character */
1215       r = fill_bytes (conn, &c, 1);
1216       if (G_UNLIKELY (r == 0)) {
1217         return GST_RTSP_EEOF;
1218       } else if (G_UNLIKELY (r < 0)) {
1219         if (ERRNO_IS_EAGAIN)
1220           return GST_RTSP_EINTR;
1221         if (!ERRNO_IS_EINTR)
1222           return GST_RTSP_ESYS;
1223         continue;
1224       }
1225     }
1226
1227     /* special treatment of line endings */
1228     if (c == '\r' || c == '\n') {
1229       guint8 read_ahead;
1230
1231     retry:
1232       /* need to read ahead one more character to know what to do... */
1233       r = fill_bytes (conn, &read_ahead, 1);
1234       if (G_UNLIKELY (r == 0)) {
1235         return GST_RTSP_EEOF;
1236       } else if (G_UNLIKELY (r < 0)) {
1237         if (ERRNO_IS_EAGAIN) {
1238           /* remember the original character we read and try again next time */
1239           if (conn->read_ahead == 0)
1240             conn->read_ahead = c;
1241           return GST_RTSP_EINTR;
1242         }
1243         if (!ERRNO_IS_EINTR)
1244           return GST_RTSP_ESYS;
1245         goto retry;
1246       }
1247
1248       if (read_ahead == ' ' || read_ahead == '\t') {
1249         if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1250           /* got \r\n\r followed by whitespace, treat it as a normal line
1251            * followed by one starting with LWS */
1252           conn->read_ahead = read_ahead;
1253           break;
1254         } else {
1255           /* got LWS, change the line ending to a space and continue */
1256           c = ' ';
1257           conn->read_ahead = read_ahead;
1258         }
1259       } else if (conn->read_ahead == READ_AHEAD_CRLFCR) {
1260         if (read_ahead == '\r' || read_ahead == '\n') {
1261           /* got \r\n\r\r or \r\n\r\n, treat it as the end of the headers */
1262           conn->read_ahead = READ_AHEAD_EOH;
1263           break;
1264         } else {
1265           /* got \r\n\r followed by something else, this is not really
1266            * supported since we have probably just eaten the first character
1267            * of the body or the next message, so just ignore the second \r
1268            * and live with it... */
1269           conn->read_ahead = read_ahead;
1270           break;
1271         }
1272       } else if (conn->read_ahead == READ_AHEAD_CRLF) {
1273         if (read_ahead == '\r') {
1274           /* got \r\n\r so far, need one more character... */
1275           conn->read_ahead = READ_AHEAD_CRLFCR;
1276           goto retry;
1277         } else if (read_ahead == '\n') {
1278           /* got \r\n\n, treat it as the end of the headers */
1279           conn->read_ahead = READ_AHEAD_EOH;
1280           break;
1281         } else {
1282           /* found the end of a line, keep read_ahead for the next line */
1283           conn->read_ahead = read_ahead;
1284           break;
1285         }
1286       } else if (c == read_ahead) {
1287         /* got double \r or \n, treat it as the end of the headers */
1288         conn->read_ahead = READ_AHEAD_EOH;
1289         break;
1290       } else if (c == '\r' && read_ahead == '\n') {
1291         /* got \r\n so far, still need more to know what to do... */
1292         conn->read_ahead = READ_AHEAD_CRLF;
1293         goto retry;
1294       } else {
1295         /* found the end of a line, keep read_ahead for the next line */
1296         conn->read_ahead = read_ahead;
1297         break;
1298       }
1299     }
1300
1301     if (G_LIKELY (*idx < size - 1))
1302       buffer[(*idx)++] = c;
1303   }
1304   buffer[*idx] = '\0';
1305
1306   return GST_RTSP_OK;
1307 }
1308
1309 /**
1310  * gst_rtsp_connection_write:
1311  * @conn: a #GstRTSPConnection
1312  * @data: the data to write
1313  * @size: the size of @data
1314  * @timeout: a timeout value or #NULL
1315  *
1316  * Attempt to write @size bytes of @data to the connected @conn, blocking up to
1317  * the specified @timeout. @timeout can be #NULL, in which case this function
1318  * might block forever.
1319  * 
1320  * This function can be cancelled with gst_rtsp_connection_flush().
1321  *
1322  * Returns: #GST_RTSP_OK on success.
1323  */
1324 GstRTSPResult
1325 gst_rtsp_connection_write (GstRTSPConnection * conn, const guint8 * data,
1326     guint size, GTimeVal * timeout)
1327 {
1328   guint offset;
1329   gint retval;
1330   GstClockTime to;
1331   GstRTSPResult res;
1332
1333   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1334   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
1335   g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL);
1336
1337   gst_poll_set_controllable (conn->fdset, TRUE);
1338   gst_poll_fd_ctl_write (conn->fdset, conn->writefd, TRUE);
1339   gst_poll_fd_ctl_read (conn->fdset, conn->readfd, FALSE);
1340   /* clear all previous poll results */
1341   gst_poll_fd_ignored (conn->fdset, conn->writefd);
1342   gst_poll_fd_ignored (conn->fdset, conn->readfd);
1343
1344   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
1345
1346   offset = 0;
1347
1348   while (TRUE) {
1349     /* try to write */
1350     res = write_bytes (conn->writefd->fd, data, &offset, size);
1351     if (G_LIKELY (res == GST_RTSP_OK))
1352       break;
1353     if (G_UNLIKELY (res != GST_RTSP_EINTR))
1354       goto write_error;
1355
1356     /* not all is written, wait until we can write more */
1357     do {
1358       retval = gst_poll_wait (conn->fdset, to);
1359     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
1360
1361     if (G_UNLIKELY (retval == 0))
1362       goto timeout;
1363
1364     if (G_UNLIKELY (retval == -1)) {
1365       if (errno == EBUSY)
1366         goto stopped;
1367       else
1368         goto select_error;
1369     }
1370
1371     /* could also be an error with read socket */
1372     if (gst_poll_fd_has_error (conn->fdset, conn->readfd))
1373       goto socket_error;
1374   }
1375   return GST_RTSP_OK;
1376
1377   /* ERRORS */
1378 timeout:
1379   {
1380     return GST_RTSP_ETIMEOUT;
1381   }
1382 select_error:
1383   {
1384     return GST_RTSP_ESYS;
1385   }
1386 stopped:
1387   {
1388     return GST_RTSP_EINTR;
1389   }
1390 socket_error:
1391   {
1392     return GST_RTSP_ENET;
1393   }
1394 write_error:
1395   {
1396     return res;
1397   }
1398 }
1399
1400 static GString *
1401 message_to_string (GstRTSPConnection * conn, GstRTSPMessage * message)
1402 {
1403   GString *str = NULL;
1404
1405   str = g_string_new ("");
1406
1407   switch (message->type) {
1408     case GST_RTSP_MESSAGE_REQUEST:
1409       /* create request string, add CSeq */
1410       g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
1411           "CSeq: %d\r\n",
1412           gst_rtsp_method_as_text (message->type_data.request.method),
1413           message->type_data.request.uri, conn->cseq++);
1414       /* add session id if we have one */
1415       if (conn->session_id[0] != '\0') {
1416         gst_rtsp_message_remove_header (message, GST_RTSP_HDR_SESSION, -1);
1417         gst_rtsp_message_add_header (message, GST_RTSP_HDR_SESSION,
1418             conn->session_id);
1419       }
1420       /* add any authentication headers */
1421       add_auth_header (conn, message);
1422       break;
1423     case GST_RTSP_MESSAGE_RESPONSE:
1424       /* create response string */
1425       g_string_append_printf (str, "RTSP/1.0 %d %s\r\n",
1426           message->type_data.response.code, message->type_data.response.reason);
1427       break;
1428     case GST_RTSP_MESSAGE_HTTP_REQUEST:
1429       /* create request string */
1430       g_string_append_printf (str, "%s %s HTTP/%s\r\n",
1431           gst_rtsp_method_as_text (message->type_data.request.method),
1432           message->type_data.request.uri,
1433           gst_rtsp_version_as_text (message->type_data.request.version));
1434       /* add any authentication headers */
1435       add_auth_header (conn, message);
1436       break;
1437     case GST_RTSP_MESSAGE_HTTP_RESPONSE:
1438       /* create response string */
1439       g_string_append_printf (str, "HTTP/%s %d %s\r\n",
1440           gst_rtsp_version_as_text (message->type_data.request.version),
1441           message->type_data.response.code, message->type_data.response.reason);
1442       break;
1443     case GST_RTSP_MESSAGE_DATA:
1444     {
1445       guint8 data_header[4];
1446
1447       /* prepare data header */
1448       data_header[0] = '$';
1449       data_header[1] = message->type_data.data.channel;
1450       data_header[2] = (message->body_size >> 8) & 0xff;
1451       data_header[3] = message->body_size & 0xff;
1452
1453       /* create string with header and data */
1454       str = g_string_append_len (str, (gchar *) data_header, 4);
1455       str =
1456           g_string_append_len (str, (gchar *) message->body,
1457           message->body_size);
1458       break;
1459     }
1460     default:
1461       g_string_free (str, TRUE);
1462       g_return_val_if_reached (NULL);
1463       break;
1464   }
1465
1466   /* append headers and body */
1467   if (message->type != GST_RTSP_MESSAGE_DATA) {
1468     gchar date_string[100];
1469
1470     gen_date_string (date_string, sizeof (date_string));
1471
1472     /* add date header */
1473     gst_rtsp_message_remove_header (message, GST_RTSP_HDR_DATE, -1);
1474     gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string);
1475
1476     /* append headers */
1477     gst_rtsp_message_append_headers (message, str);
1478
1479     /* append Content-Length and body if needed */
1480     if (message->body != NULL && message->body_size > 0) {
1481       gchar *len;
1482
1483       len = g_strdup_printf ("%d", message->body_size);
1484       g_string_append_printf (str, "%s: %s\r\n",
1485           gst_rtsp_header_as_text (GST_RTSP_HDR_CONTENT_LENGTH), len);
1486       g_free (len);
1487       /* header ends here */
1488       g_string_append (str, "\r\n");
1489       str =
1490           g_string_append_len (str, (gchar *) message->body,
1491           message->body_size);
1492     } else {
1493       /* just end headers */
1494       g_string_append (str, "\r\n");
1495     }
1496   }
1497
1498   return str;
1499 }
1500
1501 /**
1502  * gst_rtsp_connection_send:
1503  * @conn: a #GstRTSPConnection
1504  * @message: the message to send
1505  * @timeout: a timeout value or #NULL
1506  *
1507  * Attempt to send @message to the connected @conn, blocking up to
1508  * the specified @timeout. @timeout can be #NULL, in which case this function
1509  * might block forever.
1510  * 
1511  * This function can be cancelled with gst_rtsp_connection_flush().
1512  *
1513  * Returns: #GST_RTSP_OK on success.
1514  */
1515 GstRTSPResult
1516 gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
1517     GTimeVal * timeout)
1518 {
1519   GString *string = NULL;
1520   GstRTSPResult res;
1521   gchar *str;
1522   gsize len;
1523
1524   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1525   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
1526
1527   if (G_UNLIKELY (!(string = message_to_string (conn, message))))
1528     goto no_message;
1529
1530   if (conn->tunneled) {
1531     str = g_base64_encode ((const guchar *) string->str, string->len);
1532     g_string_free (string, TRUE);
1533     len = strlen (str);
1534   } else {
1535     str = string->str;
1536     len = string->len;
1537     g_string_free (string, FALSE);
1538   }
1539
1540   /* write request */
1541   res = gst_rtsp_connection_write (conn, (guint8 *) str, len, timeout);
1542
1543   g_free (str);
1544
1545   return res;
1546
1547 no_message:
1548   {
1549     g_warning ("Wrong message");
1550     return GST_RTSP_EINVAL;
1551   }
1552 }
1553
1554 static GstRTSPResult
1555 parse_string (gchar * dest, gint size, gchar ** src)
1556 {
1557   GstRTSPResult res = GST_RTSP_OK;
1558   gint idx;
1559
1560   idx = 0;
1561   /* skip spaces */
1562   while (g_ascii_isspace (**src))
1563     (*src)++;
1564
1565   while (!g_ascii_isspace (**src) && **src != '\0') {
1566     if (idx < size - 1)
1567       dest[idx++] = **src;
1568     else
1569       res = GST_RTSP_EPARSE;
1570     (*src)++;
1571   }
1572   if (size > 0)
1573     dest[idx] = '\0';
1574
1575   return res;
1576 }
1577
1578 static GstRTSPResult
1579 parse_protocol_version (gchar * protocol, GstRTSPMsgType * type,
1580     GstRTSPVersion * version)
1581 {
1582   GstRTSPResult res = GST_RTSP_OK;
1583   gchar *ver;
1584
1585   if (G_LIKELY ((ver = strchr (protocol, '/')) != NULL)) {
1586     guint major;
1587     guint minor;
1588     gchar dummychar;
1589
1590     *ver++ = '\0';
1591
1592     /* the version number must be formatted as X.Y with nothing following */
1593     if (sscanf (ver, "%u.%u%c", &major, &minor, &dummychar) != 2)
1594       res = GST_RTSP_EPARSE;
1595
1596     if (g_ascii_strcasecmp (protocol, "RTSP") == 0) {
1597       if (major != 1 || minor != 0) {
1598         *version = GST_RTSP_VERSION_INVALID;
1599         res = GST_RTSP_ERROR;
1600       }
1601     } else if (g_ascii_strcasecmp (protocol, "HTTP") == 0) {
1602       if (*type == GST_RTSP_MESSAGE_REQUEST)
1603         *type = GST_RTSP_MESSAGE_HTTP_REQUEST;
1604       else if (*type == GST_RTSP_MESSAGE_RESPONSE)
1605         *type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
1606
1607       if (major == 1 && minor == 1) {
1608         *version = GST_RTSP_VERSION_1_1;
1609       } else if (major != 1 || minor != 0) {
1610         *version = GST_RTSP_VERSION_INVALID;
1611         res = GST_RTSP_ERROR;
1612       }
1613     } else
1614       res = GST_RTSP_EPARSE;
1615   } else
1616     res = GST_RTSP_EPARSE;
1617
1618   return res;
1619 }
1620
1621 static GstRTSPResult
1622 parse_response_status (guint8 * buffer, GstRTSPMessage * msg)
1623 {
1624   GstRTSPResult res = GST_RTSP_OK;
1625   GstRTSPResult res2;
1626   gchar versionstr[20];
1627   gchar codestr[4];
1628   gint code;
1629   gchar *bptr;
1630
1631   bptr = (gchar *) buffer;
1632
1633   if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
1634     res = GST_RTSP_EPARSE;
1635
1636   if (parse_string (codestr, sizeof (codestr), &bptr) != GST_RTSP_OK)
1637     res = GST_RTSP_EPARSE;
1638   code = atoi (codestr);
1639   if (G_UNLIKELY (*codestr == '\0' || code < 0 || code >= 600))
1640     res = GST_RTSP_EPARSE;
1641
1642   while (g_ascii_isspace (*bptr))
1643     bptr++;
1644
1645   if (G_UNLIKELY (gst_rtsp_message_init_response (msg, code, bptr,
1646               NULL) != GST_RTSP_OK))
1647     res = GST_RTSP_EPARSE;
1648
1649   res2 = parse_protocol_version (versionstr, &msg->type,
1650       &msg->type_data.response.version);
1651   if (G_LIKELY (res == GST_RTSP_OK))
1652     res = res2;
1653
1654   return res;
1655 }
1656
1657 static GstRTSPResult
1658 parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
1659 {
1660   GstRTSPResult res = GST_RTSP_OK;
1661   GstRTSPResult res2;
1662   gchar versionstr[20];
1663   gchar methodstr[20];
1664   gchar urlstr[4096];
1665   gchar *bptr;
1666   GstRTSPMethod method;
1667
1668   bptr = (gchar *) buffer;
1669
1670   if (parse_string (methodstr, sizeof (methodstr), &bptr) != GST_RTSP_OK)
1671     res = GST_RTSP_EPARSE;
1672   method = gst_rtsp_find_method (methodstr);
1673
1674   if (parse_string (urlstr, sizeof (urlstr), &bptr) != GST_RTSP_OK)
1675     res = GST_RTSP_EPARSE;
1676   if (G_UNLIKELY (*urlstr == '\0'))
1677     res = GST_RTSP_EPARSE;
1678
1679   if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
1680     res = GST_RTSP_EPARSE;
1681
1682   if (G_UNLIKELY (*bptr != '\0'))
1683     res = GST_RTSP_EPARSE;
1684
1685   if (G_UNLIKELY (gst_rtsp_message_init_request (msg, method,
1686               urlstr) != GST_RTSP_OK))
1687     res = GST_RTSP_EPARSE;
1688
1689   res2 = parse_protocol_version (versionstr, &msg->type,
1690       &msg->type_data.request.version);
1691   if (G_LIKELY (res == GST_RTSP_OK))
1692     res = res2;
1693
1694   if (G_LIKELY (msg->type == GST_RTSP_MESSAGE_REQUEST)) {
1695     /* GET and POST are not allowed as RTSP methods */
1696     if (msg->type_data.request.method == GST_RTSP_GET ||
1697         msg->type_data.request.method == GST_RTSP_POST) {
1698       msg->type_data.request.method = GST_RTSP_INVALID;
1699       if (res == GST_RTSP_OK)
1700         res = GST_RTSP_ERROR;
1701     }
1702   } else if (msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
1703     /* only GET and POST are allowed as HTTP methods */
1704     if (msg->type_data.request.method != GST_RTSP_GET &&
1705         msg->type_data.request.method != GST_RTSP_POST) {
1706       msg->type_data.request.method = GST_RTSP_INVALID;
1707       if (res == GST_RTSP_OK)
1708         res = GST_RTSP_ERROR;
1709     }
1710   }
1711
1712   return res;
1713 }
1714
1715 /* parsing lines means reading a Key: Value pair */
1716 static GstRTSPResult
1717 parse_line (guint8 * buffer, GstRTSPMessage * msg)
1718 {
1719   GstRTSPHeaderField field;
1720   gchar *line = (gchar *) buffer;
1721   gchar *value;
1722
1723   if ((value = strchr (line, ':')) == NULL || value == line)
1724     goto parse_error;
1725
1726   /* trim space before the colon */
1727   if (value[-1] == ' ')
1728     value[-1] = '\0';
1729
1730   /* replace the colon with a NUL */
1731   *value++ = '\0';
1732
1733   /* find the header */
1734   field = gst_rtsp_find_header_field (line);
1735   if (field == GST_RTSP_HDR_INVALID)
1736     goto done;
1737
1738   /* split up the value in multiple key:value pairs if it contains comma(s) */
1739   while (*value != '\0') {
1740     gchar *next_value;
1741     gchar *comma = NULL;
1742     gboolean quoted = FALSE;
1743     guint comment = 0;
1744
1745     /* trim leading space */
1746     if (*value == ' ')
1747       value++;
1748
1749     /* for headers which may not appear multiple times, and thus may not
1750      * contain multiple values on the same line, we can short-circuit the loop
1751      * below and the entire value results in just one key:value pair*/
1752     if (!gst_rtsp_header_allow_multiple (field))
1753       next_value = value + strlen (value);
1754     else
1755       next_value = value;
1756
1757     /* find the next value, taking special care of quotes and comments */
1758     while (*next_value != '\0') {
1759       if ((quoted || comment != 0) && *next_value == '\\' &&
1760           next_value[1] != '\0')
1761         next_value++;
1762       else if (comment == 0 && *next_value == '"')
1763         quoted = !quoted;
1764       else if (!quoted && *next_value == '(')
1765         comment++;
1766       else if (comment != 0 && *next_value == ')')
1767         comment--;
1768       else if (!quoted && comment == 0) {
1769         /* To quote RFC 2068: "User agents MUST take special care in parsing
1770          * the WWW-Authenticate field value if it contains more than one
1771          * challenge, or if more than one WWW-Authenticate header field is
1772          * provided, since the contents of a challenge may itself contain a
1773          * comma-separated list of authentication parameters."
1774          *
1775          * What this means is that we cannot just look for an unquoted comma
1776          * when looking for multiple values in Proxy-Authenticate and
1777          * WWW-Authenticate headers. Instead we need to look for the sequence
1778          * "comma [space] token space token" before we can split after the
1779          * comma...
1780          */
1781         if (field == GST_RTSP_HDR_PROXY_AUTHENTICATE ||
1782             field == GST_RTSP_HDR_WWW_AUTHENTICATE) {
1783           if (*next_value == ',') {
1784             if (next_value[1] == ' ') {
1785               /* skip any space following the comma so we do not mistake it for
1786                * separating between two tokens */
1787               next_value++;
1788             }
1789             comma = next_value;
1790           } else if (*next_value == ' ' && next_value[1] != ',' &&
1791               next_value[1] != '=' && comma != NULL) {
1792             next_value = comma;
1793             comma = NULL;
1794             break;
1795           }
1796         } else if (*next_value == ',')
1797           break;
1798       }
1799
1800       next_value++;
1801     }
1802
1803     /* trim space */
1804     if (value != next_value && next_value[-1] == ' ')
1805       next_value[-1] = '\0';
1806
1807     if (*next_value != '\0')
1808       *next_value++ = '\0';
1809
1810     /* add the key:value pair */
1811     if (*value != '\0')
1812       gst_rtsp_message_add_header (msg, field, value);
1813
1814     value = next_value;
1815   }
1816
1817 done:
1818   return GST_RTSP_OK;
1819
1820   /* ERRORS */
1821 parse_error:
1822   {
1823     return GST_RTSP_EPARSE;
1824   }
1825 }
1826
1827 /* convert all consecutive whitespace to a single space */
1828 static void
1829 normalize_line (guint8 * buffer)
1830 {
1831   while (*buffer) {
1832     if (g_ascii_isspace (*buffer)) {
1833       guint8 *tmp;
1834
1835       *buffer++ = ' ';
1836       for (tmp = buffer; g_ascii_isspace (*tmp); tmp++) {
1837       }
1838       if (buffer != tmp)
1839         memmove (buffer, tmp, strlen ((gchar *) tmp) + 1);
1840     } else {
1841       buffer++;
1842     }
1843   }
1844 }
1845
1846 /* returns:
1847  *  GST_RTSP_OK when a complete message was read.
1848  *  GST_RTSP_EEOF: when the read socket is closed
1849  *  GST_RTSP_EINTR: when more data is needed.
1850  *  GST_RTSP_..: some other error occured.
1851  */
1852 static GstRTSPResult
1853 build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
1854     GstRTSPConnection * conn)
1855 {
1856   GstRTSPResult res;
1857
1858   while (TRUE) {
1859     switch (builder->state) {
1860       case STATE_START:
1861       {
1862         guint8 c;
1863
1864         builder->offset = 0;
1865         res =
1866             read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 1);
1867         if (res != GST_RTSP_OK)
1868           goto done;
1869
1870         c = builder->buffer[0];
1871
1872         /* we have 1 bytes now and we can see if this is a data message or
1873          * not */
1874         if (c == '$') {
1875           /* data message, prepare for the header */
1876           builder->state = STATE_DATA_HEADER;
1877         } else if (c == '\n' || c == '\r') {
1878           /* skip \n and \r */
1879           builder->offset = 0;
1880         } else {
1881           builder->line = 0;
1882           builder->state = STATE_READ_LINES;
1883         }
1884         break;
1885       }
1886       case STATE_DATA_HEADER:
1887       {
1888         res =
1889             read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 4);
1890         if (res != GST_RTSP_OK)
1891           goto done;
1892
1893         gst_rtsp_message_init_data (message, builder->buffer[1]);
1894
1895         builder->body_len = (builder->buffer[2] << 8) | builder->buffer[3];
1896         builder->body_data = g_malloc (builder->body_len + 1);
1897         builder->body_data[builder->body_len] = '\0';
1898         builder->offset = 0;
1899         builder->state = STATE_DATA_BODY;
1900         break;
1901       }
1902       case STATE_DATA_BODY:
1903       {
1904         res =
1905             read_bytes (conn, builder->body_data, &builder->offset,
1906             builder->body_len);
1907         if (res != GST_RTSP_OK)
1908           goto done;
1909
1910         /* we have the complete body now, store in the message adjusting the
1911          * length to include the trailing '\0' */
1912         gst_rtsp_message_take_body (message,
1913             (guint8 *) builder->body_data, builder->body_len + 1);
1914         builder->body_data = NULL;
1915         builder->body_len = 0;
1916
1917         builder->state = STATE_END;
1918         break;
1919       }
1920       case STATE_READ_LINES:
1921       {
1922         res = read_line (conn, builder->buffer, &builder->offset,
1923             sizeof (builder->buffer));
1924         if (res != GST_RTSP_OK)
1925           goto done;
1926
1927         /* we have a regular response */
1928         if (builder->buffer[0] == '\0') {
1929           gchar *hdrval;
1930
1931           /* empty line, end of message header */
1932           /* see if there is a Content-Length header, but ignore it if this
1933            * is a POST request with an x-sessioncookie header */
1934           if (gst_rtsp_message_get_header (message,
1935                   GST_RTSP_HDR_CONTENT_LENGTH, &hdrval, 0) == GST_RTSP_OK &&
1936               (message->type != GST_RTSP_MESSAGE_HTTP_REQUEST ||
1937                   message->type_data.request.method != GST_RTSP_POST ||
1938                   gst_rtsp_message_get_header (message,
1939                       GST_RTSP_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) {
1940             /* there is, prepare to read the body */
1941             builder->body_len = atol (hdrval);
1942             builder->body_data = g_try_malloc (builder->body_len + 1);
1943             /* we can't do much here, we need the length to know how many bytes
1944              * we need to read next and when allocation fails, something is
1945              * probably wrong with the length. */
1946             if (builder->body_data == NULL)
1947               goto invalid_body_len;
1948
1949             builder->body_data[builder->body_len] = '\0';
1950             builder->offset = 0;
1951             builder->state = STATE_DATA_BODY;
1952           } else {
1953             builder->state = STATE_END;
1954           }
1955           break;
1956         }
1957
1958         /* we have a line */
1959         normalize_line (builder->buffer);
1960         if (builder->line == 0) {
1961           /* first line, check for response status */
1962           if (memcmp (builder->buffer, "RTSP", 4) == 0 ||
1963               memcmp (builder->buffer, "HTTP", 4) == 0) {
1964             builder->status = parse_response_status (builder->buffer, message);
1965           } else {
1966             builder->status = parse_request_line (builder->buffer, message);
1967           }
1968         } else {
1969           /* else just parse the line */
1970           res = parse_line (builder->buffer, message);
1971           if (res != GST_RTSP_OK)
1972             builder->status = res;
1973         }
1974         builder->line++;
1975         builder->offset = 0;
1976         break;
1977       }
1978       case STATE_END:
1979       {
1980         gchar *session_cookie;
1981         gchar *session_id;
1982
1983         if (message->type == GST_RTSP_MESSAGE_DATA) {
1984           /* data messages don't have headers */
1985           res = GST_RTSP_OK;
1986           goto done;
1987         }
1988
1989         /* save the tunnel session in the connection */
1990         if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST &&
1991             !conn->manual_http &&
1992             conn->tstate == TUNNEL_STATE_NONE &&
1993             gst_rtsp_message_get_header (message, GST_RTSP_HDR_X_SESSIONCOOKIE,
1994                 &session_cookie, 0) == GST_RTSP_OK) {
1995           strncpy (conn->tunnelid, session_cookie, TUNNELID_LEN);
1996           conn->tunnelid[TUNNELID_LEN - 1] = '\0';
1997           conn->tunneled = TRUE;
1998         }
1999
2000         /* save session id in the connection for further use */
2001         if (message->type == GST_RTSP_MESSAGE_RESPONSE &&
2002             gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
2003                 &session_id, 0) == GST_RTSP_OK) {
2004           gint maxlen, i;
2005
2006           maxlen = sizeof (conn->session_id) - 1;
2007           /* the sessionid can have attributes marked with ;
2008            * Make sure we strip them */
2009           for (i = 0; session_id[i] != '\0'; i++) {
2010             if (session_id[i] == ';') {
2011               maxlen = i;
2012               /* parse timeout */
2013               do {
2014                 i++;
2015               } while (g_ascii_isspace (session_id[i]));
2016               if (g_str_has_prefix (&session_id[i], "timeout=")) {
2017                 gint to;
2018
2019                 /* if we parsed something valid, configure */
2020                 if ((to = atoi (&session_id[i + 8])) > 0)
2021                   conn->timeout = to;
2022               }
2023               break;
2024             }
2025           }
2026
2027           /* make sure to not overflow */
2028           strncpy (conn->session_id, session_id, maxlen);
2029           conn->session_id[maxlen] = '\0';
2030         }
2031         res = builder->status;
2032         goto done;
2033       }
2034       default:
2035         res = GST_RTSP_ERROR;
2036         break;
2037     }
2038   }
2039 done:
2040   return res;
2041
2042   /* ERRORS */
2043 invalid_body_len:
2044   {
2045     GST_DEBUG ("could not allocate body");
2046     return GST_RTSP_ERROR;
2047   }
2048 }
2049
2050 /**
2051  * gst_rtsp_connection_read:
2052  * @conn: a #GstRTSPConnection
2053  * @data: the data to read
2054  * @size: the size of @data
2055  * @timeout: a timeout value or #NULL
2056  *
2057  * Attempt to read @size bytes into @data from the connected @conn, blocking up to
2058  * the specified @timeout. @timeout can be #NULL, in which case this function
2059  * might block forever.
2060  *
2061  * This function can be cancelled with gst_rtsp_connection_flush().
2062  *
2063  * Returns: #GST_RTSP_OK on success.
2064  */
2065 GstRTSPResult
2066 gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size,
2067     GTimeVal * timeout)
2068 {
2069   guint offset;
2070   gint retval;
2071   GstClockTime to;
2072   GstRTSPResult res;
2073
2074   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2075   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
2076   g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
2077
2078   if (G_UNLIKELY (size == 0))
2079     return GST_RTSP_OK;
2080
2081   offset = 0;
2082
2083   /* configure timeout if any */
2084   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2085
2086   gst_poll_set_controllable (conn->fdset, TRUE);
2087   gst_poll_fd_ctl_write (conn->fdset, conn->writefd, FALSE);
2088   gst_poll_fd_ctl_read (conn->fdset, conn->readfd, TRUE);
2089
2090   while (TRUE) {
2091     res = read_bytes (conn, data, &offset, size);
2092     if (G_UNLIKELY (res == GST_RTSP_EEOF))
2093       goto eof;
2094     if (G_LIKELY (res == GST_RTSP_OK))
2095       break;
2096     if (G_UNLIKELY (res != GST_RTSP_EINTR))
2097       goto read_error;
2098
2099     do {
2100       retval = gst_poll_wait (conn->fdset, to);
2101     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
2102
2103     /* check for timeout */
2104     if (G_UNLIKELY (retval == 0))
2105       goto select_timeout;
2106
2107     if (G_UNLIKELY (retval == -1)) {
2108       if (errno == EBUSY)
2109         goto stopped;
2110       else
2111         goto select_error;
2112     }
2113
2114     /* could also be an error with write socket */
2115     if (gst_poll_fd_has_error (conn->fdset, conn->writefd))
2116       goto socket_error;
2117
2118     gst_poll_set_controllable (conn->fdset, FALSE);
2119   }
2120   return GST_RTSP_OK;
2121
2122   /* ERRORS */
2123 select_error:
2124   {
2125     return GST_RTSP_ESYS;
2126   }
2127 select_timeout:
2128   {
2129     return GST_RTSP_ETIMEOUT;
2130   }
2131 stopped:
2132   {
2133     return GST_RTSP_EINTR;
2134   }
2135 eof:
2136   {
2137     return GST_RTSP_EEOF;
2138   }
2139 socket_error:
2140   {
2141     res = GST_RTSP_ENET;
2142   }
2143 read_error:
2144   {
2145     return res;
2146   }
2147 }
2148
2149 static GstRTSPMessage *
2150 gen_tunnel_reply (GstRTSPConnection * conn, GstRTSPStatusCode code,
2151     const GstRTSPMessage * request)
2152 {
2153   GstRTSPMessage *msg;
2154   GstRTSPResult res;
2155
2156   if (gst_rtsp_status_as_text (code) == NULL)
2157     code = GST_RTSP_STS_INTERNAL_SERVER_ERROR;
2158
2159   GST_RTSP_CHECK (gst_rtsp_message_new_response (&msg, code, NULL, request),
2160       no_message);
2161
2162   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_SERVER,
2163       "GStreamer RTSP Server");
2164   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONNECTION, "close");
2165   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-store");
2166   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
2167
2168   if (code == GST_RTSP_STS_OK) {
2169     if (conn->ip)
2170       gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
2171           conn->ip);
2172     gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_TYPE,
2173         "application/x-rtsp-tunnelled");
2174   }
2175
2176   return msg;
2177
2178   /* ERRORS */
2179 no_message:
2180   {
2181     return NULL;
2182   }
2183 }
2184
2185 /**
2186  * gst_rtsp_connection_receive:
2187  * @conn: a #GstRTSPConnection
2188  * @message: the message to read
2189  * @timeout: a timeout value or #NULL
2190  *
2191  * Attempt to read into @message from the connected @conn, blocking up to
2192  * the specified @timeout. @timeout can be #NULL, in which case this function
2193  * might block forever.
2194  * 
2195  * This function can be cancelled with gst_rtsp_connection_flush().
2196  *
2197  * Returns: #GST_RTSP_OK on success.
2198  */
2199 GstRTSPResult
2200 gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message,
2201     GTimeVal * timeout)
2202 {
2203   GstRTSPResult res;
2204   GstRTSPBuilder builder;
2205   gint retval;
2206   GstClockTime to;
2207
2208   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2209   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2210   g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
2211
2212   /* configure timeout if any */
2213   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2214
2215   gst_poll_set_controllable (conn->fdset, TRUE);
2216   gst_poll_fd_ctl_write (conn->fdset, conn->writefd, FALSE);
2217   gst_poll_fd_ctl_read (conn->fdset, conn->readfd, TRUE);
2218
2219   memset (&builder, 0, sizeof (GstRTSPBuilder));
2220   while (TRUE) {
2221     res = build_next (&builder, message, conn);
2222     if (G_UNLIKELY (res == GST_RTSP_EEOF))
2223       goto eof;
2224     else if (G_LIKELY (res == GST_RTSP_OK)) {
2225       if (!conn->manual_http) {
2226         if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
2227           if (conn->tstate == TUNNEL_STATE_NONE &&
2228               message->type_data.request.method == GST_RTSP_GET) {
2229             GstRTSPMessage *response;
2230
2231             conn->tstate = TUNNEL_STATE_GET;
2232
2233             /* tunnel GET request, we can reply now */
2234             response = gen_tunnel_reply (conn, GST_RTSP_STS_OK, message);
2235             res = gst_rtsp_connection_send (conn, response, timeout);
2236             gst_rtsp_message_free (response);
2237             if (res == GST_RTSP_OK)
2238               res = GST_RTSP_ETGET;
2239             goto cleanup;
2240           } else if (conn->tstate == TUNNEL_STATE_NONE &&
2241               message->type_data.request.method == GST_RTSP_POST) {
2242             conn->tstate = TUNNEL_STATE_POST;
2243
2244             /* tunnel POST request, the caller now has to link the two
2245              * connections. */
2246             res = GST_RTSP_ETPOST;
2247             goto cleanup;
2248           } else {
2249             res = GST_RTSP_EPARSE;
2250             goto cleanup;
2251           }
2252         } else if (message->type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
2253           res = GST_RTSP_EPARSE;
2254           goto cleanup;
2255         }
2256       }
2257
2258       break;
2259     } else if (G_UNLIKELY (res != GST_RTSP_EINTR))
2260       goto read_error;
2261
2262     do {
2263       retval = gst_poll_wait (conn->fdset, to);
2264     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
2265
2266     /* check for timeout */
2267     if (G_UNLIKELY (retval == 0))
2268       goto select_timeout;
2269
2270     if (G_UNLIKELY (retval == -1)) {
2271       if (errno == EBUSY)
2272         goto stopped;
2273       else
2274         goto select_error;
2275     }
2276
2277     /* could also be an error with write socket */
2278     if (gst_poll_fd_has_error (conn->fdset, conn->writefd))
2279       goto socket_error;
2280
2281     /* once we start reading the wait cannot be controlled */
2282     if (builder.state != STATE_START)
2283       gst_poll_set_controllable (conn->fdset, FALSE);
2284   }
2285
2286   /* we have a message here */
2287   build_reset (&builder);
2288
2289   return GST_RTSP_OK;
2290
2291   /* ERRORS */
2292 select_error:
2293   {
2294     res = GST_RTSP_ESYS;
2295     goto cleanup;
2296   }
2297 select_timeout:
2298   {
2299     res = GST_RTSP_ETIMEOUT;
2300     goto cleanup;
2301   }
2302 stopped:
2303   {
2304     res = GST_RTSP_EINTR;
2305     goto cleanup;
2306   }
2307 eof:
2308   {
2309     res = GST_RTSP_EEOF;
2310     goto cleanup;
2311   }
2312 socket_error:
2313   {
2314     res = GST_RTSP_ENET;
2315     goto cleanup;
2316   }
2317 read_error:
2318 cleanup:
2319   {
2320     build_reset (&builder);
2321     gst_rtsp_message_unset (message);
2322     return res;
2323   }
2324 }
2325
2326 /**
2327  * gst_rtsp_connection_close:
2328  * @conn: a #GstRTSPConnection
2329  *
2330  * Close the connected @conn. After this call, the connection is in the same
2331  * state as when it was first created.
2332  * 
2333  * Returns: #GST_RTSP_OK on success.
2334  */
2335 GstRTSPResult
2336 gst_rtsp_connection_close (GstRTSPConnection * conn)
2337 {
2338   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2339
2340   g_free (conn->ip);
2341   conn->ip = NULL;
2342
2343   conn->read_ahead = 0;
2344
2345   g_free (conn->initial_buffer);
2346   conn->initial_buffer = NULL;
2347   conn->initial_buffer_offset = 0;
2348
2349   REMOVE_POLLFD (conn->fdset, &conn->fd0);
2350   REMOVE_POLLFD (conn->fdset, &conn->fd1);
2351   conn->writefd = NULL;
2352   conn->readfd = NULL;
2353   conn->tunneled = FALSE;
2354   conn->tstate = TUNNEL_STATE_NONE;
2355   conn->ctxp = NULL;
2356   g_free (conn->username);
2357   conn->username = NULL;
2358   g_free (conn->passwd);
2359   conn->passwd = NULL;
2360   gst_rtsp_connection_clear_auth_params (conn);
2361   conn->timeout = 60;
2362   conn->cseq = 0;
2363   conn->session_id[0] = '\0';
2364
2365   return GST_RTSP_OK;
2366 }
2367
2368 /**
2369  * gst_rtsp_connection_free:
2370  * @conn: a #GstRTSPConnection
2371  *
2372  * Close and free @conn.
2373  * 
2374  * Returns: #GST_RTSP_OK on success.
2375  */
2376 GstRTSPResult
2377 gst_rtsp_connection_free (GstRTSPConnection * conn)
2378 {
2379   GstRTSPResult res;
2380
2381   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2382
2383   res = gst_rtsp_connection_close (conn);
2384   gst_poll_free (conn->fdset);
2385   g_timer_destroy (conn->timer);
2386   gst_rtsp_url_free (conn->url);
2387   g_free (conn->proxy_host);
2388   g_free (conn);
2389 #ifdef G_OS_WIN32
2390   WSACleanup ();
2391 #endif
2392
2393   return res;
2394 }
2395
2396 /**
2397  * gst_rtsp_connection_poll:
2398  * @conn: a #GstRTSPConnection
2399  * @events: a bitmask of #GstRTSPEvent flags to check
2400  * @revents: location for result flags 
2401  * @timeout: a timeout
2402  *
2403  * Wait up to the specified @timeout for the connection to become available for
2404  * at least one of the operations specified in @events. When the function returns
2405  * with #GST_RTSP_OK, @revents will contain a bitmask of available operations on
2406  * @conn.
2407  *
2408  * @timeout can be #NULL, in which case this function might block forever.
2409  *
2410  * This function can be cancelled with gst_rtsp_connection_flush().
2411  * 
2412  * Returns: #GST_RTSP_OK on success.
2413  *
2414  * Since: 0.10.15
2415  */
2416 GstRTSPResult
2417 gst_rtsp_connection_poll (GstRTSPConnection * conn, GstRTSPEvent events,
2418     GstRTSPEvent * revents, GTimeVal * timeout)
2419 {
2420   GstClockTime to;
2421   gint retval;
2422
2423   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2424   g_return_val_if_fail (events != 0, GST_RTSP_EINVAL);
2425   g_return_val_if_fail (revents != NULL, GST_RTSP_EINVAL);
2426   g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
2427   g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL);
2428
2429   gst_poll_set_controllable (conn->fdset, TRUE);
2430
2431   /* add fd to writer set when asked to */
2432   gst_poll_fd_ctl_write (conn->fdset, conn->writefd,
2433       events & GST_RTSP_EV_WRITE);
2434
2435   /* add fd to reader set when asked to */
2436   gst_poll_fd_ctl_read (conn->fdset, conn->readfd, events & GST_RTSP_EV_READ);
2437
2438   /* configure timeout if any */
2439   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2440
2441   do {
2442     retval = gst_poll_wait (conn->fdset, to);
2443   } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
2444
2445   if (G_UNLIKELY (retval == 0))
2446     goto select_timeout;
2447
2448   if (G_UNLIKELY (retval == -1)) {
2449     if (errno == EBUSY)
2450       goto stopped;
2451     else
2452       goto select_error;
2453   }
2454
2455   *revents = 0;
2456   if (events & GST_RTSP_EV_READ) {
2457     if (gst_poll_fd_can_read (conn->fdset, conn->readfd))
2458       *revents |= GST_RTSP_EV_READ;
2459   }
2460   if (events & GST_RTSP_EV_WRITE) {
2461     if (gst_poll_fd_can_write (conn->fdset, conn->writefd))
2462       *revents |= GST_RTSP_EV_WRITE;
2463   }
2464   return GST_RTSP_OK;
2465
2466   /* ERRORS */
2467 select_timeout:
2468   {
2469     return GST_RTSP_ETIMEOUT;
2470   }
2471 select_error:
2472   {
2473     return GST_RTSP_ESYS;
2474   }
2475 stopped:
2476   {
2477     return GST_RTSP_EINTR;
2478   }
2479 }
2480
2481 /**
2482  * gst_rtsp_connection_next_timeout:
2483  * @conn: a #GstRTSPConnection
2484  * @timeout: a timeout
2485  *
2486  * Calculate the next timeout for @conn, storing the result in @timeout.
2487  *
2488  * Returns: #GST_RTSP_OK.
2489  */
2490 GstRTSPResult
2491 gst_rtsp_connection_next_timeout (GstRTSPConnection * conn, GTimeVal * timeout)
2492 {
2493   gdouble elapsed;
2494   glong sec;
2495   gulong usec;
2496   gint ctimeout;
2497
2498   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2499   g_return_val_if_fail (timeout != NULL, GST_RTSP_EINVAL);
2500
2501   ctimeout = conn->timeout;
2502   if (ctimeout >= 20) {
2503     /* Because we should act before the timeout we timeout 5
2504      * seconds in advance. */
2505     ctimeout -= 5;
2506   } else if (ctimeout >= 5) {
2507     /* else timeout 20% earlier */
2508     ctimeout -= ctimeout / 5;
2509   } else if (ctimeout >= 1) {
2510     /* else timeout 1 second earlier */
2511     ctimeout -= 1;
2512   }
2513
2514   elapsed = g_timer_elapsed (conn->timer, &usec);
2515   if (elapsed >= ctimeout) {
2516     sec = 0;
2517     usec = 0;
2518   } else {
2519     sec = ctimeout - elapsed;
2520     if (usec <= G_USEC_PER_SEC)
2521       usec = G_USEC_PER_SEC - usec;
2522     else
2523       usec = 0;
2524   }
2525
2526   timeout->tv_sec = sec;
2527   timeout->tv_usec = usec;
2528
2529   return GST_RTSP_OK;
2530 }
2531
2532 /**
2533  * gst_rtsp_connection_reset_timeout:
2534  * @conn: a #GstRTSPConnection
2535  *
2536  * Reset the timeout of @conn.
2537  *
2538  * Returns: #GST_RTSP_OK.
2539  */
2540 GstRTSPResult
2541 gst_rtsp_connection_reset_timeout (GstRTSPConnection * conn)
2542 {
2543   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2544
2545   g_timer_start (conn->timer);
2546
2547   return GST_RTSP_OK;
2548 }
2549
2550 /**
2551  * gst_rtsp_connection_flush:
2552  * @conn: a #GstRTSPConnection
2553  * @flush: start or stop the flush
2554  *
2555  * Start or stop the flushing action on @conn. When flushing, all current
2556  * and future actions on @conn will return #GST_RTSP_EINTR until the connection
2557  * is set to non-flushing mode again.
2558  * 
2559  * Returns: #GST_RTSP_OK.
2560  */
2561 GstRTSPResult
2562 gst_rtsp_connection_flush (GstRTSPConnection * conn, gboolean flush)
2563 {
2564   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2565
2566   gst_poll_set_flushing (conn->fdset, flush);
2567
2568   return GST_RTSP_OK;
2569 }
2570
2571 /**
2572  * gst_rtsp_connection_set_proxy:
2573  * @conn: a #GstRTSPConnection
2574  * @host: the proxy host
2575  * @port: the proxy port
2576  *
2577  * Set the proxy host and port.
2578  * 
2579  * Returns: #GST_RTSP_OK.
2580  *
2581  * Since: 0.10.23
2582  */
2583 GstRTSPResult
2584 gst_rtsp_connection_set_proxy (GstRTSPConnection * conn,
2585     const gchar * host, guint port)
2586 {
2587   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2588
2589   g_free (conn->proxy_host);
2590   conn->proxy_host = g_strdup (host);
2591   conn->proxy_port = port;
2592
2593   return GST_RTSP_OK;
2594 }
2595
2596 /**
2597  * gst_rtsp_connection_set_auth:
2598  * @conn: a #GstRTSPConnection
2599  * @method: authentication method
2600  * @user: the user
2601  * @pass: the password
2602  *
2603  * Configure @conn for authentication mode @method with @user and @pass as the
2604  * user and password respectively.
2605  * 
2606  * Returns: #GST_RTSP_OK.
2607  */
2608 GstRTSPResult
2609 gst_rtsp_connection_set_auth (GstRTSPConnection * conn,
2610     GstRTSPAuthMethod method, const gchar * user, const gchar * pass)
2611 {
2612   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2613
2614   if (method == GST_RTSP_AUTH_DIGEST && ((user == NULL || pass == NULL)
2615           || g_strrstr (user, ":") != NULL))
2616     return GST_RTSP_EINVAL;
2617
2618   /* Make sure the username and passwd are being set for authentication */
2619   if (method == GST_RTSP_AUTH_NONE && (user == NULL || pass == NULL))
2620     return GST_RTSP_EINVAL;
2621
2622   /* ":" chars are not allowed in usernames for basic auth */
2623   if (method == GST_RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL)
2624     return GST_RTSP_EINVAL;
2625
2626   g_free (conn->username);
2627   g_free (conn->passwd);
2628
2629   conn->auth_method = method;
2630   conn->username = g_strdup (user);
2631   conn->passwd = g_strdup (pass);
2632
2633   return GST_RTSP_OK;
2634 }
2635
2636 /**
2637  * str_case_hash:
2638  * @key: ASCII string to hash
2639  *
2640  * Hashes @key in a case-insensitive manner.
2641  *
2642  * Returns: the hash code.
2643  **/
2644 static guint
2645 str_case_hash (gconstpointer key)
2646 {
2647   const char *p = key;
2648   guint h = g_ascii_toupper (*p);
2649
2650   if (h)
2651     for (p += 1; *p != '\0'; p++)
2652       h = (h << 5) - h + g_ascii_toupper (*p);
2653
2654   return h;
2655 }
2656
2657 /**
2658  * str_case_equal:
2659  * @v1: an ASCII string
2660  * @v2: another ASCII string
2661  *
2662  * Compares @v1 and @v2 in a case-insensitive manner
2663  *
2664  * Returns: %TRUE if they are equal (modulo case)
2665  **/
2666 static gboolean
2667 str_case_equal (gconstpointer v1, gconstpointer v2)
2668 {
2669   const char *string1 = v1;
2670   const char *string2 = v2;
2671
2672   return g_ascii_strcasecmp (string1, string2) == 0;
2673 }
2674
2675 /**
2676  * gst_rtsp_connection_set_auth_param:
2677  * @conn: a #GstRTSPConnection
2678  * @param: authentication directive
2679  * @value: value
2680  *
2681  * Setup @conn with authentication directives. This is not necesary for
2682  * methods #GST_RTSP_AUTH_NONE and #GST_RTSP_AUTH_BASIC. For
2683  * #GST_RTSP_AUTH_DIGEST, directives should be taken from the digest challenge
2684  * in the WWW-Authenticate response header and can include realm, domain,
2685  * nonce, opaque, stale, algorithm, qop as per RFC2617.
2686  * 
2687  * Since: 0.10.20
2688  */
2689 void
2690 gst_rtsp_connection_set_auth_param (GstRTSPConnection * conn,
2691     const gchar * param, const gchar * value)
2692 {
2693   g_return_if_fail (conn != NULL);
2694   g_return_if_fail (param != NULL);
2695
2696   if (conn->auth_params == NULL) {
2697     conn->auth_params =
2698         g_hash_table_new_full (str_case_hash, str_case_equal, g_free, g_free);
2699   }
2700   g_hash_table_insert (conn->auth_params, g_strdup (param), g_strdup (value));
2701 }
2702
2703 /**
2704  * gst_rtsp_connection_clear_auth_params:
2705  * @conn: a #GstRTSPConnection
2706  *
2707  * Clear the list of authentication directives stored in @conn.
2708  *
2709  * Since: 0.10.20
2710  */
2711 void
2712 gst_rtsp_connection_clear_auth_params (GstRTSPConnection * conn)
2713 {
2714   g_return_if_fail (conn != NULL);
2715
2716   if (conn->auth_params != NULL) {
2717     g_hash_table_destroy (conn->auth_params);
2718     conn->auth_params = NULL;
2719   }
2720 }
2721
2722 static GstRTSPResult
2723 set_qos_dscp (gint fd, guint qos_dscp)
2724 {
2725   union gst_sockaddr sa;
2726   socklen_t slen = sizeof (sa);
2727   gint af;
2728   gint tos;
2729
2730   if (fd == -1)
2731     return GST_RTSP_OK;
2732
2733   if (getsockname (fd, &sa.sa, &slen) < 0)
2734     goto no_getsockname;
2735
2736   af = sa.sa.sa_family;
2737
2738   /* if this is an IPv4-mapped address then do IPv4 QoS */
2739   if (af == AF_INET6) {
2740     if (IN6_IS_ADDR_V4MAPPED (&sa.sa_in6.sin6_addr))
2741       af = AF_INET;
2742   }
2743
2744   /* extract and shift 6 bits of the DSCP */
2745   tos = (qos_dscp & 0x3f) << 2;
2746
2747   switch (af) {
2748     case AF_INET:
2749       if (SETSOCKOPT (fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0)
2750         goto no_setsockopt;
2751       break;
2752     case AF_INET6:
2753 #ifdef IPV6_TCLASS
2754       if (SETSOCKOPT (fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof (tos)) < 0)
2755         goto no_setsockopt;
2756       break;
2757 #endif
2758     default:
2759       goto wrong_family;
2760   }
2761
2762   return GST_RTSP_OK;
2763
2764   /* ERRORS */
2765 no_getsockname:
2766 no_setsockopt:
2767   {
2768     return GST_RTSP_ESYS;
2769   }
2770
2771 wrong_family:
2772   {
2773     return GST_RTSP_ERROR;
2774   }
2775 }
2776
2777 /**
2778  * gst_rtsp_connection_set_qos_dscp:
2779  * @conn: a #GstRTSPConnection
2780  * @qos_dscp: DSCP value
2781  *
2782  * Configure @conn to use the specified DSCP value.
2783  *
2784  * Returns: #GST_RTSP_OK on success.
2785  *
2786  * Since: 0.10.20
2787  */
2788 GstRTSPResult
2789 gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
2790 {
2791   GstRTSPResult res;
2792
2793   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2794   g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
2795   g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL);
2796
2797   res = set_qos_dscp (conn->fd0.fd, qos_dscp);
2798   if (res == GST_RTSP_OK)
2799     res = set_qos_dscp (conn->fd1.fd, qos_dscp);
2800
2801   return res;
2802 }
2803
2804
2805 /**
2806  * gst_rtsp_connection_get_url:
2807  * @conn: a #GstRTSPConnection
2808  *
2809  * Retrieve the URL of the other end of @conn.
2810  *
2811  * Returns: The URL. This value remains valid until the
2812  * connection is freed.
2813  *
2814  * Since: 0.10.23
2815  */
2816 GstRTSPUrl *
2817 gst_rtsp_connection_get_url (const GstRTSPConnection * conn)
2818 {
2819   g_return_val_if_fail (conn != NULL, NULL);
2820
2821   return conn->url;
2822 }
2823
2824 /**
2825  * gst_rtsp_connection_get_ip:
2826  * @conn: a #GstRTSPConnection
2827  *
2828  * Retrieve the IP address of the other end of @conn.
2829  *
2830  * Returns: The IP address as a string. this value remains valid until the
2831  * connection is closed.
2832  *
2833  * Since: 0.10.20
2834  */
2835 const gchar *
2836 gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
2837 {
2838   g_return_val_if_fail (conn != NULL, NULL);
2839
2840   return conn->ip;
2841 }
2842
2843 /**
2844  * gst_rtsp_connection_set_ip:
2845  * @conn: a #GstRTSPConnection
2846  * @ip: an ip address
2847  *
2848  * Set the IP address of the server.
2849  *
2850  * Since: 0.10.23
2851  */
2852 void
2853 gst_rtsp_connection_set_ip (GstRTSPConnection * conn, const gchar * ip)
2854 {
2855   g_return_if_fail (conn != NULL);
2856
2857   g_free (conn->ip);
2858   conn->ip = g_strdup (ip);
2859 }
2860
2861 /**
2862  * gst_rtsp_connection_get_readfd:
2863  * @conn: a #GstRTSPConnection
2864  *
2865  * Get the file descriptor for reading.
2866  *
2867  * Returns: the file descriptor used for reading or -1 on error. The file
2868  * descriptor remains valid until the connection is closed.
2869  *
2870  * Since: 0.10.23
2871  */
2872 gint
2873 gst_rtsp_connection_get_readfd (const GstRTSPConnection * conn)
2874 {
2875   g_return_val_if_fail (conn != NULL, -1);
2876   g_return_val_if_fail (conn->readfd != NULL, -1);
2877
2878   return conn->readfd->fd;
2879 }
2880
2881 /**
2882  * gst_rtsp_connection_get_writefd:
2883  * @conn: a #GstRTSPConnection
2884  *
2885  * Get the file descriptor for writing.
2886  *
2887  * Returns: the file descriptor used for writing or -1 on error. The file
2888  * descriptor remains valid until the connection is closed.
2889  *
2890  * Since: 0.10.23
2891  */
2892 gint
2893 gst_rtsp_connection_get_writefd (const GstRTSPConnection * conn)
2894 {
2895   g_return_val_if_fail (conn != NULL, -1);
2896   g_return_val_if_fail (conn->writefd != NULL, -1);
2897
2898   return conn->writefd->fd;
2899 }
2900
2901 /**
2902  * gst_rtsp_connection_set_http_mode:
2903  * @conn: a #GstRTSPConnection
2904  * @enable: %TRUE to enable manual HTTP mode
2905  *
2906  * By setting the HTTP mode to %TRUE the message parsing will support HTTP
2907  * messages in addition to the RTSP messages. It will also disable the
2908  * automatic handling of setting up an HTTP tunnel.
2909  *
2910  * Since: 0.10.25
2911  */
2912 void
2913 gst_rtsp_connection_set_http_mode (GstRTSPConnection * conn, gboolean enable)
2914 {
2915   g_return_if_fail (conn != NULL);
2916
2917   conn->manual_http = enable;
2918 }
2919
2920 /**
2921  * gst_rtsp_connection_set_tunneled:
2922  * @conn: a #GstRTSPConnection
2923  * @tunneled: the new state
2924  *
2925  * Set the HTTP tunneling state of the connection. This must be configured before
2926  * the @conn is connected.
2927  *
2928  * Since: 0.10.23
2929  */
2930 void
2931 gst_rtsp_connection_set_tunneled (GstRTSPConnection * conn, gboolean tunneled)
2932 {
2933   g_return_if_fail (conn != NULL);
2934   g_return_if_fail (conn->readfd == NULL);
2935   g_return_if_fail (conn->writefd == NULL);
2936
2937   conn->tunneled = tunneled;
2938 }
2939
2940 /**
2941  * gst_rtsp_connection_is_tunneled:
2942  * @conn: a #GstRTSPConnection
2943  *
2944  * Get the tunneling state of the connection. 
2945  *
2946  * Returns: if @conn is using HTTP tunneling.
2947  *
2948  * Since: 0.10.23
2949  */
2950 gboolean
2951 gst_rtsp_connection_is_tunneled (const GstRTSPConnection * conn)
2952 {
2953   g_return_val_if_fail (conn != NULL, FALSE);
2954
2955   return conn->tunneled;
2956 }
2957
2958 /**
2959  * gst_rtsp_connection_get_tunnelid:
2960  * @conn: a #GstRTSPConnection
2961  *
2962  * Get the tunnel session id the connection. 
2963  *
2964  * Returns: returns a non-empty string if @conn is being tunneled over HTTP.
2965  *
2966  * Since: 0.10.23
2967  */
2968 const gchar *
2969 gst_rtsp_connection_get_tunnelid (const GstRTSPConnection * conn)
2970 {
2971   g_return_val_if_fail (conn != NULL, NULL);
2972
2973   if (!conn->tunneled)
2974     return NULL;
2975
2976   return conn->tunnelid;
2977 }
2978
2979 /**
2980  * gst_rtsp_connection_do_tunnel:
2981  * @conn: a #GstRTSPConnection
2982  * @conn2: a #GstRTSPConnection or %NULL
2983  *
2984  * If @conn received the first tunnel connection and @conn2 received
2985  * the second tunnel connection, link the two connections together so that
2986  * @conn manages the tunneled connection.
2987  *
2988  * After this call, @conn2 cannot be used anymore and must be freed with
2989  * gst_rtsp_connection_free().
2990  *
2991  * If @conn2 is %NULL then only the base64 decoding context will be setup for
2992  * @conn.
2993  *
2994  * Returns: return GST_RTSP_OK on success.
2995  *
2996  * Since: 0.10.23
2997  */
2998 GstRTSPResult
2999 gst_rtsp_connection_do_tunnel (GstRTSPConnection * conn,
3000     GstRTSPConnection * conn2)
3001 {
3002   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
3003
3004   if (conn2 != NULL) {
3005     g_return_val_if_fail (conn->tstate == TUNNEL_STATE_GET, GST_RTSP_EINVAL);
3006     g_return_val_if_fail (conn2->tstate == TUNNEL_STATE_POST, GST_RTSP_EINVAL);
3007     g_return_val_if_fail (!memcmp (conn2->tunnelid, conn->tunnelid,
3008             TUNNELID_LEN), GST_RTSP_EINVAL);
3009
3010     /* both connections have fd0 as the read/write socket. start by taking the
3011      * socket from conn2 and set it as the socket in conn */
3012     conn->fd1 = conn2->fd0;
3013
3014     /* clean up some of the state of conn2 */
3015     gst_poll_remove_fd (conn2->fdset, &conn2->fd0);
3016     conn2->fd0.fd = -1;
3017     conn2->readfd = conn2->writefd = NULL;
3018
3019     /* We make fd0 the write socket and fd1 the read socket. */
3020     conn->writefd = &conn->fd0;
3021     conn->readfd = &conn->fd1;
3022
3023     conn->tstate = TUNNEL_STATE_COMPLETE;
3024   }
3025
3026   /* we need base64 decoding for the readfd */
3027   conn->ctx.state = 0;
3028   conn->ctx.save = 0;
3029   conn->ctx.cout = 0;
3030   conn->ctx.coutl = 0;
3031   conn->ctxp = &conn->ctx;
3032
3033   return GST_RTSP_OK;
3034 }
3035
3036 #define READ_ERR    (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
3037 #define READ_COND   (G_IO_IN | READ_ERR)
3038 #define WRITE_ERR   (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
3039 #define WRITE_COND  (G_IO_OUT | WRITE_ERR)
3040
3041 typedef struct
3042 {
3043   guint8 *data;
3044   guint size;
3045   guint id;
3046 } GstRTSPRec;
3047
3048 /* async functions */
3049 struct _GstRTSPWatch
3050 {
3051   GSource source;
3052
3053   GstRTSPConnection *conn;
3054
3055   GstRTSPBuilder builder;
3056   GstRTSPMessage message;
3057
3058   GPollFD readfd;
3059   GPollFD writefd;
3060
3061   /* queued message for transmission */
3062   guint id;
3063   GMutex *mutex;
3064   GQueue *messages;
3065   guint8 *write_data;
3066   guint write_off;
3067   guint write_size;
3068   guint write_id;
3069
3070   GstRTSPWatchFuncs funcs;
3071
3072   gpointer user_data;
3073   GDestroyNotify notify;
3074 };
3075
3076 static gboolean
3077 gst_rtsp_source_prepare (GSource * source, gint * timeout)
3078 {
3079   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3080
3081   if (watch->conn->initial_buffer != NULL)
3082     return TRUE;
3083
3084   *timeout = (watch->conn->timeout * 1000);
3085
3086   return FALSE;
3087 }
3088
3089 static gboolean
3090 gst_rtsp_source_check (GSource * source)
3091 {
3092   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3093
3094   if (watch->readfd.revents & READ_COND)
3095     return TRUE;
3096
3097   if (watch->writefd.revents & WRITE_COND)
3098     return TRUE;
3099
3100   return FALSE;
3101 }
3102
3103 static gboolean
3104 gst_rtsp_source_dispatch (GSource * source, GSourceFunc callback G_GNUC_UNUSED,
3105     gpointer user_data G_GNUC_UNUSED)
3106 {
3107   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3108   GstRTSPResult res = GST_RTSP_ERROR;
3109   gboolean keep_running = TRUE;
3110
3111   /* first read as much as we can */
3112   if (watch->readfd.revents & READ_COND || watch->conn->initial_buffer != NULL) {
3113     do {
3114       if (watch->readfd.revents & READ_ERR)
3115         goto read_error;
3116
3117       res = build_next (&watch->builder, &watch->message, watch->conn);
3118       if (res == GST_RTSP_EINTR)
3119         break;
3120       else if (G_UNLIKELY (res == GST_RTSP_EEOF)) {
3121         watch->readfd.events = 0;
3122         watch->readfd.revents = 0;
3123         g_source_remove_poll ((GSource *) watch, &watch->readfd);
3124         /* When we are in tunnelled mode, the read socket can be closed and we
3125          * should be prepared for a new POST method to reopen it */
3126         if (watch->conn->tstate == TUNNEL_STATE_COMPLETE) {
3127           /* remove the read connection for the tunnel */
3128           /* we accept a new POST request */
3129           watch->conn->tstate = TUNNEL_STATE_GET;
3130           /* and signal that we lost our tunnel */
3131           if (watch->funcs.tunnel_lost)
3132             res = watch->funcs.tunnel_lost (watch, watch->user_data);
3133           goto read_done;
3134         } else
3135           goto eof;
3136       } else if (G_LIKELY (res == GST_RTSP_OK)) {
3137         if (!watch->conn->manual_http &&
3138             watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
3139           if (watch->conn->tstate == TUNNEL_STATE_NONE &&
3140               watch->message.type_data.request.method == GST_RTSP_GET) {
3141             GstRTSPMessage *response;
3142             GstRTSPStatusCode code;
3143
3144             watch->conn->tstate = TUNNEL_STATE_GET;
3145
3146             if (watch->funcs.tunnel_start)
3147               code = watch->funcs.tunnel_start (watch, watch->user_data);
3148             else
3149               code = GST_RTSP_STS_OK;
3150
3151             /* queue the response */
3152             response = gen_tunnel_reply (watch->conn, code, &watch->message);
3153             gst_rtsp_watch_send_message (watch, response, NULL);
3154             gst_rtsp_message_free (response);
3155             goto read_done;
3156           } else if (watch->conn->tstate == TUNNEL_STATE_NONE &&
3157               watch->message.type_data.request.method == GST_RTSP_POST) {
3158             watch->conn->tstate = TUNNEL_STATE_POST;
3159
3160             /* in the callback the connection should be tunneled with the
3161              * GET connection */
3162             if (watch->funcs.tunnel_complete)
3163               watch->funcs.tunnel_complete (watch, watch->user_data);
3164             goto read_done;
3165           }
3166         }
3167       }
3168
3169       if (!watch->conn->manual_http) {
3170         /* if manual HTTP support is not enabled, then restore the message to
3171          * what it would have looked like without the support for parsing HTTP
3172          * messages being present */
3173         if (watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
3174           watch->message.type = GST_RTSP_MESSAGE_REQUEST;
3175           watch->message.type_data.request.method = GST_RTSP_INVALID;
3176           if (watch->message.type_data.request.version != GST_RTSP_VERSION_1_0)
3177             watch->message.type_data.request.version = GST_RTSP_VERSION_INVALID;
3178           res = GST_RTSP_EPARSE;
3179         } else if (watch->message.type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
3180           watch->message.type = GST_RTSP_MESSAGE_RESPONSE;
3181           if (watch->message.type_data.response.version != GST_RTSP_VERSION_1_0)
3182             watch->message.type_data.response.version =
3183                 GST_RTSP_VERSION_INVALID;
3184           res = GST_RTSP_EPARSE;
3185         }
3186       }
3187
3188       if (G_LIKELY (res == GST_RTSP_OK)) {
3189         if (watch->funcs.message_received)
3190           watch->funcs.message_received (watch, &watch->message,
3191               watch->user_data);
3192       } else {
3193         goto read_error;
3194       }
3195
3196     read_done:
3197       gst_rtsp_message_unset (&watch->message);
3198       build_reset (&watch->builder);
3199     } while (FALSE);
3200   }
3201
3202   if (watch->writefd.revents & WRITE_COND) {
3203     if (watch->writefd.revents & WRITE_ERR)
3204       goto write_error;
3205
3206     g_mutex_lock (watch->mutex);
3207     do {
3208       if (watch->write_data == NULL) {
3209         GstRTSPRec *rec;
3210
3211         /* get a new message from the queue */
3212         rec = g_queue_pop_tail (watch->messages);
3213         if (rec == NULL)
3214           break;
3215
3216         watch->write_off = 0;
3217         watch->write_data = rec->data;
3218         watch->write_size = rec->size;
3219         watch->write_id = rec->id;
3220
3221         g_slice_free (GstRTSPRec, rec);
3222       }
3223
3224       res = write_bytes (watch->writefd.fd, watch->write_data,
3225           &watch->write_off, watch->write_size);
3226       g_mutex_unlock (watch->mutex);
3227
3228       if (res == GST_RTSP_EINTR)
3229         goto write_blocked;
3230       else if (G_LIKELY (res == GST_RTSP_OK)) {
3231         if (watch->funcs.message_sent)
3232           watch->funcs.message_sent (watch, watch->write_id, watch->user_data);
3233       } else {
3234         goto write_error;
3235       }
3236       g_mutex_lock (watch->mutex);
3237
3238       g_free (watch->write_data);
3239       watch->write_data = NULL;
3240     } while (TRUE);
3241
3242     watch->writefd.events = WRITE_ERR;
3243
3244     g_mutex_unlock (watch->mutex);
3245   }
3246
3247 write_blocked:
3248   return keep_running;
3249
3250   /* ERRORS */
3251 eof:
3252   {
3253     if (watch->funcs.closed)
3254       watch->funcs.closed (watch, watch->user_data);
3255
3256     /* always stop when the readfd returns EOF in non-tunneled mode */
3257     return FALSE;
3258   }
3259 read_error:
3260   {
3261     watch->readfd.events = 0;
3262     watch->readfd.revents = 0;
3263     g_source_remove_poll ((GSource *) watch, &watch->readfd);
3264     keep_running = (watch->writefd.events != 0);
3265
3266     if (keep_running) {
3267       if (watch->funcs.error_full)
3268         GST_RTSP_CHECK (watch->funcs.error_full (watch, res, &watch->message,
3269                 0, watch->user_data), error);
3270       else
3271         goto error;
3272     } else
3273       goto eof;
3274   }
3275 write_error:
3276   {
3277     watch->writefd.events = 0;
3278     watch->writefd.revents = 0;
3279     g_source_remove_poll ((GSource *) watch, &watch->writefd);
3280     keep_running = (watch->readfd.events != 0);
3281
3282     if (keep_running) {
3283       if (watch->funcs.error_full)
3284         GST_RTSP_CHECK (watch->funcs.error_full (watch, res, NULL,
3285                 watch->write_id, watch->user_data), error);
3286       else
3287         goto error;
3288     } else
3289       goto eof;
3290   }
3291 error:
3292   {
3293     if (watch->funcs.error)
3294       watch->funcs.error (watch, res, watch->user_data);
3295
3296     return keep_running;
3297   }
3298 }
3299
3300 static void
3301 gst_rtsp_rec_free (gpointer data)
3302 {
3303   GstRTSPRec *rec = data;
3304
3305   g_free (rec->data);
3306   g_slice_free (GstRTSPRec, rec);
3307 }
3308
3309 static void
3310 gst_rtsp_source_finalize (GSource * source)
3311 {
3312   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3313
3314   build_reset (&watch->builder);
3315   gst_rtsp_message_unset (&watch->message);
3316
3317   g_queue_foreach (watch->messages, (GFunc) gst_rtsp_rec_free, NULL);
3318   g_queue_free (watch->messages);
3319   watch->messages = NULL;
3320   g_free (watch->write_data);
3321
3322   g_mutex_free (watch->mutex);
3323
3324   if (watch->notify)
3325     watch->notify (watch->user_data);
3326 }
3327
3328 static GSourceFuncs gst_rtsp_source_funcs = {
3329   gst_rtsp_source_prepare,
3330   gst_rtsp_source_check,
3331   gst_rtsp_source_dispatch,
3332   gst_rtsp_source_finalize,
3333   NULL,
3334   NULL
3335 };
3336
3337 /**
3338  * gst_rtsp_watch_new:
3339  * @conn: a #GstRTSPConnection
3340  * @funcs: watch functions
3341  * @user_data: user data to pass to @funcs
3342  * @notify: notify when @user_data is not referenced anymore
3343  *
3344  * Create a watch object for @conn. The functions provided in @funcs will be
3345  * called with @user_data when activity happened on the watch.
3346  *
3347  * The new watch is usually created so that it can be attached to a
3348  * maincontext with gst_rtsp_watch_attach(). 
3349  *
3350  * @conn must exist for the entire lifetime of the watch.
3351  *
3352  * Returns: a #GstRTSPWatch that can be used for asynchronous RTSP
3353  * communication. Free with gst_rtsp_watch_unref () after usage.
3354  *
3355  * Since: 0.10.23
3356  */
3357 GstRTSPWatch *
3358 gst_rtsp_watch_new (GstRTSPConnection * conn,
3359     GstRTSPWatchFuncs * funcs, gpointer user_data, GDestroyNotify notify)
3360 {
3361   GstRTSPWatch *result;
3362
3363   g_return_val_if_fail (conn != NULL, NULL);
3364   g_return_val_if_fail (funcs != NULL, NULL);
3365   g_return_val_if_fail (conn->readfd != NULL, NULL);
3366   g_return_val_if_fail (conn->writefd != NULL, NULL);
3367
3368   result = (GstRTSPWatch *) g_source_new (&gst_rtsp_source_funcs,
3369       sizeof (GstRTSPWatch));
3370
3371   result->conn = conn;
3372   result->builder.state = STATE_START;
3373
3374   result->mutex = g_mutex_new ();
3375   result->messages = g_queue_new ();
3376
3377   result->readfd.fd = -1;
3378   result->writefd.fd = -1;
3379
3380   gst_rtsp_watch_reset (result);
3381
3382   result->funcs = *funcs;
3383   result->user_data = user_data;
3384   result->notify = notify;
3385
3386   return result;
3387 }
3388
3389 /**
3390  * gst_rtsp_watch_reset:
3391  * @watch: a #GstRTSPWatch
3392  *
3393  * Reset @watch, this is usually called after gst_rtsp_connection_do_tunnel()
3394  * when the file descriptors of the connection might have changed.
3395  *
3396  * Since: 0.10.23
3397  */
3398 void
3399 gst_rtsp_watch_reset (GstRTSPWatch * watch)
3400 {
3401   if (watch->readfd.fd != -1)
3402     g_source_remove_poll ((GSource *) watch, &watch->readfd);
3403   if (watch->writefd.fd != -1)
3404     g_source_remove_poll ((GSource *) watch, &watch->writefd);
3405
3406   watch->readfd.fd = watch->conn->readfd->fd;
3407   watch->readfd.events = READ_COND;
3408   watch->readfd.revents = 0;
3409
3410   watch->writefd.fd = watch->conn->writefd->fd;
3411   watch->writefd.events = WRITE_ERR;
3412   watch->writefd.revents = 0;
3413
3414   if (watch->readfd.fd != -1)
3415     g_source_add_poll ((GSource *) watch, &watch->readfd);
3416   if (watch->writefd.fd != -1)
3417     g_source_add_poll ((GSource *) watch, &watch->writefd);
3418 }
3419
3420 /**
3421  * gst_rtsp_watch_attach:
3422  * @watch: a #GstRTSPWatch
3423  * @context: a GMainContext (if NULL, the default context will be used)
3424  *
3425  * Adds a #GstRTSPWatch to a context so that it will be executed within that context.
3426  *
3427  * Returns: the ID (greater than 0) for the watch within the GMainContext. 
3428  *
3429  * Since: 0.10.23
3430  */
3431 guint
3432 gst_rtsp_watch_attach (GstRTSPWatch * watch, GMainContext * context)
3433 {
3434   g_return_val_if_fail (watch != NULL, 0);
3435
3436   return g_source_attach ((GSource *) watch, context);
3437 }
3438
3439 /**
3440  * gst_rtsp_watch_unref:
3441  * @watch: a #GstRTSPWatch
3442  *
3443  * Decreases the reference count of @watch by one. If the resulting reference
3444  * count is zero the watch and associated memory will be destroyed.
3445  *
3446  * Since: 0.10.23
3447  */
3448 void
3449 gst_rtsp_watch_unref (GstRTSPWatch * watch)
3450 {
3451   g_return_if_fail (watch != NULL);
3452
3453   g_source_unref ((GSource *) watch);
3454 }
3455
3456 /**
3457  * gst_rtsp_watch_write_data:
3458  * @watch: a #GstRTSPWatch
3459  * @data: the data to queue
3460  * @size: the size of @data
3461  * @id: location for a message ID or %NULL
3462  *
3463  * Write @data using the connection of the @watch. If it cannot be sent
3464  * immediately, it will be queued for transmission in @watch. The contents of
3465  * @message will then be serialized and transmitted when the connection of the
3466  * @watch becomes writable. In case the @message is queued, the ID returned in
3467  * @id will be non-zero and used as the ID argument in the message_sent
3468  * callback.
3469  *
3470  * This function will take ownership of @data and g_free() it after use.
3471  *
3472  * Returns: #GST_RTSP_OK on success.
3473  *
3474  * Since: 0.10.25
3475  */
3476 GstRTSPResult
3477 gst_rtsp_watch_write_data (GstRTSPWatch * watch, const guint8 * data,
3478     guint size, guint * id)
3479 {
3480   GstRTSPResult res;
3481   GstRTSPRec *rec;
3482   guint off = 0;
3483   GMainContext *context = NULL;
3484
3485   g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3486   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
3487   g_return_val_if_fail (size != 0, GST_RTSP_EINVAL);
3488
3489   g_mutex_lock (watch->mutex);
3490
3491   /* try to send the message synchronously first */
3492   if (watch->messages->length == 0) {
3493     res = write_bytes (watch->writefd.fd, data, &off, size);
3494     if (res != GST_RTSP_EINTR) {
3495       if (id != NULL)
3496         *id = 0;
3497       g_free ((gpointer) data);
3498       goto done;
3499     }
3500   }
3501
3502   /* make a record with the data and id for sending async */
3503   rec = g_slice_new (GstRTSPRec);
3504   if (off == 0) {
3505     rec->data = (guint8 *) data;
3506     rec->size = size;
3507   } else {
3508     rec->data = g_memdup (data + off, size - off);
3509     rec->size = size - off;
3510     g_free ((gpointer) data);
3511   }
3512
3513   do {
3514     /* make sure rec->id is never 0 */
3515     rec->id = ++watch->id;
3516   } while (G_UNLIKELY (rec->id == 0));
3517
3518   /* add the record to a queue. FIXME we would like to have an upper limit here */
3519   g_queue_push_head (watch->messages, rec);
3520
3521   /* make sure the main context will now also check for writability on the
3522    * socket */
3523   if (watch->writefd.events != WRITE_COND) {
3524     watch->writefd.events = WRITE_COND;
3525     context = ((GSource *) watch)->context;
3526   }
3527
3528   if (id != NULL)
3529     *id = rec->id;
3530   res = GST_RTSP_OK;
3531
3532 done:
3533   g_mutex_unlock (watch->mutex);
3534
3535   if (context)
3536     g_main_context_wakeup (context);
3537
3538   return res;
3539 }
3540
3541 /**
3542  * gst_rtsp_watch_send_message:
3543  * @watch: a #GstRTSPWatch
3544  * @message: a #GstRTSPMessage
3545  * @id: location for a message ID or %NULL
3546  *
3547  * Send a @message using the connection of the @watch. If it cannot be sent
3548  * immediately, it will be queued for transmission in @watch. The contents of
3549  * @message will then be serialized and transmitted when the connection of the
3550  * @watch becomes writable. In case the @message is queued, the ID returned in
3551  * @id will be non-zero and used as the ID argument in the message_sent
3552  * callback.
3553  *
3554  * Returns: #GST_RTSP_OK on success.
3555  *
3556  * Since: 0.10.25
3557  */
3558 GstRTSPResult
3559 gst_rtsp_watch_send_message (GstRTSPWatch * watch, GstRTSPMessage * message,
3560     guint * id)
3561 {
3562   GString *str;
3563   guint size;
3564
3565   g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3566   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3567
3568   /* make a record with the message as a string and id */
3569   str = message_to_string (watch->conn, message);
3570   size = str->len;
3571   return gst_rtsp_watch_write_data (watch,
3572       (guint8 *) g_string_free (str, FALSE), size, id);
3573 }