sanitized negotiation code
authorMichael Wallner <mike@php.net>
Thu, 8 Mar 2012 15:48:20 +0000 (15:48 +0000)
committerMichael Wallner <mike@php.net>
Thu, 8 Mar 2012 15:48:20 +0000 (15:48 +0000)
php_http_negotiate.c
phpunit/ParamsTest.php
tests/negotiate001.phpt

index 69c73934b02e9dbec810236bd0a5b99bed8895d4..5661a3609de57f57503568f5fcad596c977be04a 100644 (file)
@@ -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(&params);
                zval_dtor(&arr);
                zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC);
index 5647a199ad2a96f78a57dcc89bfc9a959c3e6610..a06d72e3a71c7917af9fff93c7ea9ccedebcf684 100644 (file)
@@ -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() {
index b71efe03756845cfbf290d67ecd825d710ae7257..d7bd755d300733b245c26168e8a06feacf758aed 100644 (file)
@@ -3,7 +3,7 @@ negotiate
 --SKIPIF--
 <?php include "skipif.inc"; ?>
 --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