[vlc-devel] [PATCH] access/http: Improved cookie handling

Antti Ajanki antti.ajanki at iki.fi
Thu Jul 24 10:16:56 CEST 2014


* Moved cookie code to a separate file.
* Implemented domain and path matching algorithms from RFC 6265.
* Generates a single Cookie header consisting of a concatenation of all
   matching cookie values.
* Sends Secure cookies only on https streams.
* Introduces vlc_ascii_strcase(n)cmp functions for comparing strings
   independently of the locale
---
  include/vlc_strings.h        |   26 +++
  modules/access/Makefile.am   |    2 +-
  modules/access/http.c        |  169 ++-----------------
  modules/access/httpcookies.c |  380 
++++++++++++++++++++++++++++++++++++++++++
  modules/access/httpcookies.h |   63 +++++++
  src/libvlccore.sym           |    2 +
  src/text/strings.c           |   38 +++++
  7 files changed, 527 insertions(+), 153 deletions(-)
  create mode 100644 modules/access/httpcookies.c
  create mode 100644 modules/access/httpcookies.h

diff --git a/include/vlc_strings.h b/include/vlc_strings.h
index 3ce4884..c2b2d8e 100644
--- a/include/vlc_strings.h
+++ b/include/vlc_strings.h
@@ -34,6 +34,32 @@
   * @{
   */

+static inline int vlc_ascii_toupper( int c )
+{
+    if ( c >= 'a' && c <= 'z' )
+        return c + ( 'A' - 'a' );
+    else
+        return c;
+}
+
+static inline int vlc_ascii_tolower( int c )
+{
+    if ( c >= 'A' && c <= 'Z' )
+        return c + ( 'a' - 'A' );
+    else
+        return c;
+}
+
+/**
+ * Compare two ASCII strings ignoring case.
+ *
+ * The result is independent of the locale. If there are non-ASCII
+ * characters in the strings, their cases are NOT ignored in the
+ * comparison.
+ */
+VLC_API int vlc_ascii_strcasecmp( const char *psz1, const char *psz2 );
+VLC_API int vlc_ascii_strncasecmp( const char *psz1, const char *psz2, 
size_t n );
+
  VLC_API void resolve_xml_special_chars( char *psz_value );
  VLC_API char * convert_xml_special_chars( const char *psz_content );

diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
index 1094192..9116ee1 100644
--- a/modules/access/Makefile.am
+++ b/modules/access/Makefile.am
@@ -350,7 +350,7 @@ libftp_plugin_la_SOURCES = access/ftp.c
  libftp_plugin_la_LIBADD = $(SOCKET_LIBS)
  access_LTLIBRARIES += libftp_plugin.la

-libhttp_plugin_la_SOURCES = access/http.c
+libhttp_plugin_la_SOURCES = access/http.c access/httpcookies.h 
access/httpcookies.c
  libhttp_plugin_la_LIBADD = $(SOCKET_LIBS)
  if HAVE_ZLIB
  libhttp_plugin_la_LIBADD += -lz
diff --git a/modules/access/http.c b/modules/access/http.c
index 78a186c..8394f82 100644
--- a/modules/access/http.c
+++ b/modules/access/http.c
@@ -47,6 +47,7 @@
  #include <vlc_input.h>
  #include <vlc_md5.h>
  #include <vlc_http.h>
+#include "httpcookies.h"

  #ifdef HAVE_ZLIB_H
  #   include <zlib.h>
@@ -185,12 +186,12 @@ struct access_sys_t
      bool b_persist;
      bool b_has_size;

-    vlc_array_t * cookies;
+    http_cookie_jar_t * cookies;
  };

  /* */
  static int OpenWithCookies( vlc_object_t *p_this, const char *psz_access,
-                            unsigned i_redirect, vlc_array_t *cookies );
+                            unsigned i_redirect, http_cookie_jar_t 
*cookies );

  /* */
  static ssize_t Read( access_t *, uint8_t *, size_t );
@@ -203,12 +204,6 @@ static int Connect( access_t *, uint64_t );
  static int Request( access_t *p_access, uint64_t i_tell );
  static void Disconnect( access_t * );

-/* Small Cookie utilities. Cookies support is partial. */
-static char * cookie_get_content( const char * cookie );
-static char * cookie_get_domain( const char * cookie );
-static char * cookie_get_name( const char * cookie );
-static void cookie_append( vlc_array_t * cookies, char * cookie );
-

  static void AuthReply( access_t *p_acces, const char *psz_prefix,
                         vlc_url_t *p_url, http_auth_t *p_auth );
@@ -234,7 +229,7 @@ static int Open( vlc_object_t *p_this )
   * @return vlc error codes
   */
  static int OpenWithCookies( vlc_object_t *p_this, const char *psz_access,
-                            unsigned i_redirect, vlc_array_t *cookies )
+                            unsigned i_redirect, http_cookie_jar_t 
*cookies )
  {
      access_t     *p_access = (access_t*)p_this;
      access_sys_t *p_sys;
@@ -284,7 +279,7 @@ static int OpenWithCookies( vlc_object_t *p_this, 
const char *psz_access,

      /* Only forward an store cookies if the corresponding option is 
activated */
      if( var_CreateGetBool( p_access, "http-forward-cookies" ) )
-        p_sys->cookies = (cookies != NULL) ? cookies : vlc_array_new();
+        p_sys->cookies = (cookies != NULL) ? cookies : http_cookies_new();
      else
          p_sys->cookies = NULL;

@@ -602,13 +597,7 @@ error:
      Disconnect( p_access );
      vlc_tls_Delete( p_sys->p_creds );

-    if( p_sys->cookies )
-    {
-        int i;
-        for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
-            free(vlc_array_item_at_index( p_sys->cookies, i ));
-        vlc_array_destroy( p_sys->cookies );
-    }
+    http_cookies_destroy( p_sys->cookies );

  #ifdef HAVE_ZLIB_H
      inflateEnd( &p_sys->inflate.stream );
@@ -644,13 +633,7 @@ static void Close( vlc_object_t *p_this )
      Disconnect( p_access );
      vlc_tls_Delete( p_sys->p_creds );

-    if( p_sys->cookies )
-    {
-        int i;
-        for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
-            free(vlc_array_item_at_index( p_sys->cookies, i ));
-        vlc_array_destroy( p_sys->cookies );
-    }
+    http_cookies_destroy( p_sys->cookies );

  #ifdef HAVE_ZLIB_H
      inflateEnd( &p_sys->inflate.stream );
@@ -1188,26 +1171,13 @@ static int Request( access_t *p_access, uint64_t 
i_tell )
      /* Cookies */
      if( p_sys->cookies )
      {
-        int i;
-        for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
+        char * psz_cookiestring = http_cookies_for_url( p_sys->cookies, 
&p_sys->url, VLC_OBJECT(p_access) );
+        if ( psz_cookiestring )
          {
-            const char * cookie = vlc_array_item_at_index( 
p_sys->cookies, i );
-            char * psz_cookie_content = cookie_get_content( cookie );
-            char * psz_cookie_domain = cookie_get_domain( cookie );
-
-            assert( psz_cookie_content );
-
-            /* FIXME: This is clearly not conforming to the rfc */
-            bool is_in_right_domain = (!psz_cookie_domain || strstr( 
p_sys->url.psz_host, psz_cookie_domain ));
-
-            if( is_in_right_domain )
-            {
-                msg_Dbg( p_access, "Sending Cookie %s", 
psz_cookie_content );
-                if( net_Printf( p_access, p_sys->fd, pvs, "Cookie: 
%s\r\n", psz_cookie_content ) < 0 )
-                    msg_Err( p_access, "failed to send Cookie" );
-            }
-            free( psz_cookie_content );
-            free( psz_cookie_domain );
+            msg_Dbg( p_access, "Sending Cookie %s", psz_cookiestring );
+            if( net_Printf( p_access, p_sys->fd, pvs, "Cookie: %s\r\n", 
psz_cookiestring ) < 0 )
+                msg_Err( p_access, "failed to send Cookie" );
+            free( psz_cookiestring );
          }
      }

@@ -1504,8 +1474,10 @@ static int Request( access_t *p_access, uint64_t 
i_tell )
          {
              if( p_sys->cookies )
              {
-                msg_Dbg( p_access, "Accepting Cookie: %s", p );
-                cookie_append( p_sys->cookies, strdup(p) );
+                if ( http_cookies_append( p_sys->cookies, p, 
&p_sys->url ) )
+                    msg_Dbg( p_access, "Accepting Cookie: %s", p );
+                else
+                    msg_Dbg( p_access, "Rejected Cookie: %s", p );
              }
              else
                  msg_Dbg( p_access, "We have a Cookie we won't 
remember: %s", p );
@@ -1577,113 +1549,6 @@ static void Disconnect( access_t *p_access )
  }

  /*****************************************************************************
- * Cookies (FIXME: we may want to rewrite that using a nice structure 
to hold
- * them) (FIXME: only support the "domain=" param)
- 
*****************************************************************************/
-
-/* Get the NAME=VALUE part of the Cookie */
-static char * cookie_get_content( const char * cookie )
-{
-    char * ret = strdup( cookie );
-    if( !ret ) return NULL;
-    char * str = ret;
-    /* Look for a ';' */
-    while( *str && *str != ';' ) str++;
-    /* Replace it by a end-char */
-    if( *str == ';' ) *str = 0;
-    return ret;
-}
-
-/* Get the domain where the cookie is stored */
-static char * cookie_get_domain( const char * cookie )
-{
-    const char * str = cookie;
-    static const char domain[] = "domain=";
-    if( !str )
-        return NULL;
-    /* Look for a ';' */
-    while( *str )
-    {
-        if( !strncmp( str, domain, sizeof(domain) - 1 /* minus \0 */ ) )
-        {
-            str += sizeof(domain) - 1 /* minus \0 */;
-            char * ret = strdup( str );
-            /* Now remove the next ';' if present */
-            char * ret_iter = ret;
-            while( *ret_iter && *ret_iter != ';' ) ret_iter++;
-            if( *ret_iter == ';' )
-                *ret_iter = 0;
-            return ret;
-        }
-        /* Go to next ';' field */
-        while( *str && *str != ';' ) str++;
-        if( *str == ';' ) str++;
-        /* skip blank */
-        while( *str && *str == ' ' ) str++;
-    }
-    return NULL;
-}
-
-/* Get NAME in the NAME=VALUE field */
-static char * cookie_get_name( const char * cookie )
-{
-    char * ret = cookie_get_content( cookie ); /* NAME=VALUE */
-    if( !ret ) return NULL;
-    char * str = ret;
-    while( *str && *str != '=' ) str++;
-    *str = 0;
-    return ret;
-}
-
-/* Add a cookie in cookies, checking to see how it should be added */
-static void cookie_append( vlc_array_t * cookies, char * cookie )
-{
-    int i;
-
-    if( !cookie )
-        return;
-
-    char * cookie_name = cookie_get_name( cookie );
-
-    /* Don't send invalid cookies */
-    if( !cookie_name )
-        return;
-
-    char * cookie_domain = cookie_get_domain( cookie );
-    for( i = 0; i < vlc_array_count( cookies ); i++ )
-    {
-        char * current_cookie = vlc_array_item_at_index( cookies, i );
-        char * current_cookie_name = cookie_get_name( current_cookie );
-        char * current_cookie_domain = cookie_get_domain( current_cookie );
-
-        assert( current_cookie_name );
-
-        bool is_domain_matching = (
-                      ( !cookie_domain && !current_cookie_domain ) ||
-                      ( cookie_domain && current_cookie_domain &&
-                        !strcmp( cookie_domain, current_cookie_domain ) 
) );
-
-        if( is_domain_matching && !strcmp( cookie_name, 
current_cookie_name )  )
-        {
-            /* Remove previous value for this cookie */
-            free( current_cookie );
-            vlc_array_remove( cookies, i );
-
-            /* Clean */
-            free( current_cookie_name );
-            free( current_cookie_domain );
-            break;
-        }
-        free( current_cookie_name );
-        free( current_cookie_domain );
-    }
-    free( cookie_name );
-    free( cookie_domain );
-    vlc_array_append( cookies, cookie );
-}
-
-
-/*****************************************************************************
   * HTTP authentication
*****************************************************************************/

diff --git a/modules/access/httpcookies.c b/modules/access/httpcookies.c
new file mode 100644
index 0000000..0c60551
--- /dev/null
+++ b/modules/access/httpcookies.c
@@ -0,0 +1,380 @@
+/*****************************************************************************
+ * httpcookies.h: HTTP cookie utilities
+ 
*****************************************************************************
+ * Copyright (C) 2014 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Antti Ajanki <antti.ajanki at iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ 
*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_messages.h>
+#include <vlc_strings.h>
+#include "httpcookies.h"
+
+typedef struct http_cookie_t
+{
+    char *psz_name;
+    char *psz_value;
+    char *psz_domain;
+    char *psz_path;
+    bool b_host_only;
+    bool b_secure;
+} http_cookie_t;
+
+static http_cookie_t * cookie_parse( const char * cookie_header, const 
vlc_url_t * url );
+static void cookie_destroy( http_cookie_t * p_cookie );
+static char * cookie_get_content( const char * cookie );
+static char * cookie_get_domain( const char * cookie );
+static char * cookie_get_attribute_value( const char * cookie, const 
char *attr );
+static bool cookie_has_attribute( const char * cookie, const char *attr );
+static bool cookie_should_be_sent( const http_cookie_t * cookie, const 
vlc_url_t * url );
+static bool cookie_is_valid( const http_cookie_t * cookie, const char 
*host );
+static bool cookie_domain_matches( const http_cookie_t * cookie, const 
char *host );
+static bool cookie_path_matches( const http_cookie_t * cookie, const 
char *path );
+static bool cookie_domain_is_public_suffix( const char *domain );
+static char * cookie_default_path( const char *request_path );
+
+http_cookie_jar_t * http_cookies_new()
+{
+    return vlc_array_new();
+}
+
+void http_cookies_destroy( http_cookie_jar_t * p_jar )
+{
+    if ( !p_jar )
+        return;
+
+    int i;
+    for( i = 0; i < vlc_array_count( p_jar ); i++ )
+        cookie_destroy( vlc_array_item_at_index( p_jar, i ) );
+    vlc_array_destroy( p_jar );
+}
+
+bool http_cookies_append( http_cookie_jar_t * p_jar, const char * 
psz_cookie_header, const vlc_url_t *p_url )
+{
+    int i;
+
+    http_cookie_t *cookie = cookie_parse( psz_cookie_header, p_url );
+    if( !cookie || !cookie_is_valid( cookie, p_url->psz_host ) )
+    {
+        cookie_destroy( cookie );
+        return false;
+    }
+
+    for( i = 0; i < vlc_array_count( p_jar ); i++ )
+    {
+        http_cookie_t *iter = vlc_array_item_at_index( p_jar, i );
+
+        assert( iter->psz_name );
+        assert( iter->psz_domain );
+
+        bool domains_match =
+            vlc_ascii_strcasecmp( cookie->psz_domain, iter->psz_domain 
) == 0;
+        bool paths_match = strcmp( cookie->psz_path, iter->psz_path ) == 0;
+        bool names_match = strcmp( cookie->psz_name, iter->psz_name ) == 0;
+        if( domains_match && paths_match && names_match )
+        {
+            /* Remove previous value for this cookie */
+            vlc_array_remove( p_jar, i );
+            cookie_destroy(iter);
+            break;
+        }
+    }
+    vlc_array_append( p_jar, cookie );
+
+    return true;
+}
+
+char * http_cookies_for_url( http_cookie_jar_t * p_jar, const vlc_url_t 
* p_url, vlc_object_t *p_msgobj )
+{
+    int i;
+    char *psz_cookiebuf = NULL;
+    for( i = 0; i < vlc_array_count( p_jar ); i++ )
+    {
+        const http_cookie_t * cookie = vlc_array_item_at_index( p_jar, i );
+        if ( cookie_should_be_sent( cookie, p_url ) )
+        {
+            char *psz_updated_buf = NULL;
+            if ( asprintf(&psz_updated_buf, "%s%s%s=%s",
+                          psz_cookiebuf ? psz_cookiebuf : "",
+                          psz_cookiebuf ? "; " : "",
+                          cookie->psz_name,
+                          cookie->psz_value) == -1 )
+            {
+                msg_Err( p_msgobj, "Failed to append a cookie" );
+                continue;
+            }
+            if ( psz_cookiebuf ) free( psz_cookiebuf );
+            psz_cookiebuf = psz_updated_buf;
+        }
+    }
+
+    return psz_cookiebuf;
+}
+
+static http_cookie_t * cookie_parse( const char * cookie_header, const 
vlc_url_t * url )
+{
+    http_cookie_t *cookie = calloc(1, sizeof(http_cookie_t));
+    if ( !cookie )
+        return NULL;
+
+    char *content = cookie_get_content( cookie_header );
+    if ( !content )
+    {
+        cookie_destroy( cookie );
+        return NULL;
+    }
+
+    const char *eq = strchr(content, '=');
+    if ( eq )
+    {
+        cookie->psz_name = strndup( content, eq-content );
+        cookie->psz_value = strdup( eq + 1 );
+    }
+    else
+    {
+        cookie->psz_name = strdup( content );
+        cookie->psz_value = strdup( "" );
+    }
+
+    cookie->psz_domain = cookie_get_domain( cookie_header );
+    if ( !cookie->psz_domain || strlen(cookie->psz_domain) == 0 )
+    {
+        free(cookie->psz_domain);
+        cookie->psz_domain = strdup( url->psz_host );
+        cookie->b_host_only = true;
+    }
+    else
+        cookie->b_host_only = false;
+
+    cookie->psz_path = cookie_get_attribute_value( cookie_header, "path" );
+    if ( !cookie->psz_path || strlen(cookie->psz_path) == 0 )
+    {
+        free(cookie->psz_path);
+        cookie->psz_path = cookie_default_path( url->psz_path );
+    }
+
+    cookie->b_secure = cookie_has_attribute( cookie_header, "secure" );
+
+    FREENULL( content );
+
+    if ( !cookie->psz_domain || !cookie->psz_path ||
+         !cookie->psz_name || !cookie->psz_value )
+    {
+        cookie_destroy( cookie );
+        return NULL;
+    }
+
+    return cookie;
+}
+
+static void cookie_destroy( http_cookie_t * p_cookie )
+{
+    if ( !p_cookie )
+        return;
+
+    free( p_cookie->psz_name );
+    free( p_cookie->psz_value );
+    free( p_cookie->psz_domain );
+    free( p_cookie->psz_path );
+    free( p_cookie );
+}
+
+/* Get the NAME=VALUE part of the Cookie */
+static char * cookie_get_content( const char * cookie )
+{
+    char * ret = strdup( cookie );
+    if( !ret ) return NULL;
+    char * str = ret;
+    /* Look for a ';' */
+    while( *str && *str != ';' ) str++;
+    /* Replace it by a end-char */
+    if( *str == ';' ) *str = 0;
+    return ret;
+}
+
+/* Get the domain where the cookie is stored */
+static char * cookie_get_domain( const char * cookie )
+{
+    char *domain = cookie_get_attribute_value( cookie, "domain" );
+    if ( domain && *domain == '.' )
+    {
+        char *p = domain;
+        while ( *p == '.') p++;
+        memmove( domain, p, strlen( p ) + 1 );
+    }
+
+    return domain;
+}
+
+static char * cookie_get_attribute_value( const char * cookie, const 
char *attr )
+{
+    if( !cookie )
+        return NULL;
+    size_t attrlen = strlen( attr );
+    const char * str = strchr( cookie, ';' );
+    while( str )
+    {
+        /* skip ; */
+        str++;
+
+        /* skip blank */
+        while( *str && *str == ' ' ) str++;
+
+        if( !vlc_ascii_strncasecmp( str, attr, attrlen ) &&
+            ( str[attrlen] == '=' ) )
+        {
+            str += attrlen + 1;
+            char * ret = strdup( str );
+            /* Now remove the next ';' if present */
+            char * ret_iter = ret;
+            while( *ret_iter && *ret_iter != ';' ) ret_iter++;
+            if( *ret_iter == ';' )
+                *ret_iter = 0;
+            return ret;
+        }
+
+        str = strchr( str, ';' );
+    }
+    return NULL;
+}
+
+static bool cookie_has_attribute( const char * cookie, const char *attr )
+{
+    if( !cookie )
+        return false;
+
+    size_t attrlen = strlen(attr);
+    const char * str = strchr(cookie, ';');
+    while( str )
+    {
+        /* skip ; */
+        str++;
+
+        /* skip blank */
+        while( *str && *str == ' ' ) str++;
+
+        if( !vlc_ascii_strncasecmp( str, attr, attrlen ) &&
+            ( str[attrlen] == '=' || str[attrlen] == ';' || 
str[attrlen] == '\0' ) )
+            return true;
+
+        str = strchr(str, ';');
+    }
+    return false;
+}
+
+static bool cookie_should_be_sent( const http_cookie_t * cookie, const 
vlc_url_t * url )
+{
+    bool protocol_ok = !cookie->b_secure ||
+        ( url->psz_protocol && strcasecmp(url->psz_protocol, "https") 
== 0 );
+    bool domain_ok = cookie_domain_matches( cookie, url->psz_host );
+    bool path_ok = cookie_path_matches( cookie, url->psz_path );
+    return protocol_ok && domain_ok && path_ok;
+}
+
+/* Check if a cookie from host should be added to the cookie jar */
+static bool cookie_is_valid( const http_cookie_t * cookie, const char 
*host )
+{
+    return cookie && cookie->psz_name && strlen(cookie->psz_name) > 0 &&
+        cookie->psz_domain &&
+        !cookie_domain_is_public_suffix(cookie->psz_domain) &&
+        cookie_domain_matches(cookie, host);
+}
+
+static bool cookie_domain_matches( const http_cookie_t * cookie, const 
char *host )
+{
+    assert( !cookie || cookie->psz_domain );
+
+    // TODO: should convert domain names to punycode before comparing
+
+    if ( !cookie || !host )
+        return false;
+    if ( vlc_ascii_strcasecmp(cookie->psz_domain, host) == 0 )
+        return true;
+    else if ( cookie->b_host_only )
+        return false;
+
+    size_t host_len = strlen(host);
+    size_t cookie_domain_len = strlen(cookie->psz_domain);
+    int i = host_len - cookie_domain_len;
+    bool is_suffix = ( i > 0 ) &&
+        vlc_ascii_strcasecmp( &host[i], cookie->psz_domain ) == 0;
+    bool has_dot_before_suffix = host[i-1] == '.';
+    bool host_is_ipv4 = strspn(host, "0123456789.") == host_len;
+    bool host_is_ipv6 = strchr(host, ':') != NULL;
+    return is_suffix && has_dot_before_suffix &&
+        !( host_is_ipv4 || host_is_ipv6 );
+}
+
+static bool cookie_path_matches( const http_cookie_t * cookie, const 
char *uripath )
+{
+    if ( !cookie || !uripath )
+        return false;
+    else if ( strcmp(cookie->psz_path, uripath) == 0 )
+        return true;
+
+    size_t path_len = strlen( uripath );
+    size_t prefix_len = strlen( cookie->psz_path );
+    return ( path_len > prefix_len ) &&
+        ( strncmp(uripath, cookie->psz_path, prefix_len) == 0 ) &&
+        ( uripath[prefix_len - 1] == '/' || uripath[prefix_len] == '/' );
+}
+
+static bool cookie_domain_is_public_suffix( const char *domain )
+{
+    // FIXME: should check if domain is one of "public suffixes" at
+    // http://publicsuffix.org/. The purpose of this check is to
+    // prevent a host from setting a "too wide" cookie, for example
+    // "example.com" should not be able to set a cookie for "com".
+    // The current implementation prevents all top-level domains.
+    return domain && !strchr(domain, '.');
+}
+
+static char * cookie_default_path( const char *request_path )
+{
+    if ( !request_path || *request_path != '/' )
+        return strdup("/");
+
+    char *path;
+    const char *query_start = strchr( request_path, '?' );
+    if ( query_start )
+        path = strndup( request_path, query_start - request_path );
+    else
+        path = strdup( request_path );
+
+    if ( !path )
+        return NULL;
+
+    char *last_slash = strrchr(path, '/');
+    assert(last_slash);
+    if ( last_slash == path )
+        path[1] = '\0';
+    else
+        *last_slash = '\0';
+
+    return path;
+}
diff --git a/modules/access/httpcookies.h b/modules/access/httpcookies.h
new file mode 100644
index 0000000..8103490
--- /dev/null
+++ b/modules/access/httpcookies.h
@@ -0,0 +1,63 @@
+/*****************************************************************************
+ * httpcookies.h: HTTP cookie utilities
+ 
*****************************************************************************
+ * Copyright (C) 2014 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Antti Ajanki <antti.ajanki at iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ 
*****************************************************************************/
+
+#ifndef HTTPCOOKIES_H_
+#define HTTPCOOKIES_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <vlc_url.h>
+#include <vlc_arrays.h>
+
+typedef struct vlc_array_t http_cookie_jar_t;
+
+http_cookie_jar_t * http_cookies_new( void );
+void http_cookies_destroy( http_cookie_jar_t * p_jar );
+
+/**
+ * Parse a value of an incoming Set-Cookie header and append the
+ * cookie to the cookie jar if appropriate.
+ *
+ * @param p_jar cookie jar object
+ * @param psz_cookie_header value of Set-Cookie
+ * @return true, if the cookie was added, false otherwise
+ */
+bool http_cookies_append( http_cookie_jar_t * p_jar, const char * 
psz_cookie_header, const vlc_url_t * p_url );
+
+/**
+ * Returns a cookie value that match the given URL.
+ *
+ * @params p_jar a cookie jar
+ * @params p_url the URL for which the cookies are returned
+ * @params p_msgobj error messages will be printed here
+ * @return string consisting of semicolon-separated cookie NAME=VALUE pairs
+ */
+char * http_cookies_for_url( http_cookie_jar_t * p_jar, const vlc_url_t 
* p_url, vlc_object_t *p_msgobj );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 325c8f6..0f6d9a1 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -479,6 +479,8 @@ video_format_Setup
  video_format_Print
  video_splitter_Delete
  video_splitter_New
+vlc_ascii_strcasecmp
+vlc_ascii_strncasecmp
  vlc_b64_decode
  vlc_b64_decode_binary
  vlc_b64_decode_binary_to_buffer
diff --git a/src/text/strings.c b/src/text/strings.c
index 55738a7..8762129 100644
--- a/src/text/strings.c
+++ b/src/text/strings.c
@@ -946,3 +946,41 @@ time_t str_duration( const char *psz_duration )
      } while ( *psz_duration );
      return res;
  }
+
+int vlc_ascii_strcasecmp( const char *psz1, const char *psz2 )
+{
+    assert( psz1 && psz2 );
+
+    const char *s1 = psz1;
+    const char *s2 = psz2;
+    int d = vlc_ascii_tolower( *s1 ) - vlc_ascii_tolower( *s2 );
+    while ( *s1 && *s2 && d == 0)
+    {
+        s1++;
+        s2++;
+        d = vlc_ascii_tolower( *s1 ) - vlc_ascii_tolower( *s2 );
+    }
+
+    return d;
+}
+
+int vlc_ascii_strncasecmp( const char *psz1, const char *psz2, size_t n )
+{
+    assert( psz1 && psz2 );
+
+    const char *s1 = psz1;
+    const char *s2 = psz2;
+    const char *s1end = psz1 + n;
+    int d = vlc_ascii_tolower( *s1 ) - vlc_ascii_tolower( *s2 );
+    while ( *s1 && *s2 && s1 < s1end && d == 0)
+    {
+        s1++;
+        s2++;
+        d = vlc_ascii_tolower( *s1 ) - vlc_ascii_tolower( *s2 );
+    }
+
+    if (s1 == s1end)
+        return 0;
+    else
+        return d;
+}
-- 
1.7.10.4





More information about the vlc-devel mailing list