asx: remove useless test
[vlc.git] / src / misc / httpcookies.c
blob77ab852b157d6fabf30bcb0678c424b14ec9f645
1 /*****************************************************************************
2 * httpcookies.c: HTTP cookie utilities
3 *****************************************************************************
4 * Copyright (C) 2014 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Antti Ajanki <antti.ajanki@iki.fi>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <assert.h>
33 #include <vlc_common.h>
34 #include <vlc_messages.h>
35 #include <vlc_strings.h>
36 #include <vlc_http.h>
38 typedef struct http_cookie_t
40 char *psz_name;
41 char *psz_value;
42 char *psz_domain;
43 char *psz_path;
44 bool b_host_only;
45 bool b_secure;
46 } http_cookie_t;
48 static char *cookie_get_attribute_value( const char *cookie, const char *attr )
50 size_t attrlen = strlen( attr );
51 const char * str = strchr( cookie, ';' );
52 while( str )
54 /* skip ; and blank */
55 str++;
56 str = str + strspn( str, " " );
58 if( !vlc_ascii_strncasecmp( str, attr, attrlen )
59 && (str[attrlen] == '=') )
61 str += attrlen + 1;
62 size_t value_length = strcspn( str, ";" );
63 return strndup( str, value_length );
66 str = strchr( str, ';' );
68 return NULL;
71 static bool cookie_has_attribute( const char *cookie, const char *attr )
73 size_t attrlen = strlen(attr);
74 const char * str = strchr(cookie, ';');
75 while( str )
77 /* skip ; and blank */
78 str++;
79 str += strspn( str, " " );
81 if( !vlc_ascii_strncasecmp( str, attr, attrlen )
82 && (str[attrlen] == '=' || str[attrlen] == ';' || str[attrlen] == '\0') )
83 return true;
85 str = strchr(str, ';');
87 return false;
90 /* Get the domain where the cookie is stored */
91 static char *cookie_get_domain( const char *cookie )
93 char *domain = cookie_get_attribute_value( cookie, "domain" );
94 if( domain == NULL )
95 return NULL;
97 if( domain[0] == '.' )
99 const char *real_domain = domain + strspn( domain, "." );
100 memmove( domain, real_domain, strlen( real_domain ) + 1 );
102 return domain;
105 static bool cookie_domain_matches( const http_cookie_t *cookie,
106 const char *host )
108 // TODO: should convert domain names to punycode before comparing
110 if (host == NULL)
111 return false;
112 if ( vlc_ascii_strcasecmp(cookie->psz_domain, host) == 0 )
113 return true;
114 else if ( cookie->b_host_only )
115 return false;
117 size_t host_len = strlen(host);
118 size_t cookie_domain_len = strlen(cookie->psz_domain);
119 bool is_suffix = false, has_dot_before_suffix = false;
121 if( host_len > cookie_domain_len )
123 size_t i = host_len - cookie_domain_len;
125 is_suffix = vlc_ascii_strcasecmp( &host[i], cookie->psz_domain ) == 0;
126 has_dot_before_suffix = host[i-1] == '.';
129 bool host_is_ipv4 = strspn(host, "0123456789.") == host_len;
130 bool host_is_ipv6 = strchr(host, ':') != NULL;
131 return is_suffix && has_dot_before_suffix &&
132 !( host_is_ipv4 || host_is_ipv6 );
135 static char *cookie_get_path(const char *cookie)
137 return cookie_get_attribute_value(cookie, "path");
140 static bool cookie_path_matches( const http_cookie_t * cookie, const char *uripath )
142 if (uripath == NULL )
143 return false;
144 else if ( strcmp(cookie->psz_path, uripath) == 0 )
145 return true;
147 size_t path_len = strlen( uripath );
148 size_t prefix_len = strlen( cookie->psz_path );
149 return ( path_len > prefix_len ) &&
150 ( strncmp(uripath, cookie->psz_path, prefix_len) == 0 ) &&
151 ( uripath[prefix_len - 1] == '/' || uripath[prefix_len] == '/' );
154 static bool cookie_should_be_sent(const http_cookie_t *cookie, bool secure,
155 const char *host, const char *path)
157 bool protocol_ok = secure || !cookie->b_secure;
158 bool domain_ok = cookie_domain_matches(cookie, host);
159 bool path_ok = cookie_path_matches(cookie, path);
160 return protocol_ok && domain_ok && path_ok;
163 static char *cookie_default_path( const char *request_path )
165 if ( request_path == NULL || request_path[0] != '/' )
166 return strdup("/");
168 char *path;
169 const char *query_start = strchr( request_path, '?' );
170 if ( query_start != NULL )
171 path = strndup( request_path, query_start - request_path );
172 else
173 path = strdup( request_path );
175 if ( path == NULL )
176 return NULL;
178 char *last_slash = strrchr(path, '/');
179 assert(last_slash != NULL);
180 if ( last_slash == path )
181 path[1] = '\0';
182 else
183 *last_slash = '\0';
185 return path;
188 static void cookie_destroy(http_cookie_t *cookie)
190 assert(cookie != NULL);
191 free(cookie->psz_name);
192 free(cookie->psz_value);
193 free(cookie->psz_domain);
194 free(cookie->psz_path);
195 free(cookie);
198 VLC_MALLOC VLC_USED
199 static http_cookie_t *cookie_parse(const char *value,
200 const char *host, const char *path)
202 http_cookie_t *cookie = malloc(sizeof (*cookie));
203 if (unlikely(cookie == NULL))
204 return NULL;
206 cookie->psz_domain = NULL;
207 cookie->psz_path = NULL;
209 /* Get the NAME=VALUE part of the Cookie */
210 size_t value_length = strcspn(value, ";");
211 const char *p = memchr(value, '=', value_length);
213 if (p != NULL)
215 cookie->psz_name = strndup(value, p - value);
216 p++;
217 cookie->psz_value = strndup(p, value_length - (p - value));
218 if (unlikely(cookie->psz_value == NULL))
219 goto error;
221 else
223 cookie->psz_name = strndup(value, value_length);
224 cookie->psz_value = NULL;
227 if (unlikely(cookie->psz_name == NULL))
228 goto error;
230 /* Cookie name is a token; it cannot be empty. */
231 if (cookie->psz_name[0] == '\0')
232 goto error;
234 /* Get domain */
235 cookie->psz_domain = cookie_get_domain(value);
236 if (cookie->psz_domain == NULL)
238 cookie->psz_domain = strdup(host);
239 if (unlikely(cookie->psz_domain == NULL))
240 goto error;
242 cookie->b_host_only = true;
244 else
245 cookie->b_host_only = false;
247 /* Get path */
248 cookie->psz_path = cookie_get_path(value);
249 if (cookie->psz_path == NULL)
251 cookie->psz_path = cookie_default_path(path);
252 if (unlikely(cookie->psz_path == NULL))
253 goto error;
256 /* Get secure flag */
257 cookie->b_secure = cookie_has_attribute(value, "secure");
259 return cookie;
261 error:
262 cookie_destroy(cookie);
263 return NULL;
266 struct vlc_http_cookie_jar_t
268 vlc_array_t cookies;
269 vlc_mutex_t lock;
272 vlc_http_cookie_jar_t * vlc_http_cookies_new(void)
274 vlc_http_cookie_jar_t * jar = malloc( sizeof( vlc_http_cookie_jar_t ) );
275 if ( unlikely(jar == NULL) )
276 return NULL;
278 vlc_array_init( &jar->cookies );
279 vlc_mutex_init( &jar->lock );
281 return jar;
284 void vlc_http_cookies_destroy( vlc_http_cookie_jar_t * p_jar )
286 if ( !p_jar )
287 return;
289 for( size_t i = 0; i < vlc_array_count( &p_jar->cookies ); i++ )
290 cookie_destroy( vlc_array_item_at_index( &p_jar->cookies, i ) );
292 vlc_array_clear( &p_jar->cookies );
293 vlc_mutex_destroy( &p_jar->lock );
295 free( p_jar );
298 bool vlc_http_cookies_store(vlc_http_cookie_jar_t *p_jar, const char *cookies,
299 const char *host, const char *path)
301 assert(host != NULL);
302 assert(path != NULL);
304 http_cookie_t *cookie = cookie_parse(cookies, host, path);
305 if (cookie == NULL)
306 return false;
308 /* Check if a cookie from host should be added to the cookie jar */
309 // FIXME: should check if domain is one of "public suffixes" at
310 // http://publicsuffix.org/. The purpose of this check is to
311 // prevent a host from setting a "too wide" cookie, for example
312 // "example.com" should not be able to set a cookie for "com".
313 // The current implementation prevents all top-level domains.
314 if (strchr(cookie->psz_domain, '.') == NULL
315 || !cookie_domain_matches(cookie, host))
317 cookie_destroy(cookie);
318 return false;
321 vlc_mutex_lock( &p_jar->lock );
323 for( size_t i = 0; i < vlc_array_count( &p_jar->cookies ); i++ )
325 http_cookie_t *iter = vlc_array_item_at_index( &p_jar->cookies, i );
327 assert( iter->psz_name );
328 assert( iter->psz_domain );
329 assert( iter->psz_path );
331 bool domains_match =
332 vlc_ascii_strcasecmp( cookie->psz_domain, iter->psz_domain ) == 0;
333 bool paths_match = strcmp( cookie->psz_path, iter->psz_path ) == 0;
334 bool names_match = strcmp( cookie->psz_name, iter->psz_name ) == 0;
335 if( domains_match && paths_match && names_match )
337 /* Remove previous value for this cookie */
338 vlc_array_remove( &p_jar->cookies, i );
339 cookie_destroy(iter);
340 break;
344 bool b_ret = (vlc_array_append( &p_jar->cookies, cookie ) == 0);
345 if( !b_ret )
346 cookie_destroy( cookie );
348 vlc_mutex_unlock( &p_jar->lock );
350 return b_ret;
353 char *vlc_http_cookies_fetch(vlc_http_cookie_jar_t *p_jar, bool secure,
354 const char *host, const char *path)
356 char *psz_cookiebuf = NULL;
358 vlc_mutex_lock( &p_jar->lock );
360 for( size_t i = 0; i < vlc_array_count( &p_jar->cookies ); i++ )
362 const http_cookie_t * cookie = vlc_array_item_at_index( &p_jar->cookies, i );
363 if (cookie_should_be_sent(cookie, secure, host, path))
365 char *psz_updated_buf = NULL;
366 if ( asprintf(&psz_updated_buf, "%s%s%s=%s",
367 psz_cookiebuf ? psz_cookiebuf : "",
368 psz_cookiebuf ? "; " : "",
369 cookie->psz_name ? cookie->psz_name : "",
370 cookie->psz_value ? cookie->psz_value : "") == -1 )
372 // TODO: report error
373 free( psz_cookiebuf );
374 vlc_mutex_unlock( &p_jar->lock );
375 return NULL;
377 free( psz_cookiebuf );
378 psz_cookiebuf = psz_updated_buf;
382 vlc_mutex_unlock( &p_jar->lock );
384 return psz_cookiebuf;