xref: /PHP-7.1/ext/standard/http.c (revision 03f3b847)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 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, ZEND_LONG_FMT, 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