1999-04-17 00:37:12 +00:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2019-01-30 09:03:12 +00:00
| Copyright ( c ) The PHP Group |
1999-04-17 00:37:12 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2006-01-01 12:51:34 +00:00
| This source file is subject to version 3.01 of the PHP license , |
1999-07-16 13:13:16 +00:00
| that is bundled with this package in the file LICENSE , and is |
2003-06-10 20:04:29 +00:00
| available through the world - wide - web at the following url : |
2021-05-06 10:16:35 +00:00
| https : //www.php.net/license/3_01.txt |
1999-07-16 13:13:16 +00:00
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world - wide - web , please send a note to |
| license @ php . net so we can mail you a copy immediately . |
1999-04-17 00:37:12 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2018-11-01 16:30:28 +00:00
| Author : Zeev Suraski < zeev @ php . net > |
1999-04-17 00:37:12 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
# include "php.h"
1999-12-04 19:19:57 +00:00
# include "php_browscap.h"
1999-04-17 00:37:12 +00:00
# include "php_ini.h"
2004-03-15 21:26:39 +00:00
# include "php_string.h"
2008-08-07 12:50:17 +00:00
# include "ext/pcre/php_pcre.h"
2007-11-05 12:27:42 +00:00
2007-09-28 02:05:10 +00:00
# include "zend_ini_scanner.h"
1999-04-17 00:37:12 +00:00
# include "zend_globals.h"
2016-12-15 16:22:08 +00:00
# define BROWSCAP_NUM_CONTAINS 5
2016-12-15 14:12:48 +00:00
typedef struct {
zend_string * key ;
zend_string * value ;
} browscap_kv ;
typedef struct {
zend_string * pattern ;
2016-12-15 16:22:08 +00:00
zend_string * parent ;
2016-12-15 14:12:48 +00:00
uint32_t kv_start ;
uint32_t kv_end ;
2016-12-15 16:22:08 +00:00
/* We ensure that the length fits in 16 bits, so this is fine */
uint16_t contains_start [ BROWSCAP_NUM_CONTAINS ] ;
uint8_t contains_len [ BROWSCAP_NUM_CONTAINS ] ;
uint8_t prefix_len ;
2016-12-15 14:12:48 +00:00
} browscap_entry ;
2011-05-01 18:37:20 +00:00
typedef struct {
HashTable * htab ;
2016-12-15 14:12:48 +00:00
browscap_kv * kv ;
uint32_t kv_used ;
uint32_t kv_size ;
2011-05-01 18:37:20 +00:00
char filename [ MAXPATHLEN ] ;
} browser_data ;
/* browser data defined in startup phase, eagerly loaded in MINIT */
static browser_data global_bdata = { 0 } ;
/* browser data defined in activation phase, lazily loaded in get_browser.
* Per request and per thread , if applicable */
ZEND_BEGIN_MODULE_GLOBALS ( browscap )
browser_data activation_bdata ;
ZEND_END_MODULE_GLOBALS ( browscap )
2012-06-22 15:32:46 +00:00
ZEND_DECLARE_MODULE_GLOBALS ( browscap )
2015-07-29 08:35:57 +00:00
# define BROWSCAP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(browscap, v)
1999-04-17 00:37:12 +00:00
2000-07-25 08:09:00 +00:00
# define DEFAULT_SECTION_NAME "Default Browser Capability Settings"
2000-07-23 11:11:35 +00:00
2001-07-29 08:40:41 +00:00
/* OBJECTS_FIXME: This whole extension needs going through. The use of objects looks pretty broken here */
2000-10-29 23:10:22 +00:00
2016-12-15 14:12:48 +00:00
static void browscap_entry_dtor ( zval * zvalue )
2000-10-29 23:10:22 +00:00
{
2016-12-15 14:12:48 +00:00
browscap_entry * entry = Z_PTR_P ( zvalue ) ;
2018-05-28 13:27:12 +00:00
zend_string_release_ex ( entry - > pattern , 0 ) ;
2016-12-15 16:22:08 +00:00
if ( entry - > parent ) {
2018-05-28 13:27:12 +00:00
zend_string_release_ex ( entry - > parent , 0 ) ;
2011-05-01 18:37:20 +00:00
}
2016-12-15 14:12:48 +00:00
efree ( entry ) ;
2011-05-01 18:37:20 +00:00
}
2016-12-15 14:12:48 +00:00
static void browscap_entry_dtor_persistent ( zval * zvalue )
{
browscap_entry * entry = Z_PTR_P ( zvalue ) ;
2018-05-28 13:27:12 +00:00
zend_string_release_ex ( entry - > pattern , 1 ) ;
2016-12-15 16:22:08 +00:00
if ( entry - > parent ) {
2018-05-28 13:27:12 +00:00
zend_string_release_ex ( entry - > parent , 1 ) ;
2000-10-29 23:10:22 +00:00
}
2016-12-15 14:12:48 +00:00
pefree ( entry , 1 ) ;
}
2021-01-15 11:30:54 +00:00
static inline bool is_placeholder ( char c ) {
2016-12-15 16:22:08 +00:00
return c = = ' ? ' | | c = = ' * ' ;
}
/* Length of prefix not containing any wildcards */
static uint8_t browscap_compute_prefix_len ( zend_string * pattern ) {
size_t i ;
for ( i = 0 ; i < ZSTR_LEN ( pattern ) ; i + + ) {
if ( is_placeholder ( ZSTR_VAL ( pattern ) [ i ] ) ) {
break ;
}
}
2017-08-23 23:36:14 +00:00
return ( uint8_t ) MIN ( i , UINT8_MAX ) ;
2016-12-15 16:22:08 +00:00
}
static size_t browscap_compute_contains (
zend_string * pattern , size_t start_pos ,
uint16_t * contains_start , uint8_t * contains_len ) {
size_t i = start_pos ;
/* Find first non-placeholder character after prefix */
for ( ; i < ZSTR_LEN ( pattern ) ; i + + ) {
if ( ! is_placeholder ( ZSTR_VAL ( pattern ) [ i ] ) ) {
/* Skip the case of a single non-placeholder character.
* Let ' s try to find something longer instead . */
if ( i + 1 < ZSTR_LEN ( pattern ) & &
! is_placeholder ( ZSTR_VAL ( pattern ) [ i + 1 ] ) ) {
break ;
}
}
}
2017-08-23 23:36:14 +00:00
* contains_start = ( uint16_t ) i ;
2016-12-15 16:22:08 +00:00
/* Find first placeholder character after that */
for ( ; i < ZSTR_LEN ( pattern ) ; i + + ) {
if ( is_placeholder ( ZSTR_VAL ( pattern ) [ i ] ) ) {
break ;
}
}
2017-08-23 23:36:14 +00:00
* contains_len = ( uint8_t ) MIN ( i - * contains_start , UINT8_MAX ) ;
2016-12-15 16:22:08 +00:00
return i ;
}
/* Length of regex, including escapes, anchors, etc. */
2016-12-15 14:12:48 +00:00
static size_t browscap_compute_regex_len ( zend_string * pattern ) {
size_t i , len = ZSTR_LEN ( pattern ) ;
for ( i = 0 ; i < ZSTR_LEN ( pattern ) ; i + + ) {
switch ( ZSTR_VAL ( pattern ) [ i ] ) {
case ' * ' :
case ' . ' :
case ' \\ ' :
case ' ( ' :
case ' ) ' :
case ' ~ ' :
case ' + ' :
len + + ;
break ;
}
2000-10-29 23:10:22 +00:00
}
2017-12-31 04:35:25 +00:00
2016-12-15 14:12:48 +00:00
return len + sizeof ( " ~^$~ " ) - 1 ;
2000-10-29 23:10:22 +00:00
}
2016-12-15 14:12:48 +00:00
static zend_string * browscap_convert_pattern ( zend_string * pattern , int persistent ) /* { { { */
2000-10-29 23:10:22 +00:00
{
2016-06-23 09:47:06 +00:00
size_t i , j = 0 ;
2000-10-29 23:10:22 +00:00
char * t ;
2014-02-13 13:54:23 +00:00
zend_string * res ;
2014-03-17 20:55:25 +00:00
char * lc_pattern ;
2016-12-15 14:12:48 +00:00
ALLOCA_FLAG ( use_heap ) ;
2000-10-29 23:10:22 +00:00
2016-12-15 14:12:48 +00:00
res = zend_string_alloc ( browscap_compute_regex_len ( pattern ) , persistent ) ;
2015-06-30 01:05:24 +00:00
t = ZSTR_VAL ( res ) ;
2003-03-14 17:54:38 +00:00
2016-12-15 14:12:48 +00:00
lc_pattern = do_alloca ( ZSTR_LEN ( pattern ) + 1 , use_heap ) ;
zend_str_tolower_copy ( lc_pattern , ZSTR_VAL ( pattern ) , ZSTR_LEN ( pattern ) ) ;
2004-03-15 21:26:39 +00:00
2014-09-17 13:38:56 +00:00
t [ j + + ] = ' ~ ' ;
2008-08-07 12:50:17 +00:00
t [ j + + ] = ' ^ ' ;
2004-03-15 21:26:39 +00:00
2016-12-15 14:12:48 +00:00
for ( i = 0 ; i < ZSTR_LEN ( pattern ) ; i + + , j + + ) {
2014-03-17 20:55:25 +00:00
switch ( lc_pattern [ i ] ) {
2000-10-29 23:10:22 +00:00
case ' ? ' :
t [ j ] = ' . ' ;
break ;
case ' * ' :
t [ j + + ] = ' . ' ;
t [ j ] = ' * ' ;
break ;
case ' . ' :
t [ j + + ] = ' \\ ' ;
t [ j ] = ' . ' ;
break ;
2008-08-07 12:50:17 +00:00
case ' \\ ' :
t [ j + + ] = ' \\ ' ;
t [ j ] = ' \\ ' ;
break ;
case ' ( ' :
t [ j + + ] = ' \\ ' ;
t [ j ] = ' ( ' ;
break ;
case ' ) ' :
t [ j + + ] = ' \\ ' ;
t [ j ] = ' ) ' ;
break ;
2014-09-17 13:38:56 +00:00
case ' ~ ' :
2008-08-07 12:50:17 +00:00
t [ j + + ] = ' \\ ' ;
2014-09-17 13:38:56 +00:00
t [ j ] = ' ~ ' ;
2008-08-07 12:50:17 +00:00
break ;
2016-08-26 23:02:58 +00:00
case ' + ' :
t [ j + + ] = ' \\ ' ;
t [ j ] = ' + ' ;
break ;
2000-10-29 23:10:22 +00:00
default :
2014-03-17 20:55:25 +00:00
t [ j ] = lc_pattern [ i ] ;
2000-10-29 23:10:22 +00:00
break ;
}
}
2003-04-28 21:49:47 +00:00
2004-03-15 21:26:39 +00:00
t [ j + + ] = ' $ ' ;
2014-09-17 13:38:56 +00:00
t [ j + + ] = ' ~ ' ;
2000-10-29 23:10:22 +00:00
t [ j ] = 0 ;
2016-12-15 14:12:48 +00:00
2015-06-30 01:05:24 +00:00
ZSTR_LEN ( res ) = j ;
2016-12-15 14:12:48 +00:00
free_alloca ( lc_pattern , use_heap ) ;
return res ;
2000-10-29 23:10:22 +00:00
}
2001-06-06 13:06:12 +00:00
/* }}} */
2000-10-29 23:10:22 +00:00
2016-12-15 14:12:48 +00:00
typedef struct _browscap_parser_ctx {
browser_data * bdata ;
browscap_entry * current_entry ;
zend_string * current_section_name ;
HashTable str_interned ;
} browscap_parser_ctx ;
static zend_string * browscap_intern_str (
2021-01-15 11:30:54 +00:00
browscap_parser_ctx * ctx , zend_string * str , bool persistent ) {
2016-12-15 14:12:48 +00:00
zend_string * interned = zend_hash_find_ptr ( & ctx - > str_interned , str ) ;
if ( interned ) {
zend_string_addref ( interned ) ;
} else {
interned = zend_string_copy ( str ) ;
2019-06-21 10:10:09 +00:00
if ( persistent ) {
2023-11-08 19:06:18 +00:00
interned = zend_new_interned_string ( interned ) ;
2019-06-21 10:10:09 +00:00
}
2016-12-15 14:12:48 +00:00
zend_hash_add_new_ptr ( & ctx - > str_interned , interned , interned ) ;
}
return interned ;
}
static zend_string * browscap_intern_str_ci (
2021-01-15 11:30:54 +00:00
browscap_parser_ctx * ctx , zend_string * str , bool persistent ) {
2016-12-15 14:12:48 +00:00
zend_string * lcname ;
zend_string * interned ;
ALLOCA_FLAG ( use_heap ) ;
ZSTR_ALLOCA_ALLOC ( lcname , ZSTR_LEN ( str ) , use_heap ) ;
zend_str_tolower_copy ( ZSTR_VAL ( lcname ) , ZSTR_VAL ( str ) , ZSTR_LEN ( str ) ) ;
interned = zend_hash_find_ptr ( & ctx - > str_interned , lcname ) ;
if ( interned ) {
zend_string_addref ( interned ) ;
} else {
2021-05-07 10:09:56 +00:00
interned = zend_string_init ( ZSTR_VAL ( lcname ) , ZSTR_LEN ( lcname ) , persistent ) ;
2019-06-21 10:10:09 +00:00
if ( persistent ) {
interned = zend_new_interned_string ( interned ) ;
}
2016-12-15 14:12:48 +00:00
zend_hash_add_new_ptr ( & ctx - > str_interned , interned , interned ) ;
}
2017-01-03 01:12:28 +00:00
ZSTR_ALLOCA_FREE ( lcname , use_heap ) ;
2016-12-15 14:12:48 +00:00
return interned ;
}
static void browscap_add_kv (
2021-01-15 11:30:54 +00:00
browser_data * bdata , zend_string * key , zend_string * value , bool persistent ) {
2016-12-15 14:12:48 +00:00
if ( bdata - > kv_used = = bdata - > kv_size ) {
bdata - > kv_size * = 2 ;
bdata - > kv = safe_perealloc ( bdata - > kv , sizeof ( browscap_kv ) , bdata - > kv_size , 0 , persistent ) ;
}
bdata - > kv [ bdata - > kv_used ] . key = key ;
bdata - > kv [ bdata - > kv_used ] . value = value ;
bdata - > kv_used + + ;
}
static HashTable * browscap_entry_to_array ( browser_data * bdata , browscap_entry * entry ) {
zval tmp ;
uint32_t i ;
2017-09-19 23:25:56 +00:00
HashTable * ht = zend_new_array ( 8 ) ;
2016-12-15 14:12:48 +00:00
2016-12-15 16:22:08 +00:00
ZVAL_STR ( & tmp , browscap_convert_pattern ( entry - > pattern , 0 ) ) ;
2016-12-15 14:12:48 +00:00
zend_hash_str_add ( ht , " browser_name_regex " , sizeof ( " browser_name_regex " ) - 1 , & tmp ) ;
ZVAL_STR_COPY ( & tmp , entry - > pattern ) ;
zend_hash_str_add ( ht , " browser_name_pattern " , sizeof ( " browser_name_pattern " ) - 1 , & tmp ) ;
2016-12-15 16:22:08 +00:00
if ( entry - > parent ) {
ZVAL_STR_COPY ( & tmp , entry - > parent ) ;
zend_hash_str_add ( ht , " parent " , sizeof ( " parent " ) - 1 , & tmp ) ;
}
2016-12-15 14:12:48 +00:00
for ( i = entry - > kv_start ; i < entry - > kv_end ; i + + ) {
ZVAL_STR_COPY ( & tmp , bdata - > kv [ i ] . value ) ;
zend_hash_add ( ht , bdata - > kv [ i ] . key , & tmp ) ;
}
return ht ;
}
2014-12-13 22:06:14 +00:00
static void php_browscap_parser_cb ( zval * arg1 , zval * arg2 , zval * arg3 , int callback_type , void * arg ) /* { { { */
2000-10-29 23:10:22 +00:00
{
2016-12-15 14:12:48 +00:00
browscap_parser_ctx * ctx = arg ;
browser_data * bdata = ctx - > bdata ;
2017-10-05 23:54:14 +00:00
int persistent = GC_FLAGS ( bdata - > htab ) & IS_ARRAY_PERSISTENT ;
2015-01-03 09:22:58 +00:00
2002-10-14 18:13:18 +00:00
if ( ! arg1 ) {
return ;
}
2000-10-29 23:10:22 +00:00
switch ( callback_type ) {
case ZEND_INI_PARSER_ENTRY :
2016-12-15 14:12:48 +00:00
if ( ctx - > current_entry ! = NULL & & arg2 ) {
zend_string * new_key , * new_value ;
2007-09-28 02:05:10 +00:00
/* Set proper value for true/false settings */
2021-04-08 14:27:51 +00:00
if ( zend_string_equals_literal_ci ( Z_STR_P ( arg2 ) , " on " )
| | zend_string_equals_literal_ci ( Z_STR_P ( arg2 ) , " yes " )
| | zend_string_equals_literal_ci ( Z_STR_P ( arg2 ) , " true " )
2007-09-28 02:05:10 +00:00
) {
2017-11-02 12:56:35 +00:00
new_value = ZSTR_CHAR ( ' 1 ' ) ;
2021-04-08 14:27:51 +00:00
} else if ( zend_string_equals_literal_ci ( Z_STR_P ( arg2 ) , " no " )
| | zend_string_equals_literal_ci ( Z_STR_P ( arg2 ) , " off " )
| | zend_string_equals_literal_ci ( Z_STR_P ( arg2 ) , " none " )
| | zend_string_equals_literal_ci ( Z_STR_P ( arg2 ) , " false " )
2007-09-28 02:05:10 +00:00
) {
2017-11-02 12:56:35 +00:00
new_value = ZSTR_EMPTY_ALLOC ( ) ;
2007-09-28 02:05:10 +00:00
} else { /* Other than true/false setting */
2019-06-21 10:10:09 +00:00
new_value = browscap_intern_str ( ctx , Z_STR_P ( arg2 ) , persistent ) ;
2017-11-02 11:10:40 +00:00
}
2021-04-08 14:27:51 +00:00
if ( zend_string_equals_literal_ci ( Z_STR_P ( arg1 ) , " parent " ) ) {
2021-10-02 16:38:55 +00:00
/* parent entry cannot be same as current section -> causes infinite loop! */
2016-12-15 16:22:08 +00:00
if ( ctx - > current_section_name ! = NULL & &
2021-04-08 14:27:51 +00:00
zend_string_equals_ci ( ctx - > current_section_name , Z_STR_P ( arg2 ) )
2016-12-15 16:22:08 +00:00
) {
zend_error ( E_CORE_ERROR , " Invalid browscap ini file: "
" 'Parent' value cannot be same as the section name: %s "
" (in file %s) " , ZSTR_VAL ( ctx - > current_section_name ) , INI_STR ( " browscap " ) ) ;
return ;
}
if ( ctx - > current_entry - > parent ) {
zend_string_release ( ctx - > current_entry - > parent ) ;
}
2017-11-02 11:10:40 +00:00
2016-12-15 16:22:08 +00:00
ctx - > current_entry - > parent = new_value ;
2014-02-13 13:54:23 +00:00
} else {
2016-12-15 16:22:08 +00:00
new_key = browscap_intern_str_ci ( ctx , Z_STR_P ( arg1 ) , persistent ) ;
browscap_add_kv ( bdata , new_key , new_value , persistent ) ;
ctx - > current_entry - > kv_end = bdata - > kv_used ;
2014-02-13 13:54:23 +00:00
}
2000-10-29 23:10:22 +00:00
}
break ;
2016-12-15 16:22:08 +00:00
case ZEND_INI_PARSER_SECTION :
{
browscap_entry * entry ;
zend_string * pattern = Z_STR_P ( arg1 ) ;
size_t pos ;
int i ;
if ( ZSTR_LEN ( pattern ) > UINT16_MAX ) {
php_error_docref ( NULL , E_WARNING ,
" Skipping excessively long pattern of length %zd " , ZSTR_LEN ( pattern ) ) ;
break ;
}
2017-11-02 11:10:40 +00:00
if ( persistent ) {
pattern = zend_new_interned_string ( zend_string_copy ( pattern ) ) ;
if ( ZSTR_IS_INTERNED ( pattern ) ) {
2018-01-16 22:58:51 +00:00
Z_TYPE_FLAGS_P ( arg1 ) = 0 ;
2017-11-02 11:10:40 +00:00
} else {
zend_string_release ( pattern ) ;
}
}
2016-12-15 16:22:08 +00:00
entry = ctx - > current_entry
= pemalloc ( sizeof ( browscap_entry ) , persistent ) ;
zend_hash_update_ptr ( bdata - > htab , pattern , entry ) ;
2007-11-05 12:27:42 +00:00
2016-12-15 16:22:08 +00:00
if ( ctx - > current_section_name ) {
zend_string_release ( ctx - > current_section_name ) ;
}
ctx - > current_section_name = zend_string_copy ( pattern ) ;
2000-10-29 23:10:22 +00:00
2016-12-15 16:22:08 +00:00
entry - > pattern = zend_string_copy ( pattern ) ;
entry - > kv_end = entry - > kv_start = bdata - > kv_used ;
entry - > parent = NULL ;
2003-03-14 17:54:38 +00:00
2016-12-15 16:22:08 +00:00
pos = entry - > prefix_len = browscap_compute_prefix_len ( pattern ) ;
for ( i = 0 ; i < BROWSCAP_NUM_CONTAINS ; i + + ) {
pos = browscap_compute_contains ( pattern , pos ,
& entry - > contains_start [ i ] , & entry - > contains_len [ i ] ) ;
2000-10-29 23:10:22 +00:00
}
break ;
2016-12-15 16:22:08 +00:00
}
2000-10-29 23:10:22 +00:00
}
}
2001-06-06 13:06:12 +00:00
/* }}} */
2000-10-29 23:10:22 +00:00
2014-12-13 22:06:14 +00:00
static int browscap_read_file ( char * filename , browser_data * browdata , int persistent ) /* { { { */
2000-10-29 23:10:22 +00:00
{
2016-06-23 09:47:06 +00:00
zend_file_handle fh ;
2016-12-15 14:12:48 +00:00
browscap_parser_ctx ctx = { 0 } ;
2021-03-16 17:31:36 +00:00
FILE * fp ;
2015-01-03 09:22:58 +00:00
2011-05-01 18:37:20 +00:00
if ( filename = = NULL | | filename [ 0 ] = = ' \0 ' ) {
return FAILURE ;
}
2015-01-03 09:22:58 +00:00
2021-03-16 17:31:36 +00:00
fp = VCWD_FOPEN ( filename , " r " ) ;
if ( ! fp ) {
2020-09-10 11:28:18 +00:00
zend_error ( E_CORE_WARNING , " Cannot open \" %s \" for reading " , filename ) ;
2011-05-01 18:37:20 +00:00
return FAILURE ;
}
2021-03-16 17:31:36 +00:00
zend_stream_init_fp ( & fh , fp , filename ) ;
2016-12-15 14:12:48 +00:00
browdata - > htab = pemalloc ( sizeof * browdata - > htab , persistent ) ;
2020-08-26 10:57:24 +00:00
zend_hash_init ( browdata - > htab , 0 , NULL ,
persistent ? browscap_entry_dtor_persistent : browscap_entry_dtor , persistent ) ;
2016-12-15 14:12:48 +00:00
browdata - > kv_size = 16 * 1024 ;
browdata - > kv_used = 0 ;
browdata - > kv = pemalloc ( sizeof ( browscap_kv ) * browdata - > kv_size , persistent ) ;
/* Create parser context */
ctx . bdata = browdata ;
ctx . current_entry = NULL ;
ctx . current_section_name = NULL ;
2023-11-08 19:06:18 +00:00
/* No dtor because we don't inc the refcount for the reference stored within the hash table's entry value
* as the hash table is only temporary anyway . */
zend_hash_init ( & ctx . str_interned , 8 , NULL , NULL , persistent ) ;
2016-12-15 14:12:48 +00:00
2023-03-19 21:11:09 +00:00
zend_parse_ini_file ( & fh , persistent , ZEND_INI_SCANNER_RAW ,
2016-12-15 14:12:48 +00:00
( zend_ini_parser_cb_t ) php_browscap_parser_cb , & ctx ) ;
/* Destroy parser context */
if ( ctx . current_section_name ) {
zend_string_release ( ctx . current_section_name ) ;
2011-05-01 18:37:20 +00:00
}
2016-12-15 14:12:48 +00:00
zend_hash_destroy ( & ctx . str_interned ) ;
2021-03-16 17:31:36 +00:00
zend_destroy_file_handle ( & fh ) ;
2015-01-03 09:22:58 +00:00
2011-05-01 18:37:20 +00:00
return SUCCESS ;
}
/* }}} */
# ifdef ZTS
2014-12-13 22:06:14 +00:00
static void browscap_globals_ctor ( zend_browscap_globals * browscap_globals ) /* { { { */
2011-05-01 18:37:20 +00:00
{
browscap_globals - > activation_bdata . htab = NULL ;
2016-12-15 14:12:48 +00:00
browscap_globals - > activation_bdata . kv = NULL ;
2011-05-01 18:37:20 +00:00
browscap_globals - > activation_bdata . filename [ 0 ] = ' \0 ' ;
}
/* }}} */
# endif
2014-12-13 22:06:14 +00:00
static void browscap_bdata_dtor ( browser_data * bdata , int persistent ) /* { { { */
2011-05-01 18:37:20 +00:00
{
if ( bdata - > htab ! = NULL ) {
2016-12-15 14:12:48 +00:00
uint32_t i ;
2011-05-01 18:37:20 +00:00
zend_hash_destroy ( bdata - > htab ) ;
pefree ( bdata - > htab , persistent ) ;
bdata - > htab = NULL ;
2016-12-15 14:12:48 +00:00
for ( i = 0 ; i < bdata - > kv_used ; i + + ) {
zend_string_release ( bdata - > kv [ i ] . key ) ;
zend_string_release ( bdata - > kv [ i ] . value ) ;
}
pefree ( bdata - > kv , persistent ) ;
bdata - > kv = NULL ;
2011-05-01 18:37:20 +00:00
}
bdata - > filename [ 0 ] = ' \0 ' ;
}
/* }}} */
2020-07-01 13:32:55 +00:00
/* {{{ PHP_INI_MH */
2011-05-01 18:37:20 +00:00
PHP_INI_MH ( OnChangeBrowscap )
{
if ( stage = = PHP_INI_STAGE_STARTUP ) {
/* value handled in browscap.c's MINIT */
return SUCCESS ;
} else if ( stage = = PHP_INI_STAGE_ACTIVATE ) {
browser_data * bdata = & BROWSCAP_G ( activation_bdata ) ;
if ( bdata - > filename [ 0 ] ! = ' \0 ' ) {
2014-12-13 22:06:14 +00:00
browscap_bdata_dtor ( bdata , 0 ) ;
2011-05-01 18:37:20 +00:00
}
2015-06-30 01:05:24 +00:00
if ( VCWD_REALPATH ( ZSTR_VAL ( new_value ) , bdata - > filename ) = = NULL ) {
2000-10-29 23:10:22 +00:00
return FAILURE ;
}
2011-05-01 18:37:20 +00:00
return SUCCESS ;
}
2015-01-03 09:22:58 +00:00
2011-05-01 18:37:20 +00:00
return FAILURE ;
}
/* }}} */
2000-10-29 23:10:22 +00:00
2011-05-01 18:37:20 +00:00
PHP_MINIT_FUNCTION ( browscap ) /* {{{ */
{
char * browscap = INI_STR ( " browscap " ) ;
2011-06-03 00:42:07 +00:00
2011-05-01 18:37:20 +00:00
# ifdef ZTS
2015-02-21 03:35:16 +00:00
ts_allocate_id ( & browscap_globals_id , sizeof ( browser_data ) , ( ts_allocate_ctor ) browscap_globals_ctor , NULL ) ;
2011-05-01 18:37:20 +00:00
# endif
/* ctor call not really needed for non-ZTS */
2000-10-29 23:10:22 +00:00
2011-06-03 00:42:07 +00:00
if ( browscap & & browscap [ 0 ] ) {
2014-12-13 22:06:14 +00:00
if ( browscap_read_file ( browscap , & global_bdata , 1 ) = = FAILURE ) {
2011-06-03 00:42:07 +00:00
return FAILURE ;
}
}
2000-10-29 23:10:22 +00:00
return SUCCESS ;
}
2001-06-06 13:06:12 +00:00
/* }}} */
2000-10-29 23:10:22 +00:00
2011-05-01 18:37:20 +00:00
PHP_RSHUTDOWN_FUNCTION ( browscap ) /* {{{ */
2000-10-29 23:10:22 +00:00
{
2011-05-01 18:37:20 +00:00
browser_data * bdata = & BROWSCAP_G ( activation_bdata ) ;
if ( bdata - > filename [ 0 ] ! = ' \0 ' ) {
2014-12-13 22:06:14 +00:00
browscap_bdata_dtor ( bdata , 0 ) ;
2000-10-29 23:10:22 +00:00
}
2015-01-03 09:22:58 +00:00
2011-05-01 18:37:20 +00:00
return SUCCESS ;
}
/* }}} */
PHP_MSHUTDOWN_FUNCTION ( browscap ) /* {{{ */
{
2014-12-13 22:06:14 +00:00
browscap_bdata_dtor ( & global_bdata , 1 ) ;
2015-01-03 09:22:58 +00:00
2000-10-29 23:10:22 +00:00
return SUCCESS ;
}
2001-06-06 13:06:12 +00:00
/* }}} */
2000-10-29 23:10:22 +00:00
2016-12-15 16:22:08 +00:00
static inline size_t browscap_get_minimum_length ( browscap_entry * entry ) {
size_t len = entry - > prefix_len ;
int i ;
for ( i = 0 ; i < BROWSCAP_NUM_CONTAINS ; i + + ) {
len + = entry - > contains_len [ i ] ;
}
return len ;
}
2018-12-18 23:49:56 +00:00
static int browser_reg_compare ( browscap_entry * entry , zend_string * agent_name , browscap_entry * * found_entry_ptr ) /* { { { */
1999-04-17 00:37:12 +00:00
{
2016-12-15 14:12:48 +00:00
browscap_entry * found_entry = * found_entry_ptr ;
2017-02-22 11:09:59 +00:00
ALLOCA_FLAG ( use_heap )
2016-12-15 16:22:08 +00:00
zend_string * pattern_lc , * regex ;
const char * cur ;
int i ;
2016-12-15 14:12:48 +00:00
2017-10-12 10:48:36 +00:00
pcre2_code * re ;
pcre2_match_data * match_data ;
2018-12-26 16:20:13 +00:00
uint32_t capture_count ;
2017-10-12 10:48:36 +00:00
int rc ;
1999-04-17 00:37:12 +00:00
2016-12-15 16:22:08 +00:00
/* Agent name too short */
if ( ZSTR_LEN ( agent_name ) < browscap_get_minimum_length ( entry ) ) {
return 0 ;
2000-07-23 11:32:18 +00:00
}
2004-03-15 21:26:39 +00:00
2016-12-15 16:22:08 +00:00
/* Quickly discard patterns where the prefix doesn't match. */
if ( zend_binary_strcasecmp (
ZSTR_VAL ( agent_name ) , entry - > prefix_len ,
ZSTR_VAL ( entry - > pattern ) , entry - > prefix_len ) ! = 0 ) {
2004-03-15 21:26:39 +00:00
return 0 ;
}
2016-12-15 16:22:08 +00:00
/* Lowercase the pattern, the agent name is already lowercase */
ZSTR_ALLOCA_ALLOC ( pattern_lc , ZSTR_LEN ( entry - > pattern ) , use_heap ) ;
zend_str_tolower_copy ( ZSTR_VAL ( pattern_lc ) , ZSTR_VAL ( entry - > pattern ) , ZSTR_LEN ( entry - > pattern ) ) ;
/* Check if the agent contains the "contains" portions */
cur = ZSTR_VAL ( agent_name ) + entry - > prefix_len ;
for ( i = 0 ; i < BROWSCAP_NUM_CONTAINS ; i + + ) {
if ( entry - > contains_len [ i ] ! = 0 ) {
cur = zend_memnstr ( cur ,
ZSTR_VAL ( pattern_lc ) + entry - > contains_start [ i ] ,
entry - > contains_len [ i ] ,
ZSTR_VAL ( agent_name ) + ZSTR_LEN ( agent_name ) ) ;
if ( ! cur ) {
ZSTR_ALLOCA_FREE ( pattern_lc , use_heap ) ;
return 0 ;
}
cur + = entry - > contains_len [ i ] ;
2003-05-20 17:59:16 +00:00
}
2000-07-23 11:32:18 +00:00
}
2004-03-15 21:26:39 +00:00
2016-12-15 16:22:08 +00:00
/* See if we have an exact match, if so, we're done... */
if ( zend_string_equals ( agent_name , pattern_lc ) ) {
* found_entry_ptr = entry ;
ZSTR_ALLOCA_FREE ( pattern_lc , use_heap ) ;
2018-12-18 23:49:56 +00:00
return 1 ;
2016-12-15 16:22:08 +00:00
}
regex = browscap_convert_pattern ( entry - > pattern , 0 ) ;
2018-12-26 16:20:13 +00:00
re = pcre_get_compiled_regex ( regex , & capture_count ) ;
2008-08-07 12:50:17 +00:00
if ( re = = NULL ) {
2016-12-15 16:22:08 +00:00
ZSTR_ALLOCA_FREE ( pattern_lc , use_heap ) ;
zend_string_release ( regex ) ;
1999-04-17 00:37:12 +00:00
return 0 ;
}
2008-08-07 12:50:17 +00:00
2017-10-12 10:48:36 +00:00
match_data = php_pcre_create_match_data ( capture_count , re ) ;
if ( ! match_data ) {
ZSTR_ALLOCA_FREE ( pattern_lc , use_heap ) ;
zend_string_release ( regex ) ;
return 0 ;
}
2018-12-26 16:20:13 +00:00
rc = pcre2_match ( re , ( PCRE2_SPTR ) ZSTR_VAL ( agent_name ) , ZSTR_LEN ( agent_name ) , 0 , 0 , match_data , php_pcre_mctx ( ) ) ;
2017-10-12 10:48:36 +00:00
php_pcre_free_match_data ( match_data ) ;
2023-02-20 09:56:10 +00:00
if ( rc > = 0 ) {
2004-03-15 21:26:39 +00:00
/* If we've found a possible browser, we need to do a comparison of the
number of characters changed in the user agent being checked versus
the previous match found and the current match . */
2016-12-15 14:12:48 +00:00
if ( found_entry ) {
2014-09-28 19:30:49 +00:00
size_t i , prev_len = 0 , curr_len = 0 ;
2016-12-15 14:12:48 +00:00
zend_string * previous_match = found_entry - > pattern ;
zend_string * current_match = entry - > pattern ;
2004-03-15 21:26:39 +00:00
2016-12-15 14:12:48 +00:00
for ( i = 0 ; i < ZSTR_LEN ( previous_match ) ; i + + ) {
switch ( ZSTR_VAL ( previous_match ) [ i ] ) {
2004-03-15 21:26:39 +00:00
case ' ? ' :
case ' * ' :
/* do nothing, ignore these characters in the count */
break ;
default :
+ + prev_len ;
}
}
2016-12-15 14:12:48 +00:00
for ( i = 0 ; i < ZSTR_LEN ( current_match ) ; i + + ) {
switch ( ZSTR_VAL ( current_match ) [ i ] ) {
2004-03-15 21:26:39 +00:00
case ' ? ' :
case ' * ' :
/* do nothing, ignore these characters in the count */
break ;
default :
+ + curr_len ;
}
}
/* Pick which browser pattern replaces the least amount of
characters when compared to the original user agent string . . . */
2014-09-21 18:54:14 +00:00
if ( prev_len < curr_len ) {
2016-12-15 14:12:48 +00:00
* found_entry_ptr = entry ;
2004-03-15 21:26:39 +00:00
}
2016-12-15 14:12:48 +00:00
} else {
* found_entry_ptr = entry ;
2004-03-15 21:26:39 +00:00
}
}
2016-12-15 16:22:08 +00:00
ZSTR_ALLOCA_FREE ( pattern_lc , use_heap ) ;
zend_string_release ( regex ) ;
1999-04-17 00:37:12 +00:00
return 0 ;
}
2001-06-06 13:06:12 +00:00
/* }}} */
1999-04-17 00:37:12 +00:00
2014-02-13 13:54:23 +00:00
static void browscap_zval_copy_ctor ( zval * p ) /* { { { */
2013-06-06 16:49:04 +00:00
{
2018-01-19 10:41:35 +00:00
if ( Z_REFCOUNTED_P ( p ) ) {
zend_string * str ;
ZEND_ASSERT ( Z_TYPE_P ( p ) = = IS_STRING ) ;
str = Z_STR_P ( p ) ;
if ( ! ( GC_FLAGS ( str ) & GC_PERSISTENT ) ) {
GC_ADDREF ( str ) ;
} else {
ZVAL_NEW_STR ( p , zend_string_init ( ZSTR_VAL ( str ) , ZSTR_LEN ( str ) , 0 ) ) ;
}
}
2014-04-15 17:56:30 +00:00
}
/* }}} */
2013-06-06 16:49:04 +00:00
2020-07-01 13:32:55 +00:00
/* {{{ Get information about the capabilities of a browser. If browser_name is omitted or null, HTTP_USER_AGENT is used. Returns an object by default; if return_array is true, returns an array. */
1999-05-16 11:19:26 +00:00
PHP_FUNCTION ( get_browser )
1999-04-17 00:37:12 +00:00
{
2016-12-15 16:22:08 +00:00
zend_string * agent_name = NULL , * lookup_browser_name ;
2021-01-15 11:30:54 +00:00
bool return_array = 0 ;
2011-05-01 18:37:20 +00:00
browser_data * bdata ;
2016-12-15 14:12:48 +00:00
browscap_entry * found_entry = NULL ;
HashTable * agent_ht ;
1999-04-17 00:37:12 +00:00
2019-11-06 16:50:48 +00:00
ZEND_PARSE_PARAMETERS_START ( 0 , 2 )
Z_PARAM_OPTIONAL
2021-01-14 10:58:08 +00:00
Z_PARAM_STR_OR_NULL ( agent_name )
2019-11-06 16:50:48 +00:00
Z_PARAM_BOOL ( return_array )
ZEND_PARSE_PARAMETERS_END ( ) ;
2011-05-01 18:37:20 +00:00
if ( BROWSCAP_G ( activation_bdata ) . filename [ 0 ] ! = ' \0 ' ) {
bdata = & BROWSCAP_G ( activation_bdata ) ;
if ( bdata - > htab = = NULL ) { /* not initialized yet */
2014-12-13 22:06:14 +00:00
if ( browscap_read_file ( bdata - > filename , bdata , 0 ) = = FAILURE ) {
2011-05-01 18:37:20 +00:00
RETURN_FALSE ;
}
}
} else {
if ( ! global_bdata . htab ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " browscap ini directive not set " ) ;
2011-05-01 18:37:20 +00:00
RETURN_FALSE ;
}
bdata = & global_bdata ;
1999-04-17 00:37:12 +00:00
}
2003-03-14 17:54:38 +00:00
2007-11-05 12:27:42 +00:00
if ( agent_name = = NULL ) {
2016-12-15 16:22:08 +00:00
zval * http_user_agent = NULL ;
if ( Z_TYPE ( PG ( http_globals ) [ TRACK_VARS_SERVER ] ) = = IS_ARRAY
2021-04-09 13:37:59 +00:00
| | zend_is_auto_global ( ZSTR_KNOWN ( ZEND_STR_AUTOGLOBAL_SERVER ) ) ) {
2016-12-15 16:22:08 +00:00
http_user_agent = zend_hash_str_find (
Z_ARRVAL_P ( & PG ( http_globals ) [ TRACK_VARS_SERVER ] ) ,
" HTTP_USER_AGENT " , sizeof ( " HTTP_USER_AGENT " ) - 1 ) ;
}
if ( http_user_agent = = NULL ) {
2014-12-13 22:06:14 +00:00
php_error_docref ( NULL , E_WARNING , " HTTP_USER_AGENT variable is not set, cannot determine user agent name " ) ;
2003-03-14 17:54:38 +00:00
RETURN_FALSE ;
}
2016-12-15 16:22:08 +00:00
agent_name = Z_STR_P ( http_user_agent ) ;
2003-03-14 17:54:38 +00:00
}
2016-12-15 16:22:08 +00:00
lookup_browser_name = zend_string_tolower ( agent_name ) ;
found_entry = zend_hash_find_ptr ( bdata - > htab , lookup_browser_name ) ;
2016-12-15 14:12:48 +00:00
if ( found_entry = = NULL ) {
2018-12-18 23:49:56 +00:00
browscap_entry * entry ;
ZEND_HASH_FOREACH_PTR ( bdata - > htab , entry ) {
if ( browser_reg_compare ( entry , lookup_browser_name , & found_entry ) ) {
break ;
}
} ZEND_HASH_FOREACH_END ( ) ;
2003-03-14 17:54:38 +00:00
2016-12-15 14:12:48 +00:00
if ( found_entry = = NULL ) {
found_entry = zend_hash_str_find_ptr ( bdata - > htab ,
DEFAULT_SECTION_NAME , sizeof ( DEFAULT_SECTION_NAME ) - 1 ) ;
if ( found_entry = = NULL ) {
2018-12-23 19:20:04 +00:00
zend_string_release ( lookup_browser_name ) ;
2016-12-15 14:12:48 +00:00
RETURN_FALSE ;
}
1999-04-17 00:37:12 +00:00
}
}
2016-12-15 14:12:48 +00:00
agent_ht = browscap_entry_to_array ( bdata , found_entry ) ;
2003-03-14 17:54:38 +00:00
if ( return_array ) {
2016-12-15 14:12:48 +00:00
RETVAL_ARR ( agent_ht ) ;
} else {
object_and_properties_init ( return_value , zend_standard_class_def , agent_ht ) ;
2003-03-14 17:54:38 +00:00
}
2003-04-28 21:49:47 +00:00
2016-12-15 16:22:08 +00:00
while ( found_entry - > parent ) {
found_entry = zend_hash_find_ptr ( bdata - > htab , found_entry - > parent ) ;
if ( found_entry = = NULL ) {
1999-04-17 00:37:12 +00:00
break ;
}
2003-03-14 17:54:38 +00:00
2016-12-15 14:12:48 +00:00
agent_ht = browscap_entry_to_array ( bdata , found_entry ) ;
2003-03-14 17:54:38 +00:00
if ( return_array ) {
2016-12-15 14:12:48 +00:00
zend_hash_merge ( Z_ARRVAL_P ( return_value ) , agent_ht , ( copy_ctor_func_t ) browscap_zval_copy_ctor , 0 ) ;
} else {
zend_hash_merge ( Z_OBJPROP_P ( return_value ) , agent_ht , ( copy_ctor_func_t ) browscap_zval_copy_ctor , 0 ) ;
2003-03-14 17:54:38 +00:00
}
2016-12-15 14:12:48 +00:00
zend_hash_destroy ( agent_ht ) ;
efree ( agent_ht ) ;
1999-04-17 00:37:12 +00:00
}
2004-03-15 21:26:39 +00:00
2018-05-28 13:27:12 +00:00
zend_string_release_ex ( lookup_browser_name , 0 ) ;
1999-04-17 00:37:12 +00:00
}
2000-03-29 11:38:47 +00:00
/* }}} */