xref: /PHP-8.3/ext/standard/http.c (revision 37f67a9a)
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