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