xref: /PHP-7.4/ext/standard/http.c (revision 317dfab8)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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 #include "php_http.h"
20 #include "php_ini.h"
21 #include "url.h"
22 
23 #define URL_DEFAULT_ARG_SEP "&"
24 
25 /* {{{ 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)26 PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr,
27 				const char *num_prefix, size_t num_prefix_len,
28 				const char *key_prefix, size_t key_prefix_len,
29 				const char *key_suffix, size_t key_suffix_len,
30 			  zval *type, char *arg_sep, int enc_type)
31 {
32 	zend_string *key = NULL;
33 	char *newprefix, *p;
34 	const char *prop_name;
35 	size_t arg_sep_len, newprefix_len, prop_len;
36 	zend_ulong idx;
37 	zval *zdata = NULL;
38 
39 	if (!ht) {
40 		return FAILURE;
41 	}
42 
43 	if (GC_IS_RECURSIVE(ht)) {
44 		/* Prevent recursion */
45 		return SUCCESS;
46 	}
47 
48 	if (!arg_sep) {
49 		arg_sep = INI_STR("arg_separator.output");
50 		if (!arg_sep || !strlen(arg_sep)) {
51 			arg_sep = URL_DEFAULT_ARG_SEP;
52 		}
53 	}
54 	arg_sep_len = strlen(arg_sep);
55 
56 	ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zdata) {
57 		zend_bool is_dynamic = 1;
58 		if (Z_TYPE_P(zdata) == IS_INDIRECT) {
59 			zdata = Z_INDIRECT_P(zdata);
60 			if (Z_ISUNDEF_P(zdata)) {
61 				continue;
62 			}
63 
64 			is_dynamic = 0;
65 		}
66 
67 		/* handling for private & protected object properties */
68 		if (key) {
69 			prop_name = ZSTR_VAL(key);
70 			prop_len = ZSTR_LEN(key);
71 
72 			if (type != NULL && zend_check_property_access(Z_OBJ_P(type), key, is_dynamic) != SUCCESS) {
73 				/* property not visible in this scope */
74 				continue;
75 			}
76 
77 			if (ZSTR_VAL(key)[0] == '\0' && type != NULL) {
78 				const char *tmp;
79 				zend_unmangle_property_name_ex(key, &tmp, &prop_name, &prop_len);
80 			} else {
81 				prop_name = ZSTR_VAL(key);
82 				prop_len = ZSTR_LEN(key);
83 			}
84 		} else {
85 			prop_name = NULL;
86 			prop_len = 0;
87 		}
88 
89 		ZVAL_DEREF(zdata);
90 		if (Z_TYPE_P(zdata) == IS_ARRAY || Z_TYPE_P(zdata) == IS_OBJECT) {
91 			if (key) {
92 				zend_string *ekey;
93 				if (enc_type == PHP_QUERY_RFC3986) {
94 					ekey = php_raw_url_encode(prop_name, prop_len);
95 				} else {
96 					ekey = php_url_encode(prop_name, prop_len);
97 				}
98 				newprefix_len = key_suffix_len + ZSTR_LEN(ekey) + key_prefix_len + 3 /* %5B */;
99 				newprefix = emalloc(newprefix_len + 1);
100 				p = newprefix;
101 
102 				if (key_prefix) {
103 					memcpy(p, key_prefix, key_prefix_len);
104 					p += key_prefix_len;
105 				}
106 
107 				memcpy(p, ZSTR_VAL(ekey), ZSTR_LEN(ekey));
108 				p += ZSTR_LEN(ekey);
109 				zend_string_free(ekey);
110 
111 				if (key_suffix) {
112 					memcpy(p, key_suffix, key_suffix_len);
113 					p += key_suffix_len;
114 				}
115 				*(p++) = '%';
116 				*(p++) = '5';
117 				*(p++) = 'B';
118 				*p = '\0';
119 			} else {
120 				char *ekey;
121 				size_t ekey_len;
122 				/* Is an integer key */
123 				ekey_len = spprintf(&ekey, 0, ZEND_LONG_FMT, idx);
124 				newprefix_len = key_prefix_len + num_prefix_len + ekey_len + key_suffix_len + 3 /* %5B */;
125 				newprefix = emalloc(newprefix_len + 1);
126 				p = newprefix;
127 
128 				if (key_prefix) {
129 					memcpy(p, key_prefix, key_prefix_len);
130 					p += key_prefix_len;
131 				}
132 
133 				if (num_prefix) {
134 					memcpy(p, num_prefix, num_prefix_len);
135 					p += num_prefix_len;
136 				}
137 
138 				memcpy(p, ekey, ekey_len);
139 				p += ekey_len;
140 				efree(ekey);
141 
142 				if (key_suffix) {
143 					memcpy(p, key_suffix, key_suffix_len);
144 					p += key_suffix_len;
145 				}
146 				*(p++) = '%';
147 				*(p++) = '5';
148 				*(p++) = 'B';
149 				*p = '\0';
150 			}
151 			if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
152 				GC_PROTECT_RECURSION(ht);
153 			}
154 			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);
155 			if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
156 				GC_UNPROTECT_RECURSION(ht);
157 			}
158 			efree(newprefix);
159 		} else if (Z_TYPE_P(zdata) == IS_NULL || Z_TYPE_P(zdata) == IS_RESOURCE) {
160 			/* Skip these types */
161 			continue;
162 		} else {
163 			if (formstr->s) {
164 				smart_str_appendl(formstr, arg_sep, arg_sep_len);
165 			}
166 			/* Simple key=value */
167 			if (key_prefix) {
168 				smart_str_appendl(formstr, key_prefix, key_prefix_len);
169 			}
170 			if (key) {
171 				zend_string *ekey;
172 				if (enc_type == PHP_QUERY_RFC3986) {
173 					ekey = php_raw_url_encode(prop_name, prop_len);
174 				} else {
175 					ekey = php_url_encode(prop_name, prop_len);
176 				}
177 				smart_str_append(formstr, ekey);
178 				zend_string_free(ekey);
179 			} else {
180 				/* Numeric key */
181 				if (num_prefix) {
182 					smart_str_appendl(formstr, num_prefix, num_prefix_len);
183 				}
184 				smart_str_append_long(formstr, idx);
185 			}
186 			if (key_suffix) {
187 				smart_str_appendl(formstr, key_suffix, key_suffix_len);
188 			}
189 			smart_str_appendl(formstr, "=", 1);
190 			switch (Z_TYPE_P(zdata)) {
191 				case IS_STRING: {
192 						zend_string *ekey;
193 						if (enc_type == PHP_QUERY_RFC3986) {
194 							ekey = php_raw_url_encode(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata));
195 						} else {
196 							ekey = php_url_encode(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata));
197 						}
198 						smart_str_append(formstr, ekey);
199 						zend_string_free(ekey);
200 					}
201 					break;
202 				case IS_LONG:
203 					smart_str_append_long(formstr, Z_LVAL_P(zdata));
204 					break;
205 				case IS_FALSE:
206 					smart_str_appendl(formstr, "0", sizeof("0")-1);
207 					break;
208 				case IS_TRUE:
209 					smart_str_appendl(formstr, "1", sizeof("1")-1);
210 					break;
211 				default:
212 					{
213 						zend_string *ekey;
214 						zend_string *tmp;
215 						zend_string *str= zval_get_tmp_string(zdata, &tmp);
216 						if (enc_type == PHP_QUERY_RFC3986) {
217 							ekey = php_raw_url_encode(ZSTR_VAL(str), ZSTR_LEN(str));
218 						} else {
219 							ekey = php_url_encode(ZSTR_VAL(str), ZSTR_LEN(str));
220 						}
221 						smart_str_append(formstr, ekey);
222 						zend_tmp_string_release(tmp);
223 						zend_string_free(ekey);
224 					}
225 			}
226 		}
227 	} ZEND_HASH_FOREACH_END();
228 
229 	return SUCCESS;
230 }
231 /* }}} */
232 
233 /* {{{ proto string http_build_query(mixed formdata [, string prefix [, string arg_separator [, int enc_type]]])
234    Generates a form-encoded query string from an associative array or object. */
PHP_FUNCTION(http_build_query)235 PHP_FUNCTION(http_build_query)
236 {
237 	zval *formdata;
238 	char *prefix = NULL, *arg_sep=NULL;
239 	size_t arg_sep_len = 0, prefix_len = 0;
240 	smart_str formstr = {0};
241 	zend_long enc_type = PHP_QUERY_RFC1738;
242 
243 	ZEND_PARSE_PARAMETERS_START(1, 4)
244 		Z_PARAM_ARRAY_OR_OBJECT(formdata)
245 		Z_PARAM_OPTIONAL
246 		Z_PARAM_STRING(prefix, prefix_len)
247 		Z_PARAM_STRING(arg_sep, arg_sep_len)
248 		Z_PARAM_LONG(enc_type)
249 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
250 
251 	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) {
252 		if (formstr.s) {
253 			smart_str_free(&formstr);
254 		}
255 		RETURN_FALSE;
256 	}
257 
258 	if (!formstr.s) {
259 		RETURN_EMPTY_STRING();
260 	}
261 
262 	smart_str_0(&formstr);
263 
264 	RETURN_NEW_STR(formstr.s);
265 }
266 /* }}} */
267