Merge branch 'v2.6.x'
[m6w6/ext-http] / src / php_http_header.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data)
16 {
17 php_http_header_parser_t ctx;
18 php_http_buffer_t buf;
19 php_http_header_parser_state_t rs;
20
21 if (!php_http_buffer_from_string_ex(&buf, header, length)) {
22 php_error_docref(NULL, E_WARNING, "Could not allocate buffer");
23 return FAILURE;
24 }
25
26 if (!php_http_header_parser_init(&ctx)) {
27 php_http_buffer_dtor(&buf);
28 php_error_docref(NULL, E_WARNING, "Could not initialize header parser");
29 return FAILURE;
30 }
31
32 rs = php_http_header_parser_parse(&ctx, &buf, PHP_HTTP_HEADER_PARSER_CLEANUP, headers, callback_func, callback_data);
33 php_http_header_parser_dtor(&ctx);
34 php_http_buffer_dtor(&buf);
35
36 return rs == PHP_HTTP_HEADER_PARSER_STATE_FAILURE ? FAILURE : SUCCESS;
37 }
38
39 void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg)
40 {
41 php_http_arrkey_t key;
42 zval *header;
43
44 ZEND_HASH_FOREACH_KEY_VAL(headers, key.h, key.key, header)
45 {
46 if (key.key) {
47 php_http_header_to_callback_ex(key.key->val, header, crlf, cb, cb_arg);
48 }
49 }
50 ZEND_HASH_FOREACH_END();
51 /*
52 <<<<<<< HEAD
53 php_http_arrkey_t key;
54 zval *header, *single_header;
55
56 ZEND_HASH_FOREACH_KEY_VAL(headers, key.h, key.key, header)
57 {
58 if (key.key) {
59 if (zend_string_equals_literal(key.key, "Set-Cookie") && Z_TYPE_P(header) == IS_ARRAY) {
60 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(header), single_header)
61 {
62 if (Z_TYPE_P(single_header) == IS_ARRAY) {
63 php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, single_header);
64
65 if (cookie) {
66 char *buf;
67 size_t len;
68
69 php_http_cookie_list_to_string(cookie, &buf, &len);
70 cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", buf);
71 php_http_cookie_list_free(&cookie);
72 efree(buf);
73 }
74 } else {
75 zend_string *zs = php_http_header_value_to_string(single_header);
76
77 cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", zs->val);
78 zend_string_release(zs);
79 }
80 }
81 ZEND_HASH_FOREACH_END();
82 } else {
83 zend_string *zs = php_http_header_value_to_string(header);
84
85 cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.key->val, zs->val);
86 zend_string_release(zs);
87 }
88 =======
89 >>>>>>> 343738ad56eb70017704fdac57cf0d74da3d0f2e
90 */
91 }
92
93 void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers)
94 {
95 php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str);
96 }
97
98 void php_http_header_to_callback_ex(const char *key, zval *val, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg)
99 {
100 zval *aval;
101 zend_string *str;
102
103 switch (Z_TYPE_P(val)) {
104 case IS_ARRAY:
105 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), aval)
106 {
107 php_http_header_to_callback_ex(key, aval, crlf, cb, cb_arg);
108 }
109 ZEND_HASH_FOREACH_END();
110 break;
111
112 case IS_TRUE:
113 cb(cb_arg, "%s: true%s", key, crlf ? PHP_HTTP_CRLF:"");
114 break;
115
116 case IS_FALSE:
117 cb(cb_arg, "%s: false%s", key, crlf ? PHP_HTTP_CRLF:"");
118 break;
119
120 default:
121 str = zval_get_string(val);
122 cb(cb_arg, "%s: %s%s", key, str->val, crlf ? PHP_HTTP_CRLF:"");
123 zend_string_release(str);
124 break;
125 }
126 }
127
128 void php_http_header_to_string_ex(php_http_buffer_t *str, const char *key, zval *val)
129 {
130 php_http_header_to_callback_ex(key, val, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str);
131 }
132
133 zend_string *php_http_header_value_array_to_string(zval *header)
134 {
135 zval *val;
136 php_http_buffer_t str;
137
138 php_http_buffer_init(&str);
139 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(header), val)
140 {
141 zend_string *zs = php_http_header_value_to_string(val);
142
143 php_http_buffer_appendf(&str, str.used ? ", %s":"%s", zs->val);
144 zend_string_release(zs);
145 }
146 ZEND_HASH_FOREACH_END();
147 php_http_buffer_fix(&str);
148
149 return php_http_cs2zs(str.data, str.used);
150 }
151
152 zend_string *php_http_header_value_to_string(zval *header)
153 {
154 switch (Z_TYPE_P(header)) {
155 case IS_TRUE:
156 return zend_string_init(ZEND_STRL("true"), 0);
157 case IS_FALSE:
158 return zend_string_init(ZEND_STRL("false"), 0);
159 case IS_ARRAY:
160 return php_http_header_value_array_to_string(header);
161 default:
162 return zval_get_string(header);
163 }
164 }
165
166 static zend_class_entry *php_http_header_class_entry;
167 zend_class_entry *php_http_header_get_class_entry(void)
168 {
169 return php_http_header_class_entry;
170 }
171
172 ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader___construct, 0, 0, 0)
173 ZEND_ARG_INFO(0, name)
174 ZEND_ARG_INFO(0, value)
175 ZEND_END_ARG_INFO();
176 PHP_METHOD(HttpHeader, __construct)
177 {
178 char *name_str = NULL, *value_str = NULL;
179 size_t name_len = 0, value_len = 0;
180
181 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return);
182
183 if (name_str && name_len) {
184 char *pretty_str = estrndup(name_str, name_len);
185 zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), php_http_pretty_key(pretty_str, name_len, 1, 1), name_len);
186 efree(pretty_str);
187 }
188 if (value_str && value_len) {
189 zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("value"), value_str, value_len);
190 }
191 }
192
193 ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_serialize, 0, 0, 0)
194 ZEND_END_ARG_INFO();
195 PHP_METHOD(HttpHeader, serialize)
196 {
197 if (SUCCESS == zend_parse_parameters_none()) {
198 php_http_buffer_t buf;
199 zend_string *zs;
200 zval name_tmp, value_tmp;
201
202 php_http_buffer_init(&buf);
203 zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0, &name_tmp));
204 php_http_buffer_appendz(&buf, zs);
205 zend_string_release(zs);
206
207 zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0, &value_tmp));
208 if (zs->len) {
209 php_http_buffer_appends(&buf, ": ");
210 php_http_buffer_appendz(&buf, zs);
211 } else {
212 php_http_buffer_appends(&buf, ":");
213 }
214 zend_string_release(zs);
215
216 RETURN_STR(php_http_cs2zs(buf.data, buf.used));
217 }
218 RETURN_EMPTY_STRING();
219 }
220
221 ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_unserialize, 0, 0, 1)
222 ZEND_ARG_INFO(0, serialized)
223 ZEND_END_ARG_INFO();
224 PHP_METHOD(HttpHeader, unserialize)
225 {
226 char *serialized_str;
227 size_t serialized_len;
228
229 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &serialized_str, &serialized_len)) {
230 HashTable ht;
231
232 zend_hash_init(&ht, 1, NULL, ZVAL_PTR_DTOR, 0);
233 if (SUCCESS == php_http_header_parse(serialized_str, serialized_len, &ht, NULL, NULL)) {
234 if (zend_hash_num_elements(&ht)) {
235 zend_string *zs, *key;
236 zend_ulong idx;
237
238 zend_hash_internal_pointer_reset(&ht);
239 switch (zend_hash_get_current_key(&ht, &key, &idx)) {
240 case HASH_KEY_IS_STRING:
241 zend_update_property_str(php_http_header_class_entry, getThis(), ZEND_STRL("name"), key);
242 break;
243 case HASH_KEY_IS_LONG:
244 zend_update_property_long(php_http_header_class_entry, getThis(), ZEND_STRL("name"), idx);
245 break;
246 default:
247 break;
248 }
249 zs = zval_get_string(zend_hash_get_current_data(&ht));
250 zend_update_property_str(php_http_header_class_entry, getThis(), ZEND_STRL("value"), zs);
251 zend_string_release(zs);
252 }
253 }
254 zend_hash_destroy(&ht);
255 }
256
257 }
258
259 ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_match, 0, 0, 1)
260 ZEND_ARG_INFO(0, value)
261 ZEND_ARG_INFO(0, flags)
262 ZEND_END_ARG_INFO();
263 PHP_METHOD(HttpHeader, match)
264 {
265 char *val_str = NULL;
266 size_t val_len = 0;
267 zend_long flags = PHP_HTTP_MATCH_LOOSE;
268 zend_string *zs;
269 zval value_tmp;
270
271 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|sl", &val_str, &val_len, &flags)) {
272 return;
273 }
274
275 zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0, &value_tmp));
276 RETVAL_BOOL(php_http_match(zs->val, val_str, flags));
277 zend_string_release(zs);
278 }
279
280 ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_negotiate, 0, 0, 1)
281 ZEND_ARG_INFO(0, supported)
282 ZEND_ARG_INFO(1, result)
283 ZEND_END_ARG_INFO();
284 PHP_METHOD(HttpHeader, negotiate)
285 {
286 HashTable *supported, *rs;
287 zval name_tmp, value_tmp, *rs_array = NULL;
288 zend_string *zs;
289 char *sep_str = NULL;
290 size_t sep_len = 0;
291
292 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "H|z", &supported, &rs_array)) {
293 return;
294 }
295 if (rs_array) {
296 ZVAL_DEREF(rs_array);
297 zval_dtor(rs_array);
298 array_init(rs_array);
299 }
300
301 zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0, &name_tmp));
302 if (zend_string_equals_literal(zs, "Accept")) {
303 sep_str = "/";
304 sep_len = 1;
305 } else if (zend_string_equals_literal(zs, "Accept-Language")) {
306 sep_str = "-";
307 sep_len = 1;
308 }
309 zend_string_release(zs);
310
311 zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0, &value_tmp));
312 if ((rs = php_http_negotiate(zs->val, zs->len, supported, sep_str, sep_len))) {
313 PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs, supported, rs_array);
314 } else {
315 PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array);
316 }
317 zend_string_release(zs);
318 }
319
320 ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_getParams, 0, 0, 0)
321 ZEND_ARG_INFO(0, param_sep)
322 ZEND_ARG_INFO(0, arg_sep)
323 ZEND_ARG_INFO(0, val_sep)
324 ZEND_ARG_INFO(0, flags)
325 ZEND_END_ARG_INFO();
326 PHP_METHOD(HttpHeader, getParams)
327 {
328 zval value_tmp, zctor, zparams_obj, *zargs = NULL;
329
330 ZVAL_STRINGL(&zctor, "__construct", lenof("__construct"));
331
332 object_init_ex(&zparams_obj, php_http_params_get_class_entry());
333
334 zargs = (zval *) ecalloc(ZEND_NUM_ARGS()+1, sizeof(zval));
335 ZVAL_COPY_VALUE(&zargs[0], zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0, &value_tmp));
336 if (ZEND_NUM_ARGS()) {
337 zend_get_parameters_array(ZEND_NUM_ARGS(), ZEND_NUM_ARGS(), &zargs[1]);
338 }
339
340 if (SUCCESS == call_user_function(NULL, &zparams_obj, &zctor, return_value, ZEND_NUM_ARGS()+1, zargs)) {
341 RETVAL_ZVAL(&zparams_obj, 0, 1);
342 }
343
344 zval_ptr_dtor(&zctor);
345 if (zargs) {
346 efree(zargs);
347 }
348 }
349
350 ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_parse, 0, 0, 1)
351 ZEND_ARG_INFO(0, string)
352 ZEND_ARG_INFO(0, header_class)
353 ZEND_END_ARG_INFO();
354 PHP_METHOD(HttpHeader, parse)
355 {
356 char *header_str;
357 size_t header_len;
358 zend_class_entry *ce = NULL;
359
360 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|C", &header_str, &header_len, &ce)) {
361 array_init(return_value);
362
363 if (SUCCESS != php_http_header_parse(header_str, header_len, Z_ARRVAL_P(return_value), NULL, NULL)) {
364 zval_dtor(return_value);
365 RETURN_FALSE;
366 } else {
367 if (ce && instanceof_function(ce, php_http_header_class_entry)) {
368 php_http_arrkey_t key;
369 zval *val;
370
371 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(return_value), key.h, key.key, val)
372 {
373 zval zkey, zho;
374
375 if (key.key) {
376 ZVAL_STR_COPY(&zkey, key.key);
377 } else {
378 ZVAL_LONG(&zkey, key.h);
379 }
380
381 object_init_ex(&zho, ce);
382 Z_TRY_ADDREF_P(val);
383 zend_call_method_with_2_params(&zho, ce, NULL, "__construct", NULL, &zkey, val);
384 zval_ptr_dtor(val);
385 zval_ptr_dtor(&zkey);
386
387 if (key.key) {
388 add_assoc_zval_ex(return_value, key.key->val, key.key->len, &zho);
389 } else {
390 add_index_zval(return_value, key.h, &zho);
391 }
392 }
393 ZEND_HASH_FOREACH_END();
394 }
395 }
396 }
397 }
398
399 static zend_function_entry php_http_header_methods[] = {
400 PHP_ME(HttpHeader, __construct, ai_HttpHeader___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
401 PHP_ME(HttpHeader, serialize, ai_HttpHeader_serialize, ZEND_ACC_PUBLIC)
402 ZEND_MALIAS(HttpHeader, __toString, serialize, ai_HttpHeader_serialize, ZEND_ACC_PUBLIC)
403 ZEND_MALIAS(HttpHeader, toString, serialize, ai_HttpHeader_serialize, ZEND_ACC_PUBLIC)
404 PHP_ME(HttpHeader, unserialize, ai_HttpHeader_unserialize, ZEND_ACC_PUBLIC)
405 PHP_ME(HttpHeader, match, ai_HttpHeader_match, ZEND_ACC_PUBLIC)
406 PHP_ME(HttpHeader, negotiate, ai_HttpHeader_negotiate, ZEND_ACC_PUBLIC)
407 PHP_ME(HttpHeader, getParams, ai_HttpHeader_getParams, ZEND_ACC_PUBLIC)
408 PHP_ME(HttpHeader, parse, ai_HttpHeader_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
409 EMPTY_FUNCTION_ENTRY
410 };
411
412 PHP_MINIT_FUNCTION(http_header)
413 {
414 zend_class_entry ce = {0};
415
416 INIT_NS_CLASS_ENTRY(ce, "http", "Header", php_http_header_methods);
417 php_http_header_class_entry = zend_register_internal_class(&ce);
418 zend_class_implements(php_http_header_class_entry, 1, zend_ce_serializable);
419 zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_LOOSE"), PHP_HTTP_MATCH_LOOSE);
420 zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_CASE"), PHP_HTTP_MATCH_CASE);
421 zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_WORD"), PHP_HTTP_MATCH_WORD);
422 zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_FULL"), PHP_HTTP_MATCH_FULL);
423 zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_STRICT"), PHP_HTTP_MATCH_STRICT);
424 zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC);
425 zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("value"), ZEND_ACC_PUBLIC);
426
427 return SUCCESS;
428 }
429
430 /*
431 * Local variables:
432 * tab-width: 4
433 * c-basic-offset: 4
434 * End:
435 * vim600: noet sw=4 ts=4 fdm=marker
436 * vim<600: noet sw=4 ts=4
437 */
438