- servers may return a header multiple times, join them in http_message_header()
[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", STR_PTR(msg->http.info.request.method));
641 ASSOC_STRING(array, "requestUrl", STR_PTR(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", STR_PTR(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, 0))) {
813 RETVAL_ZVAL(header, 1, 1);
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 = NULL, 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 if (new_headers) {
858 INIT_ZARR(old_headers, &obj->message->hdrs);
859 array_copy(new_headers, &old_headers);
860 }
861 }
862 /* }}} */
863
864 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
865 *
866 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
867 *
868 * Expects an associative array as parameter containing the additional HTTP headers
869 * to add to the messages existing headers. If the optional bool parameter is true,
870 * and a header with the same name of one to add exists already, this respective
871 * header will be converted to an array containing both header values, otherwise
872 * it will be overwritten with the new header value.
873 */
874 PHP_METHOD(HttpMessage, addHeaders)
875 {
876 zval old_headers, *new_headers;
877 zend_bool append = 0;
878 getObject(http_message_object, obj);
879
880 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
881 return;
882 }
883
884 INIT_ZARR(old_headers, &obj->message->hdrs);
885 if (append) {
886 array_append(new_headers, &old_headers);
887 } else {
888 array_merge(new_headers, &old_headers);
889 }
890 }
891 /* }}} */
892
893 /* {{{ proto int HttpMessage::getType()
894 *
895 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
896 *
897 * Returns the HttpMessage::TYPE.
898 */
899 PHP_METHOD(HttpMessage, getType)
900 {
901 NO_ARGS;
902
903 if (return_value_used) {
904 getObject(http_message_object, obj);
905 RETURN_LONG(obj->message->type);
906 }
907 }
908 /* }}} */
909
910 /* {{{ proto void HttpMessage::setType(int type)
911 *
912 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
913 *
914 * Expects an int parameter, the HttpMessage::TYPE.
915 */
916 PHP_METHOD(HttpMessage, setType)
917 {
918 long type;
919 getObject(http_message_object, obj);
920
921 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
922 return;
923 }
924 http_message_set_type(obj->message, type);
925 }
926 /* }}} */
927
928 /* {{{ proto int HttpMessage::getResponseCode()
929 *
930 * Get the Response Code of the Message.
931 *
932 * Returns the HTTP response code if the message is of type
933 * HttpMessage::TYPE_RESPONSE, else FALSE.
934 */
935 PHP_METHOD(HttpMessage, getResponseCode)
936 {
937 NO_ARGS;
938
939 if (return_value_used) {
940 getObject(http_message_object, obj);
941 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
942 RETURN_LONG(obj->message->http.info.response.code);
943 }
944 }
945 /* }}} */
946
947 /* {{{ proto bool HttpMessage::setResponseCode(int code)
948 *
949 * Set the response code of an HTTP Response Message.
950 *
951 * Expects an int parameter with the HTTP response code.
952 *
953 * Returns TRUE on success, or FALSE if the message is not of type
954 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
955 */
956 PHP_METHOD(HttpMessage, setResponseCode)
957 {
958 long code;
959 getObject(http_message_object, obj);
960
961 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
962
963 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
964 RETURN_FALSE;
965 }
966 if (code < 100 || code > 510) {
967 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
968 RETURN_FALSE;
969 }
970
971 obj->message->http.info.response.code = code;
972 RETURN_TRUE;
973 }
974 /* }}} */
975
976 /* {{{ proto string HttpMessage::getResponseStatus()
977 *
978 * Get the Response Status of the message (i.e. the string following the response code).
979 *
980 * Returns the HTTP response status string if the message is of type
981 * HttpMessage::TYPE_RESPONSE, else FALSE.
982 */
983 PHP_METHOD(HttpMessage, getResponseStatus)
984 {
985 NO_ARGS;
986
987 if (return_value_used) {
988 getObject(http_message_object, obj);
989 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
990 RETURN_STRING(obj->message->http.info.response.status, 1);
991 }
992 }
993 /* }}} */
994
995 /* {{{ proto bool HttpMessage::setResponseStatus(string status)
996 *
997 * Set the Response Status of the HTTP message (i.e. the string following the response code).
998 *
999 * Expects a string parameter containing the response status text.
1000 *
1001 * Returns TRUE on success or FALSE if the message is not of type
1002 * HttpMessage::TYPE_RESPONSE.
1003 */
1004 PHP_METHOD(HttpMessage, setResponseStatus)
1005 {
1006 char *status;
1007 int status_len;
1008 getObject(http_message_object, obj);
1009
1010 HTTP_CHECK_MESSAGE_TYPE_RESPONSE(obj->message, RETURN_FALSE);
1011
1012 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &status, &status_len)) {
1013 RETURN_FALSE;
1014 }
1015 STR_SET(obj->message->http.info.response.status, estrdup(status));
1016 RETURN_TRUE;
1017 }
1018 /* }}} */
1019
1020 /* {{{ proto string HttpMessage::getRequestMethod()
1021 *
1022 * Get the Request Method of the Message.
1023 *
1024 * Returns the request method name on success, or FALSE if the message is
1025 * not of type HttpMessage::TYPE_REQUEST.
1026 */
1027 PHP_METHOD(HttpMessage, getRequestMethod)
1028 {
1029 NO_ARGS;
1030
1031 if (return_value_used) {
1032 getObject(http_message_object, obj);
1033 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1034 RETURN_STRING(obj->message->http.info.request.method, 1);
1035 }
1036 }
1037 /* }}} */
1038
1039 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
1040 *
1041 * Set the Request Method of the HTTP Message.
1042 *
1043 * Expects a string parameter containing the request method name.
1044 *
1045 * Returns TRUE on success, or FALSE if the message is not of type
1046 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
1047 */
1048 PHP_METHOD(HttpMessage, setRequestMethod)
1049 {
1050 char *method;
1051 int method_len;
1052 getObject(http_message_object, obj);
1053
1054 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1055
1056 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
1057 RETURN_FALSE;
1058 }
1059 if (method_len < 1) {
1060 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
1061 RETURN_FALSE;
1062 }
1063 if (!http_request_method_exists(1, 0, method)) {
1064 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unknown request method: %s", method);
1065 RETURN_FALSE;
1066 }
1067
1068 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
1069 RETURN_TRUE;
1070 }
1071 /* }}} */
1072
1073 /* {{{ proto string HttpMessage::getRequestUrl()
1074 *
1075 * Get the Request URL of the Message.
1076 *
1077 * Returns the request url as string on success, or FALSE if the message
1078 * is not of type HttpMessage::TYPE_REQUEST.
1079 */
1080 PHP_METHOD(HttpMessage, getRequestUrl)
1081 {
1082 NO_ARGS;
1083
1084 if (return_value_used) {
1085 getObject(http_message_object, obj);
1086 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1087 RETURN_STRING(obj->message->http.info.request.url, 1);
1088 }
1089 }
1090 /* }}} */
1091
1092 /* {{{ proto bool HttpMessage::setRequestUrl(string url)
1093 *
1094 * Set the Request URL of the HTTP Message.
1095 *
1096 * Expects a string parameters containing the request url.
1097 *
1098 * Returns TRUE on success, or FALSE if the message is not of type
1099 * HttpMessage::TYPE_REQUEST or supplied URL was empty.
1100 */
1101 PHP_METHOD(HttpMessage, setRequestUrl)
1102 {
1103 char *URI;
1104 int URIlen;
1105 getObject(http_message_object, obj);
1106
1107 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
1108 RETURN_FALSE;
1109 }
1110 HTTP_CHECK_MESSAGE_TYPE_REQUEST(obj->message, RETURN_FALSE);
1111 if (URIlen < 1) {
1112 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUrl to an empty string");
1113 RETURN_FALSE;
1114 }
1115
1116 STR_SET(obj->message->http.info.request.url, estrndup(URI, URIlen));
1117 RETURN_TRUE;
1118 }
1119 /* }}} */
1120
1121 /* {{{ proto string HttpMessage::getHttpVersion()
1122 *
1123 * Get the HTTP Protocol Version of the Message.
1124 *
1125 * Returns the HTTP protocol version as string.
1126 */
1127 PHP_METHOD(HttpMessage, getHttpVersion)
1128 {
1129 NO_ARGS;
1130
1131 if (return_value_used) {
1132 char ver[4] = {0};
1133 getObject(http_message_object, obj);
1134
1135 sprintf(ver, "%1.1lf", obj->message->http.version);
1136 RETURN_STRINGL(ver, 3, 1);
1137 }
1138 }
1139 /* }}} */
1140
1141 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
1142 *
1143 * Set the HTTP Protocol version of the Message.
1144 *
1145 * Expects a string parameter containing the HTTP protocol version.
1146 *
1147 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
1148 */
1149 PHP_METHOD(HttpMessage, setHttpVersion)
1150 {
1151 char v[4];
1152 zval *zv;
1153 getObject(http_message_object, obj);
1154
1155 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
1156 return;
1157 }
1158
1159 convert_to_double(zv);
1160 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
1161 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
1162 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
1163 RETURN_FALSE;
1164 }
1165
1166 obj->message->http.version = Z_DVAL_P(zv);
1167 RETURN_TRUE;
1168 }
1169 /* }}} */
1170
1171 /* {{{ proto string HttpMessage::guessContentType(string magic_file[, int magic_mode = MAGIC_MIME])
1172 *
1173 * Attempts to guess the content type of supplied payload through libmagic.
1174 *
1175 * Expects a string parameter specifying the magic.mime database to use.
1176 * Additionally accepts an optional int parameter, being flags for libmagic.
1177 *
1178 * Returns the guessed content type on success, or FALSE on failure.
1179 *
1180 * Throws HttpRuntimeException, HttpInvalidParamException
1181 * if http.only_exceptions is TRUE.
1182 */
1183 PHP_METHOD(HttpMessage, guessContentType)
1184 {
1185 #ifdef HTTP_HAVE_MAGIC
1186 char *magic_file, *ct = NULL;
1187 int magic_file_len;
1188 long magic_mode = MAGIC_MIME;
1189
1190 RETVAL_FALSE;
1191 SET_EH_THROW_HTTP();
1192 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) {
1193 getObject(http_message_object, obj);
1194 if ((ct = http_guess_content_type(magic_file, magic_mode, PHPSTR_VAL(&obj->message->body), PHPSTR_LEN(&obj->message->body), SEND_DATA))) {
1195 RETVAL_STRING(ct, 0);
1196 }
1197 }
1198 SET_EH_NORMAL();
1199 #else
1200 http_error(HE_THROW, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available");
1201 RETURN_FALSE;
1202 #endif
1203 }
1204 /* }}} */
1205
1206 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
1207 *
1208 * Get parent Message.
1209 *
1210 * Returns the parent HttpMessage on success, or NULL if there's none.
1211 *
1212 * Throws HttpRuntimeException.
1213 */
1214 PHP_METHOD(HttpMessage, getParentMessage)
1215 {
1216 SET_EH_THROW_HTTP();
1217 NO_ARGS {
1218 getObject(http_message_object, obj);
1219
1220 if (obj->message->parent) {
1221 RETVAL_OBJVAL(obj->parent, 1);
1222 } else {
1223 http_error(HE_WARNING, HTTP_E_RUNTIME, "HttpMessage does not have a parent message");
1224 }
1225 }
1226 SET_EH_NORMAL();
1227 }
1228 /* }}} */
1229
1230 /* {{{ proto bool HttpMessage::send()
1231 *
1232 * Send the Message according to its type as Response or Request.
1233 * This provides limited functionality compared to HttpRequest and HttpResponse.
1234 *
1235 * Returns TRUE on success, or FALSE on failure.
1236 */
1237 PHP_METHOD(HttpMessage, send)
1238 {
1239 getObject(http_message_object, obj);
1240
1241 NO_ARGS;
1242
1243 RETURN_SUCCESS(http_message_send(obj->message));
1244 }
1245 /* }}} */
1246
1247 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
1248 *
1249 * Get the string representation of the Message.
1250 *
1251 * Accepts a bool parameter which specifies whether the returned string
1252 * should also contain any parent messages.
1253 *
1254 * Returns the full message as string.
1255 */
1256 PHP_METHOD(HttpMessage, toString)
1257 {
1258 if (return_value_used) {
1259 char *string;
1260 size_t length;
1261 zend_bool include_parent = 0;
1262 getObject(http_message_object, obj);
1263
1264 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
1265 RETURN_FALSE;
1266 }
1267
1268 if (include_parent) {
1269 http_message_serialize(obj->message, &string, &length);
1270 } else {
1271 http_message_tostring(obj->message, &string, &length);
1272 }
1273 RETURN_STRINGL(string, length, 0);
1274 }
1275 }
1276 /* }}} */
1277
1278 /* {{{ proto HttpRequest|HttpResponse HttpMessage::toMessageTypeObject(void)
1279 *
1280 * Creates an object regarding to the type of the message.
1281 *
1282 * Returns either an HttpRequest or HttpResponse object on success, or NULL on failure.
1283 *
1284 * Throws HttpRuntimeException, HttpMessageTypeException, HttpHeaderException.
1285 */
1286 PHP_METHOD(HttpMessage, toMessageTypeObject)
1287 {
1288 SET_EH_THROW_HTTP();
1289
1290 NO_ARGS;
1291
1292 if (return_value_used) {
1293 getObject(http_message_object, obj);
1294
1295 switch (obj->message->type) {
1296 case HTTP_MSG_REQUEST:
1297 {
1298 #ifdef HTTP_HAVE_CURL
1299 int method;
1300 char *url;
1301 zval tmp, body, *array, *headers, *host = http_message_header(obj->message, "Host");
1302 php_url hurl, *purl = php_url_parse(obj->message->http.info.request.url);
1303
1304 MAKE_STD_ZVAL(array);
1305 array_init(array);
1306
1307 memset(&hurl, 0, sizeof(php_url));
1308 hurl.host = host ? Z_STRVAL_P(host) : NULL;
1309 zval_ptr_dtor(&host);
1310 http_build_url(HTTP_URL_REPLACE, purl, &hurl, NULL, &url, NULL);
1311 php_url_free(purl);
1312 add_assoc_string(array, "url", url, 0);
1313
1314 if ( (method = http_request_method_exists(1, 0, obj->message->http.info.request.method)) ||
1315 (method = http_request_method_register(obj->message->http.info.request.method, strlen(obj->message->http.info.request.method)))) {
1316 add_assoc_long(array, "method", method);
1317 }
1318
1319 if (10 == (int) (obj->message->http.version * 10)) {
1320 add_assoc_long(array, "protocol", CURL_HTTP_VERSION_1_0);
1321 }
1322
1323 MAKE_STD_ZVAL(headers);
1324 array_init(headers);
1325 INIT_ZARR(tmp, &obj->message->hdrs);
1326 array_copy(&tmp, headers);
1327 add_assoc_zval(array, "headers", headers);
1328
1329 object_init_ex(return_value, http_request_object_ce);
1330 zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setoptions", NULL, array);
1331 zval_ptr_dtor(&array);
1332
1333 INIT_PZVAL(&body);
1334 ZVAL_STRINGL(&body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 0);
1335 zend_call_method_with_1_params(&return_value, http_request_object_ce, NULL, "setrawpostdata", NULL, &body);
1336 #else
1337 http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpRequest (missing curl support)");
1338 #endif
1339 break;
1340 }
1341
1342 case HTTP_MSG_RESPONSE:
1343 {
1344 #ifndef WONKY
1345 HashPosition pos1, pos2;
1346 ulong idx;
1347 uint key_len;
1348 char *key = NULL;
1349 zval **header, **h, *body;
1350
1351 if (obj->message->http.info.response.code) {
1352 http_send_status(obj->message->http.info.response.code);
1353 }
1354
1355 object_init_ex(return_value, http_response_object_ce);
1356
1357 FOREACH_HASH_KEYLENVAL(pos1, &obj->message->hdrs, key, key_len, idx, header) {
1358 if (key) {
1359 zval zkey;
1360
1361 INIT_PZVAL(&zkey);
1362 ZVAL_STRINGL(&zkey, key, key_len, 0);
1363
1364 switch (Z_TYPE_PP(header)) {
1365 case IS_ARRAY:
1366 case IS_OBJECT:
1367 FOREACH_HASH_VAL(pos2, HASH_OF(*header), h) {
1368 ZVAL_ADDREF(*h);
1369 zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, &zkey, *h);
1370 zval_ptr_dtor(h);
1371 }
1372 break;
1373
1374 default:
1375 ZVAL_ADDREF(*header);
1376 zend_call_method_with_2_params(&return_value, http_response_object_ce, NULL, "setheader", NULL, &zkey, *header);
1377 zval_ptr_dtor(header);
1378 break;
1379 }
1380 key = NULL;
1381 }
1382 }
1383
1384 MAKE_STD_ZVAL(body);
1385 ZVAL_STRINGL(body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message), 1);
1386 zend_call_method_with_1_params(&return_value, http_response_object_ce, NULL, "setdata", NULL, body);
1387 zval_ptr_dtor(&body);
1388 #else
1389 http_error(HE_WARNING, HTTP_E_RUNTIME, "Cannot transform HttpMessage to HttpResponse (need PHP 5.1+)");
1390 #endif
1391 break;
1392 }
1393
1394 default:
1395 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is neither of type HttpMessage::TYPE_REQUEST nor HttpMessage::TYPE_RESPONSE");
1396 break;
1397 }
1398 }
1399 SET_EH_NORMAL();
1400 }
1401 /* }}} */
1402
1403 /* {{{ proto int HttpMessage::count()
1404 *
1405 * Implements Countable.
1406 *
1407 * Returns the number of parent messages + 1.
1408 */
1409 PHP_METHOD(HttpMessage, count)
1410 {
1411 NO_ARGS {
1412 long i;
1413 getObject(http_message_object, obj);
1414
1415 http_message_count(i, obj->message);
1416 RETURN_LONG(i);
1417 }
1418 }
1419 /* }}} */
1420
1421 /* {{{ proto string HttpMessage::serialize()
1422 *
1423 * Implements Serializable.
1424 *
1425 * Returns the serialized representation of the HttpMessage.
1426 */
1427 PHP_METHOD(HttpMessage, serialize)
1428 {
1429 NO_ARGS {
1430 char *string;
1431 size_t length;
1432 getObject(http_message_object, obj);
1433
1434 http_message_serialize(obj->message, &string, &length);
1435 RETURN_STRINGL(string, length, 0);
1436 }
1437 }
1438 /* }}} */
1439
1440 /* {{{ proto void HttpMessage::unserialize(string serialized)
1441 *
1442 * Implements Serializable.
1443 *
1444 * Re-constructs the HttpMessage based upon the serialized string.
1445 */
1446 PHP_METHOD(HttpMessage, unserialize)
1447 {
1448 int length;
1449 char *serialized;
1450 getObject(http_message_object, obj);
1451
1452 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) {
1453 http_message_dtor(obj->message);
1454 if (!http_message_parse_ex(obj->message, serialized, (size_t) length)) {
1455 http_error(HE_ERROR, HTTP_E_RUNTIME, "Could not unserialize HttpMessage");
1456 http_message_init(obj->message);
1457 }
1458 }
1459 }
1460 /* }}} */
1461
1462 /* {{{ proto HttpMessage HttpMessage::detach(void)
1463 *
1464 * Returns a clone of an HttpMessage object detached from any parent messages.
1465 */
1466 PHP_METHOD(HttpMessage, detach)
1467 {
1468 http_info info;
1469 http_message *msg;
1470 getObject(http_message_object, obj);
1471
1472 NO_ARGS;
1473
1474 info.type = obj->message->type;
1475 memcpy(&HTTP_INFO(&info), &HTTP_INFO(obj->message), sizeof(struct http_info));
1476
1477 msg = http_message_new();
1478 http_message_set_info(msg, &info);
1479
1480 zend_hash_copy(&msg->hdrs, &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
1481 phpstr_append(&msg->body, PHPSTR_VAL(obj->message), PHPSTR_LEN(obj->message));
1482
1483 RETVAL_OBJVAL(http_message_object_new_ex(Z_OBJCE_P(getThis()), msg, NULL), 0);
1484 }
1485 /* }}} */
1486
1487 /* {{{ proto void HttpMessage::prepend(HttpMessage message[, bool top = true])
1488 *
1489 * Prepends message(s) to the HTTP message.
1490 *
1491 * Expects an HttpMessage object as parameter.
1492 *
1493 * Throws HttpInvalidParamException if the message is located within the same message chain.
1494 */
1495 PHP_METHOD(HttpMessage, prepend)
1496 {
1497 zval *prepend;
1498 zend_bool top = 1;
1499
1500 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, http_message_object_ce, &top)) {
1501 http_message *msg[2];
1502 getObject(http_message_object, obj);
1503 getObjectEx(http_message_object, prepend_obj, prepend);
1504
1505 /* safety check */
1506 for (msg[0] = obj->message; msg[0]; msg[0] = msg[0]->parent) {
1507 for (msg[1] = prepend_obj->message; msg[1]; msg[1] = msg[1]->parent) {
1508 if (msg[0] == msg[1]) {
1509 http_error(HE_THROW, HTTP_E_INVALID_PARAM, "Cannot prepend a message located within the same message chain");
1510 return;
1511 }
1512 }
1513 }
1514
1515 http_message_object_prepend_ex(getThis(), prepend, top);
1516 }
1517 }
1518 /* }}} */
1519
1520 /* {{{ proto HttpMessage HttpMessage::reverse()
1521 *
1522 * Reorders the message chain in reverse order.
1523 *
1524 * Returns the most parent HttpMessage object.
1525 */
1526 PHP_METHOD(HttpMessage, reverse)
1527 {
1528 NO_ARGS {
1529 http_message_object_reverse(getThis(), return_value);
1530 }
1531 }
1532 /* }}} */
1533
1534 /* {{{ proto void HttpMessage::rewind(void)
1535 *
1536 * Implements Iterator.
1537 */
1538 PHP_METHOD(HttpMessage, rewind)
1539 {
1540 NO_ARGS {
1541 getObject(http_message_object, obj);
1542
1543 if (obj->iterator) {
1544 zval_ptr_dtor(&obj->iterator);
1545 }
1546 ZVAL_ADDREF(getThis());
1547 obj->iterator = getThis();
1548 }
1549 }
1550 /* }}} */
1551
1552 /* {{{ proto bool HttpMessage::valid(void)
1553 *
1554 * Implements Iterator.
1555 */
1556 PHP_METHOD(HttpMessage, valid)
1557 {
1558 NO_ARGS {
1559 getObject(http_message_object, obj);
1560
1561 RETURN_BOOL(obj->iterator != NULL);
1562 }
1563 }
1564 /* }}} */
1565
1566 /* {{{ proto void HttpMessage::next(void)
1567 *
1568 * Implements Iterator.
1569 */
1570 PHP_METHOD(HttpMessage, next)
1571 {
1572 NO_ARGS {
1573 getObject(http_message_object, obj);
1574 getObjectEx(http_message_object, itr, obj->iterator);
1575
1576 if (itr && itr->parent.handle) {
1577 zval *old = obj->iterator;
1578 MAKE_STD_ZVAL(obj->iterator);
1579 ZVAL_OBJVAL(obj->iterator, itr->parent, 1);
1580 zval_ptr_dtor(&old);
1581 } else {
1582 zval_ptr_dtor(&obj->iterator);
1583 obj->iterator = NULL;
1584 }
1585 }
1586 }
1587 /* }}} */
1588
1589 /* {{{ proto int HttpMessage::key(void)
1590 *
1591 * Implements Iterator.
1592 */
1593 PHP_METHOD(HttpMessage, key)
1594 {
1595 NO_ARGS {
1596 getObject(http_message_object, obj);
1597
1598 RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0);
1599 }
1600 }
1601 /* }}} */
1602
1603 /* {{{ proto HttpMessage HttpMessage::current(void)
1604 *
1605 * Implements Iterator.
1606 */
1607 PHP_METHOD(HttpMessage, current)
1608 {
1609 NO_ARGS {
1610 getObject(http_message_object, obj);
1611
1612 if (obj->iterator) {
1613 RETURN_ZVAL(obj->iterator, 1, 0);
1614 }
1615 }
1616 }
1617 /* }}} */
1618
1619 #endif /* ZEND_ENGINE_2 */
1620
1621 /*
1622 * Local variables:
1623 * tab-width: 4
1624 * c-basic-offset: 4
1625 * End:
1626 * vim600: noet sw=4 ts=4 fdm=marker
1627 * vim<600: noet sw=4 ts=4
1628 */
1629