- fix unsetting response headers
[m6w6/ext-http] / http_message_object.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-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_SAPI
16 #define HTTP_WANT_CURL
17 #define HTTP_WANT_MAGIC
18 #include "php_http.h"
19
20 #ifdef ZEND_ENGINE_2
21
22 #include "zend_interfaces.h"
23 #include "ext/standard/url.h"
24
25 #include "php_http_api.h"
26 #include "php_http_send_api.h"
27 #include "php_http_url_api.h"
28 #include "php_http_message_api.h"
29 #include "php_http_message_object.h"
30 #include "php_http_exception_object.h"
31 #include "php_http_response_object.h"
32 #include "php_http_request_method_api.h"
33 #include "php_http_request_api.h"
34 #include "php_http_request_object.h"
35
36 #if defined(HTTP_HAVE_SPL) && !defined(WONKY)
37 /* SPL doesn't install its headers */
38 extern PHPAPI zend_class_entry *spl_ce_Countable;
39 #endif
40
41 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpMessage, method, 0, req_args)
42 #define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpMessage, method, 0)
43 #define HTTP_MESSAGE_ME(method, visibility) PHP_ME(HttpMessage, method, HTTP_ARGS(HttpMessage, method), visibility)
44
45 HTTP_BEGIN_ARGS(__construct, 0)
46 HTTP_ARG_VAL(message, 0)
47 HTTP_END_ARGS;
48
49 HTTP_BEGIN_ARGS(fromString, 1)
50 HTTP_ARG_VAL(message, 0)
51 HTTP_END_ARGS;
52
53 HTTP_EMPTY_ARGS(getBody);
54 HTTP_BEGIN_ARGS(setBody, 1)
55 HTTP_ARG_VAL(body, 0)
56 HTTP_END_ARGS;
57
58 HTTP_BEGIN_ARGS(getHeader, 1)
59 HTTP_ARG_VAL(header, 0)
60 HTTP_END_ARGS;
61
62 HTTP_EMPTY_ARGS(getHeaders);
63 HTTP_BEGIN_ARGS(setHeaders, 1)
64 HTTP_ARG_VAL(headers, 0)
65 HTTP_END_ARGS;
66
67 HTTP_BEGIN_ARGS(addHeaders, 1)
68 HTTP_ARG_VAL(headers, 0)
69 HTTP_ARG_VAL(append, 0)
70 HTTP_END_ARGS;
71
72 HTTP_EMPTY_ARGS(getType);
73 HTTP_BEGIN_ARGS(setType, 1)
74 HTTP_ARG_VAL(type, 0)
75 HTTP_END_ARGS;
76
77 HTTP_EMPTY_ARGS(getResponseCode);
78 HTTP_BEGIN_ARGS(setResponseCode, 1)
79 HTTP_ARG_VAL(response_code, 0)
80 HTTP_END_ARGS;
81
82 HTTP_EMPTY_ARGS(getResponseStatus);
83 HTTP_BEGIN_ARGS(setResponseStatus, 1)
84 HTTP_ARG_VAL(response_status, 0)
85 HTTP_END_ARGS;
86
87 HTTP_EMPTY_ARGS(getRequestMethod);
88 HTTP_BEGIN_ARGS(setRequestMethod, 1)
89 HTTP_ARG_VAL(request_method, 0)
90 HTTP_END_ARGS;
91
92 HTTP_EMPTY_ARGS(getRequestUrl);
93 HTTP_BEGIN_ARGS(setRequestUrl, 1)
94 HTTP_ARG_VAL(url, 0)
95 HTTP_END_ARGS;
96
97 HTTP_EMPTY_ARGS(getHttpVersion);
98 HTTP_BEGIN_ARGS(setHttpVersion, 1)
99 HTTP_ARG_VAL(http_version, 0)
100 HTTP_END_ARGS;
101
102 HTTP_BEGIN_ARGS(guessContentType, 1)
103 HTTP_ARG_VAL(magic_file, 0)
104 HTTP_ARG_VAL(magic_mode, 0)
105 HTTP_END_ARGS;
106
107 HTTP_EMPTY_ARGS(getParentMessage);
108 HTTP_EMPTY_ARGS(send);
109 HTTP_BEGIN_ARGS(toString, 0)
110 HTTP_ARG_VAL(include_parent, 0)
111 HTTP_END_ARGS;
112
113 HTTP_EMPTY_ARGS(toMessageTypeObject);
114
115 HTTP_EMPTY_ARGS(count);
116
117 HTTP_EMPTY_ARGS(serialize);
118 HTTP_BEGIN_ARGS(unserialize, 1)
119 HTTP_ARG_VAL(serialized, 0)
120 HTTP_END_ARGS;
121
122 HTTP_EMPTY_ARGS(rewind);
123 HTTP_EMPTY_ARGS(valid);
124 HTTP_EMPTY_ARGS(key);
125 HTTP_EMPTY_ARGS(current);
126 HTTP_EMPTY_ARGS(next);
127
128 HTTP_EMPTY_ARGS(detach);
129 HTTP_BEGIN_ARGS(prepend, 1)
130 HTTP_ARG_OBJ(HttpMessage, message, 0)
131 HTTP_END_ARGS;
132 HTTP_EMPTY_ARGS(reverse);
133
134 #define http_message_object_read_prop _http_message_object_read_prop
135 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC);
136 #define http_message_object_write_prop _http_message_object_write_prop
137 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC);
138 #define http_message_object_get_props _http_message_object_get_props
139 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC);
140
141 #define OBJ_PROP_CE http_message_object_ce
142 zend_class_entry *http_message_object_ce;
143 zend_function_entry http_message_object_fe[] = {
144 HTTP_MESSAGE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
145 HTTP_MESSAGE_ME(getBody, ZEND_ACC_PUBLIC)
146 HTTP_MESSAGE_ME(setBody, ZEND_ACC_PUBLIC)
147 HTTP_MESSAGE_ME(getHeader, ZEND_ACC_PUBLIC)
148 HTTP_MESSAGE_ME(getHeaders, ZEND_ACC_PUBLIC)
149 HTTP_MESSAGE_ME(setHeaders, ZEND_ACC_PUBLIC)
150 HTTP_MESSAGE_ME(addHeaders, ZEND_ACC_PUBLIC)
151 HTTP_MESSAGE_ME(getType, ZEND_ACC_PUBLIC)
152 HTTP_MESSAGE_ME(setType, ZEND_ACC_PUBLIC)
153 HTTP_MESSAGE_ME(getResponseCode, ZEND_ACC_PUBLIC)
154 HTTP_MESSAGE_ME(setResponseCode, ZEND_ACC_PUBLIC)
155 HTTP_MESSAGE_ME(getResponseStatus, ZEND_ACC_PUBLIC)
156 HTTP_MESSAGE_ME(setResponseStatus, ZEND_ACC_PUBLIC)
157 HTTP_MESSAGE_ME(getRequestMethod, ZEND_ACC_PUBLIC)
158 HTTP_MESSAGE_ME(setRequestMethod, ZEND_ACC_PUBLIC)
159 HTTP_MESSAGE_ME(getRequestUrl, ZEND_ACC_PUBLIC)
160 HTTP_MESSAGE_ME(setRequestUrl, ZEND_ACC_PUBLIC)
161 HTTP_MESSAGE_ME(getHttpVersion, ZEND_ACC_PUBLIC)
162 HTTP_MESSAGE_ME(setHttpVersion, ZEND_ACC_PUBLIC)
163 HTTP_MESSAGE_ME(guessContentType, ZEND_ACC_PUBLIC)
164 HTTP_MESSAGE_ME(getParentMessage, ZEND_ACC_PUBLIC)
165 HTTP_MESSAGE_ME(send, ZEND_ACC_PUBLIC)
166 HTTP_MESSAGE_ME(toString, ZEND_ACC_PUBLIC)
167 HTTP_MESSAGE_ME(toMessageTypeObject, ZEND_ACC_PUBLIC)
168
169 /* implements Countable */
170 HTTP_MESSAGE_ME(count, ZEND_ACC_PUBLIC)
171
172 /* implements Serializable */
173 HTTP_MESSAGE_ME(serialize, ZEND_ACC_PUBLIC)
174 HTTP_MESSAGE_ME(unserialize, ZEND_ACC_PUBLIC)
175
176 /* implements Iterator */
177 HTTP_MESSAGE_ME(rewind, ZEND_ACC_PUBLIC)
178 HTTP_MESSAGE_ME(valid, ZEND_ACC_PUBLIC)
179 HTTP_MESSAGE_ME(current, ZEND_ACC_PUBLIC)
180 HTTP_MESSAGE_ME(key, ZEND_ACC_PUBLIC)
181 HTTP_MESSAGE_ME(next, ZEND_ACC_PUBLIC)
182
183 ZEND_MALIAS(HttpMessage, __toString, toString, HTTP_ARGS(HttpMessage, toString), ZEND_ACC_PUBLIC)
184
185 HTTP_MESSAGE_ME(fromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
186
187 HTTP_MESSAGE_ME(detach, ZEND_ACC_PUBLIC)
188 HTTP_MESSAGE_ME(prepend, ZEND_ACC_PUBLIC)
189 HTTP_MESSAGE_ME(reverse, ZEND_ACC_PUBLIC)
190
191 EMPTY_FUNCTION_ENTRY
192 };
193 static zend_object_handlers http_message_object_handlers;
194
195 PHP_MINIT_FUNCTION(http_message_object)
196 {
197 HTTP_REGISTER_CLASS_EX(HttpMessage, http_message_object, NULL, 0);
198
199 #ifndef WONKY
200 # ifdef HTTP_HAVE_SPL
201 zend_class_implements(http_message_object_ce TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator);
202 # else
203 zend_class_implements(http_message_object_ce TSRMLS_CC, 2, zend_ce_serializable, zend_ce_iterator);
204 # endif
205 #else
206 zend_class_implements(http_message_object_ce TSRMLS_CC, 1, zend_ce_iterator);
207 #endif
208
209 http_message_object_handlers.clone_obj = _http_message_object_clone_obj;
210 http_message_object_handlers.read_property = http_message_object_read_prop;
211 http_message_object_handlers.write_property = http_message_object_write_prop;
212 http_message_object_handlers.get_properties = http_message_object_get_props;
213 http_message_object_handlers.get_property_ptr_ptr = NULL;
214
215 DCL_PROP(PROTECTED, long, type, HTTP_MSG_NONE);
216 DCL_PROP(PROTECTED, string, body, "");
217 DCL_PROP(PROTECTED, string, requestMethod, "");
218 DCL_PROP(PROTECTED, string, requestUrl, "");
219 DCL_PROP(PROTECTED, string, responseStatus, "");
220 DCL_PROP(PROTECTED, long, responseCode, 0);
221 DCL_PROP_N(PROTECTED, httpVersion);
222 DCL_PROP_N(PROTECTED, headers);
223 DCL_PROP_N(PROTECTED, parentMessage);
224
225 #ifndef WONKY
226 DCL_CONST(long, "TYPE_NONE", HTTP_MSG_NONE);
227 DCL_CONST(long, "TYPE_REQUEST", HTTP_MSG_REQUEST);
228 DCL_CONST(long, "TYPE_RESPONSE", HTTP_MSG_RESPONSE);
229 #endif
230
231 HTTP_LONG_CONSTANT("HTTP_MSG_NONE", HTTP_MSG_NONE);
232 HTTP_LONG_CONSTANT("HTTP_MSG_REQUEST", HTTP_MSG_REQUEST);
233 HTTP_LONG_CONSTANT("HTTP_MSG_RESPONSE", HTTP_MSG_RESPONSE);
234
235 return SUCCESS;
236 }
237
238 void _http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC)
239 {
240 int i;
241 getObject(http_message_object, obj);
242
243 /* count */
244 http_message_count(i, obj->message);
245
246 if (i > 1) {
247 zval o;
248 zend_object_value *ovalues = NULL;
249 http_message_object **objects = NULL;
250 int last = i - 1;
251
252 objects = ecalloc(i, sizeof(http_message_object *));
253 ovalues = ecalloc(i, sizeof(zend_object_value));
254
255 /* we are the first message */
256 objects[0] = obj;
257 ovalues[0] = getThis()->value.obj;
258
259 /* fetch parents */
260 INIT_PZVAL(&o);
261 o.type = IS_OBJECT;
262 for (i = 1; obj->parent.handle; ++i) {
263 o.value.obj = obj->parent;
264 ovalues[i] = o.value.obj;
265 objects[i] = obj = zend_object_store_get_object(&o TSRMLS_CC);
266 }
267
268 /* reorder parents */
269 for (last = --i; i; --i) {
270 objects[i]->message->parent = objects[i-1]->message;
271 objects[i]->parent = ovalues[i-1];
272 }
273 objects[0]->message->parent = NULL;
274 objects[0]->parent.handle = 0;
275 objects[0]->parent.handlers = NULL;
276
277 /* add ref (why?) */
278 Z_OBJ_ADDREF_P(getThis());
279 RETVAL_OBJVAL(ovalues[last], 1);
280
281 efree(objects);
282 efree(ovalues);
283 } else {
284 RETURN_ZVAL(getThis(), 1, 0);
285 }
286 }
287
288 void _http_message_object_prepend_ex(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC)
289 {
290 zval m;
291 http_message *save_parent_msg = NULL;
292 zend_object_value save_parent_obj = {0, NULL};
293 getObject(http_message_object, obj);
294 getObjectEx(http_message_object, prepend_obj, prepend);
295
296 INIT_PZVAL(&m);
297 m.type = IS_OBJECT;
298
299 if (!top) {
300 save_parent_obj = obj->parent;
301 save_parent_msg = obj->message->parent;
302 } else {
303 /* iterate to the most parent object */
304 while (obj->parent.handle) {
305 m.value.obj = obj->parent;
306 obj = zend_object_store_get_object(&m TSRMLS_CC);
307 }
308 }
309
310 /* prepend */
311 obj->parent = prepend->value.obj;
312 obj->message->parent = prepend_obj->message;
313
314 /* add ref */
315 zend_objects_store_add_ref(prepend TSRMLS_CC);
316 while (prepend_obj->parent.handle) {
317 m.value.obj = prepend_obj->parent;
318 zend_objects_store_add_ref(&m TSRMLS_CC);
319 prepend_obj = zend_object_store_get_object(&m TSRMLS_CC);
320 }
321
322 if (!top) {
323 prepend_obj->parent = save_parent_obj;
324 prepend_obj->message->parent = save_parent_msg;
325 }
326 }
327
328 zend_object_value _http_message_object_new(zend_class_entry *ce TSRMLS_DC)
329 {
330 return http_message_object_new_ex(ce, NULL, NULL);
331 }
332
333 zend_object_value _http_message_object_new_ex(zend_class_entry *ce, http_message *msg, http_message_object **ptr TSRMLS_DC)
334 {
335 zend_object_value ov;
336 http_message_object *o;
337
338 o = ecalloc(1, sizeof(http_message_object));
339 o->zo.ce = ce;
340
341 if (ptr) {
342 *ptr = o;
343 }
344
345 if (msg) {
346 o->message = msg;
347 if (msg->parent) {
348 o->parent = http_message_object_new_ex(ce, msg->parent, NULL);
349 }
350 }
351
352 ALLOC_HASHTABLE(OBJ_PROP(o));
353 zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0);
354 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
355
356 ov.handle = putObject(http_message_object, o);
357 ov.handlers = &http_message_object_handlers;
358
359 return ov;
360 }
361
362 zend_object_value _http_message_object_clone_obj(zval *this_ptr TSRMLS_DC)
363 {
364 zend_object_value new_ov;
365 http_message_object *new_obj = NULL;
366 getObject(http_message_object, old_obj);
367
368 new_ov = http_message_object_new_ex(old_obj->zo.ce, http_message_dup(old_obj->message), &new_obj);
369 zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
370
371 return new_ov;
372 }
373
374 void _http_message_object_free(zend_object *object TSRMLS_DC)
375 {
376 http_message_object *o = (http_message_object *) object;
377
378 if (o->message) {
379 http_message_dtor(o->message);
380 efree(o->message);
381 }
382 if (o->parent.handle) {
383 zval p;
384
385 INIT_PZVAL(&p);
386 p.type = IS_OBJECT;
387 p.value.obj = o->parent;
388 zend_objects_store_del_ref(&p TSRMLS_CC);
389 }
390 freeObject(o);
391 }
392
393 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC)
394 {
395 getObjectEx(http_message_object, obj, object);
396 http_message *msg = obj->message;
397 zval *return_value;
398 #ifdef WONKY
399 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)+1);
400 #else
401 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
402
403 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
404 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
405 }
406 #endif
407
408 if (type == BP_VAR_W) {
409 zend_error(E_ERROR, "Cannot access HttpMessage properties by reference or array key/index");
410 return NULL;
411 }
412
413 ALLOC_ZVAL(return_value);
414 return_value->refcount = 0;
415 return_value->is_ref = 0;
416
417 #ifdef WONKY
418 switch (h)
419 #else
420 switch (pinfo->h)
421 #endif
422 {
423 case HTTP_MSG_PROPHASH_TYPE:
424 case HTTP_MSG_CHILD_PROPHASH_TYPE:
425 RETVAL_LONG(msg->type);
426 break;
427
428 case HTTP_MSG_PROPHASH_HTTP_VERSION:
429 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
430 RETVAL_DOUBLE(msg->http.version);
431 break;
432
433 case HTTP_MSG_PROPHASH_BODY:
434 case HTTP_MSG_CHILD_PROPHASH_BODY:
435 phpstr_fix(PHPSTR(msg));
436 RETVAL_PHPSTR(PHPSTR(msg), 0, 1);
437 break;
438
439 case HTTP_MSG_PROPHASH_HEADERS:
440 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
441 array_init(return_value);
442 zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
443 break;
444
445 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
446 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
447 if (msg->parent) {
448 RETVAL_OBJVAL(obj->parent, 1);
449 } else {
450 RETVAL_NULL();
451 }
452 break;
453
454 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
455 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
456 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.method) {
457 RETVAL_STRING(msg->http.info.request.method, 1);
458 } else {
459 RETVAL_NULL();
460 }
461 break;
462
463 case HTTP_MSG_PROPHASH_REQUEST_URL:
464 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URL:
465 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.url) {
466 RETVAL_STRING(msg->http.info.request.url, 1);
467 } else {
468 RETVAL_NULL();
469 }
470 break;
471
472 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
473 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
474 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
475 RETVAL_LONG(msg->http.info.response.code);
476 } else {
477 RETVAL_NULL();
478 }
479 break;
480
481 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
482 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
483 if (HTTP_MSG_TYPE(RESPONSE, msg) && msg->http.info.response.status) {
484 RETVAL_STRING(msg->http.info.response.status, 1);
485 } else {
486 RETVAL_NULL();
487 }
488 break;
489
490 default:
491 #ifdef WONKY
492 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
493 #else
494 RETVAL_NULL();
495 #endif
496 }
497
498 return return_value;
499 }
500
501 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC)
502 {
503 getObjectEx(http_message_object, obj, object);
504 http_message *msg = obj->message;
505 zval *cpy = NULL;
506 #ifdef WONKY
507 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
508 #else
509 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
510
511 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
512 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
513 return;
514 }
515 #endif
516
517 cpy = zval_copy(Z_TYPE_P(value), value);
518
519 #ifdef WONKY
520 switch (h)
521 #else
522 switch (pinfo->h)
523 #endif
524 {
525 case HTTP_MSG_PROPHASH_TYPE:
526 case HTTP_MSG_CHILD_PROPHASH_TYPE:
527 convert_to_long(cpy);
528 http_message_set_type(msg, Z_LVAL_P(cpy));
529 break;
530
531 case HTTP_MSG_PROPHASH_HTTP_VERSION:
532 case HTTP_MSG_CHILD_PROPHASH_HTTP_VERSION:
533 convert_to_double(cpy);
534 msg->http.version = Z_DVAL_P(cpy);
535 break;
536
537 case HTTP_MSG_PROPHASH_BODY:
538 case HTTP_MSG_CHILD_PROPHASH_BODY:
539 convert_to_string(cpy);
540 phpstr_dtor(PHPSTR(msg));
541 phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(cpy), Z_STRLEN_P(cpy));
542 break;
543
544 case HTTP_MSG_PROPHASH_HEADERS:
545 case HTTP_MSG_CHILD_PROPHASH_HEADERS:
546 convert_to_array(cpy);
547 zend_hash_clean(&msg->hdrs);
548 zend_hash_copy(&msg->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
549 break;
550
551 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
552 case HTTP_MSG_CHILD_PROPHASH_PARENT_MESSAGE:
553 if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), http_message_object_ce TSRMLS_CC)) {
554 if (msg->parent) {
555 zval tmp;
556 tmp.value.obj = obj->parent;
557 Z_OBJ_DELREF(tmp);
558 }
559 Z_OBJ_ADDREF_P(value);
560 obj->parent = value->value.obj;
561 }
562 break;
563
564 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
565 case HTTP_MSG_CHILD_PROPHASH_REQUEST_METHOD:
566 if (HTTP_MSG_TYPE(REQUEST, msg)) {
567 convert_to_string(cpy);
568 STR_SET(msg->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
569 }
570 break;
571
572 case HTTP_MSG_PROPHASH_REQUEST_URL:
573 case HTTP_MSG_CHILD_PROPHASH_REQUEST_URL:
574 if (HTTP_MSG_TYPE(REQUEST, msg)) {
575 convert_to_string(cpy);
576 STR_SET(msg->http.info.request.url, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
577 }
578 break;
579
580 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
581 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_CODE:
582 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
583 convert_to_long(cpy);
584 msg->http.info.response.code = Z_LVAL_P(cpy);
585 }
586 break;
587
588 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
589 case HTTP_MSG_CHILD_PROPHASH_RESPONSE_STATUS:
590 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
591 convert_to_string(cpy);
592 STR_SET(msg->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)));
593 }
594 break;
595
596 default:
597 #ifdef WONKY
598 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
599 #endif
600 break;
601 }
602 zval_free(&cpy);
603 }
604
605 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC)
606 {
607 zval *headers;
608 getObjectEx(http_message_object, obj, object);
609 http_message *msg = obj->message;
610 HashTable *props = OBJ_PROP(obj);
611 zval array, *parent;
612
613 INIT_ZARR(array, props);
614
615 #define ASSOC_PROP(array, ptype, name, val) \
616 { \
617 char *m_prop_name; \
618 int m_prop_len; \
619 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
620 add_assoc_ ##ptype## _ex(&array, m_prop_name, sizeof(name)+3, val); \
621 efree(m_prop_name); \
622 }
623 #define ASSOC_STRING(array, name, val) ASSOC_STRINGL(array, name, val, strlen(val))
624 #define ASSOC_STRINGL(array, name, val, len) \
625 { \
626 char *m_prop_name; \
627 int m_prop_len; \
628 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
629 add_assoc_stringl_ex(&array, m_prop_name, sizeof(name)+3, val, len, 1); \
630 efree(m_prop_name); \
631 }
632
633 ASSOC_PROP(array, long, "type", msg->type);
634 ASSOC_PROP(array, double, "httpVersion", msg->http.version);
635
636 switch (msg->type) {
637 case HTTP_MSG_REQUEST:
638 ASSOC_PROP(array, long, "responseCode", 0);
639 ASSOC_STRINGL(array, "responseStatus", "", 0);
640 ASSOC_STRING(array, "requestMethod", msg->http.info.request.method?msg->http.info.request.method:"");
641 ASSOC_STRING(array, "requestUrl", msg->http.info.request.url?msg->http.info.request.url:"");
642 break;
643
644 case HTTP_MSG_RESPONSE:
645 ASSOC_PROP(array, long, "responseCode", msg->http.info.response.code);
646 ASSOC_STRING(array, "responseStatus", msg->http.info.response.status?msg->http.info.response.status:"");
647 ASSOC_STRINGL(array, "requestMethod", "", 0);
648 ASSOC_STRINGL(array, "requestUrl", "", 0);
649 break;
650
651 case HTTP_MSG_NONE:
652 default:
653 ASSOC_PROP(array, long, "responseCode", 0);
654 ASSOC_STRINGL(array, "responseStatus", "", 0);
655 ASSOC_STRINGL(array, "requestMethod", "", 0);
656 ASSOC_STRINGL(array, "requestUrl", "", 0);
657 break;
658 }
659
660 MAKE_STD_ZVAL(headers);
661 array_init(headers);
662 zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
663 ASSOC_PROP(array, zval, "headers", headers);
664 ASSOC_STRINGL(array, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg));
665
666 MAKE_STD_ZVAL(parent);
667 if (msg->parent) {
668 ZVAL_OBJVAL(parent, obj->parent, 1);
669 } else {
670 ZVAL_NULL(parent);
671 }
672 ASSOC_PROP(array, zval, "parentMessage", parent);
673
674 return OBJ_PROP(obj);
675 }
676
677 /* ### USERLAND ### */
678
679 /* {{{ proto void HttpMessage::__construct([string message])
680 *
681 * Instantiate a new HttpMessage object.
682 *
683 * Accepts an optional string parameter containing a single or several
684 * consecutive HTTP messages. The constructed object will actually
685 * represent the *last* message of the passed string. If there were
686 * prior messages, those can be accessed by HttpMessage::getParentMessage().
687 *
688 * Throws HttpMalformedHeaderException.
689 */
690 PHP_METHOD(HttpMessage, __construct)
691 {
692 int length = 0;
693 char *message = NULL;
694
695 getObject(http_message_object, obj);
696
697 SET_EH_THROW_HTTP();
698 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) {
699 http_message *msg = obj->message;
700
701 http_message_dtor(msg);
702 if ((obj->message = http_message_parse_ex(msg, message, length))) {
703 if (obj->message->parent) {
704 obj->parent = http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL);
705 }
706 } else {
707 obj->message = http_message_init(msg);
708 }
709 }
710 if (!obj->message) {
711 obj->message = http_message_new();
712 }
713 SET_EH_NORMAL();
714 }
715 /* }}} */
716
717 /* {{{ proto static HttpMessage HttpMessage::fromString(string raw_message[, string class_name = "HttpMessage"])
718 *
719 * Create an HttpMessage object from a string. Kind of a static constructor.
720 *
721 * Expects a string parameter containing a single or several consecutive
722 * HTTP messages. Accepts an optional string parameter specifying the class to use.
723 *
724 * Returns an HttpMessage object on success or NULL on failure.
725 *
726 * Throws HttpMalformedHeadersException.
727 */
728 PHP_METHOD(HttpMessage, fromString)
729 {
730 char *string = NULL, *class_name = NULL;
731 int length = 0, class_length = 0;
732 http_message *msg = NULL;
733
734 RETVAL_NULL();
735
736 SET_EH_THROW_HTTP();
737 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &string, &length, &class_name, &class_length)) {
738 if ((msg = http_message_parse(string, length))) {
739 zend_class_entry *ce = http_message_object_ce;
740
741 if (class_name && *class_name) {
742 ce = zend_fetch_class(class_name, class_length, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
743 if (ce && !instanceof_function(ce, http_message_object_ce TSRMLS_CC)) {
744 http_error_ex(HE_WARNING, HTTP_E_RUNTIME, "Class %s does not extend HttpMessage", class_name);
745 ce = NULL;
746 }
747 }
748 if (ce) {
749 RETVAL_OBJVAL(http_message_object_new_ex(ce, msg, NULL), 0);
750 }
751 }
752 }
753 SET_EH_NORMAL();
754 }
755 /* }}} */
756
757 /* {{{ proto string HttpMessage::getBody()
758 *
759 * Get the body of the parsed HttpMessage.
760 *
761 * Returns the message body as string.
762 */
763 PHP_METHOD(HttpMessage, getBody)
764 {
765 NO_ARGS;
766
767 if (return_value_used) {
768 getObject(http_message_object, obj);
769 RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1);
770 }
771 }
772 /* }}} */
773
774 /* {{{ proto void HttpMessage::setBody(string body)
775 *
776 * Set the body of the HttpMessage.
777 * NOTE: Don't forget to update any headers accordingly.
778 *
779 * Expects a string parameter containing the new body of the message.
780 */
781 PHP_METHOD(HttpMessage, setBody)
782 {
783 char *body;
784 int len;
785 getObject(http_message_object, obj);
786
787 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) {
788 phpstr_dtor(PHPSTR(obj->message));
789 phpstr_from_string_ex(PHPSTR(obj->message), body, len);
790 }
791 }
792 /* }}} */
793
794 /* {{{ proto string HttpMessage::getHeader(string header)
795 *
796 * Get message header.
797 *
798 * Returns the header value on success or NULL if the header does not exist.
799 */
800 PHP_METHOD(HttpMessage, getHeader)
801 {
802 zval *header;
803 char *orig_header, *nice_header;
804 int header_len;
805 getObject(http_message_object, obj);
806
807 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &orig_header, &header_len)) {
808 RETURN_FALSE;
809 }
810
811 nice_header = pretty_key(estrndup(orig_header, header_len), header_len, 1, 1);
812 if ((header = http_message_header_ex(obj->message, nice_header, header_len + 1))) {
813 RETVAL_ZVAL(header, 1, 0);
814 }
815 efree(nice_header);
816 }
817 /* }}} */
818
819 /* {{{ proto array HttpMessage::getHeaders()
820 *
821 * Get Message Headers.
822 *
823 * Returns an associative array containing the messages HTTP headers.
824 */
825 PHP_METHOD(HttpMessage, getHeaders)
826 {
827 NO_ARGS;
828
829 if (return_value_used) {
830 zval headers;
831 getObject(http_message_object, obj);
832
833 INIT_ZARR(headers, &obj->message->hdrs);
834 array_init(return_value);
835 array_copy(&headers, return_value);
836 }
837 }
838 /* }}} */
839
840 /* {{{ proto void HttpMessage::setHeaders(array headers)
841 *
842 * Sets new headers.
843 *
844 * Expects an associative array as parameter containing the new HTTP headers,
845 * which will replace *all* previous HTTP headers of the message.
846 */
847 PHP_METHOD(HttpMessage, setHeaders)
848 {
849 zval *new_headers, old_headers;
850 getObject(http_message_object, obj);
851
852 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_headers)) {
853 return;
854 }
855
856 zend_hash_clean(&obj->message->hdrs);
857 INIT_ZARR(old_headers, &obj->message->hdrs);
858 array_copy(new_headers, &old_headers);
859 }
860 /* }}} */
861
862 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
863 *
864 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
865 *
866 * Expects an associative array as parameter containing the additional HTTP headers
867 * to add to the messages existing headers. If the optional bool parameter is true,
868 * and a header with the same name of one to add exists already, this respective
869 * header will be converted to an array containing both header values, otherwise
870 * it will be overwritten with the new header value.
871 */
872 PHP_METHOD(HttpMessage, addHeaders)
873 {
874 zval old_headers, *new_headers;
875 zend_bool append = 0;
876 getObject(http_message_object, obj);
877
878 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
879 return;
880 }
881
882 INIT_ZARR(old_headers, &obj->message->hdrs);
883 if (append) {
884 array_append(new_headers, &old_headers);
885 } else {
886 array_merge(new_headers, &old_headers);
887 }
888 }
889 /* }}} */
890
891 /* {{{ proto int HttpMessage::getType()
892 *
893 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
894 *
895 * Returns the HttpMessage::TYPE.
896 */
897 PHP_METHOD(HttpMessage, getType)
898 {
899 NO_ARGS;
900
901 if (return_value_used) {
902 getObject(http_message_object, obj);
903 RETURN_LONG(obj->message->type);
904 }
905 }
906 /* }}} */
907
908 /* {{{ proto void HttpMessage::setType(int type)
909 *
910 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
911 *
912 * Expects an int parameter, the HttpMessage::TYPE.
913 */
914 PHP_METHOD(HttpMessage, setType)
915 {
916 long type;
917 getObject(http_message_object, obj);
918
919 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
920 return;
921 }
922 http_message_set_type(obj->message, type);
923 }
924 /* }}} */
925
926 /* {{{ proto int HttpMessage::getResponseCode()
927 *
928 * Get the Response Code of the Message.
929 *
930 * Returns the HTTP response code if the message is of type
931 * HttpMessage::TYPE_RESPONSE, else FALSE.
932 */
933 PHP_METHOD(HttpMessage, getResponseCode)
934 {
935 NO_ARGS;
936
937 if (return_value_used) {
938 getObject(http_message_object, obj);
939 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
940 RETURN_LONG(obj->message->http.info.response.code);
941 }
942 }
943 /* }}} */
944
945 /* {{{ proto bool HttpMessage::setResponseCode(int code)
946 *
947 * Set the response code of an HTTP Response Message.
948 *
949 * Expects an int parameter with the HTTP response code.
950 *
951 * Returns TRUE on success, or FALSE if the message is not of type
952 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
953 */
954 PHP_METHOD(HttpMessage, setResponseCode)
955 {
956 long code;
957 getObject(http_message_object, obj);
958
959 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
960
961 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
962 RETURN_FALSE;
963 }
964 if (code < 100 || code > 510) {
965 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
966 RETURN_FALSE;
967 }
968
969 obj->message->http.info.response.code = code;
970 RETURN_TRUE;
971 }
972 /* }}} */
973
974 /* {{{ proto string HttpMessage::getResponseStatus()
975 *
976 * Get the Response Status of the message (i.e. the string following the response code).
977 *
978 * Returns the HTTP response status string if the message is of type
979 * HttpMessage::TYPE_RESPONSE, else FALSE.
980 */
981 PHP_METHOD(HttpMessage, getResponseStatus)
982 {
983 NO_ARGS;
984
985 if (return_value_used) {
986 getObject(http_message_object, obj);
987 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
988 RETURN_STRING(obj->message->http.info.response.status, 1);
989 }
990 }
991 /* }}} */
992
993 /* {{{ proto bool HttpMessage::setResponseStatus(string status)
994 *
995 * Set the Response Status of the HTTP message (i.e. the string following the response code).
996 *
997 * Expects a string parameter containing the response status text.
998 *
999 * Returns TRUE on success or FALSE if the message is not of type
1000 * HttpMessage::TYPE_RESPONSE.
1001 */
1002 PHP_METHOD(HttpMessage, setResponseStatus)
1003 {
1004 char *status;
1005 int status_len;
1006 getObject(http_message_object, obj);
1007
1008 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
1009
1010 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &status, &status_len)) {
1011 RETURN_FALSE;
1012 }
1013 STR_SET(obj->message->http.info.response.status, estrdup(status));
1014 RETURN_TRUE;
1015 }
1016 /* }}} */
1017
1018 /* {{{ proto string HttpMessage::getRequestMethod()
1019 *
1020 * Get the Request Method of the Message.
1021 *
1022 * Returns the request method name on success, or FALSE if the message is
1023 * not of type HttpMessage::TYPE_REQUEST.
1024 */
1025 PHP_METHOD(HttpMessage, getRequestMethod)
1026 {
1027 NO_ARGS;
1028
1029 if (return_value_used) {
1030 getObject(http_message_object, obj);
1031 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1032 RETURN_STRING(obj->message->http.info.request.method, 1);
1033 }
1034 }
1035 /* }}} */
1036
1037 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
1038 *
1039 * Set the Request Method of the HTTP Message.
1040 *
1041 * Expects a string parameter containing the request method name.
1042 *
1043 * Returns TRUE on success, or FALSE if the message is not of type
1044 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
1045 */
1046 PHP_METHOD(HttpMessage, setRequestMethod)
1047 {
1048 char *method;
1049 int method_len;
1050 getObject(http_message_object, obj);
1051
1052 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1053
1054 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
1055 RETURN_FALSE;
1056 }
1057 if (method_len < 1) {
1058 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
1059 RETURN_FALSE;
1060 }
1061 if (!http_request_method_exists(1, 0, method)) {
1062 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unknown request method: %s", method);
1063 RETURN_FALSE;
1064 }
1065
1066 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
1067 RETURN_TRUE;
1068 }
1069 /* }}} */
1070
1071 /* {{{ proto string HttpMessage::getRequestUrl()
1072 *
1073 * Get the Request URL of the Message.
1074 *
1075 * Returns the request url as string on success, or FALSE if the message
1076 * is not of type HttpMessage::TYPE_REQUEST.
1077 */
1078 PHP_METHOD(HttpMessage, getRequestUrl)
1079 {
1080 NO_ARGS;
1081
1082 if (return_value_used) {
1083 getObject(http_message_object, obj);
1084 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1085 RETURN_STRING(obj->message->http.info.request.url, 1);
1086 }
1087 }
1088 /* }}} */
1089
1090 /* {{{ proto bool HttpMessage::setRequestUrl(string url)
1091 *
1092 * Set the Request URL of the HTTP Message.
1093 *
1094 * Expects a string parameters containing the request url.
1095 *
1096 * Returns TRUE on success, or FALSE if the message is not of type
1097 * HttpMessage::TYPE_REQUEST or supplied URL was empty.
1098 */
1099 PHP_METHOD(HttpMessage, setRequestUrl)
1100 {
1101 char *URI;
1102 int URIlen;
1103 getObject(http_message_object, obj);
1104
1105 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
1106 RETURN_FALSE;
1107 }
1108 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1109 if (URIlen < 1) {
1110 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUrl to an empty string");
1111 RETURN_FALSE;
1112 }
1113
1114 STR_SET(obj->message->http.info.request.url, estrndup(URI, URIlen));
1115 RETURN_TRUE;
1116 }
1117 /* }}} */
1118
1119 /* {{{ proto string HttpMessage::getHttpVersion()
1120 *
1121 * Get the HTTP Protocol Version of the Message.
1122 *
1123 * Returns the HTTP protocol version as string.
1124 */
1125 PHP_METHOD(HttpMessage, getHttpVersion)
1126 {
1127 NO_ARGS;
1128
1129 if (return_value_used) {
1130 char ver[4] = {0};
1131 getObject(http_message_object, obj);
1132
1133 sprintf(ver, "%1.1lf", obj->message->http.version);
1134 RETURN_STRINGL(ver, 3, 1);
1135 }
1136 }
1137 /* }}} */
1138
1139 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
1140 *
1141 * Set the HTTP Protocol version of the Message.
1142 *
1143 * Expects a string parameter containing the HTTP protocol version.
1144 *
1145 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
1146 */
1147 PHP_METHOD(HttpMessage, setHttpVersion)
1148 {
1149 char v[4];
1150 zval *zv;
1151 getObject(http_message_object, obj);
1152
1153 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
1154 return;
1155 }
1156
1157 convert_to_double(zv);
1158 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
1159 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
1160 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
1161 RETURN_FALSE;
1162 }
1163
1164 obj->message->http.version = Z_DVAL_P(zv);
1165 RETURN_TRUE;
1166 }
1167 /* }}} */
1168
1169 /* {{{ proto string HttpMessage::guessContentType(string magic_file[, int magic_mode = MAGIC_MIME])
1170 *
1171 * Attempts to guess the content type of supplied payload through libmagic.
1172 *
1173 * Expects a string parameter specifying the magic.mime database to use.
1174 * Additionally accepts an optional int parameter, being flags for libmagic.
1175 *
1176 * Returns the guessed content type on success, or FALSE on failure.
1177 *
1178 * Throws HttpRuntimeException, HttpInvalidParamException
1179 * if http.only_exceptions is TRUE.
1180 */
1181 PHP_METHOD(HttpMessage, guessContentType)
1182 {
1183 #ifdef HTTP_HAVE_MAGIC
1184 char *magic_file, *ct = NULL;
1185 int magic_file_len;
1186 long magic_mode = MAGIC_MIME;
1187
1188 RETVAL_FALSE;
1189 SET_EH_THROW_HTTP();
1190 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) {
1191 getObject(http_message_object, obj);
1192 if ((ct = http_guess_content_type(magic_file, magic_mode, PHPSTR_VAL(&obj->message->body), PHPSTR_LEN(&obj->message->body), SEND_DATA))) {
1193 RETVAL_STRING(ct, 0);
1194 }
1195 }
1196 SET_EH_NORMAL();
1197 #else
1198 http_error(HE_THROW, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available");
1199 RETURN_FALSE;
1200 #endif
1201 }
1202 /* }}} */
1203
1204 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
1205 *
1206 * Get parent Message.
1207 *
1208 * Returns the parent HttpMessage on success, or NULL if there's none.
1209 *
1210 * Throws HttpRuntimeException.
1211 */
1212 PHP_METHOD(HttpMessage, getParentMessage)
1213 {
1214 SET_EH_THROW_HTTP();
1215 NO_ARGS {
1216 getObject(http_message_object, obj);
1217
1218 if (obj->message->parent) {
1219 RETVAL_OBJVAL(obj->parent, 1);
1220 } else {
1221 http_error(HE_WARNING, HTTP_E_RUNTIME, "HttpMessage does not have a parent message");
1222 }
1223 }
1224 SET_EH_NORMAL();
1225 }
1226 /* }}} */
1227
1228 /* {{{ proto bool HttpMessage::send()
1229 *
1230 * Send the Message according to its type as Response or Request.
1231 * This provides limited functionality compared to HttpRequest and HttpResponse.
1232 *
1233 * Returns TRUE on success, or FALSE on failure.
1234 */
1235 PHP_METHOD(HttpMessage, send)
1236 {
1237 getObject(http_message_object, obj);
1238
1239 NO_ARGS;
1240
1241 RETURN_SUCCESS(http_message_send(obj->message));
1242 }
1243 /* }}} */
1244
1245 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
1246 *
1247 * Get the string representation of the Message.
1248 *
1249 * Accepts a bool parameter which specifies whether the returned string
1250 * should also contain any parent messages.
1251 *
1252 * Returns the full message as string.
1253 */
1254 PHP_METHOD(HttpMessage, toString)
1255 {
1256 if (return_value_used) {
1257 char *string;
1258 size_t length;
1259 zend_bool include_parent = 0;
1260 getObject(http_message_object, obj);
1261
1262 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
1263 RETURN_FALSE;
1264 }
1265
1266 if (include_parent) {
1267 http_message_serialize(obj->message, &string, &length);
1268 } else {
1269 http_message_tostring(obj->message, &string, &length);
1270 }
1271 RETURN_STRINGL(string, length, 0);
1272 }
1273 }
1274 /* }}} */
1275
1276 /* {{{ proto HttpRequest|HttpResponse HttpMessage::toMessageTypeObject(void)
1277 *
1278 * Creates an object regarding to the type of the message.
1279 *
1280 * Returns either an HttpRequest or HttpResponse object on success, or NULL on failure.
1281 *
1282 * Throws HttpRuntimeException, HttpMessageTypeException, HttpHeaderException.
1283 */
1284 PHP_METHOD(HttpMessage, toMessageTypeObject)
1285 {
1286 SET_EH_THROW_HTTP();
1287
1288 NO_ARGS;
1289
1290 if (return_value_used) {
1291 getObject(http_message_object, obj);
1292
1293 switch (obj->message->type) {
1294 case HTTP_MSG_REQUEST:
1295 {
1296 #ifdef HTTP_HAVE_CURL
1297 int method;
1298 char *url;
1299 zval tmp, body, *array, *headers, *host = http_message_header(obj->message, "Host");
1300 php_url hurl, *purl = php_url_parse(obj->message->http.info.request.url);
1301
1302 MAKE_STD_ZVAL(array);
1303 array_init(array);
1304
1305 memset(&hurl, 0, sizeof(php_url));
1306 hurl.host = host ? Z_STRVAL_P(host) : NULL;
1307 http_build_url(HTTP_URL_REPLACE, purl, &hurl, NULL, &url, NULL);
1308 php_url_free(purl);
1309 add_assoc_string(array, "url", url, 0);
1310
1311 if ( (method = http_request_method_exists(1, 0, obj->message->http.info.request.method)) ||
1312 (method = http_request_method_register(obj->message->http.info.request.method, strlen(obj->message->http.info.request.method)))) {
1313 add_assoc_long(array, "method", method);
1314 }
1315
1316 if (10 == (int) (obj->message->http.version * 10)) {
1317 add_assoc_long(array, "protocol", CURL_HTTP_VERSION_1_0);
1318 }
1319
1320 MAKE_STD_ZVAL(headers);
1321 array_init(headers);
1322 INIT_ZARR(tmp, &obj->message->hdrs);
1323 array_copy(&tmp, headers);
1324 add_assoc_zval(array, "headers", headers);
1325
1326 object_init_ex(return_value, http_request_object_ce);
1327 zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setoptions", NULL, array);
1328 zval_ptr_dtor(&array);
1329
1330 INIT_PZVAL(&body);
1331 ZVAL_STRINGL(&body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 0);
1332 zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setrawpostdata", NULL, &body);
1333 #else
1334 http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpRequest (missing curl support)");
1335 #endif
1336 break;
1337 }
1338
1339 case HTTP_MSG_RESPONSE:
1340 {
1341 #ifndef WONKY
1342 HashPosition pos1, pos2;
1343 ulong idx;
1344 uint key_len;
1345 char *key = NULL;
1346 zval **header, **h, *body;
1347
1348 if (obj->message->http.info.response.code) {
1349 http_send_status(obj->message->http.info.response.code);
1350 }
1351
1352 object_init_ex(return_value, http_response_object_ce);
1353
1354 FOREACH_HASH_KEYLENVAL(pos1, &obj->message->hdrs, key, key_len, idx, header) {
1355 if (key) {
1356 zval zkey;
1357
1358 INIT_PZVAL(&zkey);
1359 ZVAL_STRINGL(&zkey, key, key_len, 0);
1360
1361 switch (Z_TYPE_PP(header)) {
1362 case IS_ARRAY:
1363 case IS_OBJECT:
1364 FOREACH_HASH_VAL(pos2, HASH_OF(*header), h) {
1365 ZVAL_ADDREF(*h);
1366 zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, &zkey, *h);
1367 zval_ptr_dtor(h);
1368 }
1369 break;
1370
1371 default:
1372 ZVAL_ADDREF(*header);
1373 zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, &zkey, *header);
1374 zval_ptr_dtor(header);
1375 break;
1376 }
1377 key = NULL;
1378 }
1379 }
1380
1381 MAKE_STD_ZVAL(body);
1382 ZVAL_STRINGL(body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 1);
1383 zend_call_method_with_1_params(&return_value, http_response_object_ce, NULL, "setdata", NULL, body);
1384 zval_ptr_dtor(&body);
1385 #else
1386 http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpResponse (need PHP 5.1+)");
1387 #endif
1388 break;
1389 }
1390
1391 default:
1392 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is neither of type HttpMessage::TYPE_REQUEST nor HttpMessage::TYPE_RESPONSE");
1393 break;
1394 }
1395 }
1396 SET_EH_NORMAL();
1397 }
1398 /* }}} */
1399
1400 /* {{{ proto int HttpMessage::count()
1401 *
1402 * Implements Countable.
1403 *
1404 * Returns the number of parent messages + 1.
1405 */
1406 PHP_METHOD(HttpMessage, count)
1407 {
1408 NO_ARGS {
1409 long i;
1410 getObject(http_message_object, obj);
1411
1412 http_message_count(i, obj->message);
1413 RETURN_LONG(i);
1414 }
1415 }
1416 /* }}} */
1417
1418 /* {{{ proto string HttpMessage::serialize()
1419 *
1420 * Implements Serializable.
1421 *
1422 * Returns the serialized representation of the HttpMessage.
1423 */
1424 PHP_METHOD(HttpMessage, serialize)
1425 {
1426 NO_ARGS {
1427 char *string;
1428 size_t length;
1429 getObject(http_message_object, obj);
1430
1431 http_message_serialize(obj->message, &string, &length);
1432 RETURN_STRINGL(string, length, 0);
1433 }
1434 }
1435 /* }}} */
1436
1437 /* {{{ proto void HttpMessage::unserialize(string serialized)
1438 *
1439 * Implements Serializable.
1440 *
1441 * Re-constructs the HttpMessage based upon the serialized string.
1442 */
1443 PHP_METHOD(HttpMessage, unserialize)
1444 {
1445 int length;
1446 char *serialized;
1447 getObject(http_message_object, obj);
1448
1449 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
1450 http_message_dtor(obj->message);
1451 if (!http_message_parse_ex(obj->message, serialized, (size_t) length)) {
1452 http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage");
1453 http_message_init(obj->message);
1454 }
1455 }
1456 }
1457 /* }}} */
1458
1459 /* {{{ proto HttpMessage HttpMessage::detach(void)
1460 *
1461 * Returns a clone of an HttpMessage object detached from any parent messages.
1462 */
1463 PHP_METHOD(HttpMessage, detach)
1464 {
1465 http_info info;
1466 http_message *msg;
1467 getObject(http_message_object, obj);
1468
1469 NO_ARGS;
1470
1471 info.type = obj->message->type;
1472 memcpy(&HTTP_INFO(&info), &HTTP_INFO(obj->message), sizeof(struct http_info));
1473
1474 msg = http_message_new();
1475 http_message_set_info(msg, &info);
1476
1477 zend_hash_copy(&msg->hdrs, &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
1478 phpstr_append(&msg->body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message));
1479
1480 RETVAL_OBJVAL(http_message_object_new_ex(Z_OBJCE_P(getThis()), msg, NULL), 0);
1481 }
1482 /* }}} */
1483
1484 /* {{{ proto void HttpMessage::prepend(HttpMessage message[, bool top = true])
1485 *
1486 * Prepends message(s) to the HTTP message.
1487 *
1488 * Expects an HttpMessage object as parameter.
1489 *
1490 * Throws HttpInvalidParamException if the message is located within the same message chain.
1491 */
1492 PHP_METHOD(HttpMessage, prepend)
1493 {
1494 zval *prepend;
1495 zend_bool top = 1;
1496
1497 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, http_message_object_ce, &top)) {
1498 http_message *msg[2];
1499 getObject(http_message_object, obj);
1500 getObjectEx(http_message_object, prepend_obj, prepend);
1501
1502 /* safety check */
1503 for (msg[0] = obj->message; msg[0]; msg[0] = msg[0]->parent) {
1504 for (msg[1] = prepend_obj->message; msg[1]; msg[1] = msg[1]->parent) {
1505 if (msg[0] == msg[1]) {
1506 http_error(HE_THROW, HTTP_E_INVALID_PARAM, "Cannot prepend a message located within the same message chain");
1507 return;
1508 }
1509 }
1510 }
1511
1512 http_message_object_prepend_ex(getThis(), prepend, top);
1513 }
1514 }
1515 /* }}} */
1516
1517 /* {{{ proto HttpMessage HttpMessage::reverse()
1518 *
1519 * Reorders the message chain in reverse order.
1520 *
1521 * Returns the most parent HttpMessage object.
1522 */
1523 PHP_METHOD(HttpMessage, reverse)
1524 {
1525 NO_ARGS {
1526 http_message_object_reverse(getThis(), return_value);
1527 }
1528 }
1529 /* }}} */
1530
1531 /* {{{ proto void HttpMessage::rewind(void)
1532 *
1533 * Implements Iterator.
1534 */
1535 PHP_METHOD(HttpMessage, rewind)
1536 {
1537 NO_ARGS {
1538 getObject(http_message_object, obj);
1539
1540 if (obj->iterator) {
1541 zval_ptr_dtor(&obj->iterator);
1542 }
1543 ZVAL_ADDREF(getThis());
1544 obj->iterator = getThis();
1545 }
1546 }
1547 /* }}} */
1548
1549 /* {{{ proto bool HttpMessage::valid(void)
1550 *
1551 * Implements Iterator.
1552 */
1553 PHP_METHOD(HttpMessage, valid)
1554 {
1555 NO_ARGS {
1556 getObject(http_message_object, obj);
1557
1558 RETURN_BOOL(obj->iterator != NULL);
1559 }
1560 }
1561 /* }}} */
1562
1563 /* {{{ proto void HttpMessage::next(void)
1564 *
1565 * Implements Iterator.
1566 */
1567 PHP_METHOD(HttpMessage, next)
1568 {
1569 NO_ARGS {
1570 getObject(http_message_object, obj);
1571 getObjectEx(http_message_object, itr, obj->iterator);
1572
1573 if (itr && itr->parent.handle) {
1574 zval *old = obj->iterator;
1575 MAKE_STD_ZVAL(obj->iterator);
1576 ZVAL_OBJVAL(obj->iterator, itr->parent, 1);
1577 zval_ptr_dtor(&old);
1578 } else {
1579 zval_ptr_dtor(&obj->iterator);
1580 obj->iterator = NULL;
1581 }
1582 }
1583 }
1584 /* }}} */
1585
1586 /* {{{ proto int HttpMessage::key(void)
1587 *
1588 * Implements Iterator.
1589 */
1590 PHP_METHOD(HttpMessage, key)
1591 {
1592 NO_ARGS {
1593 getObject(http_message_object, obj);
1594
1595 RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0);
1596 }
1597 }
1598 /* }}} */
1599
1600 /* {{{ proto HttpMessage HttpMessage::current(void)
1601 *
1602 * Implements Iterator.
1603 */
1604 PHP_METHOD(HttpMessage, current)
1605 {
1606 NO_ARGS {
1607 getObject(http_message_object, obj);
1608
1609 if (obj->iterator) {
1610 RETURN_ZVAL(obj->iterator, 1, 0);
1611 }
1612 }
1613 }
1614 /* }}} */
1615
1616 #endif /* ZEND_ENGINE_2 */
1617
1618 /*
1619 * Local variables:
1620 * tab-width: 4
1621 * c-basic-offset: 4
1622 * End:
1623 * vim600: noet sw=4 ts=4 fdm=marker
1624 * vim<600: noet sw=4 ts=4
1625 */
1626