From 40b8f999b7705c5474030aa67530a8b293f9ea8c Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Thu, 8 Mar 2012 15:48:20 +0000 Subject: [PATCH] sanitized negotiation code --- php_http_negotiate.c | 85 +++++++++++++++++++++++++++++++++-------- phpunit/ParamsTest.php | 2 +- tests/negotiate001.phpt | 4 +- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/php_http_negotiate.c b/php_http_negotiate.c index 69c7393..5661a36 100644 --- a/php_http_negotiate.c +++ b/php_http_negotiate.c @@ -25,19 +25,76 @@ static int php_http_negotiate_sort(const void *a, const void *b TSRMLS_DC) return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0)); } +#define M_PRI 5 +#define M_SEC 2 +#define M_ANY 1 +#define M_NOT 0 +#define M_ALL -1 +static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len) +{ + int match = M_NOT; + + if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) { + /* that was easy */ + match = M_ALL; + } else if (sep_str && sep_len) { + const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len); + size_t param_pri_len = param_sec ? param_sec - param_str : param_len; + const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len); + size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len; + size_t cmp_len = MIN(param_pri_len, supported_pri_len); + + if (((*param_str == '*') || (*supported_str == '*')) + || ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len)) + || ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len)) + ) { + match += M_PRI; + } + + if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) { + match += M_SEC; + } + + if ((param_sec && *(param_sec + sep_len) == '*') + || (supported_sec && *(supported_sec + sep_len) == '*') + || ((*param_str == '*') || (*supported_str == '*')) + ) { + match += M_ANY; + } + } +#if 0 + fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match); +#endif + return match; +} + static int php_http_negotiate_reduce(void *p TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) { - char *tmp; - zval **q, *supported = php_http_ztyp(IS_STRING, *(zval **)p); + unsigned best_match = 0; + HashPosition pos; + php_http_array_hashkey_t key = php_http_array_hashkey_init(0); + zval **q = NULL, **val, *supported = php_http_ztyp(IS_STRING, *(zval **)p); HashTable *params = va_arg(args, HashTable *); HashTable *result = va_arg(args, HashTable *); + const char *sep_str = va_arg(args, const char *); + size_t sep_len = va_arg(args, size_t); + + FOREACH_HASH_KEYVAL(pos, params, key, val) { + if (key.type == HASH_KEY_IS_STRING) { + unsigned match = php_http_negotiate_match(key.str, key.len-1, Z_STRVAL_P(supported), Z_STRLEN_P(supported), sep_str, sep_len); - tmp = php_strtolower(estrndup(Z_STRVAL_P(supported), Z_STRLEN_P(supported)), Z_STRLEN_P(supported)); - if (SUCCESS == zend_symtable_find(params, tmp, Z_STRLEN_P(supported) + 1, (void *) &q)) { + if (match > best_match) { + best_match = match; + q = val; + } + } + } + + if (q && Z_DVAL_PP(q)) { Z_ADDREF_PP(q); - zend_symtable_update(result, Z_STRVAL_P(supported), Z_STRLEN_P(supported) + 1, (void *) q, sizeof(zval *), NULL); + zend_hash_update(result, Z_STRVAL_P(supported), Z_STRLEN_P(supported) + 1, (void *) q, sizeof(zval *), NULL); } - efree(tmp); + zval_ptr_dtor(&supported); return ZEND_HASH_APPLY_KEEP; } @@ -75,7 +132,7 @@ PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_l q = Z_DVAL_P(tmp); zval_ptr_dtor(&tmp); - if (!q) { + if (0&&!q) { STR_FREE(key.str); continue; } @@ -84,15 +141,7 @@ PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_l } if (key.type == HASH_KEY_IS_STRING) { - const char *ptr; - - php_strtolower(key.str, key.len - 1); add_assoc_double_ex(&arr, key.str, key.len, q); - - if (primary_sep_str && primary_sep_len && (ptr = php_http_locate_str(key.str, key.len - 1, primary_sep_str, primary_sep_len))) { - key.str[ptr - key.str] = '\0'; - add_assoc_double_ex(&arr, key.str, ptr - key.str + 1, q - i / 1000.0); - } } else { add_index_double(&arr, key.num, q); } @@ -100,9 +149,13 @@ PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_l STR_FREE(key.str); } +#if 0 + zend_print_zval_r(&arr, 1 TSRMLS_CC); +#endif + ALLOC_HASHTABLE(result); zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_apply_with_arguments(supported TSRMLS_CC, php_http_negotiate_reduce, 2, Z_ARRVAL(arr), result); + zend_hash_apply_with_arguments(supported TSRMLS_CC, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len); zend_hash_destroy(¶ms); zval_dtor(&arr); zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC); diff --git a/phpunit/ParamsTest.php b/phpunit/ParamsTest.php index 5647a19..a06d72e 100644 --- a/phpunit/ParamsTest.php +++ b/phpunit/ParamsTest.php @@ -47,7 +47,7 @@ class ParamsTest extends PHPUnit_Framework_TestCase { ), $p->params ); - $this->assertEquals("form-data;name=upload;filename=\"trick\\\"\0\\\"ed\"", (string) $p); + $this->assertEquals("form-data;name=upload;filename=\"trick\\\"\\0\\\"ed\"", (string) $p); } function testEmpty() { diff --git a/tests/negotiate001.phpt b/tests/negotiate001.phpt index b71efe0..d7bd755 100644 --- a/tests/negotiate001.phpt +++ b/tests/negotiate001.phpt @@ -3,7 +3,7 @@ negotiate --SKIPIF-- --ENV-- -HTTP_ACCEPT=text/html,text/plain,text/xml;q=0.1,*/*;q=0 +HTTP_ACCEPT=text/html,text/plain,text/xml;q=0.1,image/*;q=0.1,*/*;q=0 HTTP_ACCEPT_CHARSET=utf-8,iso-8859-1;q=0.8,iso-8859-15;q=0 HTTP_ACCEPT_ENCODING=gzip,deflate;q=0 HTTP_ACCEPT_LANGUAGE=de-DE,de-AT;q=0.9,en;q=0.8,fr;q=0 @@ -95,7 +95,7 @@ LANGUAGE de: Array ( - [de] => 0.899 + [de] => 0.99 [en] => 0.8 ) de-DE: Array -- 2.30.2