xref: /PHP-8.4/ext/mbstring/mb_gpc.c (revision fd2d8696)
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    | Author: Rui Hirokawa <hirokawa@php.net>                              |
14    |         Moriyoshi Koizumi <moriyoshi@php.net>                        |
15    +----------------------------------------------------------------------+
16  */
17 
18 /* {{{ includes */
19 #include "php.h"
20 #include "php_variables.h"
21 #include "libmbfl/mbfl/mbfilter_pass.h"
22 #include "mbstring.h"
23 #include "ext/standard/url.h"
24 
25 #include "php_globals.h"
26 #include "TSRM.h"
27 
28 #include "mb_gpc.h"
29 /* }}} */
30 
ZEND_EXTERN_MODULE_GLOBALS(mbstring)31 ZEND_EXTERN_MODULE_GLOBALS(mbstring)
32 
33 /* {{{ MBSTRING_API SAPI_TREAT_DATA_FUNC(mbstr_treat_data)
34  * http input processing */
35 MBSTRING_API SAPI_TREAT_DATA_FUNC(mbstr_treat_data)
36 {
37 	char *res = NULL, *separator=NULL;
38 	const char *c_var;
39 	zval v_array;
40 	int free_buffer=0;
41 	const mbfl_encoding *detected;
42 	php_mb_encoding_handler_info_t info;
43 
44 	if (!MBSTRG(encoding_translation)) {
45 		php_default_treat_data(arg, str, destArray);
46 		return;
47 	}
48 
49 	switch (arg) {
50 		case PARSE_POST:
51 		case PARSE_GET:
52 		case PARSE_COOKIE:
53 			array_init(&v_array);
54 			switch (arg) {
55 				case PARSE_POST:
56 					ZVAL_COPY_VALUE(&PG(http_globals)[TRACK_VARS_POST], &v_array);
57 					break;
58 				case PARSE_GET:
59 					ZVAL_COPY_VALUE(&PG(http_globals)[TRACK_VARS_GET], &v_array);
60 					break;
61 				case PARSE_COOKIE:
62 					ZVAL_COPY_VALUE(&PG(http_globals)[TRACK_VARS_COOKIE], &v_array);
63 					break;
64 			}
65 			break;
66 		default:
67 			ZVAL_COPY_VALUE(&v_array, destArray);
68 			break;
69 	}
70 
71 	switch (arg) {
72 		case PARSE_POST:
73 			sapi_handle_post(&v_array);
74 			return;
75 		case PARSE_GET: /* GET data */
76 			c_var = SG(request_info).query_string;
77 			if (c_var && *c_var) {
78 				res = (char *) estrdup(c_var);
79 				free_buffer = 1;
80 			}
81 			break;
82 		case PARSE_COOKIE: /* Cookie data */
83 			c_var = SG(request_info).cookie_data;
84 			if (c_var && *c_var) {
85 				res = (char *) estrdup(c_var);
86 				free_buffer = 1;
87 			}
88 			break;
89 		case PARSE_STRING: /* String data */
90 			res = str;
91 			free_buffer = 1;
92 			break;
93 	}
94 
95 	if (!res) {
96 		return;
97 	}
98 
99 	switch (arg) {
100 		case PARSE_POST:
101 		case PARSE_GET:
102 		case PARSE_STRING:
103 			separator = (char *) estrdup(PG(arg_separator).input);
104 			break;
105 		case PARSE_COOKIE:
106 			separator = ";\0";
107 			break;
108 	}
109 
110 	switch (arg) {
111 		case PARSE_POST:
112 			MBSTRG(http_input_identify_post) = NULL;
113 			break;
114 		case PARSE_GET:
115 			MBSTRG(http_input_identify_get) = NULL;
116 			break;
117 		case PARSE_COOKIE:
118 			MBSTRG(http_input_identify_cookie) = NULL;
119 			break;
120 		case PARSE_STRING:
121 			MBSTRG(http_input_identify_string) = NULL;
122 			break;
123 	}
124 
125 	info.data_type              = arg;
126 	info.separator              = separator;
127 	info.report_errors          = false;
128 	info.to_encoding            = MBSTRG(internal_encoding);
129 	info.from_encodings         = MBSTRG(http_input_list);
130 	info.num_from_encodings     = MBSTRG(http_input_list_size);
131 
132 	MBSTRG(illegalchars) = 0;
133 
134 	detected = _php_mb_encoding_handler_ex(&info, &v_array, res);
135 	MBSTRG(http_input_identify) = detected;
136 
137 	if (detected) {
138 		switch(arg){
139 		case PARSE_POST:
140 			MBSTRG(http_input_identify_post) = detected;
141 			break;
142 		case PARSE_GET:
143 			MBSTRG(http_input_identify_get) = detected;
144 			break;
145 		case PARSE_COOKIE:
146 			MBSTRG(http_input_identify_cookie) = detected;
147 			break;
148 		case PARSE_STRING:
149 			MBSTRG(http_input_identify_string) = detected;
150 			break;
151 		}
152 	}
153 
154 	if (arg != PARSE_COOKIE) {
155 		efree(separator);
156 	}
157 
158 	if (free_buffer) {
159 		efree(res);
160 	}
161 }
162 /* }}} */
163 
164 /* {{{ mbfl_no_encoding _php_mb_encoding_handler_ex() */
_php_mb_encoding_handler_ex(const php_mb_encoding_handler_info_t * info,zval * array_ptr,char * res)165 const mbfl_encoding *_php_mb_encoding_handler_ex(const php_mb_encoding_handler_info_t *info, zval *array_ptr, char *res)
166 {
167 	char *var, *val;
168 	char *strtok_buf = NULL, **val_list = NULL;
169 	size_t n, num = 1, *len_list = NULL;
170 	size_t new_val_len;
171 	const mbfl_encoding *from_encoding = NULL;
172 
173 	if (!res || *res == '\0') {
174 		goto out;
175 	}
176 
177 	/* count variables contained in `res`.
178 	 * separator may contain multiple separator chars; ANY of them demarcate variables */
179 	for (char *s1 = res; *s1; s1++) {
180 		for (const char *s2 = info->separator; *s2; s2++) {
181 			if (*s1 == *s2) {
182 				num++;
183 			}
184 		}
185 	}
186 	num *= 2; /* need space for variable name and value */
187 
188 	val_list = (char **)ecalloc(num, sizeof(char *));
189 	len_list = (size_t *)ecalloc(num, sizeof(size_t));
190 
191 	/* split and decode the query */
192 	n = 0;
193 	var = php_strtok_r(res, info->separator, &strtok_buf);
194 	while (var)  {
195 		val = strchr(var, '=');
196 		if (val) { /* have a value */
197 			len_list[n] = php_url_decode(var, val-var);
198 			val_list[n] = var;
199 			n++;
200 
201 			*val++ = '\0';
202 			val_list[n] = val;
203 			len_list[n] = php_url_decode(val, strlen(val));
204 		} else {
205 			len_list[n] = php_url_decode(var, strlen(var));
206 			val_list[n] = var;
207 			n++;
208 
209 			val_list[n] = "";
210 			len_list[n] = 0;
211 		}
212 		n++;
213 		var = php_strtok_r(NULL, info->separator, &strtok_buf);
214 	}
215 
216 	zend_long max_input_vars = REQUEST_PARSE_BODY_OPTION_GET(max_input_vars, PG(max_input_vars));
217 	if (ZEND_SIZE_T_GT_ZEND_LONG(n, max_input_vars * 2)) {
218 		php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", max_input_vars);
219 		goto out;
220 	}
221 
222 	num = n; /* make sure to process initialized vars only */
223 
224 	/* initialize converter */
225 	if (info->num_from_encodings == 0) {
226 		from_encoding = &mbfl_encoding_pass;
227 	} else if (info->num_from_encodings == 1) {
228 		from_encoding = info->from_encodings[0];
229 	} else {
230 		from_encoding = mb_guess_encoding_for_strings((const unsigned char**)val_list, len_list, num, info->from_encodings, info->num_from_encodings, MBSTRG(strict_detection), false);
231 		if (!from_encoding) {
232 			if (info->report_errors) {
233 				php_error_docref(NULL, E_WARNING, "Unable to detect encoding");
234 			}
235 			from_encoding = &mbfl_encoding_pass;
236 		}
237 	}
238 
239 	/* convert encoding */
240 	n = 0;
241 	while (n < num) {
242 		if (from_encoding != &mbfl_encoding_pass && info->to_encoding != &mbfl_encoding_pass) {
243 			unsigned int num_errors = 0;
244 			zend_string *converted_var = mb_fast_convert((unsigned char*)val_list[n], len_list[n], from_encoding, info->to_encoding, MBSTRG(current_filter_illegal_substchar), MBSTRG(current_filter_illegal_mode), &num_errors);
245 			MBSTRG(illegalchars) += num_errors;
246 			n++;
247 
248 			num_errors = 0;
249 			zend_string *converted_val = mb_fast_convert((unsigned char*)val_list[n], len_list[n], from_encoding, info->to_encoding, MBSTRG(current_filter_illegal_substchar), MBSTRG(current_filter_illegal_mode), &num_errors);
250 			MBSTRG(illegalchars) += num_errors;
251 			n++;
252 
253 			/* `val` must be a pointer returned by `emalloc` */
254 			val = estrndup(ZSTR_VAL(converted_val), ZSTR_LEN(converted_val));
255 			if (sapi_module.input_filter(info->data_type, ZSTR_VAL(converted_var), &val, ZSTR_LEN(converted_val), &new_val_len)) {
256 				/* add variable to symbol table */
257 				php_register_variable_safe(ZSTR_VAL(converted_var), val, new_val_len, array_ptr);
258 			}
259 			zend_string_free(converted_var);
260 			zend_string_free(converted_val);
261 		} else {
262 			var = val_list[n++];
263 			val = estrndup(val_list[n], len_list[n]);
264 			if (sapi_module.input_filter(info->data_type, var, &val, len_list[n], &new_val_len)) {
265 				php_register_variable_safe(var, val, new_val_len, array_ptr);
266 			}
267 			n++;
268 		}
269 		efree(val);
270 	}
271 
272 out:
273 	if (val_list != NULL) {
274 		efree((void *)val_list);
275 	}
276 	if (len_list != NULL) {
277 		efree((void *)len_list);
278 	}
279 
280 	return from_encoding;
281 }
282 /* }}} */
283 
284 /* {{{ SAPI_POST_HANDLER_FUNC(php_mb_post_handler) */
SAPI_POST_HANDLER_FUNC(php_mb_post_handler)285 SAPI_POST_HANDLER_FUNC(php_mb_post_handler)
286 {
287 	const mbfl_encoding *detected;
288 	php_mb_encoding_handler_info_t info;
289 	zend_string *post_data_str = NULL;
290 
291 	MBSTRG(http_input_identify_post) = NULL;
292 
293 	info.data_type              = PARSE_POST;
294 	info.separator              = "&";
295 	info.report_errors          = false;
296 	info.to_encoding            = MBSTRG(internal_encoding);
297 	info.from_encodings         = MBSTRG(http_input_list);
298 	info.num_from_encodings     = MBSTRG(http_input_list_size);
299 
300 	php_stream_rewind(SG(request_info).request_body);
301 	post_data_str = php_stream_copy_to_mem(SG(request_info).request_body, PHP_STREAM_COPY_ALL, 0);
302 	detected = _php_mb_encoding_handler_ex(&info, arg, post_data_str ? ZSTR_VAL(post_data_str) : NULL);
303 	if (post_data_str) {
304 		zend_string_release_ex(post_data_str, 0);
305 	}
306 
307 	MBSTRG(http_input_identify) = detected;
308 	if (detected) {
309 		MBSTRG(http_input_identify_post) = detected;
310 	}
311 }
312 /* }}} */
313