+ switch (*ptr) {
+ case '[':
+ if (++level > PG(max_input_nesting_level)) {
+ zval_ptr_dtor(&arr);
+ php_error_docref(NULL, E_WARNING, "Max input nesting level of %ld exceeded", (long) PG(max_input_nesting_level));
+ return;
+ }
+ if (ptr - var == 0) {
+ ++var;
+ break;
+ }
+ /* no break */
+
+ case ']':
+
+ ZVAL_NULL(&tmp);
+ convert_to_array(cur);
+
+ if (ptr - var) {
+ char chr = *ptr;
+ *ptr = '\0';
+ cur = zend_symtable_str_update(Z_ARRVAL_P(cur), var, ptr - var, &tmp);
+ *ptr = chr;
+ } else {
+ cur = zend_hash_next_index_insert(Z_ARRVAL_P(cur), &tmp);
+ }
+
+ var = NULL;
+ break;
+ }
+
+ ++ptr;
+ }
+
+ if (zend_hash_num_elements(Z_ARRVAL(arr))) {
+ zval_dtor(zv);
+ ZVAL_COPY_VALUE(zv, &arr);
+ } else {
+ zval_ptr_dtor(&arr);
+ }
+}
+
+static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags);
+static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags);
+
+static void prepare_dimension(php_http_buffer_t *buf, php_http_buffer_t *keybuf, zval *zvalue, const char *pss, size_t psl, const char *vss, size_t vsl, unsigned flags)
+{
+ HashTable *ht = HASH_OF(zvalue);
+ php_http_arrkey_t key;
+ zval *val;
+ php_http_buffer_t prefix;
+
+ if (!ZEND_HASH_GET_APPLY_COUNT(ht)) {
+ ZEND_HASH_INC_APPLY_COUNT(ht);
+ php_http_buffer_init(&prefix);
+ php_http_buffer_append(&prefix, keybuf->data, keybuf->used);
+
+ ZEND_HASH_FOREACH_KEY_VAL_IND(ht, key.h, key.key, val)
+ {
+ if (key.key && !*key.key->val) {
+ /* only public properties */
+ continue;
+ }
+
+ php_http_buffer_appends(&prefix, "[");
+ if (key.key) {
+ php_http_buffer_append(&prefix, key.key->val, key.key->len);
+ } else {
+ php_http_buffer_appendf(&prefix, "%lu", key.h);
+ }
+ php_http_buffer_appends(&prefix, "]");
+
+ if (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT) {
+ prepare_dimension(buf, &prefix, val, pss, psl, vss, vsl, flags);
+ } else {
+ zend_string *cpy = zval_get_string(val);
+ zval tmp;
+
+ ZVAL_STR(&tmp, cpy);
+ shift_key(buf, prefix.data, prefix.used, pss, psl, flags);
+ shift_val(buf, &tmp, vss, vsl, flags);
+ zend_string_release(cpy);
+ }
+
+ php_http_buffer_cut(&prefix, keybuf->used, prefix.used - keybuf->used);
+ }
+ ZEND_HASH_FOREACH_END();
+ ZEND_HASH_DEC_APPLY_COUNT(ht);
+
+ php_http_buffer_dtor(&prefix);
+ }
+}
+
+static inline void sanitize_key(unsigned flags, const char *str, size_t len, zval *zv, zend_bool *rfc5987)
+{
+ char *eos;
+ zend_string *zs = zend_string_init(str, len, 0);
+
+ zval_dtor(zv);
+ ZVAL_STR(zv, php_trim(zs, NULL, 0, 3));
+ zend_string_release(zs);
+
+ if (flags & PHP_HTTP_PARAMS_ESCAPED) {
+ sanitize_escaped(zv);
+ }
+
+ if (!Z_STRLEN_P(zv)) {
+ return;
+ }
+
+ eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1];
+ if (*eos == '*') {
+ *eos = '\0';
+ *rfc5987 = 1;
+ Z_STRLEN_P(zv) -= 1;
+ }
+
+ if (flags & PHP_HTTP_PARAMS_URLENCODED) {
+ sanitize_urlencoded(zv);
+ }
+
+ if (flags & PHP_HTTP_PARAMS_DIMENSION) {
+ sanitize_dimension(zv);
+ }
+}
+
+static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1)
+{
+ char *ptr;
+
+ /* examples:
+ * iso-8850-1'de'bl%f6der%20schei%df%21
+ * utf-8'de-DE'bl%c3%b6der%20schei%c3%9f%21
+ */
+
+ switch (Z_STRVAL_P(zv)[0]) {
+ case 'I':
+ case 'i':
+ if (!strncasecmp(Z_STRVAL_P(zv), "iso-8859-1", lenof("iso-8859-1"))) {
+ *latin1 = 1;
+ ptr = Z_STRVAL_P(zv) + lenof("iso-8859-1");
+ break;
+ }
+ /* no break */
+ case 'U':
+ case 'u':
+ if (!strncasecmp(Z_STRVAL_P(zv), "utf-8", lenof("utf-8"))) {
+ *latin1 = 0;
+ ptr = Z_STRVAL_P(zv) + lenof("utf-8");
+ break;
+ }
+ /* no break */
+ default:
+ return;
+ }
+
+ /* extract language */
+ if (*ptr == '\'') {
+ for (*language = ++ptr; *ptr && *ptr != '\''; ++ptr);
+ if (!*ptr) {
+ *language = NULL;
+ return;
+ }
+ *language = estrndup(*language, ptr - *language);
+
+ /* remainder */
+ ptr = estrdup(++ptr);
+ zval_dtor(zv);
+ ZVAL_STR(zv, php_http_cs2zs(ptr, strlen(ptr)));
+ }
+}
+
+static void utf8encode(zval *zv)
+{
+ size_t pos, len = 0;
+ unsigned char *ptr = (unsigned char *) Z_STRVAL_P(zv);
+
+ while (*ptr) {
+ if (*ptr++ >= 0x80) {
+ ++len;
+ }
+ ++len;
+ }
+
+ ptr = safe_emalloc(1, len, 1);
+ for (len = 0, pos = 0; len <= Z_STRLEN_P(zv); ++len, ++pos) {
+ ptr[pos] = Z_STRVAL_P(zv)[len];
+ if ((ptr[pos]) >= 0x80) {
+ ptr[pos + 1] = 0x80 | (ptr[pos] & 0x3f);
+ ptr[pos] = 0xc0 | ((ptr[pos] >> 6) & 0x1f);
+ ++pos;
+ }
+ }
+ zval_dtor(zv);
+ ZVAL_STR(zv, php_http_cs2zs((char *) ptr, pos-1));
+}
+
+static inline void sanitize_value(unsigned flags, const char *str, size_t len, zval *zv, zend_bool rfc5987)
+{
+ char *language = NULL;
+ zend_bool latin1 = 0;
+ zend_string *zs = zend_string_init(str, len, 0);
+
+ zval_dtor(zv);
+ ZVAL_STR(zv, php_trim(zs, NULL, 0, 3));
+ zend_string_release(zs);
+
+ if (rfc5987) {
+ sanitize_rfc5987(zv, &language, &latin1);
+ }
+
+ if (flags & PHP_HTTP_PARAMS_ESCAPED) {
+ sanitize_escaped(zv);
+ }
+
+ if ((flags & PHP_HTTP_PARAMS_URLENCODED) || (rfc5987 && language)) {
+ sanitize_urlencoded(zv);
+ }
+
+ if (rfc5987 && language) {
+ zval tmp;
+
+ if (latin1) {
+ utf8encode(zv);
+ }
+
+ ZVAL_COPY_VALUE(&tmp, zv);
+ array_init(zv);
+ add_assoc_zval(zv, language, &tmp);
+ efree(language);
+ }
+}
+
+static inline void prepare_key(unsigned flags, char *old_key, size_t old_len, char **new_key, size_t *new_len)
+{
+ zval zv;
+
+ ZVAL_STRINGL(&zv, old_key, old_len);
+
+ if (flags & PHP_HTTP_PARAMS_URLENCODED) {
+ prepare_urlencoded(&zv);
+ }
+
+ if (flags & PHP_HTTP_PARAMS_ESCAPED) {
+ prepare_escaped(&zv);
+ }
+
+ *new_key = estrndup(Z_STRVAL(zv), Z_STRLEN(zv));
+ *new_len = Z_STRLEN(zv);
+ zval_ptr_dtor(&zv);
+}
+
+static inline void prepare_value(unsigned flags, zval *zv)
+{
+ if (flags & PHP_HTTP_PARAMS_URLENCODED) {
+ prepare_urlencoded(zv);
+ }
+
+ if (flags & PHP_HTTP_PARAMS_ESCAPED) {
+ prepare_escaped(zv);
+ }
+}
+
+static void merge_param(HashTable *params, zval *zdata, zval **current_param, zval **current_args)
+{
+ zval *ptr, *zdata_ptr;
+ php_http_arrkey_t hkey = {0};
+
+#if 0
+ {
+ zval tmp;
+ INIT_PZVAL_ARRAY(&tmp, params);
+ fprintf(stderr, "params = ");
+ zend_print_zval_r(&tmp, 1 TSRMLS_CC);
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ zend_hash_get_current_key(Z_ARRVAL_P(zdata), &hkey.key, &hkey.h);
+
+ if ((hkey.key && !zend_hash_exists(params, hkey.key))
+ || (!hkey.key && !zend_hash_index_exists(params, hkey.h))
+ ) {
+ zval tmp, arg, *args;
+
+ /* create the entry if it doesn't exist */
+ ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata));
+ Z_TRY_ADDREF_P(ptr);
+ array_init(&tmp);
+ add_assoc_zval_ex(&tmp, ZEND_STRL("value"), ptr);
+
+ array_init(&arg);
+ args = zend_hash_str_update(Z_ARRVAL(tmp), "arguments", lenof("arguments"), &arg);
+ *current_args = args;
+
+ if (hkey.key) {
+ ptr = zend_hash_update(params, hkey.key, &tmp);
+ } else {
+ ptr = zend_hash_index_update(params, hkey.h, &tmp);
+ }
+ } else {
+ /* merge */
+ if (hkey.key) {
+ ptr = zend_hash_find(params, hkey.key);
+ } else {
+ ptr = zend_hash_index_find(params, hkey.h);
+ }
+
+ zdata_ptr = zdata;
+
+ if (Z_TYPE_P(ptr) == IS_ARRAY
+ && (ptr = zend_hash_str_find(Z_ARRVAL_P(ptr), "value", lenof("value")))
+ && (zdata_ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata_ptr)))
+ ) {
+ /*
+ * params = [arr => [value => [0 => 1]]]
+ * ^- ptr
+ * zdata = [arr => [0 => NULL]]
+ * ^- zdata_ptr
+ */
+ zval *test_ptr;
+
+ while (Z_TYPE_P(zdata_ptr) == IS_ARRAY && (test_ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata_ptr)))) {
+ if (Z_TYPE_P(test_ptr) == IS_ARRAY) {
+ /* now find key in ptr */
+ if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(zdata_ptr), &hkey.key, &hkey.h)) {
+ zval *tmp_ptr = ptr;
+
+ if ((ptr = zend_hash_find(Z_ARRVAL_P(ptr), hkey.key))) {
+ zdata_ptr = test_ptr;
+ } else {
+ ptr = tmp_ptr;
+ Z_TRY_ADDREF_P(test_ptr);
+ ptr = zend_hash_update(Z_ARRVAL_P(ptr), hkey.key, test_ptr);
+ break;
+ }
+ } else {
+ if ((ptr = zend_hash_index_find(Z_ARRVAL_P(ptr), hkey.h))) {
+ zdata_ptr = test_ptr;
+ } else if (hkey.h) {
+ Z_TRY_ADDREF_P(test_ptr);
+ ptr = zend_hash_index_update(Z_ARRVAL_P(ptr), hkey.h, test_ptr);
+ break;
+ } else {
+ Z_TRY_ADDREF_P(test_ptr);
+ ptr = zend_hash_next_index_insert(Z_ARRVAL_P(ptr), test_ptr);
+ break;
+ }
+ }
+ } else {
+ /* this is the leaf */
+ Z_TRY_ADDREF_P(test_ptr);
+ if (Z_TYPE_P(ptr) != IS_ARRAY) {
+ zval_dtor(ptr);
+ array_init(ptr);
+ }
+ if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(zdata_ptr), &hkey.key, &hkey.h)) {
+ ptr = zend_hash_update(Z_ARRVAL_P(ptr), hkey.key, test_ptr);
+ } else if (hkey.h) {
+ ptr = zend_hash_index_update(Z_ARRVAL_P(ptr), hkey.h, test_ptr);
+ } else {
+ ptr = zend_hash_next_index_insert(Z_ARRVAL_P(ptr), test_ptr);
+ }
+ break;
+ }
+ }