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