1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Sara Golemon <pollita@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php_http.h"
18 #include "php_ini.h"
19 #include "url.h"
20
php_url_encode_scalar(zval * scalar,smart_str * form_str,int encoding_type,zend_ulong index_int,const char * index_string,size_t index_string_len,const char * num_prefix,size_t num_prefix_len,const zend_string * key_prefix,const zend_string * arg_sep)21 static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
22 int encoding_type, zend_ulong index_int,
23 const char *index_string, size_t index_string_len,
24 const char *num_prefix, size_t num_prefix_len,
25 const zend_string *key_prefix,
26 const zend_string *arg_sep)
27 {
28 if (form_str->s) {
29 smart_str_append(form_str, arg_sep);
30 }
31 /* Simple key=value */
32 if (key_prefix) {
33 smart_str_append(form_str, key_prefix);
34 }
35 if (index_string) {
36 zend_string *encoded_key;
37 if (encoding_type == PHP_QUERY_RFC3986) {
38 encoded_key = php_raw_url_encode(index_string, index_string_len);
39 } else {
40 encoded_key = php_url_encode(index_string, index_string_len);
41 }
42 smart_str_append(form_str, encoded_key);
43 zend_string_free(encoded_key);
44 } else {
45 /* Numeric key */
46 if (num_prefix) {
47 smart_str_appendl(form_str, num_prefix, num_prefix_len);
48 }
49 smart_str_append_long(form_str, index_int);
50 }
51 if (key_prefix) {
52 smart_str_appendl(form_str, "%5D", strlen("%5D"));
53 }
54 smart_str_appendc(form_str, '=');
55
56 switch (Z_TYPE_P(scalar)) {
57 case IS_STRING: {
58 zend_string *encoded_data;
59 if (encoding_type == PHP_QUERY_RFC3986) {
60 encoded_data = php_raw_url_encode(Z_STRVAL_P(scalar), Z_STRLEN_P(scalar));
61 } else {
62 encoded_data = php_url_encode(Z_STRVAL_P(scalar), Z_STRLEN_P(scalar));
63 }
64 smart_str_append(form_str, encoded_data);
65 zend_string_free(encoded_data);
66 break;
67 }
68 case IS_LONG:
69 smart_str_append_long(form_str, Z_LVAL_P(scalar));
70 break;
71 case IS_DOUBLE: {
72 zend_string *encoded_data;
73 zend_string *tmp = zend_double_to_str(Z_DVAL_P(scalar));
74 if (encoding_type == PHP_QUERY_RFC3986) {
75 encoded_data = php_raw_url_encode(ZSTR_VAL(tmp), ZSTR_LEN(tmp));
76 } else {
77 encoded_data = php_url_encode(ZSTR_VAL(tmp), ZSTR_LEN(tmp));
78 }
79 smart_str_append(form_str, encoded_data);
80 zend_string_free(tmp);
81 zend_string_free(encoded_data);
82 break;
83 }
84 case IS_FALSE:
85 smart_str_appendc(form_str, '0');
86 break;
87 case IS_TRUE:
88 smart_str_appendc(form_str, '1');
89 break;
90 /* All possible types are either handled here or previously */
91 EMPTY_SWITCH_DEFAULT_CASE();
92 }
93 }
94
95 /* {{{ php_url_encode_hash */
php_url_encode_hash_ex(HashTable * ht,smart_str * formstr,const char * num_prefix,size_t num_prefix_len,const zend_string * key_prefix,zval * type,const zend_string * arg_sep,int enc_type)96 PHPAPI void php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
97 const char *num_prefix, size_t num_prefix_len,
98 const zend_string *key_prefix,
99 zval *type, const zend_string *arg_sep, int enc_type)
100 {
101 zend_string *key = NULL;
102 const char *prop_name;
103 size_t prop_len;
104 zend_ulong idx;
105 zval *zdata = NULL;
106 ZEND_ASSERT(ht);
107
108 if (GC_IS_RECURSIVE(ht)) {
109 /* Prevent recursion */
110 return;
111 }
112
113 if (!arg_sep) {
114 arg_sep = zend_ini_str("arg_separator.output", strlen("arg_separator.output"), false);
115 if (ZSTR_LEN(arg_sep) == 0) {
116 arg_sep = ZSTR_CHAR('&');
117 }
118 }
119
120 ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zdata) {
121 bool is_dynamic = 1;
122 if (Z_TYPE_P(zdata) == IS_INDIRECT) {
123 zdata = Z_INDIRECT_P(zdata);
124 if (Z_ISUNDEF_P(zdata)) {
125 continue;
126 }
127
128 is_dynamic = 0;
129 }
130
131 /* handling for private & protected object properties */
132 if (key) {
133 prop_name = ZSTR_VAL(key);
134 prop_len = ZSTR_LEN(key);
135
136 if (type != NULL && zend_check_property_access(Z_OBJ_P(type), key, is_dynamic) != SUCCESS) {
137 /* property not visible in this scope */
138 continue;
139 }
140
141 if (ZSTR_VAL(key)[0] == '\0' && type != NULL) {
142 const char *tmp;
143 zend_unmangle_property_name_ex(key, &tmp, &prop_name, &prop_len);
144 } else {
145 prop_name = ZSTR_VAL(key);
146 prop_len = ZSTR_LEN(key);
147 }
148 } else {
149 prop_name = NULL;
150 prop_len = 0;
151 }
152
153 ZVAL_DEREF(zdata);
154 if (Z_TYPE_P(zdata) == IS_ARRAY || Z_TYPE_P(zdata) == IS_OBJECT) {
155 zend_string *new_prefix;
156 if (key) {
157 zend_string *encoded_key;
158 if (enc_type == PHP_QUERY_RFC3986) {
159 encoded_key = php_raw_url_encode(prop_name, prop_len);
160 } else {
161 encoded_key = php_url_encode(prop_name, prop_len);
162 }
163
164 if (key_prefix) {
165 new_prefix = zend_string_concat3(ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix), ZSTR_VAL(encoded_key), ZSTR_LEN(encoded_key), "%5D%5B", strlen("%5D%5B"));
166 } else {
167 new_prefix = zend_string_concat2(ZSTR_VAL(encoded_key), ZSTR_LEN(encoded_key), "%5B", strlen("%5B"));
168 }
169 zend_string_release_ex(encoded_key, false);
170 } else { /* is integer index */
171 char *index_int_as_str;
172 size_t index_int_as_str_len;
173
174 index_int_as_str_len = spprintf(&index_int_as_str, 0, ZEND_LONG_FMT, idx);
175
176 if (key_prefix && num_prefix) {
177 /* zend_string_concat4() */
178 size_t len = ZSTR_LEN(key_prefix) + num_prefix_len + index_int_as_str_len + strlen("%5D%5B");
179 new_prefix = zend_string_alloc(len, 0);
180
181 memcpy(ZSTR_VAL(new_prefix), ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix));
182 memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix), num_prefix, num_prefix_len);
183 memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix) + num_prefix_len, index_int_as_str, index_int_as_str_len);
184 memcpy(ZSTR_VAL(new_prefix) + ZSTR_LEN(key_prefix) + num_prefix_len +index_int_as_str_len, "%5D%5B", strlen("%5D%5B"));
185 ZSTR_VAL(new_prefix)[len] = '\0';
186 } else if (key_prefix) {
187 new_prefix = zend_string_concat3(ZSTR_VAL(key_prefix), ZSTR_LEN(key_prefix), index_int_as_str, index_int_as_str_len, "%5D%5B", strlen("%5D%5B"));
188 } else if (num_prefix) {
189 new_prefix = zend_string_concat3(num_prefix, num_prefix_len, index_int_as_str, index_int_as_str_len, "%5B", strlen("%5B"));
190 } else {
191 new_prefix = zend_string_concat2(index_int_as_str, index_int_as_str_len, "%5B", strlen("%5B"));
192 }
193 efree(index_int_as_str);
194 }
195 GC_TRY_PROTECT_RECURSION(ht);
196 php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, new_prefix, (Z_TYPE_P(zdata) == IS_OBJECT ? zdata : NULL), arg_sep, enc_type);
197 GC_TRY_UNPROTECT_RECURSION(ht);
198 zend_string_release_ex(new_prefix, false);
199 } else if (Z_TYPE_P(zdata) == IS_NULL || Z_TYPE_P(zdata) == IS_RESOURCE) {
200 /* Skip these types */
201 continue;
202 } else {
203 php_url_encode_scalar(zdata, formstr,
204 enc_type, idx,
205 prop_name, prop_len,
206 num_prefix, num_prefix_len,
207 key_prefix,
208 arg_sep);
209 }
210 } ZEND_HASH_FOREACH_END();
211 }
212 /* }}} */
213
214 /* If there is a prefix we need to close the key with an encoded ] ("%5D") */
215 /* {{{ Generates a form-encoded query string from an associative array or object. */
PHP_FUNCTION(http_build_query)216 PHP_FUNCTION(http_build_query)
217 {
218 zval *formdata;
219 char *prefix = NULL;
220 size_t prefix_len = 0;
221 zend_string *arg_sep = NULL;
222 smart_str formstr = {0};
223 zend_long enc_type = PHP_QUERY_RFC1738;
224
225 ZEND_PARSE_PARAMETERS_START(1, 4)
226 Z_PARAM_ARRAY_OR_OBJECT(formdata)
227 Z_PARAM_OPTIONAL
228 Z_PARAM_STRING(prefix, prefix_len)
229 Z_PARAM_STR_OR_NULL(arg_sep)
230 Z_PARAM_LONG(enc_type)
231 ZEND_PARSE_PARAMETERS_END();
232
233 php_url_encode_hash_ex(HASH_OF(formdata), &formstr, prefix, prefix_len, /* key_prefix */ NULL, (Z_TYPE_P(formdata) == IS_OBJECT ? formdata : NULL), arg_sep, (int)enc_type);
234
235 RETURN_STR(smart_str_extract(&formstr));
236 }
237 /* }}} */
238