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

Rémi Denis-Courmont remi at remlab.net
Thu Jul 24 18:26:38 CEST 2014


	Moikka,

Le jeudi 24 juillet 2014, 12:07:05 Antti Ajanki a écrit :
> * 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

I'd split that to a preceding patch.

Also inline...

> ---
>  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" );

Yeah well, given msg_Err() typically allocates memory, we tend not to call it 
on memory error.

(I don't know if we should be concerned about excessively heavy cookie jars, 
but that won't fix it.)

> +                continue;
> +            }
> +            if ( psz_cookiebuf ) free( psz_cookiebuf );

Redundant if()

> +            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 )

Add unlikely()

> +        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( "" );

Maybe use NULL when there is no value? You need to deal with NULL anyway.

> +    }
> +
> +    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 );

I am not sure I follow the code, but looking for the terminator in the 
original string, and then using strndup() directly should be better.

Also in cookie_get_attribute_value().

> +    if( !ret ) return NULL;
> +    char * str = ret;
> +    /* Look for a ';' */
> +    while( *str && *str != ';' ) str++;

strcspn() or strchr() ?

> +    /* 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++;

strspn() or strrchr()


> +        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++;

Ditto.

> +
> +        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++;

Ditto.

> +            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++;

Ditto.

> +
> +        if( !vlc_ascii_strncasecmp( str, attr, attrlen ) &&
> +            ( str[attrlen] == '=' || str[attrlen] == ';' || str[attrlen] ==
> '\0' ) )

OK. I'd use memchr() but that's just me.

> +            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)

Nit: checking *s2 is redundant.

> +    {
> +        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)

Same here.

> +    {
> +        s1++;
> +        s2++;
> +        d = vlc_ascii_tolower( *s1 ) - vlc_ascii_tolower( *s2 );
> +    }
> +
> +    if (s1 == s1end)
> +        return 0;
> +    else
> +        return d;
> +}

-- 
Rémi Denis-Courmont
http://www.remlab.net/




More information about the vlc-devel mailing list