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