gst-libs/gst/rtsp/gstrtspconnection.c: A successful gst_poll_wait() doesn't always...
[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_add_header (message, GST_RTSP_HDR_AUTHORIZATION,
402           auth_string);
403
404       g_free (user_pass);
405       g_free (user_pass64);
406       g_free (auth_string);
407       break;
408     }
409     case GST_RTSP_AUTH_DIGEST:{
410       gchar response[33], hex_urp[33];
411       gchar *auth_string, *auth_string2;
412       gchar *realm;
413       gchar *nonce;
414       gchar *opaque;
415       const gchar *uri;
416       const gchar *method;
417
418       /* we need to have some params set */
419       if (conn->auth_params == NULL)
420         break;
421
422       /* we need the realm and nonce */
423       realm = (gchar *) g_hash_table_lookup (conn->auth_params, "realm");
424       nonce = (gchar *) g_hash_table_lookup (conn->auth_params, "nonce");
425       if (realm == NULL || nonce == NULL)
426         break;
427
428       auth_digest_compute_hex_urp (conn->username, realm, conn->passwd,
429           hex_urp);
430
431       method = gst_rtsp_method_as_text (message->type_data.request.method);
432       uri = message->type_data.request.uri;
433
434       /* Assume no qop, algorithm=md5, stale=false */
435       /* For algorithm MD5, a1 = urp. */
436       auth_digest_compute_response (method, uri, hex_urp, nonce, response);
437       auth_string = g_strdup_printf ("Digest username=\"%s\", "
438           "realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
439           conn->username, realm, nonce, uri, response);
440
441       opaque = (gchar *) g_hash_table_lookup (conn->auth_params, "opaque");
442       if (opaque) {
443         auth_string2 = g_strdup_printf ("%s, opaque=\"%s\"", auth_string,
444             opaque);
445         g_free (auth_string);
446         auth_string = auth_string2;
447       }
448       gst_rtsp_message_add_header (message, GST_RTSP_HDR_AUTHORIZATION,
449           auth_string);
450       g_free (auth_string);
451       break;
452     }
453     default:
454       /* Nothing to do */
455       break;
456   }
457 }
458
459 static void
460 add_date_header (GstRTSPMessage * message)
461 {
462   GTimeVal tv;
463   gchar date_string[100];
464   time_t t;
465
466 #ifdef HAVE_GMTIME_R
467   struct tm tm_;
468 #endif
469
470   g_get_current_time (&tv);
471   t = (time_t) tv.tv_sec;
472
473 #ifdef HAVE_GMTIME_R
474   strftime (date_string, sizeof (date_string), "%a, %d %b %Y %H:%M:%S GMT",
475       gmtime_r (&t, &tm_));
476 #else
477   strftime (date_string, sizeof (date_string), "%a, %d %b %Y %H:%M:%S GMT",
478       gmtime (&t));
479 #endif
480
481   gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string);
482 }
483
484 /**
485  * gst_rtsp_connection_write:
486  * @conn: a #GstRTSPConnection
487  * @data: the data to write
488  * @size: the size of @data
489  * @timeout: a timeout value or #NULL
490  *
491  * Attempt to write @size bytes of @data to the connected @conn, blocking up to
492  * the specified @timeout. @timeout can be #NULL, in which case this function
493  * might block forever.
494  * 
495  * This function can be cancelled with gst_rtsp_connection_flush().
496  *
497  * Returns: #GST_RTSP_OK on success.
498  */
499 GstRTSPResult
500 gst_rtsp_connection_write (GstRTSPConnection * conn, const guint8 * data,
501     guint size, GTimeVal * timeout)
502 {
503   guint towrite;
504   gint retval;
505   GstClockTime to;
506
507   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
508   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
509
510   gst_poll_set_controllable (conn->fdset, TRUE);
511   gst_poll_fd_ctl_write (conn->fdset, &conn->fd, TRUE);
512   gst_poll_fd_ctl_read (conn->fdset, &conn->fd, FALSE);
513   /* clear all previous poll results */
514   gst_poll_fd_ignored (conn->fdset, &conn->fd);
515
516   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
517
518   towrite = size;
519
520   while (towrite > 0) {
521     gint written;
522
523     do {
524       retval = gst_poll_wait (conn->fdset, to);
525     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
526
527     if (retval == 0)
528       goto timeout;
529
530     if (retval == -1) {
531       if (errno == EBUSY)
532         goto stopped;
533       else
534         goto select_error;
535     }
536
537     /* now we can write */
538     written = WRITE_SOCKET (conn->fd.fd, data, towrite);
539     if (written < 0) {
540       if (ERRNO_IS_NOT_EAGAIN && ERRNO_IS_NOT_EINTR)
541         goto write_error;
542     } else {
543       towrite -= written;
544       data += written;
545     }
546   }
547   return GST_RTSP_OK;
548
549   /* ERRORS */
550 timeout:
551   {
552     return GST_RTSP_ETIMEOUT;
553   }
554 select_error:
555   {
556     return GST_RTSP_ESYS;
557   }
558 stopped:
559   {
560     return GST_RTSP_EINTR;
561   }
562 write_error:
563   {
564     return GST_RTSP_ESYS;
565   }
566 }
567
568 /**
569  * gst_rtsp_connection_send:
570  * @conn: a #GstRTSPConnection
571  * @message: the message to send
572  * @timeout: a timeout value or #NULL
573  *
574  * Attempt to send @message to the connected @conn, blocking up to
575  * the specified @timeout. @timeout can be #NULL, in which case this function
576  * might block forever.
577  * 
578  * This function can be cancelled with gst_rtsp_connection_flush().
579  *
580  * Returns: #GST_RTSP_OK on success.
581  */
582 GstRTSPResult
583 gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
584     GTimeVal * timeout)
585 {
586   GString *str = NULL;
587   GstRTSPResult res;
588
589 #ifdef G_OS_WIN32
590   WSADATA w;
591   int error;
592 #endif
593
594   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
595   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
596
597 #ifdef G_OS_WIN32
598   error = WSAStartup (0x0202, &w);
599
600   if (error)
601     goto startup_error;
602
603   if (w.wVersion != 0x0202)
604     goto version_error;
605 #endif
606
607   str = g_string_new ("");
608
609   switch (message->type) {
610     case GST_RTSP_MESSAGE_REQUEST:
611       /* create request string, add CSeq */
612       g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
613           "CSeq: %d\r\n",
614           gst_rtsp_method_as_text (message->type_data.request.method),
615           message->type_data.request.uri, conn->cseq++);
616       /* add session id if we have one */
617       if (conn->session_id[0] != '\0') {
618         gst_rtsp_message_add_header (message, GST_RTSP_HDR_SESSION,
619             conn->session_id);
620       }
621       /* add any authentication headers */
622       add_auth_header (conn, message);
623       break;
624     case GST_RTSP_MESSAGE_RESPONSE:
625       /* create response string */
626       g_string_append_printf (str, "RTSP/1.0 %d %s\r\n",
627           message->type_data.response.code, message->type_data.response.reason);
628       break;
629     case GST_RTSP_MESSAGE_DATA:
630     {
631       guint8 data_header[4];
632
633       /* prepare data header */
634       data_header[0] = '$';
635       data_header[1] = message->type_data.data.channel;
636       data_header[2] = (message->body_size >> 8) & 0xff;
637       data_header[3] = message->body_size & 0xff;
638
639       /* create string with header and data */
640       str = g_string_append_len (str, (gchar *) data_header, 4);
641       str =
642           g_string_append_len (str, (gchar *) message->body,
643           message->body_size);
644       break;
645     }
646     default:
647       g_return_val_if_reached (GST_RTSP_EINVAL);
648       break;
649   }
650
651   /* append headers and body */
652   if (message->type != GST_RTSP_MESSAGE_DATA) {
653     /* add date header */
654     add_date_header (message);
655
656     /* append headers */
657     gst_rtsp_message_append_headers (message, str);
658
659     /* append Content-Length and body if needed */
660     if (message->body != NULL && message->body_size > 0) {
661       gchar *len;
662
663       len = g_strdup_printf ("%d", message->body_size);
664       g_string_append_printf (str, "%s: %s\r\n",
665           gst_rtsp_header_as_text (GST_RTSP_HDR_CONTENT_LENGTH), len);
666       g_free (len);
667       /* header ends here */
668       g_string_append (str, "\r\n");
669       str =
670           g_string_append_len (str, (gchar *) message->body,
671           message->body_size);
672     } else {
673       /* just end headers */
674       g_string_append (str, "\r\n");
675     }
676   }
677
678   /* write request */
679   res =
680       gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len, timeout);
681
682   g_string_free (str, TRUE);
683
684   return res;
685
686 #ifdef G_OS_WIN32
687 startup_error:
688   {
689     g_warning ("Error %d on WSAStartup", error);
690     return GST_RTSP_EWSASTART;
691   }
692 version_error:
693   {
694     g_warning ("Windows sockets are not version 0x202 (current 0x%x)",
695         w.wVersion);
696     WSACleanup ();
697     return GST_RTSP_EWSAVERSION;
698   }
699 #endif
700 }
701
702 static GstRTSPResult
703 read_line (gint fd, gchar * buffer, guint size)
704 {
705   guint idx;
706   gchar c;
707   gint r;
708
709   idx = 0;
710   while (TRUE) {
711     r = READ_SOCKET (fd, &c, 1);
712     if (r == 0) {
713       goto eof;
714     } else if (r < 0) {
715       if (ERRNO_IS_NOT_EAGAIN && ERRNO_IS_NOT_EINTR)
716         goto read_error;
717     } else {
718       if (c == '\n')            /* end on \n */
719         break;
720       if (c == '\r')            /* ignore \r */
721         continue;
722
723       if (idx < size - 1)
724         buffer[idx++] = c;
725     }
726   }
727   buffer[idx] = '\0';
728
729   return GST_RTSP_OK;
730
731 eof:
732   {
733     return GST_RTSP_EEOF;
734   }
735 read_error:
736   {
737     return GST_RTSP_ESYS;
738   }
739 }
740
741 static void
742 read_string (gchar * dest, gint size, gchar ** src)
743 {
744   gint idx;
745
746   idx = 0;
747   /* skip spaces */
748   while (g_ascii_isspace (**src))
749     (*src)++;
750
751   while (!g_ascii_isspace (**src) && **src != '\0') {
752     if (idx < size - 1)
753       dest[idx++] = **src;
754     (*src)++;
755   }
756   if (size > 0)
757     dest[idx] = '\0';
758 }
759
760 static void
761 read_key (gchar * dest, gint size, gchar ** src)
762 {
763   gint idx;
764
765   idx = 0;
766   while (**src != ':' && **src != '\0') {
767     if (idx < size - 1)
768       dest[idx++] = **src;
769     (*src)++;
770   }
771   if (size > 0)
772     dest[idx] = '\0';
773 }
774
775 static GstRTSPResult
776 parse_response_status (gchar * buffer, GstRTSPMessage * msg)
777 {
778   GstRTSPResult res;
779   gchar versionstr[20];
780   gchar codestr[4];
781   gint code;
782   gchar *bptr;
783
784   bptr = buffer;
785
786   read_string (versionstr, sizeof (versionstr), &bptr);
787   read_string (codestr, sizeof (codestr), &bptr);
788   code = atoi (codestr);
789
790   while (g_ascii_isspace (*bptr))
791     bptr++;
792
793   if (strcmp (versionstr, "RTSP/1.0") == 0)
794     GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL),
795         parse_error);
796   else if (strncmp (versionstr, "RTSP/", 5) == 0) {
797     GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL),
798         parse_error);
799     msg->type_data.response.version = GST_RTSP_VERSION_INVALID;
800   } else
801     goto parse_error;
802
803   return GST_RTSP_OK;
804
805 parse_error:
806   {
807     return GST_RTSP_EPARSE;
808   }
809 }
810
811 static GstRTSPResult
812 parse_request_line (gchar * buffer, GstRTSPMessage * msg)
813 {
814   GstRTSPResult res = GST_RTSP_OK;
815   gchar versionstr[20];
816   gchar methodstr[20];
817   gchar urlstr[4096];
818   gchar *bptr;
819   GstRTSPMethod method;
820
821   bptr = buffer;
822
823   read_string (methodstr, sizeof (methodstr), &bptr);
824   method = gst_rtsp_find_method (methodstr);
825
826   read_string (urlstr, sizeof (urlstr), &bptr);
827   if (*urlstr == '\0')
828     res = GST_RTSP_EPARSE;
829
830   read_string (versionstr, sizeof (versionstr), &bptr);
831
832   if (*bptr != '\0')
833     res = GST_RTSP_EPARSE;
834
835   if (strcmp (versionstr, "RTSP/1.0") == 0) {
836     if (gst_rtsp_message_init_request (msg, method, urlstr) != GST_RTSP_OK)
837       res = GST_RTSP_EPARSE;
838   } else if (strncmp (versionstr, "RTSP/", 5) == 0) {
839     if (gst_rtsp_message_init_request (msg, method, urlstr) != GST_RTSP_OK)
840       res = GST_RTSP_EPARSE;
841     msg->type_data.request.version = GST_RTSP_VERSION_INVALID;
842   } else {
843     gst_rtsp_message_init_request (msg, method, urlstr);
844     msg->type_data.request.version = GST_RTSP_VERSION_INVALID;
845     res = GST_RTSP_EPARSE;
846   }
847
848   return res;
849 }
850
851 /* parsing lines means reading a Key: Value pair */
852 static GstRTSPResult
853 parse_line (gchar * buffer, GstRTSPMessage * msg)
854 {
855   gchar key[32];
856   gchar *bptr;
857   GstRTSPHeaderField field;
858
859   bptr = buffer;
860
861   /* read key */
862   read_key (key, sizeof (key), &bptr);
863   if (*bptr != ':')
864     goto no_column;
865
866   bptr++;
867
868   field = gst_rtsp_find_header_field (key);
869   if (field != GST_RTSP_HDR_INVALID) {
870     while (g_ascii_isspace (*bptr))
871       bptr++;
872     gst_rtsp_message_add_header (msg, field, bptr);
873   }
874
875   return GST_RTSP_OK;
876
877 no_column:
878   {
879     return GST_RTSP_EPARSE;
880   }
881 }
882
883 /**
884  * gst_rtsp_connection_read_internal:
885  * @conn: a #GstRTSPConnection
886  * @data: the data to read
887  * @size: the size of @data
888  * @timeout: a timeout value or #NULL
889  * @allow_interrupt: can the pending read be interrupted
890  *
891  * Attempt to read @size bytes into @data from the connected @conn, blocking up to
892  * the specified @timeout. @timeout can be #NULL, in which case this function
893  * might block forever.
894  * 
895  * This function can be cancelled with gst_rtsp_connection_flush() only if
896  * @allow_interrupt is set.
897  *
898  * Returns: #GST_RTSP_OK on success.
899  */
900 static GstRTSPResult
901 gst_rtsp_connection_read_internal (GstRTSPConnection * conn, guint8 * data,
902     guint size, GTimeVal * timeout, gboolean allow_interrupt)
903 {
904   guint toread;
905   gint retval;
906   GstClockTime to;
907   FIONREAD_TYPE avail;
908
909   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
910   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
911
912   if (size == 0)
913     return GST_RTSP_OK;
914
915   toread = size;
916
917   /* configure timeout if any */
918   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
919
920   /* if the call fails, just go in the select.. it should not fail. Else if
921    * there is enough data to read, skip the select call al together.*/
922   if (IOCTL_SOCKET (conn->fd.fd, FIONREAD, &avail) < 0)
923     avail = 0;
924   else if (avail >= toread)
925     goto do_read;
926
927   gst_poll_set_controllable (conn->fdset, allow_interrupt);
928   gst_poll_fd_ctl_write (conn->fdset, &conn->fd, FALSE);
929   gst_poll_fd_ctl_read (conn->fdset, &conn->fd, TRUE);
930
931   while (toread > 0) {
932     gint bytes;
933
934     do {
935       retval = gst_poll_wait (conn->fdset, to);
936     } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
937
938     if (retval == -1) {
939       if (errno == EBUSY)
940         goto stopped;
941       else
942         goto select_error;
943     }
944
945     /* check for timeout */
946     if (retval == 0)
947       goto select_timeout;
948
949   do_read:
950     /* if we get here there is activity on the real fd since the select
951      * completed and the control socket was not readable. */
952     bytes = READ_SOCKET (conn->fd.fd, data, toread);
953     if (bytes == 0) {
954       goto eof;
955     } else if (bytes < 0) {
956       if (ERRNO_IS_NOT_EAGAIN && ERRNO_IS_NOT_EINTR)
957         goto read_error;
958     } else {
959       toread -= bytes;
960       data += bytes;
961     }
962   }
963   return GST_RTSP_OK;
964
965   /* ERRORS */
966 select_error:
967   {
968     return GST_RTSP_ESYS;
969   }
970 select_timeout:
971   {
972     return GST_RTSP_ETIMEOUT;
973   }
974 stopped:
975   {
976     return GST_RTSP_EINTR;
977   }
978 eof:
979   {
980     return GST_RTSP_EEOF;
981   }
982 read_error:
983   {
984     return GST_RTSP_ESYS;
985   }
986 }
987
988 /**
989  * gst_rtsp_connection_read:
990  * @conn: a #GstRTSPConnection
991  * @data: the data to read
992  * @size: the size of @data
993  * @timeout: a timeout value or #NULL
994  *
995  * Attempt to read @size bytes into @data from the connected @conn, blocking up to
996  * the specified @timeout. @timeout can be #NULL, in which case this function
997  * might block forever.
998  *
999  * This function can be cancelled with gst_rtsp_connection_flush().
1000  *
1001  * Returns: #GST_RTSP_OK on success.
1002  */
1003 GstRTSPResult
1004 gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size,
1005     GTimeVal * timeout)
1006 {
1007   return gst_rtsp_connection_read_internal (conn, data, size, timeout, TRUE);
1008 }
1009
1010
1011 static GstRTSPResult
1012 read_body (GstRTSPConnection * conn, glong content_length, GstRTSPMessage * msg,
1013     GTimeVal * timeout)
1014 {
1015   guint8 *body;
1016   GstRTSPResult res;
1017
1018   if (content_length <= 0) {
1019     body = NULL;
1020     content_length = 0;
1021     goto done;
1022   }
1023
1024   body = g_malloc (content_length + 1);
1025   body[content_length] = '\0';
1026
1027   GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, body, content_length,
1028           timeout, FALSE), read_error);
1029
1030   content_length += 1;
1031
1032 done:
1033   gst_rtsp_message_take_body (msg, (guint8 *) body, content_length);
1034
1035   return GST_RTSP_OK;
1036
1037   /* ERRORS */
1038 read_error:
1039   {
1040     g_free (body);
1041     return res;
1042   }
1043 }
1044
1045 /**
1046  * gst_rtsp_connection_receive:
1047  * @conn: a #GstRTSPConnection
1048  * @message: the message to read
1049  * @timeout: a timeout value or #NULL
1050  *
1051  * Attempt to read into @message from the connected @conn, blocking up to
1052  * the specified @timeout. @timeout can be #NULL, in which case this function
1053  * might block forever.
1054  * 
1055  * This function can be cancelled with gst_rtsp_connection_flush().
1056  *
1057  * Returns: #GST_RTSP_OK on success.
1058  */
1059 GstRTSPResult
1060 gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * message,
1061     GTimeVal * timeout)
1062 {
1063   gchar buffer[4096];
1064   gint line;
1065   glong content_length;
1066   GstRTSPResult res;
1067   gboolean need_body;
1068
1069   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1070   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
1071
1072   line = 0;
1073
1074   need_body = TRUE;
1075
1076   res = GST_RTSP_OK;
1077   /* parse first line and headers */
1078   while (res == GST_RTSP_OK) {
1079     guint8 c;
1080
1081     /* read first character, this identifies data messages */
1082     /* This is the only read() that we allow to be interrupted */
1083     GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, &c, 1, timeout,
1084             TRUE), read_error);
1085
1086     /* check for data packet, first character is $ */
1087     if (c == '$') {
1088       guint16 size;
1089
1090       /* data packets are $<1 byte channel><2 bytes length,BE><data bytes> */
1091
1092       /* read channel, which is the next char */
1093       GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn, &c, 1, timeout,
1094               FALSE), read_error);
1095
1096       /* now we create a data message */
1097       gst_rtsp_message_init_data (message, c);
1098
1099       /* next two bytes are the length of the data */
1100       GST_RTSP_CHECK (gst_rtsp_connection_read_internal (conn,
1101               (guint8 *) & size, 2, timeout, FALSE), read_error);
1102
1103       size = GUINT16_FROM_BE (size);
1104
1105       /* and read the body */
1106       res = read_body (conn, size, message, timeout);
1107       need_body = FALSE;
1108       break;
1109     } else {
1110       gint offset = 0;
1111
1112       /* we have a regular response */
1113       if (c != '\r') {
1114         buffer[0] = c;
1115         offset = 1;
1116       }
1117       /* should not happen */
1118       if (c == '\n')
1119         break;
1120
1121       /* read lines */
1122       GST_RTSP_CHECK (read_line (conn->fd.fd, buffer + offset,
1123               sizeof (buffer) - offset), read_error);
1124
1125       if (buffer[0] == '\0')
1126         break;
1127
1128       if (line == 0) {
1129         /* first line, check for response status */
1130         if (g_str_has_prefix (buffer, "RTSP")) {
1131           res = parse_response_status (buffer, message);
1132         } else {
1133           res = parse_request_line (buffer, message);
1134         }
1135       } else {
1136         /* else just parse the line */
1137         parse_line (buffer, message);
1138       }
1139     }
1140     line++;
1141   }
1142
1143   /* read the rest of the body if needed */
1144   if (need_body) {
1145     gchar *session_id;
1146     gchar *hdrval;
1147
1148     /* see if there is a Content-Length header */
1149     if (gst_rtsp_message_get_header (message, GST_RTSP_HDR_CONTENT_LENGTH,
1150             &hdrval, 0) == GST_RTSP_OK) {
1151       /* there is, read the body */
1152       content_length = atol (hdrval);
1153       GST_RTSP_CHECK (read_body (conn, content_length, message, timeout),
1154           read_error);
1155     }
1156
1157     /* save session id in the connection for further use */
1158     if (gst_rtsp_message_get_header (message, GST_RTSP_HDR_SESSION,
1159             &session_id, 0) == GST_RTSP_OK) {
1160       gint maxlen, i;
1161
1162       /* default session timeout */
1163       conn->timeout = 60;
1164
1165       maxlen = sizeof (conn->session_id) - 1;
1166       /* the sessionid can have attributes marked with ;
1167        * Make sure we strip them */
1168       for (i = 0; session_id[i] != '\0'; i++) {
1169         if (session_id[i] == ';') {
1170           maxlen = i;
1171           /* parse timeout */
1172           do {
1173             i++;
1174           } while (g_ascii_isspace (session_id[i]));
1175           if (g_str_has_prefix (&session_id[i], "timeout=")) {
1176             gint to;
1177
1178             /* if we parsed something valid, configure */
1179             if ((to = atoi (&session_id[i + 9])) > 0)
1180               conn->timeout = to;
1181           }
1182           break;
1183         }
1184       }
1185
1186       /* make sure to not overflow */
1187       strncpy (conn->session_id, session_id, maxlen);
1188       conn->session_id[maxlen] = '\0';
1189     }
1190   }
1191   return res;
1192
1193 read_error:
1194   {
1195     return res;
1196   }
1197 }
1198
1199 /**
1200  * gst_rtsp_connection_close:
1201  * @conn: a #GstRTSPConnection
1202  *
1203  * Close the connected @conn.
1204  * 
1205  * Returns: #GST_RTSP_OK on success.
1206  */
1207 GstRTSPResult
1208 gst_rtsp_connection_close (GstRTSPConnection * conn)
1209 {
1210   gint res;
1211
1212   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1213
1214   g_free (conn->ip);
1215   conn->ip = NULL;
1216
1217   if (conn->fd.fd != -1) {
1218     gst_poll_remove_fd (conn->fdset, &conn->fd);
1219     res = CLOSE_SOCKET (conn->fd.fd);
1220     conn->fd.fd = -1;
1221 #ifdef G_OS_WIN32
1222     WSACleanup ();
1223 #endif
1224     if (res != 0)
1225       goto sys_error;
1226   }
1227
1228   return GST_RTSP_OK;
1229
1230 sys_error:
1231   {
1232     return GST_RTSP_ESYS;
1233   }
1234 }
1235
1236 /**
1237  * gst_rtsp_connection_free:
1238  * @conn: a #GstRTSPConnection
1239  *
1240  * Close and free @conn.
1241  * 
1242  * Returns: #GST_RTSP_OK on success.
1243  */
1244 GstRTSPResult
1245 gst_rtsp_connection_free (GstRTSPConnection * conn)
1246 {
1247   GstRTSPResult res;
1248
1249   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1250
1251   res = gst_rtsp_connection_close (conn);
1252   gst_poll_free (conn->fdset);
1253 #ifdef G_OS_WIN32
1254   WSACleanup ();
1255 #endif
1256   g_timer_destroy (conn->timer);
1257   g_free (conn->username);
1258   g_free (conn->passwd);
1259   gst_rtsp_connection_clear_auth_params (conn);
1260   g_free (conn);
1261
1262   return res;
1263 }
1264
1265 /**
1266  * gst_rtsp_connection_poll:
1267  * @conn: a #GstRTSPConnection
1268  * @events: a bitmask of #GstRTSPEvent flags to check
1269  * @revents: location for result flags 
1270  * @timeout: a timeout
1271  *
1272  * Wait up to the specified @timeout for the connection to become available for
1273  * at least one of the operations specified in @events. When the function returns
1274  * with #GST_RTSP_OK, @revents will contain a bitmask of available operations on
1275  * @conn.
1276  *
1277  * @timeout can be #NULL, in which case this function might block forever.
1278  *
1279  * This function can be cancelled with gst_rtsp_connection_flush().
1280  * 
1281  * Returns: #GST_RTSP_OK on success.
1282  *
1283  * Since: 0.10.15
1284  */
1285 GstRTSPResult
1286 gst_rtsp_connection_poll (GstRTSPConnection * conn, GstRTSPEvent events,
1287     GstRTSPEvent * revents, GTimeVal * timeout)
1288 {
1289   GstClockTime to;
1290   gint retval;
1291
1292   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1293   g_return_val_if_fail (events != 0, GST_RTSP_EINVAL);
1294   g_return_val_if_fail (revents != NULL, GST_RTSP_EINVAL);
1295
1296   gst_poll_set_controllable (conn->fdset, TRUE);
1297
1298   /* add fd to writer set when asked to */
1299   gst_poll_fd_ctl_write (conn->fdset, &conn->fd, events & GST_RTSP_EV_WRITE);
1300
1301   /* add fd to reader set when asked to */
1302   gst_poll_fd_ctl_read (conn->fdset, &conn->fd, events & GST_RTSP_EV_READ);
1303
1304   /* configure timeout if any */
1305   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
1306
1307   do {
1308     retval = gst_poll_wait (conn->fdset, to);
1309   } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
1310
1311   if (retval == 0)
1312     goto select_timeout;
1313
1314   if (retval == -1) {
1315     if (errno == EBUSY)
1316       goto stopped;
1317     else
1318       goto select_error;
1319   }
1320
1321   *revents = 0;
1322   if (events & GST_RTSP_EV_READ) {
1323     if (gst_poll_fd_can_read (conn->fdset, &conn->fd))
1324       *revents |= GST_RTSP_EV_READ;
1325   }
1326   if (events & GST_RTSP_EV_WRITE) {
1327     if (gst_poll_fd_can_write (conn->fdset, &conn->fd))
1328       *revents |= GST_RTSP_EV_WRITE;
1329   }
1330   return GST_RTSP_OK;
1331
1332   /* ERRORS */
1333 select_timeout:
1334   {
1335     return GST_RTSP_ETIMEOUT;
1336   }
1337 select_error:
1338   {
1339     return GST_RTSP_ESYS;
1340   }
1341 stopped:
1342   {
1343     return GST_RTSP_EINTR;
1344   }
1345 }
1346
1347 /**
1348  * gst_rtsp_connection_next_timeout:
1349  * @conn: a #GstRTSPConnection
1350  * @timeout: a timeout
1351  *
1352  * Calculate the next timeout for @conn, storing the result in @timeout.
1353  * 
1354  * Returns: #GST_RTSP_OK.
1355  */
1356 GstRTSPResult
1357 gst_rtsp_connection_next_timeout (GstRTSPConnection * conn, GTimeVal * timeout)
1358 {
1359   gdouble elapsed;
1360   glong sec;
1361   gulong usec;
1362
1363   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1364   g_return_val_if_fail (timeout != NULL, GST_RTSP_EINVAL);
1365
1366   elapsed = g_timer_elapsed (conn->timer, &usec);
1367   if (elapsed >= conn->timeout) {
1368     sec = 0;
1369     usec = 0;
1370   } else {
1371     sec = conn->timeout - elapsed;
1372   }
1373
1374   timeout->tv_sec = sec;
1375   timeout->tv_usec = usec;
1376
1377   return GST_RTSP_OK;
1378 }
1379
1380 /**
1381  * gst_rtsp_connection_reset_timeout:
1382  * @conn: a #GstRTSPConnection
1383  *
1384  * Reset the timeout of @conn.
1385  * 
1386  * Returns: #GST_RTSP_OK.
1387  */
1388 GstRTSPResult
1389 gst_rtsp_connection_reset_timeout (GstRTSPConnection * conn)
1390 {
1391   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1392
1393   g_timer_start (conn->timer);
1394
1395   return GST_RTSP_OK;
1396 }
1397
1398 /**
1399  * gst_rtsp_connection_flush:
1400  * @conn: a #GstRTSPConnection
1401  * @flush: start or stop the flush
1402  *
1403  * Start or stop the flushing action on @conn. When flushing, all current
1404  * and future actions on @conn will return #GST_RTSP_EINTR until the connection
1405  * is set to non-flushing mode again.
1406  * 
1407  * Returns: #GST_RTSP_OK.
1408  */
1409 GstRTSPResult
1410 gst_rtsp_connection_flush (GstRTSPConnection * conn, gboolean flush)
1411 {
1412   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1413
1414   gst_poll_set_flushing (conn->fdset, flush);
1415
1416   return GST_RTSP_OK;
1417 }
1418
1419 /**
1420  * gst_rtsp_connection_set_auth:
1421  * @conn: a #GstRTSPConnection
1422  * @method: authentication method
1423  * @user: the user
1424  * @pass: the password
1425  *
1426  * Configure @conn for authentication mode @method with @user and @pass as the
1427  * user and password respectively.
1428  * 
1429  * Returns: #GST_RTSP_OK.
1430  */
1431 GstRTSPResult
1432 gst_rtsp_connection_set_auth (GstRTSPConnection * conn,
1433     GstRTSPAuthMethod method, const gchar * user, const gchar * pass)
1434 {
1435   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1436
1437   if (method == GST_RTSP_AUTH_DIGEST && ((user == NULL || pass == NULL)
1438           || g_strrstr (user, ":") != NULL))
1439     return GST_RTSP_EINVAL;
1440
1441   /* Make sure the username and passwd are being set for authentication */
1442   if (method == GST_RTSP_AUTH_NONE && (user == NULL || pass == NULL))
1443     return GST_RTSP_EINVAL;
1444
1445   /* ":" chars are not allowed in usernames for basic auth */
1446   if (method == GST_RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL)
1447     return GST_RTSP_EINVAL;
1448
1449   g_free (conn->username);
1450   g_free (conn->passwd);
1451
1452   conn->auth_method = method;
1453   conn->username = g_strdup (user);
1454   conn->passwd = g_strdup (pass);
1455
1456   return GST_RTSP_OK;
1457 }
1458
1459 /**
1460  * str_case_hash:
1461  * @key: ASCII string to hash
1462  *
1463  * Hashes @key in a case-insensitive manner.
1464  *
1465  * Returns: the hash code.
1466  **/
1467 static guint
1468 str_case_hash (gconstpointer key)
1469 {
1470   const char *p = key;
1471   guint h = g_ascii_toupper (*p);
1472
1473   if (h)
1474     for (p += 1; *p != '\0'; p++)
1475       h = (h << 5) - h + g_ascii_toupper (*p);
1476
1477   return h;
1478 }
1479
1480 /**
1481  * str_case_equal:
1482  * @v1: an ASCII string
1483  * @v2: another ASCII string
1484  *
1485  * Compares @v1 and @v2 in a case-insensitive manner
1486  *
1487  * Returns: %TRUE if they are equal (modulo case)
1488  **/
1489 static gboolean
1490 str_case_equal (gconstpointer v1, gconstpointer v2)
1491 {
1492   const char *string1 = v1;
1493   const char *string2 = v2;
1494
1495   return g_ascii_strcasecmp (string1, string2) == 0;
1496 }
1497
1498 /**
1499  * gst_rtsp_connection_set_auth_param:
1500  * @conn: a #GstRTSPConnection
1501  * @param: authentication directive
1502  * @value: value
1503  *
1504  * Setup @conn with authentication directives. This is not necesary for
1505  * methods #GST_RTSP_AUTH_NONE and #GST_RTSP_AUTH_BASIC. For
1506  * #GST_RTSP_AUTH_DIGEST, directives should be taken from the digest challenge
1507  * in the WWW-Authenticate response header and can include realm, domain,
1508  * nonce, opaque, stale, algorithm, qop as per RFC2617.
1509  * 
1510  * Since: 0.10.20
1511  */
1512 void
1513 gst_rtsp_connection_set_auth_param (GstRTSPConnection * conn,
1514     const gchar * param, const gchar * value)
1515 {
1516   g_return_if_fail (conn != NULL);
1517   g_return_if_fail (param != NULL);
1518
1519   if (conn->auth_params == NULL) {
1520     conn->auth_params =
1521         g_hash_table_new_full (str_case_hash, str_case_equal, g_free, g_free);
1522   }
1523   g_hash_table_insert (conn->auth_params, g_strdup (param), g_strdup (value));
1524 }
1525
1526 /**
1527  * gst_rtsp_connection_clear_auth_params:
1528  * @conn: a #GstRTSPConnection
1529  *
1530  * Clear the list of authentication directives stored in @conn.
1531  *
1532  * Since: 0.10.20
1533  */
1534 void
1535 gst_rtsp_connection_clear_auth_params (GstRTSPConnection * conn)
1536 {
1537   g_return_if_fail (conn != NULL);
1538
1539   if (conn->auth_params != NULL) {
1540     g_hash_table_destroy (conn->auth_params);
1541     conn->auth_params = NULL;
1542   }
1543 }
1544
1545 /**
1546  * gst_rtsp_connection_set_qos_dscp:
1547  * @conn: a #GstRTSPConnection
1548  * @qos_dscp: DSCP value
1549  *
1550  * Configure @conn to use the specified DSCP value.
1551  *
1552  * Returns: #GST_RTSP_OK on success.
1553  *
1554  * Since: 0.10.20
1555  */
1556 GstRTSPResult
1557 gst_rtsp_connection_set_qos_dscp (GstRTSPConnection * conn, guint qos_dscp)
1558 {
1559   struct sockaddr_storage sa_s;
1560   socklen_t sa_sl = sizeof (sa_s);
1561   gint af;
1562   gint tos;
1563
1564   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
1565   g_return_val_if_fail (conn->fd.fd >= 0, GST_RTSP_EINVAL);
1566
1567   if (getsockname (conn->fd.fd, (struct sockaddr *) &sa_s, &sa_sl) < 0)
1568     goto no_getsockname;
1569
1570   af = sa_s.ss_family;
1571
1572   /* if this is an IPv4-mapped address then do IPv4 QoS */
1573   if (af == AF_INET6) {
1574     struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) &sa_s;
1575
1576     if (IN6_IS_ADDR_V4MAPPED (&saddr6->sin6_addr))
1577       af = AF_INET;
1578   }
1579
1580   /* extract and shift 6 bits of the DSCP */
1581   tos = (qos_dscp & 0x3f) << 2;
1582
1583   switch (af) {
1584     case AF_INET:
1585       if (SETSOCKOPT (conn->fd.fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0)
1586         goto no_setsockopt;
1587       break;
1588     case AF_INET6:
1589 #ifdef IPV6_TCLASS
1590       if (SETSOCKOPT (conn->fd.fd, IPPROTO_IPV6, IPV6_TCLASS, &tos,
1591               sizeof (tos)) < 0)
1592         goto no_setsockopt;
1593       break;
1594 #endif
1595     default:
1596       goto wrong_family;
1597   }
1598
1599   return GST_RTSP_OK;
1600
1601   /* ERRORS */
1602 no_getsockname:
1603 no_setsockopt:
1604   {
1605     return GST_RTSP_ESYS;
1606   }
1607
1608 wrong_family:
1609   {
1610     return GST_RTSP_ERROR;
1611   }
1612 }
1613
1614 /**
1615  * gst_rtsp_connection_get_ip:
1616  * @conn: a #GstRTSPConnection
1617  *
1618  * Retrieve the IP address of the other end of @conn.
1619  *
1620  * Returns: The IP address as a string.
1621  *
1622  * Since: 0.10.20
1623  */
1624 const gchar *
1625 gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
1626 {
1627   g_return_val_if_fail (conn != NULL, NULL);
1628
1629   return conn->ip;
1630 }