tizen 2.3.1 release
[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 #ifdef SAMSUNG_WFD_SPEC
1447       /* This change is made because WFD Sink does not understand the first 2
1448       * bytes i.e (magic number and channel) of TCP Interleaved data. This
1449       * is done specifically to support WFD Sink's limitation */
1450
1451       guint8 data_header[2];
1452       data_header[0] = (message->body_size >> 8) & 0xff;
1453       data_header[1] = message->body_size & 0xff;
1454
1455       /* create string with header and data */
1456       str = g_string_append_len (str, (gchar *) data_header, 2);
1457 #else
1458       guint8 data_header[4];
1459       /* prepare data header */
1460       data_header[0] = '$';
1461       data_header[1] = message->type_data.data.channel;
1462       data_header[2] = (message->body_size >> 8) & 0xff;
1463       data_header[3] = message->body_size & 0xff;
1464
1465       /* create string with header and data */
1466       str = g_string_append_len (str, (gchar *) data_header, 4);
1467 #endif
1468       str =
1469           g_string_append_len (str, (gchar *) message->body,
1470           message->body_size);
1471       break;
1472     }
1473     default:
1474       g_string_free (str, TRUE);
1475       g_return_val_if_reached (NULL);
1476       break;
1477   }
1478
1479   /* append headers and body */
1480   if (message->type != GST_RTSP_MESSAGE_DATA) {
1481     gchar date_string[100];
1482
1483     gen_date_string (date_string, sizeof (date_string));
1484
1485     /* add date header */
1486     gst_rtsp_message_remove_header (message, GST_RTSP_HDR_DATE, -1);
1487     gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string);
1488
1489     /* append headers */
1490     gst_rtsp_message_append_headers (message, str);
1491
1492     /* append Content-Length and body if needed */
1493     if (message->body != NULL && message->body_size > 0) {
1494       gchar *len;
1495
1496       len = g_strdup_printf ("%d", message->body_size);
1497       g_string_append_printf (str, "%s: %s\r\n",
1498           gst_rtsp_header_as_text (GST_RTSP_HDR_CONTENT_LENGTH), len);
1499       g_free (len);
1500       /* header ends here */
1501       g_string_append (str, "\r\n");
1502       str =
1503           g_string_append_len (str, (gchar *) message->body,
1504           message->body_size);
1505     } else {
1506       /* just end headers */
1507       g_string_append (str, "\r\n");
1508     }
1509   }
1510
1511   return str;
1512 }
1513
1514 /**
1515  * gst_rtsp_connection_send:
1516  * @conn: a #GstRTSPConnection
1517  * @message: the message to send
1518  * @timeout: a timeout value or #NULL
1519  *
1520  * Attempt to send @message to the connected @conn, blocking up to
1521  * the specified @timeout. @timeout can be #NULL, in which case this function
1522  * might block forever.
1523  * 
1524  * This function can be cancelled with gst_rtsp_connection_flush().
1525  *
1526  * Returns: #GST_RTSP_OK on success.
1527  */
1528 GstRTSPResult
1529 gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
1530     GTimeVal * timeout)
1531 {
1532   GString *string = NULL;
1533   GstRTSPResult res;
1534   gchar *str;
1535   gsize len;
1536
1537   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1538   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
1539
1540   if (G_UNLIKELY (!(string = message_to_string (conn, message))))
1541     goto no_message;
1542
1543   if (conn->tunneled) {
1544     str = g_base64_encode ((const guchar *) string->str, string->len);
1545     g_string_free (string, TRUE);
1546     len = strlen (str);
1547   } else {
1548     str = string->str;
1549     len = string->len;
1550     g_string_free (string, FALSE);
1551   }
1552
1553   /* write request */
1554   res = gst_rtsp_connection_write (conn, (guint8 *) str, len, timeout);
1555
1556   g_free (str);
1557
1558   return res;
1559
1560 no_message:
1561   {
1562     g_warning ("Wrong message");
1563     return GST_RTSP_EINVAL;
1564   }
1565 }
1566
1567 static GstRTSPResult
1568 parse_string (gchar * dest, gint size, gchar ** src)
1569 {
1570   GstRTSPResult res = GST_RTSP_OK;
1571   gint idx;
1572
1573   idx = 0;
1574   /* skip spaces */
1575   while (g_ascii_isspace (**src))
1576     (*src)++;
1577
1578   while (!g_ascii_isspace (**src) && **src != '\0') {
1579     if (idx < size - 1)
1580       dest[idx++] = **src;
1581     else
1582       res = GST_RTSP_EPARSE;
1583     (*src)++;
1584   }
1585   if (size > 0)
1586     dest[idx] = '\0';
1587
1588   return res;
1589 }
1590
1591 static GstRTSPResult
1592 parse_protocol_version (gchar * protocol, GstRTSPMsgType * type,
1593     GstRTSPVersion * version)
1594 {
1595   GstRTSPResult res = GST_RTSP_OK;
1596   gchar *ver;
1597
1598   if (G_LIKELY ((ver = strchr (protocol, '/')) != NULL)) {
1599     guint major;
1600     guint minor;
1601     gchar dummychar;
1602
1603     *ver++ = '\0';
1604
1605     /* the version number must be formatted as X.Y with nothing following */
1606     if (sscanf (ver, "%u.%u%c", &major, &minor, &dummychar) != 2)
1607       res = GST_RTSP_EPARSE;
1608
1609     if (g_ascii_strcasecmp (protocol, "RTSP") == 0) {
1610       if (major != 1 || minor != 0) {
1611         *version = GST_RTSP_VERSION_INVALID;
1612         res = GST_RTSP_ERROR;
1613       }
1614     } else if (g_ascii_strcasecmp (protocol, "HTTP") == 0) {
1615       if (*type == GST_RTSP_MESSAGE_REQUEST)
1616         *type = GST_RTSP_MESSAGE_HTTP_REQUEST;
1617       else if (*type == GST_RTSP_MESSAGE_RESPONSE)
1618         *type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
1619
1620       if (major == 1 && minor == 1) {
1621         *version = GST_RTSP_VERSION_1_1;
1622       } else if (major != 1 || minor != 0) {
1623         *version = GST_RTSP_VERSION_INVALID;
1624         res = GST_RTSP_ERROR;
1625       }
1626     } else
1627       res = GST_RTSP_EPARSE;
1628   } else
1629     res = GST_RTSP_EPARSE;
1630
1631   return res;
1632 }
1633
1634 static GstRTSPResult
1635 parse_response_status (guint8 * buffer, GstRTSPMessage * msg)
1636 {
1637   GstRTSPResult res = GST_RTSP_OK;
1638   GstRTSPResult res2;
1639   gchar versionstr[20];
1640   gchar codestr[4];
1641   gint code;
1642   gchar *bptr;
1643
1644   bptr = (gchar *) buffer;
1645
1646   if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
1647     res = GST_RTSP_EPARSE;
1648
1649   if (parse_string (codestr, sizeof (codestr), &bptr) != GST_RTSP_OK)
1650     res = GST_RTSP_EPARSE;
1651   code = atoi (codestr);
1652   if (G_UNLIKELY (*codestr == '\0' || code < 0 || code >= 600))
1653     res = GST_RTSP_EPARSE;
1654
1655   while (g_ascii_isspace (*bptr))
1656     bptr++;
1657
1658   if (G_UNLIKELY (gst_rtsp_message_init_response (msg, code, bptr,
1659               NULL) != GST_RTSP_OK))
1660     res = GST_RTSP_EPARSE;
1661
1662   res2 = parse_protocol_version (versionstr, &msg->type,
1663       &msg->type_data.response.version);
1664   if (G_LIKELY (res == GST_RTSP_OK))
1665     res = res2;
1666
1667   return res;
1668 }
1669
1670 static GstRTSPResult
1671 parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
1672 {
1673   GstRTSPResult res = GST_RTSP_OK;
1674   GstRTSPResult res2;
1675   gchar versionstr[20];
1676   gchar methodstr[20];
1677   gchar urlstr[4096];
1678   gchar *bptr;
1679   GstRTSPMethod method;
1680
1681   bptr = (gchar *) buffer;
1682
1683   if (parse_string (methodstr, sizeof (methodstr), &bptr) != GST_RTSP_OK)
1684     res = GST_RTSP_EPARSE;
1685   method = gst_rtsp_find_method (methodstr);
1686
1687   if (parse_string (urlstr, sizeof (urlstr), &bptr) != GST_RTSP_OK)
1688     res = GST_RTSP_EPARSE;
1689   if (G_UNLIKELY (*urlstr == '\0'))
1690     res = GST_RTSP_EPARSE;
1691
1692   if (parse_string (versionstr, sizeof (versionstr), &bptr) != GST_RTSP_OK)
1693     res = GST_RTSP_EPARSE;
1694
1695   if (G_UNLIKELY (*bptr != '\0'))
1696     res = GST_RTSP_EPARSE;
1697
1698   if (G_UNLIKELY (gst_rtsp_message_init_request (msg, method,
1699               urlstr) != GST_RTSP_OK))
1700     res = GST_RTSP_EPARSE;
1701
1702   res2 = parse_protocol_version (versionstr, &msg->type,
1703       &msg->type_data.request.version);
1704   if (G_LIKELY (res == GST_RTSP_OK))
1705     res = res2;
1706
1707   if (G_LIKELY (msg->type == GST_RTSP_MESSAGE_REQUEST)) {
1708     /* GET and POST are not allowed as RTSP methods */
1709     if (msg->type_data.request.method == GST_RTSP_GET ||
1710         msg->type_data.request.method == GST_RTSP_POST) {
1711       msg->type_data.request.method = GST_RTSP_INVALID;
1712       if (res == GST_RTSP_OK)
1713         res = GST_RTSP_ERROR;
1714     }
1715   } else if (msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
1716     /* only GET and POST are allowed as HTTP methods */
1717     if (msg->type_data.request.method != GST_RTSP_GET &&
1718         msg->type_data.request.method != GST_RTSP_POST) {
1719       msg->type_data.request.method = GST_RTSP_INVALID;
1720       if (res == GST_RTSP_OK)
1721         res = GST_RTSP_ERROR;
1722     }
1723   }
1724
1725   return res;
1726 }
1727
1728 /* parsing lines means reading a Key: Value pair */
1729 static GstRTSPResult
1730 parse_line (guint8 * buffer, GstRTSPMessage * msg)
1731 {
1732   GstRTSPHeaderField field;
1733   gchar *line = (gchar *) buffer;
1734   gchar *value;
1735
1736   if ((value = strchr (line, ':')) == NULL || value == line)
1737     goto parse_error;
1738
1739   /* trim space before the colon */
1740   if (value[-1] == ' ')
1741     value[-1] = '\0';
1742
1743   /* replace the colon with a NUL */
1744   *value++ = '\0';
1745
1746   /* find the header */
1747   field = gst_rtsp_find_header_field (line);
1748   if (field == GST_RTSP_HDR_INVALID)
1749     goto done;
1750
1751   /* split up the value in multiple key:value pairs if it contains comma(s) */
1752   while (*value != '\0') {
1753     gchar *next_value;
1754     gchar *comma = NULL;
1755     gboolean quoted = FALSE;
1756     guint comment = 0;
1757
1758     /* trim leading space */
1759     if (*value == ' ')
1760       value++;
1761
1762     /* for headers which may not appear multiple times, and thus may not
1763      * contain multiple values on the same line, we can short-circuit the loop
1764      * below and the entire value results in just one key:value pair*/
1765     if (!gst_rtsp_header_allow_multiple (field))
1766       next_value = value + strlen (value);
1767     else
1768       next_value = value;
1769
1770     /* find the next value, taking special care of quotes and comments */
1771     while (*next_value != '\0') {
1772       if ((quoted || comment != 0) && *next_value == '\\' &&
1773           next_value[1] != '\0')
1774         next_value++;
1775       else if (comment == 0 && *next_value == '"')
1776         quoted = !quoted;
1777       else if (!quoted && *next_value == '(')
1778         comment++;
1779       else if (comment != 0 && *next_value == ')')
1780         comment--;
1781       else if (!quoted && comment == 0) {
1782         /* To quote RFC 2068: "User agents MUST take special care in parsing
1783          * the WWW-Authenticate field value if it contains more than one
1784          * challenge, or if more than one WWW-Authenticate header field is
1785          * provided, since the contents of a challenge may itself contain a
1786          * comma-separated list of authentication parameters."
1787          *
1788          * What this means is that we cannot just look for an unquoted comma
1789          * when looking for multiple values in Proxy-Authenticate and
1790          * WWW-Authenticate headers. Instead we need to look for the sequence
1791          * "comma [space] token space token" before we can split after the
1792          * comma...
1793          */
1794         if (field == GST_RTSP_HDR_PROXY_AUTHENTICATE ||
1795             field == GST_RTSP_HDR_WWW_AUTHENTICATE) {
1796           if (*next_value == ',') {
1797             if (next_value[1] == ' ') {
1798               /* skip any space following the comma so we do not mistake it for
1799                * separating between two tokens */
1800               next_value++;
1801             }
1802             comma = next_value;
1803           } else if (*next_value == ' ' && next_value[1] != ',' &&
1804               next_value[1] != '=' && comma != NULL) {
1805             next_value = comma;
1806             comma = NULL;
1807             break;
1808           }
1809         } else if (*next_value == ',')
1810           break;
1811       }
1812
1813       next_value++;
1814     }
1815
1816     /* trim space */
1817     if (value != next_value && next_value[-1] == ' ')
1818       next_value[-1] = '\0';
1819
1820     if (*next_value != '\0')
1821       *next_value++ = '\0';
1822
1823     /* add the key:value pair */
1824     if (*value != '\0')
1825       gst_rtsp_message_add_header (msg, field, value);
1826
1827     value = next_value;
1828   }
1829
1830 done:
1831   return GST_RTSP_OK;
1832
1833   /* ERRORS */
1834 parse_error:
1835   {
1836     return GST_RTSP_EPARSE;
1837   }
1838 }
1839
1840 /* convert all consecutive whitespace to a single space */
1841 static void
1842 normalize_line (guint8 * buffer)
1843 {
1844   while (*buffer) {
1845     if (g_ascii_isspace (*buffer)) {
1846       guint8 *tmp;
1847
1848       *buffer++ = ' ';
1849       for (tmp = buffer; g_ascii_isspace (*tmp); tmp++) {
1850       }
1851       if (buffer != tmp)
1852         memmove (buffer, tmp, strlen ((gchar *) tmp) + 1);
1853     } else {
1854       buffer++;
1855     }
1856   }
1857 }
1858
1859 /* returns:
1860  *  GST_RTSP_OK when a complete message was read.
1861  *  GST_RTSP_EEOF: when the read socket is closed
1862  *  GST_RTSP_EINTR: when more data is needed.
1863  *  GST_RTSP_..: some other error occured.
1864  */
1865 static GstRTSPResult
1866 build_next (GstRTSPBuilder * builder, GstRTSPMessage * message,
1867     GstRTSPConnection * conn)
1868 {
1869   GstRTSPResult res;
1870
1871   while (TRUE) {
1872     switch (builder->state) {
1873       case STATE_START:
1874       {
1875         guint8 c;
1876
1877         builder->offset = 0;
1878         res =
1879             read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 1);
1880         if (res != GST_RTSP_OK)
1881           goto done;
1882
1883         c = builder->buffer[0];
1884
1885         /* we have 1 bytes now and we can see if this is a data message or
1886          * not */
1887         if (c == '$') {
1888           /* data message, prepare for the header */
1889           builder->state = STATE_DATA_HEADER;
1890         } else if (c == '\n' || c == '\r') {
1891           /* skip \n and \r */
1892           builder->offset = 0;
1893         } else {
1894           builder->line = 0;
1895           builder->state = STATE_READ_LINES;
1896         }
1897         break;
1898       }
1899       case STATE_DATA_HEADER:
1900       {
1901         res =
1902             read_bytes (conn, (guint8 *) builder->buffer, &builder->offset, 4);
1903         if (res != GST_RTSP_OK)
1904           goto done;
1905
1906         gst_rtsp_message_init_data (message, builder->buffer[1]);
1907
1908         builder->body_len = (builder->buffer[2] << 8) | builder->buffer[3];
1909         builder->body_data = g_malloc (builder->body_len + 1);
1910         builder->body_data[builder->body_len] = '\0';
1911         builder->offset = 0;
1912         builder->state = STATE_DATA_BODY;
1913         break;
1914       }
1915       case STATE_DATA_BODY:
1916       {
1917         res =
1918             read_bytes (conn, builder->body_data, &builder->offset,
1919             builder->body_len);
1920         if (res != GST_RTSP_OK)
1921           goto done;
1922
1923         /* we have the complete body now, store in the message adjusting the
1924          * length to include the trailing '\0' */
1925         gst_rtsp_message_take_body (message,
1926             (guint8 *) builder->body_data, builder->body_len + 1);
1927         builder->body_data = NULL;
1928         builder->body_len = 0;
1929
1930         builder->state = STATE_END;
1931         break;
1932       }
1933       case STATE_READ_LINES:
1934       {
1935         res = read_line (conn, builder->buffer, &builder->offset,
1936             sizeof (builder->buffer));
1937         if (res != GST_RTSP_OK)
1938           goto done;
1939
1940         /* we have a regular response */
1941         if (builder->buffer[0] == '\0') {
1942           gchar *hdrval;
1943
1944           /* empty line, end of message header */
1945           /* see if there is a Content-Length header, but ignore it if this
1946            * is a POST request with an x-sessioncookie header */
1947           if (gst_rtsp_message_get_header (message,
1948                   GST_RTSP_HDR_CONTENT_LENGTH, &hdrval, 0) == GST_RTSP_OK &&
1949               (message->type != GST_RTSP_MESSAGE_HTTP_REQUEST ||
1950                   message->type_data.request.method != GST_RTSP_POST ||
1951                   gst_rtsp_message_get_header (message,
1952                       GST_RTSP_HDR_X_SESSIONCOOKIE, NULL, 0) != GST_RTSP_OK)) {
1953             /* there is, prepare to read the body */
1954             builder->body_len = atol (hdrval);
1955             builder->body_data = g_try_malloc (builder->body_len + 1);
1956             /* we can't do much here, we need the length to know how many bytes
1957              * we need to read next and when allocation fails, something is
1958              * probably wrong with the length. */
1959             if (builder->body_data == NULL)
1960               goto invalid_body_len;
1961
1962             builder->body_data[builder->body_len] = '\0';
1963             builder->offset = 0;
1964             builder->state = STATE_DATA_BODY;
1965           } else {
1966             builder->state = STATE_END;
1967           }
1968           break;
1969         }
1970
1971         /* we have a line */
1972         normalize_line (builder->buffer);
1973         if (builder->line == 0) {
1974           /* first line, check for response status */
1975           if (memcmp (builder->buffer, "RTSP", 4) == 0 ||
1976               memcmp (builder->buffer, "HTTP", 4) == 0) {
1977             builder->status = parse_response_status (builder->buffer, message);
1978           } else {
1979             builder->status = parse_request_line (builder->buffer, message);
1980           }
1981         } else {
1982           /* else just parse the line */
1983           res = parse_line (builder->buffer, message);
1984           if (res != GST_RTSP_OK)
1985             builder->status = res;
1986         }
1987         builder->line++;
1988         builder->offset = 0;
1989         break;
1990       }
1991       case STATE_END:
1992       {
1993         gchar *session_cookie;
1994         gchar *session_id;
1995
1996         if (message->type == GST_RTSP_MESSAGE_DATA) {
1997           /* data messages don't have headers */
1998           res = GST_RTSP_OK;
1999           goto done;
2000         }
2001
2002         /* save the tunnel session in the connection */
2003         if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST &&
2004             !conn->manual_http &&
2005             conn->tstate == TUNNEL_STATE_NONE &&
2006             gst_rtsp_message_get_header (message, GST_RTSP_HDR_X_SESSIONCOOKIE,
2007                 &session_cookie, 0) == GST_RTSP_OK) {
2008           strncpy (conn->tunnelid, session_cookie, TUNNELID_LEN);
2009           conn->tunnelid[TUNNELID_LEN - 1] = '\0';
2010           conn->tunneled = TRUE;
2011         }
2012
2013         /* save session id in the connection for further use */
2014         if (message->type == GST_RTSP_MESSAGE_RESPONSE &&
2015             gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
2016                 &session_id, 0) == GST_RTSP_OK) {
2017           gint maxlen, i;
2018
2019           maxlen = sizeof (conn->session_id) - 1;
2020           /* the sessionid can have attributes marked with ;
2021            * Make sure we strip them */
2022           for (i = 0; session_id[i] != '\0'; i++) {
2023             if (session_id[i] == ';') {
2024               maxlen = i;
2025               /* parse timeout */
2026               do {
2027                 i++;
2028               } while (g_ascii_isspace (session_id[i]));
2029               if (g_str_has_prefix (&session_id[i], "timeout=")) {
2030                 gint to;
2031
2032                 /* if we parsed something valid, configure */
2033                 if ((to = atoi (&session_id[i + 8])) > 0)
2034                   conn->timeout = to;
2035               }
2036               break;
2037             }
2038           }
2039
2040           /* make sure to not overflow */
2041           strncpy (conn->session_id, session_id, maxlen);
2042           conn->session_id[maxlen] = '\0';
2043         }
2044         res = builder->status;
2045         goto done;
2046       }
2047       default:
2048         res = GST_RTSP_ERROR;
2049         break;
2050     }
2051   }
2052 done:
2053   return res;
2054
2055   /* ERRORS */
2056 invalid_body_len:
2057   {
2058     GST_DEBUG ("could not allocate body");
2059     return GST_RTSP_ERROR;
2060   }
2061 }
2062
2063 /**
2064  * gst_rtsp_connection_read:
2065  * @conn: a #GstRTSPConnection
2066  * @data: the data to read
2067  * @size: the size of @data
2068  * @timeout: a timeout value or #NULL
2069  *
2070  * Attempt to read @size bytes into @data from the connected @conn, blocking up to
2071  * the specified @timeout. @timeout can be #NULL, in which case this function
2072  * might block forever.
2073  *
2074  * This function can be cancelled with gst_rtsp_connection_flush().
2075  *
2076  * Returns: #GST_RTSP_OK on success.
2077  */
2078 GstRTSPResult
2079 gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size,
2080     GTimeVal * timeout)
2081 {
2082   guint offset;
2083   gint retval;
2084   GstClockTime to;
2085   GstRTSPResult res;
2086
2087   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2088   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
2089   g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
2090
2091   if (G_UNLIKELY (size == 0))
2092     return GST_RTSP_OK;
2093
2094   offset = 0;
2095
2096   /* configure timeout if any */
2097   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2098
2099   gst_poll_set_controllable (conn->fdset, TRUE);
2100   gst_poll_fd_ctl_write (conn->fdset, conn->writefd, FALSE);
2101   gst_poll_fd_ctl_read (conn->fdset, conn->readfd, TRUE);
2102
2103   while (TRUE) {
2104     res = read_bytes (conn, data, &offset, size);
2105     if (G_UNLIKELY (res == GST_RTSP_EEOF))
2106       goto eof;
2107     if (G_LIKELY (res == GST_RTSP_OK))
2108       break;
2109     if (G_UNLIKELY (res != GST_RTSP_EINTR))
2110       goto read_error;
2111
2112     do {
2113       retval = gst_poll_wait (conn->fdset, to);
2114     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
2115
2116     /* check for timeout */
2117     if (G_UNLIKELY (retval == 0))
2118       goto select_timeout;
2119
2120     if (G_UNLIKELY (retval == -1)) {
2121       if (errno == EBUSY)
2122         goto stopped;
2123       else
2124         goto select_error;
2125     }
2126
2127     /* could also be an error with write socket */
2128     if (gst_poll_fd_has_error (conn->fdset, conn->writefd))
2129       goto socket_error;
2130
2131     gst_poll_set_controllable (conn->fdset, FALSE);
2132   }
2133   return GST_RTSP_OK;
2134
2135   /* ERRORS */
2136 select_error:
2137   {
2138     return GST_RTSP_ESYS;
2139   }
2140 select_timeout:
2141   {
2142     return GST_RTSP_ETIMEOUT;
2143   }
2144 stopped:
2145   {
2146     return GST_RTSP_EINTR;
2147   }
2148 eof:
2149   {
2150     return GST_RTSP_EEOF;
2151   }
2152 socket_error:
2153   {
2154     res = GST_RTSP_ENET;
2155   }
2156 read_error:
2157   {
2158     return res;
2159   }
2160 }
2161
2162 static GstRTSPMessage *
2163 gen_tunnel_reply (GstRTSPConnection * conn, GstRTSPStatusCode code,
2164     const GstRTSPMessage * request)
2165 {
2166   GstRTSPMessage *msg;
2167   GstRTSPResult res;
2168
2169   if (gst_rtsp_status_as_text (code) == NULL)
2170     code = GST_RTSP_STS_INTERNAL_SERVER_ERROR;
2171
2172   GST_RTSP_CHECK (gst_rtsp_message_new_response (&msg, code, NULL, request),
2173       no_message);
2174
2175   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_SERVER,
2176       "GStreamer RTSP Server");
2177   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONNECTION, "close");
2178   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CACHE_CONTROL, "no-store");
2179   gst_rtsp_message_add_header (msg, GST_RTSP_HDR_PRAGMA, "no-cache");
2180
2181   if (code == GST_RTSP_STS_OK) {
2182     if (conn->ip)
2183       gst_rtsp_message_add_header (msg, GST_RTSP_HDR_X_SERVER_IP_ADDRESS,
2184           conn->ip);
2185     gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CONTENT_TYPE,
2186         "application/x-rtsp-tunnelled");
2187   }
2188
2189   return msg;
2190
2191   /* ERRORS */
2192 no_message:
2193   {
2194     return NULL;
2195   }
2196 }
2197
2198 /**
2199  * gst_rtsp_connection_receive:
2200  * @conn: a #GstRTSPConnection
2201  * @message: the message to read
2202  * @timeout: a timeout value or #NULL
2203  *
2204  * Attempt to read into @message from the connected @conn, blocking up to
2205  * the specified @timeout. @timeout can be #NULL, in which case this function
2206  * might block forever.
2207  * 
2208  * This function can be cancelled with gst_rtsp_connection_flush().
2209  *
2210  * Returns: #GST_RTSP_OK on success.
2211  */
2212 GstRTSPResult
2213 gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message,
2214     GTimeVal * timeout)
2215 {
2216   GstRTSPResult res;
2217   GstRTSPBuilder builder;
2218   gint retval;
2219   GstClockTime to;
2220
2221   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2222   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
2223   g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
2224
2225   /* configure timeout if any */
2226   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2227
2228   gst_poll_set_controllable (conn->fdset, TRUE);
2229   gst_poll_fd_ctl_write (conn->fdset, conn->writefd, FALSE);
2230   gst_poll_fd_ctl_read (conn->fdset, conn->readfd, TRUE);
2231
2232   memset (&builder, 0, sizeof (GstRTSPBuilder));
2233   while (TRUE) {
2234     res = build_next (&builder, message, conn);
2235     if (G_UNLIKELY (res == GST_RTSP_EEOF))
2236       goto eof;
2237     else if (G_LIKELY (res == GST_RTSP_OK)) {
2238       if (!conn->manual_http) {
2239         if (message->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
2240           if (conn->tstate == TUNNEL_STATE_NONE &&
2241               message->type_data.request.method == GST_RTSP_GET) {
2242             GstRTSPMessage *response;
2243
2244             conn->tstate = TUNNEL_STATE_GET;
2245
2246             /* tunnel GET request, we can reply now */
2247             response = gen_tunnel_reply (conn, GST_RTSP_STS_OK, message);
2248             res = gst_rtsp_connection_send (conn, response, timeout);
2249             gst_rtsp_message_free (response);
2250             if (res == GST_RTSP_OK)
2251               res = GST_RTSP_ETGET;
2252             goto cleanup;
2253           } else if (conn->tstate == TUNNEL_STATE_NONE &&
2254               message->type_data.request.method == GST_RTSP_POST) {
2255             conn->tstate = TUNNEL_STATE_POST;
2256
2257             /* tunnel POST request, the caller now has to link the two
2258              * connections. */
2259             res = GST_RTSP_ETPOST;
2260             goto cleanup;
2261           } else {
2262             res = GST_RTSP_EPARSE;
2263             goto cleanup;
2264           }
2265         } else if (message->type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
2266           res = GST_RTSP_EPARSE;
2267           goto cleanup;
2268         }
2269       }
2270
2271       break;
2272     } else if (G_UNLIKELY (res != GST_RTSP_EINTR))
2273       goto read_error;
2274
2275     do {
2276       retval = gst_poll_wait (conn->fdset, to);
2277     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
2278
2279     /* check for timeout */
2280     if (G_UNLIKELY (retval == 0))
2281       goto select_timeout;
2282
2283     if (G_UNLIKELY (retval == -1)) {
2284       if (errno == EBUSY)
2285         goto stopped;
2286       else
2287         goto select_error;
2288     }
2289
2290     /* could also be an error with write socket */
2291     if (gst_poll_fd_has_error (conn->fdset, conn->writefd))
2292       goto socket_error;
2293
2294     /* once we start reading the wait cannot be controlled */
2295     if (builder.state != STATE_START)
2296       gst_poll_set_controllable (conn->fdset, FALSE);
2297   }
2298
2299   /* we have a message here */
2300   build_reset (&builder);
2301
2302   return GST_RTSP_OK;
2303
2304   /* ERRORS */
2305 select_error:
2306   {
2307     res = GST_RTSP_ESYS;
2308     goto cleanup;
2309   }
2310 select_timeout:
2311   {
2312     res = GST_RTSP_ETIMEOUT;
2313     goto cleanup;
2314   }
2315 stopped:
2316   {
2317     res = GST_RTSP_EINTR;
2318     goto cleanup;
2319   }
2320 eof:
2321   {
2322     res = GST_RTSP_EEOF;
2323     goto cleanup;
2324   }
2325 socket_error:
2326   {
2327     res = GST_RTSP_ENET;
2328     goto cleanup;
2329   }
2330 read_error:
2331 cleanup:
2332   {
2333     build_reset (&builder);
2334     gst_rtsp_message_unset (message);
2335     return res;
2336   }
2337 }
2338
2339 /**
2340  * gst_rtsp_connection_close:
2341  * @conn: a #GstRTSPConnection
2342  *
2343  * Close the connected @conn. After this call, the connection is in the same
2344  * state as when it was first created.
2345  * 
2346  * Returns: #GST_RTSP_OK on success.
2347  */
2348 GstRTSPResult
2349 gst_rtsp_connection_close (GstRTSPConnection * conn)
2350 {
2351   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2352
2353   g_free (conn->ip);
2354   conn->ip = NULL;
2355
2356   conn->read_ahead = 0;
2357
2358   g_free (conn->initial_buffer);
2359   conn->initial_buffer = NULL;
2360   conn->initial_buffer_offset = 0;
2361
2362   REMOVE_POLLFD (conn->fdset, &conn->fd0);
2363   REMOVE_POLLFD (conn->fdset, &conn->fd1);
2364   conn->writefd = NULL;
2365   conn->readfd = NULL;
2366   conn->tunneled = FALSE;
2367   conn->tstate = TUNNEL_STATE_NONE;
2368   conn->ctxp = NULL;
2369   g_free (conn->username);
2370   conn->username = NULL;
2371   g_free (conn->passwd);
2372   conn->passwd = NULL;
2373   gst_rtsp_connection_clear_auth_params (conn);
2374   conn->timeout = 60;
2375   conn->cseq = 0;
2376   conn->session_id[0] = '\0';
2377
2378   return GST_RTSP_OK;
2379 }
2380
2381 /**
2382  * gst_rtsp_connection_free:
2383  * @conn: a #GstRTSPConnection
2384  *
2385  * Close and free @conn.
2386  * 
2387  * Returns: #GST_RTSP_OK on success.
2388  */
2389 GstRTSPResult
2390 gst_rtsp_connection_free (GstRTSPConnection * conn)
2391 {
2392   GstRTSPResult res;
2393
2394   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2395
2396   res = gst_rtsp_connection_close (conn);
2397   gst_poll_free (conn->fdset);
2398   g_timer_destroy (conn->timer);
2399   gst_rtsp_url_free (conn->url);
2400   g_free (conn->proxy_host);
2401   g_free (conn);
2402 #ifdef G_OS_WIN32
2403   WSACleanup ();
2404 #endif
2405
2406   return res;
2407 }
2408
2409 /**
2410  * gst_rtsp_connection_poll:
2411  * @conn: a #GstRTSPConnection
2412  * @events: a bitmask of #GstRTSPEvent flags to check
2413  * @revents: location for result flags 
2414  * @timeout: a timeout
2415  *
2416  * Wait up to the specified @timeout for the connection to become available for
2417  * at least one of the operations specified in @events. When the function returns
2418  * with #GST_RTSP_OK, @revents will contain a bitmask of available operations on
2419  * @conn.
2420  *
2421  * @timeout can be #NULL, in which case this function might block forever.
2422  *
2423  * This function can be cancelled with gst_rtsp_connection_flush().
2424  * 
2425  * Returns: #GST_RTSP_OK on success.
2426  *
2427  * Since: 0.10.15
2428  */
2429 GstRTSPResult
2430 gst_rtsp_connection_poll (GstRTSPConnection * conn, GstRTSPEvent events,
2431     GstRTSPEvent * revents, GTimeVal * timeout)
2432 {
2433   GstClockTime to;
2434   gint retval;
2435
2436   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2437   g_return_val_if_fail (events != 0, GST_RTSP_EINVAL);
2438   g_return_val_if_fail (revents != NULL, GST_RTSP_EINVAL);
2439   g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
2440   g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL);
2441
2442   gst_poll_set_controllable (conn->fdset, TRUE);
2443
2444   /* add fd to writer set when asked to */
2445   gst_poll_fd_ctl_write (conn->fdset, conn->writefd,
2446       events & GST_RTSP_EV_WRITE);
2447
2448   /* add fd to reader set when asked to */
2449   gst_poll_fd_ctl_read (conn->fdset, conn->readfd, events & GST_RTSP_EV_READ);
2450
2451   /* configure timeout if any */
2452   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
2453
2454   do {
2455     retval = gst_poll_wait (conn->fdset, to);
2456   } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
2457
2458   if (G_UNLIKELY (retval == 0))
2459     goto select_timeout;
2460
2461   if (G_UNLIKELY (retval == -1)) {
2462     if (errno == EBUSY)
2463       goto stopped;
2464     else
2465       goto select_error;
2466   }
2467
2468   *revents = 0;
2469   if (events & GST_RTSP_EV_READ) {
2470     if (gst_poll_fd_can_read (conn->fdset, conn->readfd))
2471       *revents |= GST_RTSP_EV_READ;
2472   }
2473   if (events & GST_RTSP_EV_WRITE) {
2474     if (gst_poll_fd_can_write (conn->fdset, conn->writefd))
2475       *revents |= GST_RTSP_EV_WRITE;
2476   }
2477   return GST_RTSP_OK;
2478
2479   /* ERRORS */
2480 select_timeout:
2481   {
2482     return GST_RTSP_ETIMEOUT;
2483   }
2484 select_error:
2485   {
2486     return GST_RTSP_ESYS;
2487   }
2488 stopped:
2489   {
2490     return GST_RTSP_EINTR;
2491   }
2492 }
2493
2494 /**
2495  * gst_rtsp_connection_next_timeout:
2496  * @conn: a #GstRTSPConnection
2497  * @timeout: a timeout
2498  *
2499  * Calculate the next timeout for @conn, storing the result in @timeout.
2500  *
2501  * Returns: #GST_RTSP_OK.
2502  */
2503 GstRTSPResult
2504 gst_rtsp_connection_next_timeout (GstRTSPConnection * conn, GTimeVal * timeout)
2505 {
2506   gdouble elapsed;
2507   glong sec;
2508   gulong usec;
2509   gint ctimeout;
2510
2511   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2512   g_return_val_if_fail (timeout != NULL, GST_RTSP_EINVAL);
2513
2514   ctimeout = conn->timeout;
2515   if (ctimeout >= 20) {
2516     /* Because we should act before the timeout we timeout 5
2517      * seconds in advance. */
2518     ctimeout -= 5;
2519   } else if (ctimeout >= 5) {
2520     /* else timeout 20% earlier */
2521     ctimeout -= ctimeout / 5;
2522   } else if (ctimeout >= 1) {
2523     /* else timeout 1 second earlier */
2524     ctimeout -= 1;
2525   }
2526
2527   elapsed = g_timer_elapsed (conn->timer, &usec);
2528   if (elapsed >= ctimeout) {
2529     sec = 0;
2530     usec = 0;
2531   } else {
2532     sec = ctimeout - elapsed;
2533     if (usec <= G_USEC_PER_SEC)
2534       usec = G_USEC_PER_SEC - usec;
2535     else
2536       usec = 0;
2537   }
2538
2539   timeout->tv_sec = sec;
2540   timeout->tv_usec = usec;
2541
2542   return GST_RTSP_OK;
2543 }
2544
2545 /**
2546  * gst_rtsp_connection_reset_timeout:
2547  * @conn: a #GstRTSPConnection
2548  *
2549  * Reset the timeout of @conn.
2550  *
2551  * Returns: #GST_RTSP_OK.
2552  */
2553 GstRTSPResult
2554 gst_rtsp_connection_reset_timeout (GstRTSPConnection * conn)
2555 {
2556   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2557
2558   g_timer_start (conn->timer);
2559
2560   return GST_RTSP_OK;
2561 }
2562
2563 /**
2564  * gst_rtsp_connection_flush:
2565  * @conn: a #GstRTSPConnection
2566  * @flush: start or stop the flush
2567  *
2568  * Start or stop the flushing action on @conn. When flushing, all current
2569  * and future actions on @conn will return #GST_RTSP_EINTR until the connection
2570  * is set to non-flushing mode again.
2571  * 
2572  * Returns: #GST_RTSP_OK.
2573  */
2574 GstRTSPResult
2575 gst_rtsp_connection_flush (GstRTSPConnection * conn, gboolean flush)
2576 {
2577   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2578
2579   gst_poll_set_flushing (conn->fdset, flush);
2580
2581   return GST_RTSP_OK;
2582 }
2583
2584 /**
2585  * gst_rtsp_connection_set_proxy:
2586  * @conn: a #GstRTSPConnection
2587  * @host: the proxy host
2588  * @port: the proxy port
2589  *
2590  * Set the proxy host and port.
2591  * 
2592  * Returns: #GST_RTSP_OK.
2593  *
2594  * Since: 0.10.23
2595  */
2596 GstRTSPResult
2597 gst_rtsp_connection_set_proxy (GstRTSPConnection * conn,
2598     const gchar * host, guint port)
2599 {
2600   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2601
2602   g_free (conn->proxy_host);
2603   conn->proxy_host = g_strdup (host);
2604   conn->proxy_port = port;
2605
2606   return GST_RTSP_OK;
2607 }
2608
2609 /**
2610  * gst_rtsp_connection_set_auth:
2611  * @conn: a #GstRTSPConnection
2612  * @method: authentication method
2613  * @user: the user
2614  * @pass: the password
2615  *
2616  * Configure @conn for authentication mode @method with @user and @pass as the
2617  * user and password respectively.
2618  * 
2619  * Returns: #GST_RTSP_OK.
2620  */
2621 GstRTSPResult
2622 gst_rtsp_connection_set_auth (GstRTSPConnection * conn,
2623     GstRTSPAuthMethod method, const gchar * user, const gchar * pass)
2624 {
2625   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2626
2627   if (method == GST_RTSP_AUTH_DIGEST && ((user == NULL || pass == NULL)
2628           || g_strrstr (user, ":") != NULL))
2629     return GST_RTSP_EINVAL;
2630
2631   /* Make sure the username and passwd are being set for authentication */
2632   if (method == GST_RTSP_AUTH_NONE && (user == NULL || pass == NULL))
2633     return GST_RTSP_EINVAL;
2634
2635   /* ":" chars are not allowed in usernames for basic auth */
2636   if (method == GST_RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL)
2637     return GST_RTSP_EINVAL;
2638
2639   g_free (conn->username);
2640   g_free (conn->passwd);
2641
2642   conn->auth_method = method;
2643   conn->username = g_strdup (user);
2644   conn->passwd = g_strdup (pass);
2645
2646   return GST_RTSP_OK;
2647 }
2648
2649 /**
2650  * str_case_hash:
2651  * @key: ASCII string to hash
2652  *
2653  * Hashes @key in a case-insensitive manner.
2654  *
2655  * Returns: the hash code.
2656  **/
2657 static guint
2658 str_case_hash (gconstpointer key)
2659 {
2660   const char *p = key;
2661   guint h = g_ascii_toupper (*p);
2662
2663   if (h)
2664     for (p += 1; *p != '\0'; p++)
2665       h = (h << 5) - h + g_ascii_toupper (*p);
2666
2667   return h;
2668 }
2669
2670 /**
2671  * str_case_equal:
2672  * @v1: an ASCII string
2673  * @v2: another ASCII string
2674  *
2675  * Compares @v1 and @v2 in a case-insensitive manner
2676  *
2677  * Returns: %TRUE if they are equal (modulo case)
2678  **/
2679 static gboolean
2680 str_case_equal (gconstpointer v1, gconstpointer v2)
2681 {
2682   const char *string1 = v1;
2683   const char *string2 = v2;
2684
2685   return g_ascii_strcasecmp (string1, string2) == 0;
2686 }
2687
2688 /**
2689  * gst_rtsp_connection_set_auth_param:
2690  * @conn: a #GstRTSPConnection
2691  * @param: authentication directive
2692  * @value: value
2693  *
2694  * Setup @conn with authentication directives. This is not necesary for
2695  * methods #GST_RTSP_AUTH_NONE and #GST_RTSP_AUTH_BASIC. For
2696  * #GST_RTSP_AUTH_DIGEST, directives should be taken from the digest challenge
2697  * in the WWW-Authenticate response header and can include realm, domain,
2698  * nonce, opaque, stale, algorithm, qop as per RFC2617.
2699  * 
2700  * Since: 0.10.20
2701  */
2702 void
2703 gst_rtsp_connection_set_auth_param (GstRTSPConnection * conn,
2704     const gchar * param, const gchar * value)
2705 {
2706   g_return_if_fail (conn != NULL);
2707   g_return_if_fail (param != NULL);
2708
2709   if (conn->auth_params == NULL) {
2710     conn->auth_params =
2711         g_hash_table_new_full (str_case_hash, str_case_equal, g_free, g_free);
2712   }
2713   g_hash_table_insert (conn->auth_params, g_strdup (param), g_strdup (value));
2714 }
2715
2716 /**
2717  * gst_rtsp_connection_clear_auth_params:
2718  * @conn: a #GstRTSPConnection
2719  *
2720  * Clear the list of authentication directives stored in @conn.
2721  *
2722  * Since: 0.10.20
2723  */
2724 void
2725 gst_rtsp_connection_clear_auth_params (GstRTSPConnection * conn)
2726 {
2727   g_return_if_fail (conn != NULL);
2728
2729   if (conn->auth_params != NULL) {
2730     g_hash_table_destroy (conn->auth_params);
2731     conn->auth_params = NULL;
2732   }
2733 }
2734
2735 static GstRTSPResult
2736 set_qos_dscp (gint fd, guint qos_dscp)
2737 {
2738   union gst_sockaddr sa;
2739   socklen_t slen = sizeof (sa);
2740   gint af;
2741   gint tos;
2742
2743   if (fd == -1)
2744     return GST_RTSP_OK;
2745
2746   if (getsockname (fd, &sa.sa, &slen) < 0)
2747     goto no_getsockname;
2748
2749   af = sa.sa.sa_family;
2750
2751   /* if this is an IPv4-mapped address then do IPv4 QoS */
2752   if (af == AF_INET6) {
2753     if (IN6_IS_ADDR_V4MAPPED (&sa.sa_in6.sin6_addr))
2754       af = AF_INET;
2755   }
2756
2757   /* extract and shift 6 bits of the DSCP */
2758   tos = (qos_dscp & 0x3f) << 2;
2759
2760   switch (af) {
2761     case AF_INET:
2762       if (SETSOCKOPT (fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0)
2763         goto no_setsockopt;
2764       break;
2765     case AF_INET6:
2766 #ifdef IPV6_TCLASS
2767       if (SETSOCKOPT (fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof (tos)) < 0)
2768         goto no_setsockopt;
2769       break;
2770 #endif
2771     default:
2772       goto wrong_family;
2773   }
2774
2775   return GST_RTSP_OK;
2776
2777   /* ERRORS */
2778 no_getsockname:
2779 no_setsockopt:
2780   {
2781     return GST_RTSP_ESYS;
2782   }
2783
2784 wrong_family:
2785   {
2786     return GST_RTSP_ERROR;
2787   }
2788 }
2789
2790 /**
2791  * gst_rtsp_connection_set_qos_dscp:
2792  * @conn: a #GstRTSPConnection
2793  * @qos_dscp: DSCP value
2794  *
2795  * Configure @conn to use the specified DSCP value.
2796  *
2797  * Returns: #GST_RTSP_OK on success.
2798  *
2799  * Since: 0.10.20
2800  */
2801 GstRTSPResult
2802 gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
2803 {
2804   GstRTSPResult res;
2805
2806   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
2807   g_return_val_if_fail (conn->readfd != NULL, GST_RTSP_EINVAL);
2808   g_return_val_if_fail (conn->writefd != NULL, GST_RTSP_EINVAL);
2809
2810   res = set_qos_dscp (conn->fd0.fd, qos_dscp);
2811   if (res == GST_RTSP_OK)
2812     res = set_qos_dscp (conn->fd1.fd, qos_dscp);
2813
2814   return res;
2815 }
2816
2817
2818 /**
2819  * gst_rtsp_connection_get_url:
2820  * @conn: a #GstRTSPConnection
2821  *
2822  * Retrieve the URL of the other end of @conn.
2823  *
2824  * Returns: The URL. This value remains valid until the
2825  * connection is freed.
2826  *
2827  * Since: 0.10.23
2828  */
2829 GstRTSPUrl *
2830 gst_rtsp_connection_get_url (const GstRTSPConnection * conn)
2831 {
2832   g_return_val_if_fail (conn != NULL, NULL);
2833
2834   return conn->url;
2835 }
2836
2837 /**
2838  * gst_rtsp_connection_get_ip:
2839  * @conn: a #GstRTSPConnection
2840  *
2841  * Retrieve the IP address of the other end of @conn.
2842  *
2843  * Returns: The IP address as a string. this value remains valid until the
2844  * connection is closed.
2845  *
2846  * Since: 0.10.20
2847  */
2848 const gchar *
2849 gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
2850 {
2851   g_return_val_if_fail (conn != NULL, NULL);
2852
2853   return conn->ip;
2854 }
2855
2856 /**
2857  * gst_rtsp_connection_set_ip:
2858  * @conn: a #GstRTSPConnection
2859  * @ip: an ip address
2860  *
2861  * Set the IP address of the server.
2862  *
2863  * Since: 0.10.23
2864  */
2865 void
2866 gst_rtsp_connection_set_ip (GstRTSPConnection * conn, const gchar * ip)
2867 {
2868   g_return_if_fail (conn != NULL);
2869
2870   g_free (conn->ip);
2871   conn->ip = g_strdup (ip);
2872 }
2873
2874 /**
2875  * gst_rtsp_connection_get_readfd:
2876  * @conn: a #GstRTSPConnection
2877  *
2878  * Get the file descriptor for reading.
2879  *
2880  * Returns: the file descriptor used for reading or -1 on error. The file
2881  * descriptor remains valid until the connection is closed.
2882  *
2883  * Since: 0.10.23
2884  */
2885 gint
2886 gst_rtsp_connection_get_readfd (const GstRTSPConnection * conn)
2887 {
2888   g_return_val_if_fail (conn != NULL, -1);
2889   g_return_val_if_fail (conn->readfd != NULL, -1);
2890
2891   return conn->readfd->fd;
2892 }
2893
2894 /**
2895  * gst_rtsp_connection_get_writefd:
2896  * @conn: a #GstRTSPConnection
2897  *
2898  * Get the file descriptor for writing.
2899  *
2900  * Returns: the file descriptor used for writing or -1 on error. The file
2901  * descriptor remains valid until the connection is closed.
2902  *
2903  * Since: 0.10.23
2904  */
2905 gint
2906 gst_rtsp_connection_get_writefd (const GstRTSPConnection * conn)
2907 {
2908   g_return_val_if_fail (conn != NULL, -1);
2909   g_return_val_if_fail (conn->writefd != NULL, -1);
2910
2911   return conn->writefd->fd;
2912 }
2913
2914 /**
2915  * gst_rtsp_connection_set_http_mode:
2916  * @conn: a #GstRTSPConnection
2917  * @enable: %TRUE to enable manual HTTP mode
2918  *
2919  * By setting the HTTP mode to %TRUE the message parsing will support HTTP
2920  * messages in addition to the RTSP messages. It will also disable the
2921  * automatic handling of setting up an HTTP tunnel.
2922  *
2923  * Since: 0.10.25
2924  */
2925 void
2926 gst_rtsp_connection_set_http_mode (GstRTSPConnection * conn, gboolean enable)
2927 {
2928   g_return_if_fail (conn != NULL);
2929
2930   conn->manual_http = enable;
2931 }
2932
2933 /**
2934  * gst_rtsp_connection_set_tunneled:
2935  * @conn: a #GstRTSPConnection
2936  * @tunneled: the new state
2937  *
2938  * Set the HTTP tunneling state of the connection. This must be configured before
2939  * the @conn is connected.
2940  *
2941  * Since: 0.10.23
2942  */
2943 void
2944 gst_rtsp_connection_set_tunneled (GstRTSPConnection * conn, gboolean tunneled)
2945 {
2946   g_return_if_fail (conn != NULL);
2947   g_return_if_fail (conn->readfd == NULL);
2948   g_return_if_fail (conn->writefd == NULL);
2949
2950   conn->tunneled = tunneled;
2951 }
2952
2953 /**
2954  * gst_rtsp_connection_is_tunneled:
2955  * @conn: a #GstRTSPConnection
2956  *
2957  * Get the tunneling state of the connection. 
2958  *
2959  * Returns: if @conn is using HTTP tunneling.
2960  *
2961  * Since: 0.10.23
2962  */
2963 gboolean
2964 gst_rtsp_connection_is_tunneled (const GstRTSPConnection * conn)
2965 {
2966   g_return_val_if_fail (conn != NULL, FALSE);
2967
2968   return conn->tunneled;
2969 }
2970
2971 /**
2972  * gst_rtsp_connection_get_tunnelid:
2973  * @conn: a #GstRTSPConnection
2974  *
2975  * Get the tunnel session id the connection. 
2976  *
2977  * Returns: returns a non-empty string if @conn is being tunneled over HTTP.
2978  *
2979  * Since: 0.10.23
2980  */
2981 const gchar *
2982 gst_rtsp_connection_get_tunnelid (const GstRTSPConnection * conn)
2983 {
2984   g_return_val_if_fail (conn != NULL, NULL);
2985
2986   if (!conn->tunneled)
2987     return NULL;
2988
2989   return conn->tunnelid;
2990 }
2991
2992 /**
2993  * gst_rtsp_connection_do_tunnel:
2994  * @conn: a #GstRTSPConnection
2995  * @conn2: a #GstRTSPConnection or %NULL
2996  *
2997  * If @conn received the first tunnel connection and @conn2 received
2998  * the second tunnel connection, link the two connections together so that
2999  * @conn manages the tunneled connection.
3000  *
3001  * After this call, @conn2 cannot be used anymore and must be freed with
3002  * gst_rtsp_connection_free().
3003  *
3004  * If @conn2 is %NULL then only the base64 decoding context will be setup for
3005  * @conn.
3006  *
3007  * Returns: return GST_RTSP_OK on success.
3008  *
3009  * Since: 0.10.23
3010  */
3011 GstRTSPResult
3012 gst_rtsp_connection_do_tunnel (GstRTSPConnection * conn,
3013     GstRTSPConnection * conn2)
3014 {
3015   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
3016
3017   if (conn2 != NULL) {
3018     g_return_val_if_fail (conn->tstate == TUNNEL_STATE_GET, GST_RTSP_EINVAL);
3019     g_return_val_if_fail (conn2->tstate == TUNNEL_STATE_POST, GST_RTSP_EINVAL);
3020     g_return_val_if_fail (!memcmp (conn2->tunnelid, conn->tunnelid,
3021             TUNNELID_LEN), GST_RTSP_EINVAL);
3022
3023     /* both connections have fd0 as the read/write socket. start by taking the
3024      * socket from conn2 and set it as the socket in conn */
3025     conn->fd1 = conn2->fd0;
3026
3027     /* clean up some of the state of conn2 */
3028     gst_poll_remove_fd (conn2->fdset, &conn2->fd0);
3029     conn2->fd0.fd = -1;
3030     conn2->readfd = conn2->writefd = NULL;
3031
3032     /* We make fd0 the write socket and fd1 the read socket. */
3033     conn->writefd = &conn->fd0;
3034     conn->readfd = &conn->fd1;
3035
3036     conn->tstate = TUNNEL_STATE_COMPLETE;
3037   }
3038
3039   /* we need base64 decoding for the readfd */
3040   conn->ctx.state = 0;
3041   conn->ctx.save = 0;
3042   conn->ctx.cout = 0;
3043   conn->ctx.coutl = 0;
3044   conn->ctxp = &conn->ctx;
3045
3046   return GST_RTSP_OK;
3047 }
3048
3049 #define READ_ERR    (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
3050 #define READ_COND   (G_IO_IN | READ_ERR)
3051 #define WRITE_ERR   (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
3052 #define WRITE_COND  (G_IO_OUT | WRITE_ERR)
3053
3054 typedef struct
3055 {
3056   guint8 *data;
3057   guint size;
3058   guint id;
3059 } GstRTSPRec;
3060
3061 /* async functions */
3062 struct _GstRTSPWatch
3063 {
3064   GSource source;
3065
3066   GstRTSPConnection *conn;
3067
3068   GstRTSPBuilder builder;
3069   GstRTSPMessage message;
3070
3071   GPollFD readfd;
3072   GPollFD writefd;
3073
3074   /* queued message for transmission */
3075   guint id;
3076   GMutex *mutex;
3077   GQueue *messages;
3078   guint8 *write_data;
3079   guint write_off;
3080   guint write_size;
3081   guint write_id;
3082
3083   GstRTSPWatchFuncs funcs;
3084
3085   gpointer user_data;
3086   GDestroyNotify notify;
3087 };
3088
3089 static gboolean
3090 gst_rtsp_source_prepare (GSource * source, gint * timeout)
3091 {
3092   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3093
3094   if (watch->conn->initial_buffer != NULL)
3095     return TRUE;
3096
3097   *timeout = (watch->conn->timeout * 1000);
3098
3099   return FALSE;
3100 }
3101
3102 static gboolean
3103 gst_rtsp_source_check (GSource * source)
3104 {
3105   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3106
3107   if (watch->readfd.revents & READ_COND)
3108     return TRUE;
3109
3110   if (watch->writefd.revents & WRITE_COND)
3111     return TRUE;
3112
3113   return FALSE;
3114 }
3115
3116 static gboolean
3117 gst_rtsp_source_dispatch (GSource * source, GSourceFunc callback G_GNUC_UNUSED,
3118     gpointer user_data G_GNUC_UNUSED)
3119 {
3120   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3121   GstRTSPResult res = GST_RTSP_ERROR;
3122   gboolean keep_running = TRUE;
3123
3124   /* first read as much as we can */
3125   if (watch->readfd.revents & READ_COND || watch->conn->initial_buffer != NULL) {
3126     do {
3127       if (watch->readfd.revents & READ_ERR)
3128         goto read_error;
3129
3130       res = build_next (&watch->builder, &watch->message, watch->conn);
3131       if (res == GST_RTSP_EINTR)
3132         break;
3133       else if (G_UNLIKELY (res == GST_RTSP_EEOF)) {
3134         watch->readfd.events = 0;
3135         watch->readfd.revents = 0;
3136         g_source_remove_poll ((GSource *) watch, &watch->readfd);
3137         /* When we are in tunnelled mode, the read socket can be closed and we
3138          * should be prepared for a new POST method to reopen it */
3139         if (watch->conn->tstate == TUNNEL_STATE_COMPLETE) {
3140           /* remove the read connection for the tunnel */
3141           /* we accept a new POST request */
3142           watch->conn->tstate = TUNNEL_STATE_GET;
3143           /* and signal that we lost our tunnel */
3144           if (watch->funcs.tunnel_lost)
3145             res = watch->funcs.tunnel_lost (watch, watch->user_data);
3146           goto read_done;
3147         } else
3148           goto eof;
3149       } else if (G_LIKELY (res == GST_RTSP_OK)) {
3150         if (!watch->conn->manual_http &&
3151             watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
3152           if (watch->conn->tstate == TUNNEL_STATE_NONE &&
3153               watch->message.type_data.request.method == GST_RTSP_GET) {
3154             GstRTSPMessage *response;
3155             GstRTSPStatusCode code;
3156
3157             watch->conn->tstate = TUNNEL_STATE_GET;
3158
3159             if (watch->funcs.tunnel_start)
3160               code = watch->funcs.tunnel_start (watch, watch->user_data);
3161             else
3162               code = GST_RTSP_STS_OK;
3163
3164             /* queue the response */
3165             response = gen_tunnel_reply (watch->conn, code, &watch->message);
3166             gst_rtsp_watch_send_message (watch, response, NULL);
3167             gst_rtsp_message_free (response);
3168             goto read_done;
3169           } else if (watch->conn->tstate == TUNNEL_STATE_NONE &&
3170               watch->message.type_data.request.method == GST_RTSP_POST) {
3171             watch->conn->tstate = TUNNEL_STATE_POST;
3172
3173             /* in the callback the connection should be tunneled with the
3174              * GET connection */
3175             if (watch->funcs.tunnel_complete)
3176               watch->funcs.tunnel_complete (watch, watch->user_data);
3177             goto read_done;
3178           }
3179         }
3180       }
3181
3182       if (!watch->conn->manual_http) {
3183         /* if manual HTTP support is not enabled, then restore the message to
3184          * what it would have looked like without the support for parsing HTTP
3185          * messages being present */
3186         if (watch->message.type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
3187           watch->message.type = GST_RTSP_MESSAGE_REQUEST;
3188           watch->message.type_data.request.method = GST_RTSP_INVALID;
3189           if (watch->message.type_data.request.version != GST_RTSP_VERSION_1_0)
3190             watch->message.type_data.request.version = GST_RTSP_VERSION_INVALID;
3191           res = GST_RTSP_EPARSE;
3192         } else if (watch->message.type == GST_RTSP_MESSAGE_HTTP_RESPONSE) {
3193           watch->message.type = GST_RTSP_MESSAGE_RESPONSE;
3194           if (watch->message.type_data.response.version != GST_RTSP_VERSION_1_0)
3195             watch->message.type_data.response.version =
3196                 GST_RTSP_VERSION_INVALID;
3197           res = GST_RTSP_EPARSE;
3198         }
3199       }
3200
3201       if (G_LIKELY (res == GST_RTSP_OK)) {
3202         if (watch->funcs.message_received)
3203           watch->funcs.message_received (watch, &watch->message,
3204               watch->user_data);
3205       } else {
3206         goto read_error;
3207       }
3208
3209     read_done:
3210       gst_rtsp_message_unset (&watch->message);
3211       build_reset (&watch->builder);
3212     } while (FALSE);
3213   }
3214
3215   if (watch->writefd.revents & WRITE_COND) {
3216     if (watch->writefd.revents & WRITE_ERR)
3217       goto write_error;
3218
3219     g_mutex_lock (watch->mutex);
3220     do {
3221       if (watch->write_data == NULL) {
3222         GstRTSPRec *rec;
3223
3224         /* get a new message from the queue */
3225         rec = g_queue_pop_tail (watch->messages);
3226         if (rec == NULL)
3227           break;
3228
3229         watch->write_off = 0;
3230         watch->write_data = rec->data;
3231         watch->write_size = rec->size;
3232         watch->write_id = rec->id;
3233
3234         g_slice_free (GstRTSPRec, rec);
3235       }
3236
3237       res = write_bytes (watch->writefd.fd, watch->write_data,
3238           &watch->write_off, watch->write_size);
3239       g_mutex_unlock (watch->mutex);
3240
3241       if (res == GST_RTSP_EINTR)
3242         goto write_blocked;
3243       else if (G_LIKELY (res == GST_RTSP_OK)) {
3244         if (watch->funcs.message_sent)
3245           watch->funcs.message_sent (watch, watch->write_id, watch->user_data);
3246       } else {
3247         goto write_error;
3248       }
3249       g_mutex_lock (watch->mutex);
3250
3251       g_free (watch->write_data);
3252       watch->write_data = NULL;
3253     } while (TRUE);
3254
3255     watch->writefd.events = WRITE_ERR;
3256
3257     g_mutex_unlock (watch->mutex);
3258   }
3259
3260 write_blocked:
3261   return keep_running;
3262
3263   /* ERRORS */
3264 eof:
3265   {
3266     if (watch->funcs.closed)
3267       watch->funcs.closed (watch, watch->user_data);
3268
3269     /* always stop when the readfd returns EOF in non-tunneled mode */
3270     return FALSE;
3271   }
3272 read_error:
3273   {
3274     watch->readfd.events = 0;
3275     watch->readfd.revents = 0;
3276     g_source_remove_poll ((GSource *) watch, &watch->readfd);
3277     keep_running = (watch->writefd.events != 0);
3278
3279     if (keep_running) {
3280       if (watch->funcs.error_full)
3281         GST_RTSP_CHECK (watch->funcs.error_full (watch, res, &watch->message,
3282                 0, watch->user_data), error);
3283       else
3284         goto error;
3285     } else
3286       goto eof;
3287   }
3288 write_error:
3289   {
3290     watch->writefd.events = 0;
3291     watch->writefd.revents = 0;
3292     g_source_remove_poll ((GSource *) watch, &watch->writefd);
3293     keep_running = (watch->readfd.events != 0);
3294
3295     if (keep_running) {
3296       if (watch->funcs.error_full)
3297         GST_RTSP_CHECK (watch->funcs.error_full (watch, res, NULL,
3298                 watch->write_id, watch->user_data), error);
3299       else
3300         goto error;
3301     } else
3302       goto eof;
3303   }
3304 error:
3305   {
3306     if (watch->funcs.error)
3307       watch->funcs.error (watch, res, watch->user_data);
3308
3309     return keep_running;
3310   }
3311 }
3312
3313 static void
3314 gst_rtsp_rec_free (gpointer data)
3315 {
3316   GstRTSPRec *rec = data;
3317
3318   g_free (rec->data);
3319   g_slice_free (GstRTSPRec, rec);
3320 }
3321
3322 static void
3323 gst_rtsp_source_finalize (GSource * source)
3324 {
3325   GstRTSPWatch *watch = (GstRTSPWatch *) source;
3326
3327   build_reset (&watch->builder);
3328   gst_rtsp_message_unset (&watch->message);
3329
3330   g_queue_foreach (watch->messages, (GFunc) gst_rtsp_rec_free, NULL);
3331   g_queue_free (watch->messages);
3332   watch->messages = NULL;
3333   g_free (watch->write_data);
3334
3335   g_mutex_free (watch->mutex);
3336
3337   if (watch->notify)
3338     watch->notify (watch->user_data);
3339 }
3340
3341 static GSourceFuncs gst_rtsp_source_funcs = {
3342   gst_rtsp_source_prepare,
3343   gst_rtsp_source_check,
3344   gst_rtsp_source_dispatch,
3345   gst_rtsp_source_finalize,
3346   NULL,
3347   NULL
3348 };
3349
3350 /**
3351  * gst_rtsp_watch_new:
3352  * @conn: a #GstRTSPConnection
3353  * @funcs: watch functions
3354  * @user_data: user data to pass to @funcs
3355  * @notify: notify when @user_data is not referenced anymore
3356  *
3357  * Create a watch object for @conn. The functions provided in @funcs will be
3358  * called with @user_data when activity happened on the watch.
3359  *
3360  * The new watch is usually created so that it can be attached to a
3361  * maincontext with gst_rtsp_watch_attach(). 
3362  *
3363  * @conn must exist for the entire lifetime of the watch.
3364  *
3365  * Returns: a #GstRTSPWatch that can be used for asynchronous RTSP
3366  * communication. Free with gst_rtsp_watch_unref () after usage.
3367  *
3368  * Since: 0.10.23
3369  */
3370 GstRTSPWatch *
3371 gst_rtsp_watch_new (GstRTSPConnection * conn,
3372     GstRTSPWatchFuncs * funcs, gpointer user_data, GDestroyNotify notify)
3373 {
3374   GstRTSPWatch *result;
3375
3376   g_return_val_if_fail (conn != NULL, NULL);
3377   g_return_val_if_fail (funcs != NULL, NULL);
3378   g_return_val_if_fail (conn->readfd != NULL, NULL);
3379   g_return_val_if_fail (conn->writefd != NULL, NULL);
3380
3381   result = (GstRTSPWatch *) g_source_new (&gst_rtsp_source_funcs,
3382       sizeof (GstRTSPWatch));
3383
3384   result->conn = conn;
3385   result->builder.state = STATE_START;
3386
3387   result->mutex = g_mutex_new ();
3388   result->messages = g_queue_new ();
3389
3390   result->readfd.fd = -1;
3391   result->writefd.fd = -1;
3392
3393   gst_rtsp_watch_reset (result);
3394
3395   result->funcs = *funcs;
3396   result->user_data = user_data;
3397   result->notify = notify;
3398
3399   return result;
3400 }
3401
3402 /**
3403  * gst_rtsp_watch_reset:
3404  * @watch: a #GstRTSPWatch
3405  *
3406  * Reset @watch, this is usually called after gst_rtsp_connection_do_tunnel()
3407  * when the file descriptors of the connection might have changed.
3408  *
3409  * Since: 0.10.23
3410  */
3411 void
3412 gst_rtsp_watch_reset (GstRTSPWatch * watch)
3413 {
3414   if (watch->readfd.fd != -1)
3415     g_source_remove_poll ((GSource *) watch, &watch->readfd);
3416   if (watch->writefd.fd != -1)
3417     g_source_remove_poll ((GSource *) watch, &watch->writefd);
3418
3419   watch->readfd.fd = watch->conn->readfd->fd;
3420   watch->readfd.events = READ_COND;
3421   watch->readfd.revents = 0;
3422
3423   watch->writefd.fd = watch->conn->writefd->fd;
3424   watch->writefd.events = WRITE_ERR;
3425   watch->writefd.revents = 0;
3426
3427   if (watch->readfd.fd != -1)
3428     g_source_add_poll ((GSource *) watch, &watch->readfd);
3429   if (watch->writefd.fd != -1)
3430     g_source_add_poll ((GSource *) watch, &watch->writefd);
3431 }
3432
3433 /**
3434  * gst_rtsp_watch_attach:
3435  * @watch: a #GstRTSPWatch
3436  * @context: a GMainContext (if NULL, the default context will be used)
3437  *
3438  * Adds a #GstRTSPWatch to a context so that it will be executed within that context.
3439  *
3440  * Returns: the ID (greater than 0) for the watch within the GMainContext. 
3441  *
3442  * Since: 0.10.23
3443  */
3444 guint
3445 gst_rtsp_watch_attach (GstRTSPWatch * watch, GMainContext * context)
3446 {
3447   g_return_val_if_fail (watch != NULL, 0);
3448
3449   return g_source_attach ((GSource *) watch, context);
3450 }
3451
3452 /**
3453  * gst_rtsp_watch_unref:
3454  * @watch: a #GstRTSPWatch
3455  *
3456  * Decreases the reference count of @watch by one. If the resulting reference
3457  * count is zero the watch and associated memory will be destroyed.
3458  *
3459  * Since: 0.10.23
3460  */
3461 void
3462 gst_rtsp_watch_unref (GstRTSPWatch * watch)
3463 {
3464   g_return_if_fail (watch != NULL);
3465
3466   g_source_unref ((GSource *) watch);
3467 }
3468
3469 /**
3470  * gst_rtsp_watch_write_data:
3471  * @watch: a #GstRTSPWatch
3472  * @data: the data to queue
3473  * @size: the size of @data
3474  * @id: location for a message ID or %NULL
3475  *
3476  * Write @data using the connection of the @watch. If it cannot be sent
3477  * immediately, it will be queued for transmission in @watch. The contents of
3478  * @message will then be serialized and transmitted when the connection of the
3479  * @watch becomes writable. In case the @message is queued, the ID returned in
3480  * @id will be non-zero and used as the ID argument in the message_sent
3481  * callback.
3482  *
3483  * This function will take ownership of @data and g_free() it after use.
3484  *
3485  * Returns: #GST_RTSP_OK on success.
3486  *
3487  * Since: 0.10.25
3488  */
3489 GstRTSPResult
3490 gst_rtsp_watch_write_data (GstRTSPWatch * watch, const guint8 * data,
3491     guint size, guint * id)
3492 {
3493   GstRTSPResult res;
3494   GstRTSPRec *rec;
3495   guint off = 0;
3496   GMainContext *context = NULL;
3497
3498   g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3499   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
3500   g_return_val_if_fail (size != 0, GST_RTSP_EINVAL);
3501
3502   g_mutex_lock (watch->mutex);
3503
3504   /* try to send the message synchronously first */
3505   if (watch->messages->length == 0 && watch->write_data == NULL) {
3506     res = write_bytes (watch->writefd.fd, data, &off, size);
3507     if (res != GST_RTSP_EINTR) {
3508       if (id != NULL)
3509         *id = 0;
3510       g_free ((gpointer) data);
3511       goto done;
3512     }
3513   }
3514
3515   /* make a record with the data and id for sending async */
3516   rec = g_slice_new (GstRTSPRec);
3517   if (off == 0) {
3518     rec->data = (guint8 *) data;
3519     rec->size = size;
3520   } else {
3521     rec->data = g_memdup (data + off, size - off);
3522     rec->size = size - off;
3523     g_free ((gpointer) data);
3524   }
3525
3526   do {
3527     /* make sure rec->id is never 0 */
3528     rec->id = ++watch->id;
3529   } while (G_UNLIKELY (rec->id == 0));
3530
3531   /* add the record to a queue. FIXME we would like to have an upper limit here */
3532   g_queue_push_head (watch->messages, rec);
3533
3534   /* make sure the main context will now also check for writability on the
3535    * socket */
3536   if (watch->writefd.events != WRITE_COND) {
3537     watch->writefd.events = WRITE_COND;
3538     context = ((GSource *) watch)->context;
3539   }
3540
3541   if (id != NULL)
3542     *id = rec->id;
3543   res = GST_RTSP_OK;
3544
3545 done:
3546   g_mutex_unlock (watch->mutex);
3547
3548   if (context)
3549     g_main_context_wakeup (context);
3550
3551   return res;
3552 }
3553
3554 /**
3555  * gst_rtsp_watch_send_message:
3556  * @watch: a #GstRTSPWatch
3557  * @message: a #GstRTSPMessage
3558  * @id: location for a message ID or %NULL
3559  *
3560  * Send a @message using the connection of the @watch. If it cannot be sent
3561  * immediately, it will be queued for transmission in @watch. The contents of
3562  * @message will then be serialized and transmitted when the connection of the
3563  * @watch becomes writable. In case the @message is queued, the ID returned in
3564  * @id will be non-zero and used as the ID argument in the message_sent
3565  * callback.
3566  *
3567  * Returns: #GST_RTSP_OK on success.
3568  *
3569  * Since: 0.10.25
3570  */
3571 GstRTSPResult
3572 gst_rtsp_watch_send_message (GstRTSPWatch * watch, GstRTSPMessage * message,
3573     guint * id)
3574 {
3575   GString *str;
3576   guint size;
3577
3578   g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3579   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3580
3581   /* make a record with the message as a string and id */
3582   str = message_to_string (watch->conn, message);
3583   size = str->len;
3584   return gst_rtsp_watch_write_data (watch,
3585       (guint8 *) g_string_free (str, FALSE), size, id);
3586 }
3587
3588 /**
3589  * gst_rtsp_watch_queue_data:
3590  * @watch: a #GstRTSPWatch
3591  * @data: the data to queue
3592  * @size: the size of @data
3593  *
3594  * Queue @data for transmission in @watch. It will be transmitted when the
3595  * connection of the @watch becomes writable.
3596  *
3597  * This function will take ownership of @data and g_free() it after use.
3598  *
3599  * The return value of this function will be used as the id argument in the
3600  * message_sent callback.
3601  *
3602  * Deprecated: Use gst_rtsp_watch_write_data()
3603  *
3604  * Returns: an id.
3605  *
3606  * Since: 0.10.24
3607  */
3608 #ifndef GST_REMOVE_DEPRECATED
3609 #ifdef GST_DISABLE_DEPRECATED
3610 guint
3611 gst_rtsp_watch_queue_data (GstRTSPWatch * watch, const guint8 * data,
3612     guint size);
3613 #endif
3614 guint
3615 gst_rtsp_watch_queue_data (GstRTSPWatch * watch, const guint8 * data,
3616     guint size)
3617 {
3618   GstRTSPRec *rec;
3619   GMainContext *context = NULL;
3620
3621   g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3622   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
3623   g_return_val_if_fail (size != 0, GST_RTSP_EINVAL);
3624
3625   g_mutex_lock (watch->mutex);
3626
3627   /* make a record with the data and id */
3628   rec = g_slice_new (GstRTSPRec);
3629   rec->data = (guint8 *) data;
3630   rec->size = size;
3631   do {
3632     /* make sure rec->id is never 0 */
3633     rec->id = ++watch->id;
3634   } while (G_UNLIKELY (rec->id == 0));
3635
3636   /* add the record to a queue. FIXME we would like to have an upper limit here */
3637   g_queue_push_head (watch->messages, rec);
3638
3639   /* make sure the main context will now also check for writability on the
3640    * socket */
3641   if (watch->writefd.events != WRITE_COND) {
3642     watch->writefd.events = WRITE_COND;
3643     context = ((GSource *) watch)->context;
3644   }
3645   g_mutex_unlock (watch->mutex);
3646
3647   if (context)
3648     g_main_context_wakeup (context);
3649
3650   return rec->id;
3651 }
3652 #endif /* GST_REMOVE_DEPRECATED */
3653
3654 /**
3655  * gst_rtsp_watch_queue_message:
3656  * @watch: a #GstRTSPWatch
3657  * @message: a #GstRTSPMessage
3658  *
3659  * Queue a @message for transmission in @watch. The contents of this
3660  * message will be serialized and transmitted when the connection of the
3661  * @watch becomes writable.
3662  *
3663  * The return value of this function will be used as the id argument in the
3664  * message_sent callback.
3665  *
3666  * Deprecated: Use gst_rtsp_watch_send_message()
3667  *
3668  * Returns: an id.
3669  *
3670  * Since: 0.10.23
3671  */
3672 #ifndef GST_REMOVE_DEPRECATED
3673 #ifdef GST_DISABLE_DEPRECATED
3674 guint
3675 gst_rtsp_watch_queue_message (GstRTSPWatch * watch, GstRTSPMessage * message);
3676 #endif
3677 guint
3678 gst_rtsp_watch_queue_message (GstRTSPWatch * watch, GstRTSPMessage * message)
3679 {
3680   GString *str;
3681   guint size;
3682
3683   g_return_val_if_fail (watch != NULL, GST_RTSP_EINVAL);
3684   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
3685
3686   /* make a record with the message as a string and id */
3687   str = message_to_string (watch->conn, message);
3688   size = str->len;
3689   return gst_rtsp_watch_queue_data (watch,
3690       (guint8 *) g_string_free (str, FALSE), size);
3691 }
3692 #endif /* GST_REMOVE_DEPRECATED */