Add new RTSP message method to set header
[platform/upstream/gstreamer.git] / gst-libs / gst / rtsp / gstrtspconnection.c
1 /* GStreamer
2  * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.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  * <refsect2>
49  * <para>
50  * This object manages the RTSP connection to the server. It provides function
51  * to receive and send bytes and messages.
52  * </para>
53  * </refsect2>
54  *  
55  * Last reviewed on 2007-07-24 (0.10.14)
56  */
57
58 #ifdef HAVE_CONFIG_H
59 #  include <config.h>
60 #endif
61
62 #include <stdio.h>
63 #include <errno.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <time.h>
67
68 #ifdef HAVE_UNISTD_H
69 #include <unistd.h>
70 #endif
71
72
73 /* we include this here to get the G_OS_* defines */
74 #include <glib.h>
75 #include <gst/gst.h>
76
77 #ifdef G_OS_WIN32
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 <netinet/in.h>
86 #include <arpa/inet.h>
87 #include <fcntl.h>
88 #endif
89
90 #ifdef HAVE_FIONREAD_IN_SYS_FILIO
91 #include <sys/filio.h>
92 #endif
93
94 #include "gstrtspconnection.h"
95 #include "gstrtspbase64.h"
96 #include "md5.h"
97
98 #ifdef G_OS_WIN32
99 #define FIONREAD_TYPE gulong
100 #define IOCTL_SOCKET ioctlsocket
101 #define READ_SOCKET(fd, buf, len) recv (fd, (char *)buf, len, 0)
102 #define WRITE_SOCKET(fd, buf, len) send (fd, (const char *)buf, len, 0)
103 #define SETSOCKOPT(sock, level, name, val, len) setsockopt (sock, level, name, (const char *)val, len)
104 #define CLOSE_SOCKET(sock) closesocket (sock)
105 #define ERRNO_IS_NOT_EAGAIN (WSAGetLastError () != WSAEWOULDBLOCK)
106 #define ERRNO_IS_NOT_EINTR (WSAGetLastError () != WSAEINTR)
107 /* According to Microsoft's connect() documentation this one returns
108  * WSAEWOULDBLOCK and not WSAEINPROGRESS. */
109 #define ERRNO_IS_NOT_EINPROGRESS (WSAGetLastError () != WSAEWOULDBLOCK)
110 #else
111 #define FIONREAD_TYPE gint
112 #define IOCTL_SOCKET ioctl
113 #define READ_SOCKET(fd, buf, len) read (fd, buf, len)
114 #define WRITE_SOCKET(fd, buf, len) write (fd, buf, len)
115 #define SETSOCKOPT(sock, level, name, val, len) setsockopt (sock, level, name, val, len)
116 #define CLOSE_SOCKET(sock) close (sock)
117 #define ERRNO_IS_NOT_EAGAIN (errno != EAGAIN)
118 #define ERRNO_IS_NOT_EINTR (errno != EINTR)
119 #define ERRNO_IS_NOT_EINPROGRESS (errno != EINPROGRESS)
120 #endif
121
122 #ifdef G_OS_WIN32
123 static int
124 inet_aton (const char *c, struct in_addr *paddr)
125 {
126   /* note that inet_addr is deprecated on unix because
127    * inet_addr returns -1 (INADDR_NONE) for the valid 255.255.255.255
128    * address. */
129   paddr->s_addr = inet_addr (c);
130
131   if (paddr->s_addr == INADDR_NONE)
132     return 0;
133
134   return 1;
135 }
136 #endif
137
138 /**
139  * gst_rtsp_connection_create:
140  * @url: a #GstRTSPUrl 
141  * @conn: a #GstRTSPConnection
142  *
143  * Create a newly allocated #GstRTSPConnection from @url and store it in @conn.
144  * The connection will not yet attempt to connect to @url, use
145  * gst_rtsp_connection_connect().
146  *
147  * Returns: #GST_RTSP_OK when @conn contains a valid connection.
148  */
149 GstRTSPResult
150 gst_rtsp_connection_create (GstRTSPUrl * url, GstRTSPConnection ** conn)
151 {
152   GstRTSPConnection *newconn;
153
154   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
155
156   newconn = g_new0 (GstRTSPConnection, 1);
157
158   if ((newconn->fdset = gst_poll_new (TRUE)) == NULL)
159     goto no_fdset;
160
161   newconn->url = url;
162   newconn->fd.fd = -1;
163   newconn->timer = g_timer_new ();
164
165   newconn->auth_method = GST_RTSP_AUTH_NONE;
166   newconn->username = NULL;
167   newconn->passwd = NULL;
168   newconn->auth_params = NULL;
169
170   *conn = newconn;
171
172   return GST_RTSP_OK;
173
174   /* ERRORS */
175 no_fdset:
176   {
177     g_free (newconn);
178     return GST_RTSP_ESYS;
179   }
180 }
181
182 /**
183  * gst_rtsp_connection_connect:
184  * @conn: a #GstRTSPConnection 
185  * @timeout: a #GTimeVal timeout
186  *
187  * Attempt to connect to the url of @conn made with
188  * gst_rtsp_connection_create(). If @timeout is #NULL this function can block
189  * forever. If @timeout contains a valid timeout, this function will return
190  * #GST_RTSP_ETIMEOUT after the timeout expired.
191  *
192  * This function can be cancelled with gst_rtsp_connection_flush().
193  *
194  * Returns: #GST_RTSP_OK when a connection could be made.
195  */
196 GstRTSPResult
197 gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
198 {
199   gint fd;
200   struct sockaddr_in sa_in;
201   struct hostent *hostinfo;
202   const gchar *ip;
203   struct in_addr addr;
204   gint ret;
205   guint16 port;
206   GstRTSPUrl *url;
207   GstClockTime to;
208   gint retval;
209
210 #ifdef G_OS_WIN32
211   unsigned long flags = 1;
212   struct in_addr *addrp;
213 #else
214   char **addrs;
215   gchar ipbuf[INET_ADDRSTRLEN];
216 #endif /* G_OS_WIN32 */
217
218   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
219   g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
220   g_return_val_if_fail (conn->fd.fd < 0, GST_RTSP_EINVAL);
221
222   url = conn->url;
223
224   /* first check if it already is an IP address */
225   if (inet_aton (url->host, &addr)) {
226     ip = url->host;
227   } else {
228     hostinfo = gethostbyname (url->host);
229     if (!hostinfo)
230       goto not_resolved;        /* h_errno set */
231
232     if (hostinfo->h_addrtype != AF_INET)
233       goto not_ip;              /* host not an IP host */
234 #ifdef G_OS_WIN32
235     addrp = (struct in_addr *) hostinfo->h_addr_list[0];
236     /* this is not threadsafe */
237     ip = inet_ntoa (*addrp);
238 #else
239     addrs = hostinfo->h_addr_list;
240     ip = inet_ntop (AF_INET, (struct in_addr *) addrs[0], ipbuf,
241         sizeof (ipbuf));
242 #endif /* G_OS_WIN32 */
243   }
244
245   /* get the port from the url */
246   gst_rtsp_url_get_port (url, &port);
247
248   memset (&sa_in, 0, sizeof (sa_in));
249   sa_in.sin_family = AF_INET;   /* network socket */
250   sa_in.sin_port = htons (port);        /* on port */
251   sa_in.sin_addr.s_addr = inet_addr (ip);       /* on host ip */
252
253   fd = socket (AF_INET, SOCK_STREAM, 0);
254   if (fd == -1)
255     goto sys_error;
256
257   /* set to non-blocking mode so that we can cancel the connect */
258 #ifndef G_OS_WIN32
259   fcntl (fd, F_SETFL, O_NONBLOCK);
260 #else
261   ioctlsocket (fd, FIONBIO, &flags);
262 #endif /* G_OS_WIN32 */
263
264   /* add the socket to our fdset */
265   conn->fd.fd = fd;
266   gst_poll_add_fd (conn->fdset, &conn->fd);
267
268   /* we are going to connect ASYNC now */
269   ret = connect (fd, (struct sockaddr *) &sa_in, sizeof (sa_in));
270   if (ret == 0)
271     goto done;
272   if (ERRNO_IS_NOT_EINPROGRESS)
273     goto sys_error;
274
275   /* wait for connect to complete up to the specified timeout or until we got
276    * interrupted. */
277   gst_poll_fd_ctl_write (conn->fdset, &conn->fd, TRUE);
278
279   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
280
281   do {
282     retval = gst_poll_wait (conn->fdset, to);
283   } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
284
285   if (retval == 0)
286     goto timeout;
287   else if (retval == -1)
288     goto sys_error;
289
290   /* we can still have an error connecting on windows */
291   if (gst_poll_fd_has_error (conn->fdset, &conn->fd))
292     goto sys_error;
293
294   gst_poll_fd_ignored (conn->fdset, &conn->fd);
295
296 done:
297   conn->ip = g_strdup (ip);
298
299   return GST_RTSP_OK;
300
301 sys_error:
302   {
303     if (conn->fd.fd >= 0) {
304       gst_poll_remove_fd (conn->fdset, &conn->fd);
305       conn->fd.fd = -1;
306     }
307     if (fd >= 0)
308       CLOSE_SOCKET (fd);
309     return GST_RTSP_ESYS;
310   }
311 not_resolved:
312   {
313     return GST_RTSP_ENET;
314   }
315 not_ip:
316   {
317     return GST_RTSP_ENOTIP;
318   }
319 timeout:
320   {
321     if (conn->fd.fd >= 0) {
322       gst_poll_remove_fd (conn->fdset, &conn->fd);
323       conn->fd.fd = -1;
324     }
325     if (fd >= 0)
326       CLOSE_SOCKET (fd);
327     return GST_RTSP_ETIMEOUT;
328   }
329 }
330
331 static void
332 md5_digest_to_hex_string (unsigned char digest[16], char string[33])
333 {
334   static const char hexdigits[] = "0123456789abcdef";
335   int i;
336
337   for (i = 0; i < 16; i++) {
338     string[i * 2] = hexdigits[(digest[i] >> 4) & 0x0f];
339     string[i * 2 + 1] = hexdigits[digest[i] & 0x0f];
340   }
341   string[32] = 0;
342 }
343
344 static void
345 auth_digest_compute_hex_urp (const gchar * username,
346     const gchar * realm, const gchar * password, gchar hex_urp[33])
347 {
348   struct MD5Context md5_context;
349   unsigned char digest[16];
350
351   MD5Init (&md5_context);
352   MD5Update (&md5_context, username, strlen (username));
353   MD5Update (&md5_context, ":", 1);
354   MD5Update (&md5_context, realm, strlen (realm));
355   MD5Update (&md5_context, ":", 1);
356   MD5Update (&md5_context, password, strlen (password));
357   MD5Final (digest, &md5_context);
358   md5_digest_to_hex_string (digest, hex_urp);
359 }
360
361 static void
362 auth_digest_compute_response (const gchar * method,
363     const gchar * uri, const gchar * hex_a1, const gchar * nonce,
364     gchar response[33])
365 {
366   char hex_a2[33];
367   struct MD5Context md5_context;
368   unsigned char digest[16];
369
370   /* compute A2 */
371   MD5Init (&md5_context);
372   MD5Update (&md5_context, method, strlen (method));
373   MD5Update (&md5_context, ":", 1);
374   MD5Update (&md5_context, uri, strlen (uri));
375   MD5Final (digest, &md5_context);
376   md5_digest_to_hex_string (digest, hex_a2);
377
378   /* compute KD */
379   MD5Init (&md5_context);
380   MD5Update (&md5_context, hex_a1, strlen (hex_a1));
381   MD5Update (&md5_context, ":", 1);
382   MD5Update (&md5_context, nonce, strlen (nonce));
383   MD5Update (&md5_context, ":", 1);
384
385   MD5Update (&md5_context, hex_a2, 32);
386   MD5Final (digest, &md5_context);
387   md5_digest_to_hex_string (digest, response);
388 }
389
390 static void
391 add_auth_header (GstRTSPConnection * conn, GstRTSPMessage * message)
392 {
393   switch (conn->auth_method) {
394     case GST_RTSP_AUTH_BASIC:{
395       gchar *user_pass =
396           g_strdup_printf ("%s:%s", conn->username, conn->passwd);
397       gchar *user_pass64 =
398           gst_rtsp_base64_encode (user_pass, strlen (user_pass));
399       gchar *auth_string = g_strdup_printf ("Basic %s", user_pass64);
400
401       gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
402           auth_string);
403
404       g_free (user_pass);
405       g_free (user_pass64);
406       break;
407     }
408     case GST_RTSP_AUTH_DIGEST:{
409       gchar response[33], hex_urp[33];
410       gchar *auth_string, *auth_string2;
411       gchar *realm;
412       gchar *nonce;
413       gchar *opaque;
414       const gchar *uri;
415       const gchar *method;
416
417       /* we need to have some params set */
418       if (conn->auth_params == NULL)
419         break;
420
421       /* we need the realm and nonce */
422       realm = (gchar *) g_hash_table_lookup (conn->auth_params, "realm");
423       nonce = (gchar *) g_hash_table_lookup (conn->auth_params, "nonce");
424       if (realm == NULL || nonce == NULL)
425         break;
426
427       auth_digest_compute_hex_urp (conn->username, realm, conn->passwd,
428           hex_urp);
429
430       method = gst_rtsp_method_as_text (message->type_data.request.method);
431       uri = message->type_data.request.uri;
432
433       /* Assume no qop, algorithm=md5, stale=false */
434       /* For algorithm MD5, a1 = urp. */
435       auth_digest_compute_response (method, uri, hex_urp, nonce, response);
436       auth_string = g_strdup_printf ("Digest username=\"%s\", "
437           "realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
438           conn->username, realm, nonce, uri, response);
439
440       opaque = (gchar *) g_hash_table_lookup (conn->auth_params, "opaque");
441       if (opaque) {
442         auth_string2 = g_strdup_printf ("%s, opaque=\"%s\"", auth_string,
443             opaque);
444         g_free (auth_string);
445         auth_string = auth_string2;
446       }
447       gst_rtsp_message_take_header (message, GST_RTSP_HDR_AUTHORIZATION,
448           auth_string);
449       break;
450     }
451     default:
452       /* Nothing to do */
453       break;
454   }
455 }
456
457 static void
458 add_date_header (GstRTSPMessage * message)
459 {
460   GTimeVal tv;
461   gchar date_string[100];
462   time_t t;
463
464 #ifdef HAVE_GMTIME_R
465   struct tm tm_;
466 #endif
467
468   g_get_current_time (&tv);
469   t = (time_t) tv.tv_sec;
470
471 #ifdef HAVE_GMTIME_R
472   strftime (date_string, sizeof (date_string), "%a, %d %b %Y %H:%M:%S GMT",
473       gmtime_r (&t, &tm_));
474 #else
475   strftime (date_string, sizeof (date_string), "%a, %d %b %Y %H:%M:%S GMT",
476       gmtime (&t));
477 #endif
478
479   gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string);
480 }
481
482 /**
483  * gst_rtsp_connection_write:
484  * @conn: a #GstRTSPConnection
485  * @data: the data to write
486  * @size: the size of @data
487  * @timeout: a timeout value or #NULL
488  *
489  * Attempt to write @size bytes of @data to the connected @conn, blocking up to
490  * the specified @timeout. @timeout can be #NULL, in which case this function
491  * might block forever.
492  * 
493  * This function can be cancelled with gst_rtsp_connection_flush().
494  *
495  * Returns: #GST_RTSP_OK on success.
496  */
497 GstRTSPResult
498 gst_rtsp_connection_write (GstRTSPConnection * conn, const guint8 * data,
499     guint size, GTimeVal * timeout)
500 {
501   guint towrite;
502   gint retval;
503   GstClockTime to;
504
505   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
506   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
507
508   gst_poll_set_controllable (conn->fdset, TRUE);
509   gst_poll_fd_ctl_write (conn->fdset, &conn->fd, TRUE);
510   gst_poll_fd_ctl_read (conn->fdset, &conn->fd, FALSE);
511   /* clear all previous poll results */
512   gst_poll_fd_ignored (conn->fdset, &conn->fd);
513
514   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
515
516   towrite = size;
517
518   while (towrite > 0) {
519     gint written;
520
521     do {
522       retval = gst_poll_wait (conn->fdset, to);
523     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
524
525     if (retval == 0)
526       goto timeout;
527
528     if (retval == -1) {
529       if (errno == EBUSY)
530         goto stopped;
531       else
532         goto select_error;
533     }
534
535     /* now we can write */
536     written = WRITE_SOCKET (conn->fd.fd, data, towrite);
537     if (written < 0) {
538       if (ERRNO_IS_NOT_EAGAIN && ERRNO_IS_NOT_EINTR)
539         goto write_error;
540     } else {
541       towrite -= written;
542       data += written;
543     }
544   }
545   return GST_RTSP_OK;
546
547   /* ERRORS */
548 timeout:
549   {
550     return GST_RTSP_ETIMEOUT;
551   }
552 select_error:
553   {
554     return GST_RTSP_ESYS;
555   }
556 stopped:
557   {
558     return GST_RTSP_EINTR;
559   }
560 write_error:
561   {
562     return GST_RTSP_ESYS;
563   }
564 }
565
566 /**
567  * gst_rtsp_connection_send:
568  * @conn: a #GstRTSPConnection
569  * @message: the message to send
570  * @timeout: a timeout value or #NULL
571  *
572  * Attempt to send @message to the connected @conn, blocking up to
573  * the specified @timeout. @timeout can be #NULL, in which case this function
574  * might block forever.
575  * 
576  * This function can be cancelled with gst_rtsp_connection_flush().
577  *
578  * Returns: #GST_RTSP_OK on success.
579  */
580 GstRTSPResult
581 gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
582     GTimeVal * timeout)
583 {
584   GString *str = NULL;
585   GstRTSPResult res;
586
587 #ifdef G_OS_WIN32
588   WSADATA w;
589   int error;
590 #endif
591
592   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
593   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
594
595 #ifdef G_OS_WIN32
596   error = WSAStartup (0x0202, &w);
597
598   if (error)
599     goto startup_error;
600
601   if (w.wVersion != 0x0202)
602     goto version_error;
603 #endif
604
605   str = g_string_new ("");
606
607   switch (message->type) {
608     case GST_RTSP_MESSAGE_REQUEST:
609       /* create request string, add CSeq */
610       g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
611           "CSeq: %d\r\n",
612           gst_rtsp_method_as_text (message->type_data.request.method),
613           message->type_data.request.uri, conn->cseq++);
614       /* add session id if we have one */
615       if (conn->session_id[0] != '\0') {
616         gst_rtsp_message_add_header (message, GST_RTSP_HDR_SESSION,
617             conn->session_id);
618       }
619       /* add any authentication headers */
620       add_auth_header (conn, message);
621       break;
622     case GST_RTSP_MESSAGE_RESPONSE:
623       /* create response string */
624       g_string_append_printf (str, "RTSP/1.0 %d %s\r\n",
625           message->type_data.response.code, message->type_data.response.reason);
626       break;
627     case GST_RTSP_MESSAGE_DATA:
628     {
629       guint8 data_header[4];
630
631       /* prepare data header */
632       data_header[0] = '$';
633       data_header[1] = message->type_data.data.channel;
634       data_header[2] = (message->body_size >> 8) & 0xff;
635       data_header[3] = message->body_size & 0xff;
636
637       /* create string with header and data */
638       str = g_string_append_len (str, (gchar *) data_header, 4);
639       str =
640           g_string_append_len (str, (gchar *) message->body,
641           message->body_size);
642       break;
643     }
644     default:
645       g_return_val_if_reached (GST_RTSP_EINVAL);
646       break;
647   }
648
649   /* append headers and body */
650   if (message->type != GST_RTSP_MESSAGE_DATA) {
651     /* add date header */
652     add_date_header (message);
653
654     /* append headers */
655     gst_rtsp_message_append_headers (message, str);
656
657     /* append Content-Length and body if needed */
658     if (message->body != NULL && message->body_size > 0) {
659       gchar *len;
660
661       len = g_strdup_printf ("%d", message->body_size);
662       g_string_append_printf (str, "%s: %s\r\n",
663           gst_rtsp_header_as_text (GST_RTSP_HDR_CONTENT_LENGTH), len);
664       g_free (len);
665       /* header ends here */
666       g_string_append (str, "\r\n");
667       str =
668           g_string_append_len (str, (gchar *) message->body,
669           message->body_size);
670     } else {
671       /* just end headers */
672       g_string_append (str, "\r\n");
673     }
674   }
675
676   /* write request */
677   res =
678       gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len, timeout);
679
680   g_string_free (str, TRUE);
681
682   return res;
683
684 #ifdef G_OS_WIN32
685 startup_error:
686   {
687     g_warning ("Error %d on WSAStartup", error);
688     return GST_RTSP_EWSASTART;
689   }
690 version_error:
691   {
692     g_warning ("Windows sockets are not version 0x202 (current 0x%x)",
693         w.wVersion);
694     WSACleanup ();
695     return GST_RTSP_EWSAVERSION;
696   }
697 #endif
698 }
699
700 static GstRTSPResult
701 read_line (gint fd, gchar * buffer, guint size)
702 {
703   guint idx;
704   gchar c;
705   gint r;
706
707   idx = 0;
708   while (TRUE) {
709     r = READ_SOCKET (fd, &c, 1);
710     if (r == 0) {
711       goto eof;
712     } else if (r < 0) {
713       if (ERRNO_IS_NOT_EAGAIN && ERRNO_IS_NOT_EINTR)
714         goto read_error;
715     } else {
716       if (c == '\n')            /* end on \n */
717         break;
718       if (c == '\r')            /* ignore \r */
719         continue;
720
721       if (idx < size - 1)
722         buffer[idx++] = c;
723     }
724   }
725   buffer[idx] = '\0';
726
727   return GST_RTSP_OK;
728
729 eof:
730   {
731     return GST_RTSP_EEOF;
732   }
733 read_error:
734   {
735     return GST_RTSP_ESYS;
736   }
737 }
738
739 static void
740 read_string (gchar * dest, gint size, gchar ** src)
741 {
742   gint idx;
743
744   idx = 0;
745   /* skip spaces */
746   while (g_ascii_isspace (**src))
747     (*src)++;
748
749   while (!g_ascii_isspace (**src) && **src != '\0') {
750     if (idx < size - 1)
751       dest[idx++] = **src;
752     (*src)++;
753   }
754   if (size > 0)
755     dest[idx] = '\0';
756 }
757
758 static void
759 read_key (gchar * dest, gint size, gchar ** src)
760 {
761   gint idx;
762
763   idx = 0;
764   while (**src != ':' && **src != '\0') {
765     if (idx < size - 1)
766       dest[idx++] = **src;
767     (*src)++;
768   }
769   if (size > 0)
770     dest[idx] = '\0';
771 }
772
773 static GstRTSPResult
774 parse_response_status (gchar * buffer, GstRTSPMessage * msg)
775 {
776   GstRTSPResult res;
777   gchar versionstr[20];
778   gchar codestr[4];
779   gint code;
780   gchar *bptr;
781
782   bptr = buffer;
783
784   read_string (versionstr, sizeof (versionstr), &bptr);
785   read_string (codestr, sizeof (codestr), &bptr);
786   code = atoi (codestr);
787
788   while (g_ascii_isspace (*bptr))
789     bptr++;
790
791   if (strcmp (versionstr, "RTSP/1.0") == 0)
792     GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL),
793         parse_error);
794   else if (strncmp (versionstr, "RTSP/", 5) == 0) {
795     GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL),
796         parse_error);
797     msg->type_data.response.version = GST_RTSP_VERSION_INVALID;
798   } else
799     goto parse_error;
800
801   return GST_RTSP_OK;
802
803 parse_error:
804   {
805     return GST_RTSP_EPARSE;
806   }
807 }
808
809 static GstRTSPResult
810 parse_request_line (gchar * buffer, GstRTSPMessage * msg)
811 {
812   GstRTSPResult res = GST_RTSP_OK;
813   gchar versionstr[20];
814   gchar methodstr[20];
815   gchar urlstr[4096];
816   gchar *bptr;
817   GstRTSPMethod method;
818
819   bptr = buffer;
820
821   read_string (methodstr, sizeof (methodstr), &bptr);
822   method = gst_rtsp_find_method (methodstr);
823
824   read_string (urlstr, sizeof (urlstr), &bptr);
825   if (*urlstr == '\0')
826     res = GST_RTSP_EPARSE;
827
828   read_string (versionstr, sizeof (versionstr), &bptr);
829
830   if (*bptr != '\0')
831     res = GST_RTSP_EPARSE;
832
833   if (strcmp (versionstr, "RTSP/1.0") == 0) {
834     if (gst_rtsp_message_init_request (msg, method, urlstr) != GST_RTSP_OK)
835       res = GST_RTSP_EPARSE;
836   } else if (strncmp (versionstr, "RTSP/", 5) == 0) {
837     if (gst_rtsp_message_init_request (msg, method, urlstr) != GST_RTSP_OK)
838       res = GST_RTSP_EPARSE;
839     msg->type_data.request.version = GST_RTSP_VERSION_INVALID;
840   } else {
841     gst_rtsp_message_init_request (msg, method, urlstr);
842     msg->type_data.request.version = GST_RTSP_VERSION_INVALID;
843     res = GST_RTSP_EPARSE;
844   }
845
846   return res;
847 }
848
849 /* parsing lines means reading a Key: Value pair */
850 static GstRTSPResult
851 parse_line (gchar * buffer, GstRTSPMessage * msg)
852 {
853   gchar key[32];
854   gchar *bptr;
855   GstRTSPHeaderField field;
856
857   bptr = buffer;
858
859   /* read key */
860   read_key (key, sizeof (key), &bptr);
861   if (*bptr != ':')
862     goto no_column;
863
864   bptr++;
865
866   field = gst_rtsp_find_header_field (key);
867   if (field != GST_RTSP_HDR_INVALID) {
868     while (g_ascii_isspace (*bptr))
869       bptr++;
870     gst_rtsp_message_add_header (msg, field, bptr);
871   }
872
873   return GST_RTSP_OK;
874
875 no_column:
876   {
877     return GST_RTSP_EPARSE;
878   }
879 }
880
881 /**
882  * gst_rtsp_connection_read_internal:
883  * @conn: a #GstRTSPConnection
884  * @data: the data to read
885  * @size: the size of @data
886  * @timeout: a timeout value or #NULL
887  * @allow_interrupt: can the pending read be interrupted
888  *
889  * Attempt to read @size bytes into @data from the connected @conn, blocking up to
890  * the specified @timeout. @timeout can be #NULL, in which case this function
891  * might block forever.
892  * 
893  * This function can be cancelled with gst_rtsp_connection_flush() only if
894  * @allow_interrupt is set.
895  *
896  * Returns: #GST_RTSP_OK on success.
897  */
898 static GstRTSPResult
899 gst_rtsp_connection_read_internal (GstRTSPConnection * conn, guint8 * data,
900     guint size, GTimeVal * timeout, gboolean allow_interrupt)
901 {
902   guint toread;
903   gint retval;
904   GstClockTime to;
905   FIONREAD_TYPE avail;
906
907   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
908   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
909
910   if (size == 0)
911     return GST_RTSP_OK;
912
913   toread = size;
914
915   /* configure timeout if any */
916   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
917
918   /* if the call fails, just go in the select.. it should not fail. Else if
919    * there is enough data to read, skip the select call al together.*/
920   if (IOCTL_SOCKET (conn->fd.fd, FIONREAD, &avail) < 0)
921     avail = 0;
922   else if (avail >= toread)
923     goto do_read;
924
925   gst_poll_set_controllable (conn->fdset, allow_interrupt);
926   gst_poll_fd_ctl_write (conn->fdset, &conn->fd, FALSE);
927   gst_poll_fd_ctl_read (conn->fdset, &conn->fd, TRUE);
928
929   while (toread > 0) {
930     gint bytes;
931
932     do {
933       retval = gst_poll_wait (conn->fdset, to);
934     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
935
936     if (retval == -1) {
937       if (errno == EBUSY)
938         goto stopped;
939       else
940         goto select_error;
941     }
942
943     /* check for timeout */
944     if (retval == 0)
945       goto select_timeout;
946
947   do_read:
948     /* if we get here there is activity on the real fd since the select
949      * completed and the control socket was not readable. */
950     bytes = READ_SOCKET (conn->fd.fd, data, toread);
951     if (bytes == 0) {
952       goto eof;
953     } else if (bytes < 0) {
954       if (ERRNO_IS_NOT_EAGAIN && ERRNO_IS_NOT_EINTR)
955         goto read_error;
956     } else {
957       toread -= bytes;
958       data += bytes;
959     }
960   }
961   return GST_RTSP_OK;
962
963   /* ERRORS */
964 select_error:
965   {
966     return GST_RTSP_ESYS;
967   }
968 select_timeout:
969   {
970     return GST_RTSP_ETIMEOUT;
971   }
972 stopped:
973   {
974     return GST_RTSP_EINTR;
975   }
976 eof:
977   {
978     return GST_RTSP_EEOF;
979   }
980 read_error:
981   {
982     return GST_RTSP_ESYS;
983   }
984 }
985
986 /**
987  * gst_rtsp_connection_read:
988  * @conn: a #GstRTSPConnection
989  * @data: the data to read
990  * @size: the size of @data
991  * @timeout: a timeout value or #NULL
992  *
993  * Attempt to read @size bytes into @data from the connected @conn, blocking up to
994  * the specified @timeout. @timeout can be #NULL, in which case this function
995  * might block forever.
996  *
997  * This function can be cancelled with gst_rtsp_connection_flush().
998  *
999  * Returns: #GST_RTSP_OK on success.
1000  */
1001 GstRTSPResult
1002 gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size,
1003     GTimeVal * timeout)
1004 {
1005   return gst_rtsp_connection_read_internal (conn, data, size, timeout, TRUE);
1006 }
1007
1008
1009 static GstRTSPResult
1010 read_body (GstRTSPConnection * conn, glong content_length, GstRTSPMessage * msg,
1011     GTimeVal * timeout)
1012 {
1013   guint8 *body;
1014   GstRTSPResult res;
1015
1016   if (content_length <= 0) {
1017     body = NULL;
1018     content_length = 0;
1019     goto done;
1020   }
1021
1022   body = g_malloc (content_length + 1);
1023   body[content_length] = '\0';
1024
1025   GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, body, content_length,
1026           timeout, FALSE), read_error);
1027
1028   content_length += 1;
1029
1030 done:
1031   gst_rtsp_message_take_body (msg, (guint8 *) body, content_length);
1032
1033   return GST_RTSP_OK;
1034
1035   /* ERRORS */
1036 read_error:
1037   {
1038     g_free (body);
1039     return res;
1040   }
1041 }
1042
1043 /**
1044  * gst_rtsp_connection_receive:
1045  * @conn: a #GstRTSPConnection
1046  * @message: the message to read
1047  * @timeout: a timeout value or #NULL
1048  *
1049  * Attempt to read into @message from the connected @conn, blocking up to
1050  * the specified @timeout. @timeout can be #NULL, in which case this function
1051  * might block forever.
1052  * 
1053  * This function can be cancelled with gst_rtsp_connection_flush().
1054  *
1055  * Returns: #GST_RTSP_OK on success.
1056  */
1057 GstRTSPResult
1058 gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message,
1059     GTimeVal * timeout)
1060 {
1061   gchar buffer[4096];
1062   gint line;
1063   glong content_length;
1064   GstRTSPResult res;
1065   gboolean need_body;
1066
1067   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1068   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
1069
1070   line = 0;
1071
1072   need_body = TRUE;
1073
1074   res = GST_RTSP_OK;
1075   /* parse first line and headers */
1076   while (res == GST_RTSP_OK) {
1077     guint8 c;
1078
1079     /* read first character, this identifies data messages */
1080     /* This is the only read() that we allow to be interrupted */
1081     GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, &c, 1, timeout,
1082             TRUE), read_error);
1083
1084     /* check for data packet, first character is $ */
1085     if (c == '$') {
1086       guint16 size;
1087
1088       /* data packets are $<1 byte channel><2 bytes length,BE><data bytes> */
1089
1090       /* read channel, which is the next char */
1091       GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, &c, 1, timeout,
1092               FALSE), read_error);
1093
1094       /* now we create a data message */
1095       gst_rtsp_message_init_data (message, c);
1096
1097       /* next two bytes are the length of the data */
1098       GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn,
1099               (guint8 *) & size, 2, timeout, FALSE), read_error);
1100
1101       size = GUINT16_FROM_BE (size);
1102
1103       /* and read the body */
1104       res = read_body (conn, size, message, timeout);
1105       need_body = FALSE;
1106       break;
1107     } else {
1108       gint offset = 0;
1109
1110       /* we have a regular response */
1111       if (c != '\r') {
1112         buffer[0] = c;
1113         offset = 1;
1114       }
1115       /* should not happen */
1116       if (c == '\n')
1117         break;
1118
1119       /* read lines */
1120       GST_RTSP_CHECK (read_line (conn->fd.fd, buffer + offset,
1121               sizeof (buffer) - offset), read_error);
1122
1123       if (buffer[0] == '\0')
1124         break;
1125
1126       if (line == 0) {
1127         /* first line, check for response status */
1128         if (g_str_has_prefix (buffer, "RTSP")) {
1129           res = parse_response_status (buffer, message);
1130         } else {
1131           res = parse_request_line (buffer, message);
1132         }
1133       } else {
1134         /* else just parse the line */
1135         parse_line (buffer, message);
1136       }
1137     }
1138     line++;
1139   }
1140
1141   /* read the rest of the body if needed */
1142   if (need_body) {
1143     gchar *session_id;
1144     gchar *hdrval;
1145
1146     /* see if there is a Content-Length header */
1147     if (gst_rtsp_message_get_header (message, GST_RTSP_HDR_CONTENT_LENGTH,
1148             &hdrval, 0) == GST_RTSP_OK) {
1149       /* there is, read the body */
1150       content_length = atol (hdrval);
1151       GST_RTSP_CHECK (read_body (conn, content_length, message, timeout),
1152           read_error);
1153     }
1154
1155     /* save session id in the connection for further use */
1156     if (gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
1157             &session_id, 0) == GST_RTSP_OK) {
1158       gint maxlen, i;
1159
1160       /* default session timeout */
1161       conn->timeout = 60;
1162
1163       maxlen = sizeof (conn->session_id) - 1;
1164       /* the sessionid can have attributes marked with ;
1165        * Make sure we strip them */
1166       for (i = 0; session_id[i] != '\0'; i++) {
1167         if (session_id[i] == ';') {
1168           maxlen = i;
1169           /* parse timeout */
1170           do {
1171             i++;
1172           } while (g_ascii_isspace (session_id[i]));
1173           if (g_str_has_prefix (&session_id[i], "timeout=")) {
1174             gint to;
1175
1176             /* if we parsed something valid, configure */
1177             if ((to = atoi (&session_id[i + 9])) > 0)
1178               conn->timeout = to;
1179           }
1180           break;
1181         }
1182       }
1183
1184       /* make sure to not overflow */
1185       strncpy (conn->session_id, session_id, maxlen);
1186       conn->session_id[maxlen] = '\0';
1187     }
1188   }
1189   return res;
1190
1191 read_error:
1192   {
1193     return res;
1194   }
1195 }
1196
1197 /**
1198  * gst_rtsp_connection_close:
1199  * @conn: a #GstRTSPConnection
1200  *
1201  * Close the connected @conn.
1202  * 
1203  * Returns: #GST_RTSP_OK on success.
1204  */
1205 GstRTSPResult
1206 gst_rtsp_connection_close (GstRTSPConnection * conn)
1207 {
1208   gint res;
1209
1210   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1211
1212   g_free (conn->ip);
1213   conn->ip = NULL;
1214
1215   if (conn->fd.fd != -1) {
1216     gst_poll_remove_fd (conn->fdset, &conn->fd);
1217     res = CLOSE_SOCKET (conn->fd.fd);
1218     conn->fd.fd = -1;
1219 #ifdef G_OS_WIN32
1220     WSACleanup ();
1221 #endif
1222     if (res != 0)
1223       goto sys_error;
1224   }
1225
1226   return GST_RTSP_OK;
1227
1228 sys_error:
1229   {
1230     return GST_RTSP_ESYS;
1231   }
1232 }
1233
1234 /**
1235  * gst_rtsp_connection_free:
1236  * @conn: a #GstRTSPConnection
1237  *
1238  * Close and free @conn.
1239  * 
1240  * Returns: #GST_RTSP_OK on success.
1241  */
1242 GstRTSPResult
1243 gst_rtsp_connection_free (GstRTSPConnection * conn)
1244 {
1245   GstRTSPResult res;
1246
1247   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1248
1249   res = gst_rtsp_connection_close (conn);
1250   gst_poll_free (conn->fdset);
1251 #ifdef G_OS_WIN32
1252   WSACleanup ();
1253 #endif
1254   g_timer_destroy (conn->timer);
1255   g_free (conn->username);
1256   g_free (conn->passwd);
1257   gst_rtsp_connection_clear_auth_params (conn);
1258   g_free (conn);
1259
1260   return res;
1261 }
1262
1263 /**
1264  * gst_rtsp_connection_poll:
1265  * @conn: a #GstRTSPConnection
1266  * @events: a bitmask of #GstRTSPEvent flags to check
1267  * @revents: location for result flags 
1268  * @timeout: a timeout
1269  *
1270  * Wait up to the specified @timeout for the connection to become available for
1271  * at least one of the operations specified in @events. When the function returns
1272  * with #GST_RTSP_OK, @revents will contain a bitmask of available operations on
1273  * @conn.
1274  *
1275  * @timeout can be #NULL, in which case this function might block forever.
1276  *
1277  * This function can be cancelled with gst_rtsp_connection_flush().
1278  * 
1279  * Returns: #GST_RTSP_OK on success.
1280  *
1281  * Since: 0.10.15
1282  */
1283 GstRTSPResult
1284 gst_rtsp_connection_poll (GstRTSPConnection * conn, GstRTSPEvent events,
1285     GstRTSPEvent * revents, GTimeVal * timeout)
1286 {
1287   GstClockTime to;
1288   gint retval;
1289
1290   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1291   g_return_val_if_fail (events != 0, GST_RTSP_EINVAL);
1292   g_return_val_if_fail (revents != NULL, GST_RTSP_EINVAL);
1293
1294   gst_poll_set_controllable (conn->fdset, TRUE);
1295
1296   /* add fd to writer set when asked to */
1297   gst_poll_fd_ctl_write (conn->fdset, &conn->fd, events & GST_RTSP_EV_WRITE);
1298
1299   /* add fd to reader set when asked to */
1300   gst_poll_fd_ctl_read (conn->fdset, &conn->fd, events & GST_RTSP_EV_READ);
1301
1302   /* configure timeout if any */
1303   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
1304
1305   do {
1306     retval = gst_poll_wait (conn->fdset, to);
1307   } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
1308
1309   if (retval == 0)
1310     goto select_timeout;
1311
1312   if (retval == -1) {
1313     if (errno == EBUSY)
1314       goto stopped;
1315     else
1316       goto select_error;
1317   }
1318
1319   *revents = 0;
1320   if (events & GST_RTSP_EV_READ) {
1321     if (gst_poll_fd_can_read (conn->fdset, &conn->fd))
1322       *revents |= GST_RTSP_EV_READ;
1323   }
1324   if (events & GST_RTSP_EV_WRITE) {
1325     if (gst_poll_fd_can_write (conn->fdset, &conn->fd))
1326       *revents |= GST_RTSP_EV_WRITE;
1327   }
1328   return GST_RTSP_OK;
1329
1330   /* ERRORS */
1331 select_timeout:
1332   {
1333     return GST_RTSP_ETIMEOUT;
1334   }
1335 select_error:
1336   {
1337     return GST_RTSP_ESYS;
1338   }
1339 stopped:
1340   {
1341     return GST_RTSP_EINTR;
1342   }
1343 }
1344
1345 /**
1346  * gst_rtsp_connection_next_timeout:
1347  * @conn: a #GstRTSPConnection
1348  * @timeout: a timeout
1349  *
1350  * Calculate the next timeout for @conn, storing the result in @timeout.
1351  * 
1352  * Returns: #GST_RTSP_OK.
1353  */
1354 GstRTSPResult
1355 gst_rtsp_connection_next_timeout (GstRTSPConnection * conn, GTimeVal * timeout)
1356 {
1357   gdouble elapsed;
1358   glong sec;
1359   gulong usec;
1360
1361   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1362   g_return_val_if_fail (timeout != NULL, GST_RTSP_EINVAL);
1363
1364   elapsed = g_timer_elapsed (conn->timer, &usec);
1365   if (elapsed >= conn->timeout) {
1366     sec = 0;
1367     usec = 0;
1368   } else {
1369     sec = conn->timeout - elapsed;
1370   }
1371
1372   timeout->tv_sec = sec;
1373   timeout->tv_usec = usec;
1374
1375   return GST_RTSP_OK;
1376 }
1377
1378 /**
1379  * gst_rtsp_connection_reset_timeout:
1380  * @conn: a #GstRTSPConnection
1381  *
1382  * Reset the timeout of @conn.
1383  * 
1384  * Returns: #GST_RTSP_OK.
1385  */
1386 GstRTSPResult
1387 gst_rtsp_connection_reset_timeout (GstRTSPConnection * conn)
1388 {
1389   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1390
1391   g_timer_start (conn->timer);
1392
1393   return GST_RTSP_OK;
1394 }
1395
1396 /**
1397  * gst_rtsp_connection_flush:
1398  * @conn: a #GstRTSPConnection
1399  * @flush: start or stop the flush
1400  *
1401  * Start or stop the flushing action on @conn. When flushing, all current
1402  * and future actions on @conn will return #GST_RTSP_EINTR until the connection
1403  * is set to non-flushing mode again.
1404  * 
1405  * Returns: #GST_RTSP_OK.
1406  */
1407 GstRTSPResult
1408 gst_rtsp_connection_flush (GstRTSPConnection * conn, gboolean flush)
1409 {
1410   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1411
1412   gst_poll_set_flushing (conn->fdset, flush);
1413
1414   return GST_RTSP_OK;
1415 }
1416
1417 /**
1418  * gst_rtsp_connection_set_auth:
1419  * @conn: a #GstRTSPConnection
1420  * @method: authentication method
1421  * @user: the user
1422  * @pass: the password
1423  *
1424  * Configure @conn for authentication mode @method with @user and @pass as the
1425  * user and password respectively.
1426  * 
1427  * Returns: #GST_RTSP_OK.
1428  */
1429 GstRTSPResult
1430 gst_rtsp_connection_set_auth (GstRTSPConnection * conn,
1431     GstRTSPAuthMethod method, const gchar * user, const gchar * pass)
1432 {
1433   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1434
1435   if (method == GST_RTSP_AUTH_DIGEST && ((user == NULL || pass == NULL)
1436           || g_strrstr (user, ":") != NULL))
1437     return GST_RTSP_EINVAL;
1438
1439   /* Make sure the username and passwd are being set for authentication */
1440   if (method == GST_RTSP_AUTH_NONE && (user == NULL || pass == NULL))
1441     return GST_RTSP_EINVAL;
1442
1443   /* ":" chars are not allowed in usernames for basic auth */
1444   if (method == GST_RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL)
1445     return GST_RTSP_EINVAL;
1446
1447   g_free (conn->username);
1448   g_free (conn->passwd);
1449
1450   conn->auth_method = method;
1451   conn->username = g_strdup (user);
1452   conn->passwd = g_strdup (pass);
1453
1454   return GST_RTSP_OK;
1455 }
1456
1457 /**
1458  * str_case_hash:
1459  * @key: ASCII string to hash
1460  *
1461  * Hashes @key in a case-insensitive manner.
1462  *
1463  * Returns: the hash code.
1464  **/
1465 static guint
1466 str_case_hash (gconstpointer key)
1467 {
1468   const char *p = key;
1469   guint h = g_ascii_toupper (*p);
1470
1471   if (h)
1472     for (p += 1; *p != '\0'; p++)
1473       h = (h << 5) - h + g_ascii_toupper (*p);
1474
1475   return h;
1476 }
1477
1478 /**
1479  * str_case_equal:
1480  * @v1: an ASCII string
1481  * @v2: another ASCII string
1482  *
1483  * Compares @v1 and @v2 in a case-insensitive manner
1484  *
1485  * Returns: %TRUE if they are equal (modulo case)
1486  **/
1487 static gboolean
1488 str_case_equal (gconstpointer v1, gconstpointer v2)
1489 {
1490   const char *string1 = v1;
1491   const char *string2 = v2;
1492
1493   return g_ascii_strcasecmp (string1, string2) == 0;
1494 }
1495
1496 /**
1497  * gst_rtsp_connection_set_auth_param:
1498  * @conn: a #GstRTSPConnection
1499  * @param: authentication directive
1500  * @value: value
1501  *
1502  * Setup @conn with authentication directives. This is not necesary for
1503  * methods #GST_RTSP_AUTH_NONE and #GST_RTSP_AUTH_BASIC. For
1504  * #GST_RTSP_AUTH_DIGEST, directives should be taken from the digest challenge
1505  * in the WWW-Authenticate response header and can include realm, domain,
1506  * nonce, opaque, stale, algorithm, qop as per RFC2617.
1507  * 
1508  * Since: 0.10.20
1509  */
1510 void
1511 gst_rtsp_connection_set_auth_param (GstRTSPConnection * conn,
1512     const gchar * param, const gchar * value)
1513 {
1514   g_return_if_fail (conn != NULL);
1515   g_return_if_fail (param != NULL);
1516
1517   if (conn->auth_params == NULL) {
1518     conn->auth_params =
1519         g_hash_table_new_full (str_case_hash, str_case_equal, g_free, g_free);
1520   }
1521   g_hash_table_insert (conn->auth_params, g_strdup (param), g_strdup (value));
1522 }
1523
1524 /**
1525  * gst_rtsp_connection_clear_auth_params:
1526  * @conn: a #GstRTSPConnection
1527  *
1528  * Clear the list of authentication directives stored in @conn.
1529  *
1530  * Since: 0.10.20
1531  */
1532 void
1533 gst_rtsp_connection_clear_auth_params (GstRTSPConnection * conn)
1534 {
1535   g_return_if_fail (conn != NULL);
1536
1537   if (conn->auth_params != NULL) {
1538     g_hash_table_destroy (conn->auth_params);
1539     conn->auth_params = NULL;
1540   }
1541 }
1542
1543 /**
1544  * gst_rtsp_connection_set_qos_dscp:
1545  * @conn: a #GstRTSPConnection
1546  * @qos_dscp: DSCP value
1547  *
1548  * Configure @conn to use the specified DSCP value.
1549  *
1550  * Returns: #GST_RTSP_OK on success.
1551  *
1552  * Since: 0.10.20
1553  */
1554 GstRTSPResult
1555 gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
1556 {
1557   struct sockaddr_storage sa_s;
1558   socklen_t sa_sl = sizeof (sa_s);
1559   gint af;
1560   gint tos;
1561
1562   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1563   g_return_val_if_fail (conn->fd.fd >= 0, GST_RTSP_EINVAL);
1564
1565   if (getsockname (conn->fd.fd, (struct sockaddr *) &sa_s, &sa_sl) < 0)
1566     goto no_getsockname;
1567
1568   af = sa_s.ss_family;
1569
1570   /* if this is an IPv4-mapped address then do IPv4 QoS */
1571   if (af == AF_INET6) {
1572     struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) &sa_s;
1573
1574     if (IN6_IS_ADDR_V4MAPPED (&saddr6->sin6_addr))
1575       af = AF_INET;
1576   }
1577
1578   /* extract and shift 6 bits of the DSCP */
1579   tos = (qos_dscp & 0x3f) << 2;
1580
1581   switch (af) {
1582     case AF_INET:
1583       if (SETSOCKOPT (conn->fd.fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0)
1584         goto no_setsockopt;
1585       break;
1586     case AF_INET6:
1587 #ifdef IPV6_TCLASS
1588       if (SETSOCKOPT (conn->fd.fd, IPPROTO_IPV6, IPV6_TCLASS, &tos,
1589               sizeof (tos)) < 0)
1590         goto no_setsockopt;
1591       break;
1592 #endif
1593     default:
1594       goto wrong_family;
1595   }
1596
1597   return GST_RTSP_OK;
1598
1599   /* ERRORS */
1600 no_getsockname:
1601 no_setsockopt:
1602   {
1603     return GST_RTSP_ESYS;
1604   }
1605
1606 wrong_family:
1607   {
1608     return GST_RTSP_ERROR;
1609   }
1610 }
1611
1612 /**
1613  * gst_rtsp_connection_get_ip:
1614  * @conn: a #GstRTSPConnection
1615  *
1616  * Retrieve the IP address of the other end of @conn.
1617  *
1618  * Returns: The IP address as a string.
1619  *
1620  * Since: 0.10.20
1621  */
1622 const gchar *
1623 gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
1624 {
1625   g_return_val_if_fail (conn != NULL, NULL);
1626
1627   return conn->ip;
1628 }