1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Sara Golemon <pollita@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 #include "php_http.h"
22 #include "php_ini.h"
23 #include "url.h"
24
25 #define URL_DEFAULT_ARG_SEP "&"
26
27 /* {{{ php_url_encode_hash */
php_url_encode_hash_ex(HashTable * ht,smart_str * formstr,const char * num_prefix,size_t num_prefix_len,const char * key_prefix,size_t key_prefix_len,const char * key_suffix,size_t key_suffix_len,zval * type,char * arg_sep,int enc_type)28 PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
29 const char *num_prefix, size_t num_prefix_len,
30 const char *key_prefix, size_t key_prefix_len,
31 const char *key_suffix, size_t key_suffix_len,
32 zval *type, char *arg_sep, int enc_type)
33 {
34 zend_string *key = NULL;
35 char *newprefix, *p;
36 const char *prop_name;
37 size_t arg_sep_len, newprefix_len, prop_len;
38 zend_ulong idx;
39 zval *zdata = NULL;
40
41 if (!ht) {
42 return FAILURE;
43 }
44
45 if (ht->u.v.nApplyCount > 0) {
46 /* Prevent recursion */
47 return SUCCESS;
48 }
49
50 if (!arg_sep) {
51 arg_sep = INI_STR("arg_separator.output");
52 if (!arg_sep || !strlen(arg_sep)) {
53 arg_sep = URL_DEFAULT_ARG_SEP;
54 }
55 }
56 arg_sep_len = strlen(arg_sep);
57
58 ZEND_HASH_FOREACH_KEY_VAL_IND(ht, idx, key, zdata) {
59 /* handling for private & protected object properties */
60 if (key) {
61 if (ZSTR_VAL(key)[0] == '\0' && type != NULL) {
62 const char *tmp;
63
64 zend_object *zobj = Z_OBJ_P(type);
65 if (zend_check_property_access(zobj, key) != SUCCESS) {
66 /* private or protected property access outside of the class */
67 continue;
68 }
69 zend_unmangle_property_name_ex(key, &tmp, &prop_name, &prop_len);
70 } else {
71 prop_name = ZSTR_VAL(key);
72 prop_len = ZSTR_LEN(key);
73 }
74 } else {
75 prop_name = NULL;
76 prop_len = 0;
77 }
78
79 ZVAL_DEREF(zdata);
80 if (Z_TYPE_P(zdata) == IS_ARRAY || Z_TYPE_P(zdata) == IS_OBJECT) {
81 if (key) {
82 zend_string *ekey;
83 if (enc_type == PHP_QUERY_RFC3986) {
84 ekey = php_raw_url_encode(prop_name, prop_len);
85 } else {
86 ekey = php_url_encode(prop_name, prop_len);
87 }
88 newprefix_len = key_suffix_len + ZSTR_LEN(ekey) + key_prefix_len + 3 /* %5B */;
89 newprefix = emalloc(newprefix_len + 1);
90 p = newprefix;
91
92 if (key_prefix) {
93 memcpy(p, key_prefix, key_prefix_len);
94 p += key_prefix_len;
95 }
96
97 memcpy(p, ZSTR_VAL(ekey), ZSTR_LEN(ekey));
98 p += ZSTR_LEN(ekey);
99 zend_string_free(ekey);
100
101 if (key_suffix) {
102 memcpy(p, key_suffix, key_suffix_len);
103 p += key_suffix_len;
104 }
105 *(p++) = '%';
106 *(p++) = '5';
107 *(p++) = 'B';
108 *p = '\0';
109 } else {
110 char *ekey;
111 size_t ekey_len;
112 /* Is an integer key */
113 ekey_len = spprintf(&ekey, 0, "%pd", idx);
114 newprefix_len = key_prefix_len + num_prefix_len + ekey_len + key_suffix_len + 3 /* %5B */;
115 newprefix = emalloc(newprefix_len + 1);
116 p = newprefix;
117
118 if (key_prefix) {
119 memcpy(p, key_prefix, key_prefix_len);
120 p += key_prefix_len;
121 }
122
123 memcpy(p, num_prefix, num_prefix_len);
124 p += num_prefix_len;
125
126 memcpy(p, ekey, ekey_len);
127 p += ekey_len;
128 efree(ekey);
129
130 if (key_suffix) {
131 memcpy(p, key_suffix, key_suffix_len);
132 p += key_suffix_len;
133 }
134 *(p++) = '%';
135 *(p++) = '5';
136 *(p++) = 'B';
137 *p = '\0';
138 }
139 if (ZEND_HASH_APPLY_PROTECTION(ht)) {
140 ht->u.v.nApplyCount++;
141 }
142 php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, newprefix, newprefix_len, "%5D", 3, (Z_TYPE_P(zdata) == IS_OBJECT ? zdata : NULL), arg_sep, enc_type);
143 if (ZEND_HASH_APPLY_PROTECTION(ht)) {
144 ht->u.v.nApplyCount--;
145 }
146 efree(newprefix);
147 } else if (Z_TYPE_P(zdata) == IS_NULL || Z_TYPE_P(zdata) == IS_RESOURCE) {
148 /* Skip these types */
149 continue;
150 } else {
151 if (formstr->s) {
152 smart_str_appendl(formstr, arg_sep, arg_sep_len);
153 }
154 /* Simple key=value */
155 smart_str_appendl(formstr, key_prefix, key_prefix_len);
156 if (key) {
157 zend_string *ekey;
158 if (enc_type == PHP_QUERY_RFC3986) {
159 ekey = php_raw_url_encode(prop_name, prop_len);
160 } else {
161 ekey = php_url_encode(prop_name, prop_len);
162 }
163 smart_str_append(formstr, ekey);
164 zend_string_free(ekey);
165 } else {
166 /* Numeric key */
167 if (num_prefix) {
168 smart_str_appendl(formstr, num_prefix, num_prefix_len);
169 }
170 smart_str_append_long(formstr, idx);
171 }
172 smart_str_appendl(formstr, key_suffix, key_suffix_len);
173 smart_str_appendl(formstr, "=", 1);
174 switch (Z_TYPE_P(zdata)) {
175 case IS_STRING: {
176 zend_string *ekey;
177 if (enc_type == PHP_QUERY_RFC3986) {
178 ekey = php_raw_url_encode(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata));
179 } else {
180 ekey = php_url_encode(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata));
181 }
182 smart_str_append(formstr, ekey);
183 zend_string_free(ekey);
184 }
185 break;
186 case IS_LONG:
187 smart_str_append_long(formstr, Z_LVAL_P(zdata));
188 break;
189 case IS_FALSE:
190 smart_str_appendl(formstr, "0", sizeof("0")-1);
191 break;
192 case IS_TRUE:
193 smart_str_appendl(formstr, "1", sizeof("1")-1);
194 break;
195 case IS_DOUBLE:
196 {
197 char *ekey;
198 size_t ekey_len;
199 ekey_len = spprintf(&ekey, 0, "%.*G", (int) EG(precision), Z_DVAL_P(zdata));
200 smart_str_appendl(formstr, ekey, ekey_len);
201 efree(ekey);
202 }
203 break;
204 default:
205 {
206 zend_string *ekey;
207 zend_string *tmp = zval_get_string(zdata);
208 if (enc_type == PHP_QUERY_RFC3986) {
209 ekey = php_raw_url_encode(ZSTR_VAL(tmp), ZSTR_LEN(tmp));
210 } else {
211 ekey = php_url_encode(ZSTR_VAL(tmp), ZSTR_LEN(tmp));
212 }
213 smart_str_append(formstr, ekey);
214 zend_string_release(tmp);
215 zend_string_free(ekey);
216 }
217 }
218 }
219 } ZEND_HASH_FOREACH_END();
220
221 return SUCCESS;
222 }
223 /* }}} */
224
225 /* {{{ proto string http_build_query(mixed formdata [, string prefix [, string arg_separator [, int enc_type]]])
226 Generates a form-encoded query string from an associative array or object. */
PHP_FUNCTION(http_build_query)227 PHP_FUNCTION(http_build_query)
228 {
229 zval *formdata;
230 char *prefix = NULL, *arg_sep=NULL;
231 size_t arg_sep_len = 0, prefix_len = 0;
232 smart_str formstr = {0};
233 zend_long enc_type = PHP_QUERY_RFC1738;
234
235 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|ssl", &formdata, &prefix, &prefix_len, &arg_sep, &arg_sep_len, &enc_type) != SUCCESS) {
236 RETURN_FALSE;
237 }
238
239 if (Z_TYPE_P(formdata) != IS_ARRAY && Z_TYPE_P(formdata) != IS_OBJECT) {
240 php_error_docref(NULL, E_WARNING, "Parameter 1 expected to be Array or Object. Incorrect value given");
241 RETURN_FALSE;
242 }
243
244 if (php_url_encode_hash_ex(HASH_OF(formdata), &formstr, prefix, prefix_len, NULL, 0, NULL, 0, (Z_TYPE_P(formdata) == IS_OBJECT ? formdata : NULL), arg_sep, (int)enc_type) == FAILURE) {
245 if (formstr.s) {
246 smart_str_free(&formstr);
247 }
248 RETURN_FALSE;
249 }
250
251 if (!formstr.s) {
252 RETURN_EMPTY_STRING();
253 }
254
255 smart_str_0(&formstr);
256
257 RETURN_NEW_STR(formstr.s);
258 }
259 /* }}} */
260
261 /*
262 * Local variables:
263 * tab-width: 4
264 * c-basic-offset: 4
265 * End:
266 * vim600: sw=4 ts=4 fdm=marker
267 * vim<600: sw=4 ts=4
268 */
269
270