- correct DST TZ offsets
[m6w6/ext-http] / http_message_object.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22 #include "php.h"
23
24 #ifdef ZEND_ENGINE_2
25
26 #include "php_http.h"
27 #include "php_http_api.h"
28 #include "php_http_std_defs.h"
29 #include "php_http_message_object.h"
30 #include "php_http_exception_object.h"
31
32 #include "phpstr/phpstr.h"
33 #include "missing.h"
34
35 ZEND_EXTERN_MODULE_GLOBALS(http);
36
37 #define HTTP_BEGIN_ARGS(method, ret_ref, req_args) HTTP_BEGIN_ARGS_EX(HttpMessage, method, ret_ref, req_args)
38 #define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpMessage, method, ret_ref)
39 #define HTTP_MESSAGE_ME(method, visibility) PHP_ME(HttpMessage, method, HTTP_ARGS(HttpMessage, method), visibility)
40
41 HTTP_BEGIN_ARGS(__construct, 0, 0)
42 HTTP_ARG_VAL(message, 0)
43 HTTP_END_ARGS;
44
45 HTTP_BEGIN_ARGS(fromString, 1, 1)
46 HTTP_ARG_VAL(message, 0)
47 HTTP_END_ARGS;
48
49 HTTP_EMPTY_ARGS(getBody, 0);
50 HTTP_BEGIN_ARGS(setBody, 0, 1)
51 HTTP_ARG_VAL(body, 0)
52 HTTP_END_ARGS;
53
54 HTTP_EMPTY_ARGS(getHeaders, 0);
55 HTTP_BEGIN_ARGS(setHeaders, 0, 1)
56 HTTP_ARG_VAL(headers, 0)
57 HTTP_END_ARGS;
58
59 HTTP_BEGIN_ARGS(addHeaders, 0, 1)
60 HTTP_ARG_VAL(headers, 0)
61 HTTP_ARG_VAL(append, 0)
62 HTTP_END_ARGS;
63
64 HTTP_EMPTY_ARGS(getType, 0);
65 HTTP_BEGIN_ARGS(setType, 0, 1)
66 HTTP_ARG_VAL(type, 0)
67 HTTP_END_ARGS;
68
69 HTTP_EMPTY_ARGS(getResponseCode, 0);
70 HTTP_BEGIN_ARGS(setResponseCode, 0, 1)
71 HTTP_ARG_VAL(response_code, 0)
72 HTTP_END_ARGS;
73
74 HTTP_EMPTY_ARGS(getRequestMethod, 0);
75 HTTP_BEGIN_ARGS(setRequestMethod, 0, 1)
76 HTTP_ARG_VAL(request_method, 0)
77 HTTP_END_ARGS;
78
79 HTTP_EMPTY_ARGS(getRequestUri, 0);
80 HTTP_BEGIN_ARGS(setRequestUri, 0, 1)
81 HTTP_ARG_VAL(uri, 0)
82 HTTP_END_ARGS;
83
84 HTTP_EMPTY_ARGS(getHttpVersion, 0);
85 HTTP_BEGIN_ARGS(setHttpVersion, 0, 1)
86 HTTP_ARG_VAL(http_version, 0)
87 HTTP_END_ARGS;
88
89 HTTP_EMPTY_ARGS(getParentMessage, 1);
90 HTTP_EMPTY_ARGS(send, 0);
91 HTTP_BEGIN_ARGS(toString, 0, 0)
92 HTTP_ARG_VAL(include_parent, 0)
93 HTTP_END_ARGS;
94
95 #define http_message_object_declare_default_properties() _http_message_object_declare_default_properties(TSRMLS_C)
96 static inline void _http_message_object_declare_default_properties(TSRMLS_D);
97 #define http_message_object_read_prop _http_message_object_read_prop
98 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC);
99 #define http_message_object_write_prop _http_message_object_write_prop
100 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC);
101 #define http_message_object_get_props _http_message_object_get_props
102 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC);
103 #define http_message_object_clone_obj _http_message_object_clone_obj
104 static inline zend_object_value _http_message_object_clone_obj(zval *object TSRMLS_DC);
105
106 zend_class_entry *http_message_object_ce;
107 zend_function_entry http_message_object_fe[] = {
108 HTTP_MESSAGE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
109 HTTP_MESSAGE_ME(getBody, ZEND_ACC_PUBLIC)
110 HTTP_MESSAGE_ME(setBody, ZEND_ACC_PUBLIC)
111 HTTP_MESSAGE_ME(getHeaders, ZEND_ACC_PUBLIC)
112 HTTP_MESSAGE_ME(setHeaders, ZEND_ACC_PUBLIC)
113 HTTP_MESSAGE_ME(addHeaders, ZEND_ACC_PUBLIC)
114 HTTP_MESSAGE_ME(getType, ZEND_ACC_PUBLIC)
115 HTTP_MESSAGE_ME(setType, ZEND_ACC_PUBLIC)
116 HTTP_MESSAGE_ME(getResponseCode, ZEND_ACC_PUBLIC)
117 HTTP_MESSAGE_ME(setResponseCode, ZEND_ACC_PUBLIC)
118 HTTP_MESSAGE_ME(getRequestMethod, ZEND_ACC_PUBLIC)
119 HTTP_MESSAGE_ME(setRequestMethod, ZEND_ACC_PUBLIC)
120 HTTP_MESSAGE_ME(getRequestUri, ZEND_ACC_PUBLIC)
121 HTTP_MESSAGE_ME(setRequestUri, ZEND_ACC_PUBLIC)
122 HTTP_MESSAGE_ME(getHttpVersion, ZEND_ACC_PUBLIC)
123 HTTP_MESSAGE_ME(setHttpVersion, ZEND_ACC_PUBLIC)
124 HTTP_MESSAGE_ME(getParentMessage, ZEND_ACC_PUBLIC)
125 HTTP_MESSAGE_ME(send, ZEND_ACC_PUBLIC)
126 HTTP_MESSAGE_ME(toString, ZEND_ACC_PUBLIC)
127
128 ZEND_MALIAS(HttpMessage, __toString, toString, HTTP_ARGS(HttpMessage, toString), ZEND_ACC_PUBLIC)
129
130 HTTP_MESSAGE_ME(fromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
131
132 EMPTY_FUNCTION_ENTRY
133 };
134 static zend_object_handlers http_message_object_handlers;
135
136 void _http_message_object_init(INIT_FUNC_ARGS)
137 {
138 HTTP_REGISTER_CLASS_EX(HttpMessage, http_message_object, NULL, 0);
139
140 HTTP_LONG_CONSTANT("HTTP_MSG_NONE", HTTP_MSG_NONE);
141 HTTP_LONG_CONSTANT("HTTP_MSG_REQUEST", HTTP_MSG_REQUEST);
142 HTTP_LONG_CONSTANT("HTTP_MSG_RESPONSE", HTTP_MSG_RESPONSE);
143
144 http_message_object_handlers.clone_obj = http_message_object_clone_obj;
145 http_message_object_handlers.read_property = http_message_object_read_prop;
146 http_message_object_handlers.write_property = http_message_object_write_prop;
147 http_message_object_handlers.get_properties = http_message_object_get_props;
148 http_message_object_handlers.get_property_ptr_ptr = NULL;
149 }
150
151 zend_object_value _http_message_object_new(zend_class_entry *ce TSRMLS_DC)
152 {
153 return http_message_object_new_ex(ce, NULL);
154 }
155
156 zend_object_value _http_message_object_new_ex(zend_class_entry *ce, http_message *msg TSRMLS_DC)
157 {
158 zend_object_value ov;
159 http_message_object *o;
160
161 o = ecalloc(1, sizeof(http_message_object));
162 o->zo.ce = ce;
163
164 if (msg) {
165 o->message = msg;
166 if (msg->parent) {
167 o->parent = http_message_object_from_msg(msg->parent);
168 }
169 }
170
171 ALLOC_HASHTABLE(OBJ_PROP(o));
172 zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0);
173
174 ov.handle = putObject(http_message_object, o);
175 ov.handlers = &http_message_object_handlers;
176
177 return ov;
178 }
179
180 zend_object_value _http_message_object_clone(zval *this_ptr TSRMLS_DC)
181 {
182 return http_message_object_clone_obj(this_ptr TSRMLS_CC);
183 }
184
185 static inline void _http_message_object_declare_default_properties(TSRMLS_D)
186 {
187 zend_class_entry *ce = http_message_object_ce;
188
189 #ifndef WONKY
190 DCL_CONST(long, "TYPE_NONE", HTTP_MSG_NONE);
191 DCL_CONST(long, "TYPE_REQUEST", HTTP_MSG_REQUEST);
192 DCL_CONST(long, "TYPE_RESPONSE", HTTP_MSG_RESPONSE);
193 #endif
194
195 DCL_PROP(PROTECTED, long, type, HTTP_MSG_NONE);
196 DCL_PROP(PROTECTED, string, body, "");
197 DCL_PROP(PROTECTED, string, requestMethod, "");
198 DCL_PROP(PROTECTED, string, requestUri, "");
199 DCL_PROP(PROTECTED, long, responseCode, 0);
200 DCL_PROP_N(PROTECTED, httpVersion);
201 DCL_PROP_N(PROTECTED, headers);
202 DCL_PROP_N(PROTECTED, parentMessage);
203 }
204
205 void _http_message_object_free(zend_object *object TSRMLS_DC)
206 {
207 http_message_object *o = (http_message_object *) object;
208
209 if (OBJ_PROP(o)) {
210 zend_hash_destroy(OBJ_PROP(o));
211 FREE_HASHTABLE(OBJ_PROP(o));
212 }
213 if (o->message) {
214 http_message_dtor(o->message);
215 efree(o->message);
216 }
217 efree(o);
218 }
219
220 static inline zend_object_value _http_message_object_clone_obj(zval *this_ptr TSRMLS_DC)
221 {
222 getObject(http_message_object, obj);
223 return http_message_object_from_msg(http_message_dup(obj->message));
224 }
225
226 static zval *_http_message_object_read_prop(zval *object, zval *member, int type TSRMLS_DC)
227 {
228 getObjectEx(http_message_object, obj, object);
229 http_message *msg = obj->message;
230 zval *return_value;
231 #ifdef WONKY
232 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member)+1);
233 #else
234 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
235
236 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
237 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
238 }
239 #endif
240
241 return_value = &EG(uninitialized_zval);
242 return_value->refcount = 0;
243 return_value->is_ref = 0;
244
245 #ifdef WONKY
246 switch (h)
247 #else
248 switch (pinfo->h)
249 #endif
250 {
251 case HTTP_MSG_PROPHASH_TYPE:
252 RETVAL_LONG(msg->type);
253 break;
254
255 case HTTP_MSG_PROPHASH_HTTP_VERSION:
256 RETVAL_DOUBLE(msg->http.version);
257 break;
258
259 case HTTP_MSG_PROPHASH_BODY:
260 phpstr_fix(PHPSTR(msg));
261 RETVAL_PHPSTR(PHPSTR(msg), 0, 1);
262 break;
263
264 case HTTP_MSG_PROPHASH_HEADERS:
265 /* This is needed for situations like
266 * $this->headers['foo'] = 'bar';
267 */
268 if (type == BP_VAR_W) {
269 return_value->refcount = 1;
270 return_value->is_ref = 1;
271 Z_TYPE_P(return_value) = IS_ARRAY;
272 Z_ARRVAL_P(return_value) = &msg->hdrs;
273 } else {
274 array_init(return_value);
275 zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
276 }
277 break;
278
279 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
280 if (msg->parent) {
281 RETVAL_OBJVAL(obj->parent);
282 } else {
283 RETVAL_NULL();
284 }
285 break;
286
287 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
288 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.method) {
289 RETVAL_STRING(msg->http.info.request.method, 1);
290 } else {
291 RETVAL_NULL();
292 }
293 break;
294
295 case HTTP_MSG_PROPHASH_REQUEST_URI:
296 if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.URI) {
297 RETVAL_STRING(msg->http.info.request.URI, 1);
298 } else {
299 RETVAL_NULL();
300 }
301 break;
302
303 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
304 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
305 RETVAL_LONG(msg->http.info.response.code);
306 } else {
307 RETVAL_NULL();
308 }
309 break;
310
311 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
312 if (HTTP_MSG_TYPE(RESPONSE, msg) && msg->http.info.response.status) {
313 RETVAL_STRING(msg->http.info.response.status, 1);
314 } else {
315 RETVAL_NULL();
316 }
317 break;
318
319 default:
320 #ifdef WONKY
321 return zend_get_std_object_handlers()->read_property(object, member, type TSRMLS_CC);
322 #else
323 RETVAL_NULL();
324 #endif
325 break;
326 }
327
328 return return_value;
329 }
330
331 static void _http_message_object_write_prop(zval *object, zval *member, zval *value TSRMLS_DC)
332 {
333 getObjectEx(http_message_object, obj, object);
334 http_message *msg = obj->message;
335 #ifdef WONKY
336 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
337 #else
338 zend_property_info *pinfo = zend_get_property_info(obj->zo.ce, member, 1 TSRMLS_CC);
339
340 if (!pinfo || ACC_PROP_PUBLIC(pinfo->flags)) {
341 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
342 return;
343 }
344 #endif
345
346 #ifdef WONKY
347 switch (h)
348 #else
349 switch (pinfo->h)
350 #endif
351 {
352 case HTTP_MSG_PROPHASH_TYPE:
353 convert_to_long_ex(&value);
354 http_message_set_type(msg, Z_LVAL_P(value));
355 break;
356
357 case HTTP_MSG_PROPHASH_HTTP_VERSION:
358 convert_to_double_ex(&value);
359 msg->http.version = Z_DVAL_P(value);
360 break;
361
362 case HTTP_MSG_PROPHASH_BODY:
363 convert_to_string_ex(&value);
364 phpstr_dtor(PHPSTR(msg));
365 phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(value), Z_STRLEN_P(value));
366 break;
367
368 case HTTP_MSG_PROPHASH_HEADERS:
369 convert_to_array_ex(&value);
370 zend_hash_clean(&msg->hdrs);
371 zend_hash_copy(&msg->hdrs, Z_ARRVAL_P(value), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
372 break;
373
374 case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
375 if (msg->parent) {
376 zval tmp;
377 tmp.value.obj = obj->parent;
378 zend_objects_store_del_ref(&tmp TSRMLS_CC);
379 }
380 zend_objects_store_add_ref(value TSRMLS_CC);
381 obj->parent = value->value.obj;
382 break;
383
384 case HTTP_MSG_PROPHASH_REQUEST_METHOD:
385 if (HTTP_MSG_TYPE(REQUEST, msg)) {
386 convert_to_string_ex(&value);
387 STR_SET(msg->http.info.request.method, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)));
388 }
389 break;
390
391 case HTTP_MSG_PROPHASH_REQUEST_URI:
392 if (HTTP_MSG_TYPE(REQUEST, msg)) {
393 convert_to_string_ex(&value);
394 STR_SET(msg->http.info.request.URI, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)));
395 }
396 break;
397
398 case HTTP_MSG_PROPHASH_RESPONSE_CODE:
399 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
400 convert_to_long_ex(&value);
401 msg->http.info.response.code = Z_LVAL_P(value);
402 }
403 break;
404
405 case HTTP_MSG_PROPHASH_RESPONSE_STATUS:
406 if (HTTP_MSG_TYPE(RESPONSE, msg)) {
407 convert_to_string_ex(&value);
408 STR_SET(msg->http.info.response.status, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)));
409 }
410 break;
411
412 default:
413 #ifdef WONKY
414 zend_get_std_object_handlers()->write_property(object, member, value TSRMLS_CC);
415 #endif
416 break;
417 }
418 }
419
420 static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC)
421 {
422 zval *headers;
423 getObjectEx(http_message_object, obj, object);
424 http_message *msg = obj->message;
425 HashTable *props = OBJ_PROP(obj);
426 zval array;
427
428 INIT_ZARR(array, props);
429
430 #define ASSOC_PROP(array, ptype, name, val) \
431 { \
432 char *m_prop_name; \
433 int m_prop_len; \
434 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
435 add_assoc_ ##ptype## _ex(&array, m_prop_name, sizeof(name)+4, val); \
436 efree(m_prop_name); \
437 }
438 #define ASSOC_STRING(array, name, val) ASSOC_STRINGL(array, name, val, strlen(val))
439 #define ASSOC_STRINGL(array, name, val, len) \
440 { \
441 char *m_prop_name; \
442 int m_prop_len; \
443 zend_mangle_property_name(&m_prop_name, &m_prop_len, "*", 1, name, lenof(name), 0); \
444 add_assoc_stringl_ex(&array, m_prop_name, sizeof(name)+4, val, len, 1); \
445 efree(m_prop_name); \
446 }
447
448 ASSOC_PROP(array, long, "type", msg->type);
449 ASSOC_PROP(array, double, "httpVersion", msg->http.version);
450
451 switch (msg->type)
452 {
453 case HTTP_MSG_REQUEST:
454 ASSOC_PROP(array, long, "responseCode", 0);
455 ASSOC_STRINGL(array, "responseStatus", "", 0);
456 ASSOC_STRING(array, "requestMethod", msg->http.info.request.method);
457 ASSOC_STRING(array, "requestUri", msg->http.info.request.URI);
458 break;
459
460 case HTTP_MSG_RESPONSE:
461 ASSOC_PROP(array, long, "responseCode", msg->http.info.response.code);
462 ASSOC_STRING(array, "responseStatus", msg->http.info.response.status);
463 ASSOC_STRINGL(array, "requestMethod", "", 0);
464 ASSOC_STRINGL(array, "requestUri", "", 0);
465 break;
466
467 case HTTP_MSG_NONE:
468 default:
469 ASSOC_PROP(array, long, "responseCode", 0);
470 ASSOC_STRINGL(array, "responseStatus", "", 0);
471 ASSOC_STRINGL(array, "requestMethod", "", 0);
472 ASSOC_STRINGL(array, "requestUri", "", 0);
473 break;
474 }
475
476 MAKE_STD_ZVAL(headers);
477 array_init(headers);
478 zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
479 ASSOC_PROP(array, zval, "headers", headers);
480 ASSOC_STRINGL(array, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg));
481
482 return OBJ_PROP(obj);
483 }
484
485 /* ### USERLAND ### */
486
487 /* {{{ proto void HttpMessage::__construct([string message])
488 *
489 * Instantiate a new HttpMessage object.
490 *
491 * Accepts an optional string parameter containing a single or several
492 * consecutive HTTP messages. The constructed object will actually
493 * represent the *last* message of the passed string. If there were
494 * prior messages, those can be accessed by HttpMessage::getParentMessage().
495 *
496 * Throws HttpMalformedHeaderException.
497 */
498 PHP_METHOD(HttpMessage, __construct)
499 {
500 char *message = NULL;
501 int length = 0;
502 getObject(http_message_object, obj);
503
504 SET_EH_THROW_HTTP();
505 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) {
506 if (obj->message = http_message_parse(message, length)) {
507 if (obj->message->parent) {
508 obj->parent = http_message_object_from_msg(obj->message->parent);
509 }
510 }
511 } else if (!obj->message) {
512 obj->message = http_message_new();
513 }
514 SET_EH_NORMAL();
515 }
516 /* }}} */
517
518 /* {{{ proto static HttpMessage HttpMessage::fromString(string raw_message)
519 *
520 * Create an HttpMessage object from a string. Kind of a static constructor.
521 *
522 * Expects a string parameter containing a sinlge or several consecutive
523 * HTTP messages.
524 *
525 * Returns an HttpMessage object on success or NULL on failure.
526 *
527 * Throws HttpMalformedHeadersException.
528 */
529 PHP_METHOD(HttpMessage, fromString)
530 {
531 char *string = NULL;
532 int length = 0;
533 http_message *msg = NULL;
534
535 RETVAL_NULL();
536
537 SET_EH_THROW_HTTP();
538 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &length)) {
539 if (msg = http_message_parse(string, length)) {
540 Z_TYPE_P(return_value) = IS_OBJECT;
541 return_value->value.obj = http_message_object_from_msg(msg);
542 }
543 }
544 SET_EH_NORMAL();
545 }
546 /* }}} */
547
548 /* {{{ proto string HttpMessage::getBody()
549 *
550 * Get the body of the parsed HttpMessage.
551 *
552 * Returns the message body as string.
553 */
554 PHP_METHOD(HttpMessage, getBody)
555 {
556 NO_ARGS;
557
558 IF_RETVAL_USED {
559 getObject(http_message_object, obj);
560 RETURN_PHPSTR(&obj->message->body, PHPSTR_FREE_NOT, 1);
561 }
562 }
563 /* }}} */
564
565 /* {{{ proto void HttpMessage::setBody(string body)
566 *
567 * Set the body of the HttpMessage.
568 * NOTE: Don't forget to update any headers accordingly.
569 *
570 * Expects a string parameter containing the new body of the message.
571 */
572 PHP_METHOD(HttpMessage, setBody)
573 {
574 char *body;
575 int len;
576 getObject(http_message_object, obj);
577
578 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &body, &len)) {
579 phpstr_dtor(PHPSTR(obj->message));
580 phpstr_from_string_ex(PHPSTR(obj->message), body, len);
581 }
582 }
583 /* }}} */
584
585 /* {{{ proto array HttpMessage::getHeaders()
586 *
587 * Get Message Headers.
588 *
589 * Returns an associative array containing the messages HTTP headers.
590 */
591 PHP_METHOD(HttpMessage, getHeaders)
592 {
593 NO_ARGS;
594
595 IF_RETVAL_USED {
596 zval headers;
597 getObject(http_message_object, obj);
598
599 INIT_ZARR(headers, &obj->message->hdrs);
600 array_init(return_value);
601 array_copy(&headers, return_value);
602 }
603 }
604 /* }}} */
605
606 /* {{{ proto void HttpMessage::setHeaders(array headers)
607 *
608 * Sets new headers.
609 *
610 * Expects an associative array as parameter containing the new HTTP headers,
611 * which will replace *all* previous HTTP headers of the message.
612 */
613 PHP_METHOD(HttpMessage, setHeaders)
614 {
615 zval *new_headers, old_headers;
616 getObject(http_message_object, obj);
617
618 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &new_headers)) {
619 return;
620 }
621
622 zend_hash_clean(&obj->message->hdrs);
623 INIT_ZARR(old_headers, &obj->message->hdrs);
624 array_copy(new_headers, &old_headers);
625 }
626 /* }}} */
627
628 /* {{{ proto void HttpMessage::addHeaders(array headers[, bool append = false])
629 *
630 * Add headers. If append is true, headers with the same name will be separated, else overwritten.
631 *
632 * Expects an associative array as parameter containing the additional HTTP headers
633 * to add to the messages existing headers. If the optional bool parameter is true,
634 * and a header with the same name of one to add exists already, this respective
635 * header will be converted to an array containing both header values, otherwise
636 * it will be overwritten with the new header value.
637 */
638 PHP_METHOD(HttpMessage, addHeaders)
639 {
640 zval old_headers, *new_headers;
641 zend_bool append = 0;
642 getObject(http_message_object, obj);
643
644 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) {
645 return;
646 }
647
648 INIT_ZARR(old_headers, &obj->message->hdrs);
649 if (append) {
650 array_append(new_headers, &old_headers);
651 } else {
652 array_merge(new_headers, &old_headers);
653 }
654 }
655 /* }}} */
656
657 /* {{{ proto int HttpMessage::getType()
658 *
659 * Get Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
660 *
661 * Returns the HttpMessage::TYPE.
662 */
663 PHP_METHOD(HttpMessage, getType)
664 {
665 NO_ARGS;
666
667 IF_RETVAL_USED {
668 getObject(http_message_object, obj);
669 RETURN_LONG(obj->message->type);
670 }
671 }
672 /* }}} */
673
674 /* {{{ proto void HttpMessage::setType(int type)
675 *
676 * Set Message Type. (HTTP_MSG_NONE|HTTP_MSG_REQUEST|HTTP_MSG_RESPONSE)
677 *
678 * Exptects an int parameter, the HttpMessage::TYPE.
679 */
680 PHP_METHOD(HttpMessage, setType)
681 {
682 long type;
683 getObject(http_message_object, obj);
684
685 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
686 return;
687 }
688 http_message_set_type(obj->message, type);
689 }
690 /* }}} */
691
692 /* {{{ proto int HttpMessage::getResponseCode()
693 *
694 * Get the Response Code of the Message.
695 *
696 * Returns the HTTP response code if the message is of type
697 * HttpMessage::TYPE_RESPONSE, else FALSE.
698 */
699 PHP_METHOD(HttpMessage, getResponseCode)
700 {
701 NO_ARGS;
702
703 IF_RETVAL_USED {
704 getObject(http_message_object, obj);
705
706 if (!HTTP_MSG_TYPE(RESPONSE, obj->message)) {
707 http_error(HE_NOTICE, HTTP_E_MESSAGE_TYPE, "HttpMessage is not of type HTTP_MSG_RESPONSE");
708 RETURN_FALSE;
709 }
710
711 RETURN_LONG(obj->message->http.info.response.code);
712 }
713 }
714 /* }}} */
715
716 /* {{{ proto bool HttpMessage::setResponseCode(int code)
717 *
718 * Set the response code of an HTTP Response Message.
719 *
720 * Expects an int parameter with the HTTP response code.
721 *
722 * Returns TRUE on success, or FALSE if the message is not of type
723 * HttpMessage::TYPE_RESPONSE or the response code is out of range (100-510).
724 */
725 PHP_METHOD(HttpMessage, setResponseCode)
726 {
727 long code;
728 getObject(http_message_object, obj);
729
730 if (!HTTP_MSG_TYPE(RESPONSE, obj->message)) {
731 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is not of type HTTP_MSG_RESPONSE");
732 RETURN_FALSE;
733 }
734
735 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
736 RETURN_FALSE;
737 }
738 if (code < 100 || code > 510) {
739 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid response code (100-510): %ld", code);
740 RETURN_FALSE;
741 }
742
743 obj->message->http.info.response.code = code;
744 RETURN_TRUE;
745 }
746 /* }}} */
747
748 /* {{{ proto string HttpMessage::getRequestMethod()
749 *
750 * Get the Request Method of the Message.
751 *
752 * Returns the request method name on success, or FALSE if the message is
753 * not of type HttpMessage::TYPE_REQUEST.
754 */
755 PHP_METHOD(HttpMessage, getRequestMethod)
756 {
757 NO_ARGS;
758
759 IF_RETVAL_USED {
760 getObject(http_message_object, obj);
761
762 if (!HTTP_MSG_TYPE(REQUEST, obj->message)) {
763 http_error(HE_NOTICE, HTTP_E_MESSAGE_TYPE, "HttpMessage is not of type HTTP_MSG_REQUEST");
764 RETURN_FALSE;
765 }
766
767 RETURN_STRING(obj->message->http.info.request.method, 1);
768 }
769 }
770 /* }}} */
771
772 /* {{{ proto bool HttpMessage::setRequestMethod(string method)
773 *
774 * Set the Request Method of the HTTP Message.
775 *
776 * Expects a string parameter containing the request method name.
777 *
778 * Returns TRUE on success, or FALSE if the message is not of type
779 * HttpMessage::TYPE_REQUEST or an invalid request method was supplied.
780 */
781 PHP_METHOD(HttpMessage, setRequestMethod)
782 {
783 char *method;
784 int method_len;
785 getObject(http_message_object, obj);
786
787 if (!HTTP_MSG_TYPE(REQUEST, obj->message)) {
788 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is not of type HTTP_MSG_REQUEST");
789 RETURN_FALSE;
790 }
791
792 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len)) {
793 RETURN_FALSE;
794 }
795 if (method_len < 1) {
796 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestMethod to an empty string");
797 RETURN_FALSE;
798 }
799 if (SUCCESS != http_check_method(method)) {
800 http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Unkown request method: %s", method);
801 RETURN_FALSE;
802 }
803
804 STR_SET(obj->message->http.info.request.method, estrndup(method, method_len));
805 RETURN_TRUE;
806 }
807 /* }}} */
808
809 /* {{{ proto string HttpMessage::getRequestUri()
810 *
811 * Get the Request URI of the Message.
812 *
813 * Returns the request uri as string on success, or FALSE if the message
814 * is not of type HttpMessage::TYPE_REQUEST.
815 */
816 PHP_METHOD(HttpMessage, getRequestUri)
817 {
818 NO_ARGS;
819
820 IF_RETVAL_USED {
821 getObject(http_message_object, obj);
822
823 if (!HTTP_MSG_TYPE(REQUEST, obj->message)) {
824 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is not of type HTTP_MSG_REQUEST");
825 RETURN_FALSE;
826 }
827
828 RETURN_STRING(obj->message->http.info.request.URI, 1);
829 }
830 }
831 /* }}} */
832
833 /* {{{ proto bool HttpMessage::setRequestUri(string URI)
834 *
835 * Set the Request URI of the HTTP Message.
836 *
837 * Expects a string parameters containing the request uri.
838 *
839 * Returns TRUE on success, or FALSE if the message is not of type
840 * HttpMessage::TYPE_REQUEST or supplied URI was empty.
841 */
842 PHP_METHOD(HttpMessage, setRequestUri)
843 {
844 char *URI;
845 int URIlen;
846 getObject(http_message_object, obj);
847
848 if (!HTTP_MSG_TYPE(REQUEST, obj->message)) {
849 http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "HttpMessage is not of type HTTP_MSG_REQUEST");
850 RETURN_FALSE;
851 }
852 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URI, &URIlen)) {
853 RETURN_FALSE;
854 }
855 if (URIlen < 1) {
856 http_error(HE_WARNING, HTTP_E_INVALID_PARAM, "Cannot set HttpMessage::requestUri to an empty string");
857 RETURN_FALSE;
858 }
859
860 STR_SET(obj->message->http.info.request.URI, estrndup(URI, URIlen));
861 RETURN_TRUE;
862 }
863 /* }}} */
864
865 /* {{{ proto string HttpMessage::getHttpVersion()
866 *
867 * Get the HTTP Protocol Version of the Message.
868 *
869 * Returns the HTTP protocol version as string.
870 */
871 PHP_METHOD(HttpMessage, getHttpVersion)
872 {
873 NO_ARGS;
874
875 IF_RETVAL_USED {
876 char ver[4] = {0};
877 getObject(http_message_object, obj);
878
879 sprintf(ver, "%1.1lf", obj->message->http.version);
880 RETURN_STRINGL(ver, 3, 1);
881 }
882 }
883 /* }}} */
884
885 /* {{{ proto bool HttpMessage::setHttpVersion(string version)
886 *
887 * Set the HTTP Protocol version of the Message.
888 *
889 * Expects a string parameter containing the HTTP protocol version.
890 *
891 * Returns TRUE on success, or FALSE if supplied version is out of range (1.0/1.1).
892 */
893 PHP_METHOD(HttpMessage, setHttpVersion)
894 {
895 char v[4];
896 zval *zv;
897 getObject(http_message_object, obj);
898
899 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
900 return;
901 }
902
903 convert_to_double(zv);
904 sprintf(v, "%1.1lf", Z_DVAL_P(zv));
905 if (strcmp(v, "1.0") && strcmp(v, "1.1")) {
906 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v);
907 RETURN_FALSE;
908 }
909
910 obj->message->http.version = Z_DVAL_P(zv);
911 RETURN_TRUE;
912 }
913 /* }}} */
914
915 /* {{{ proto HttpMessage HttpMessage::getParentMessage()
916 *
917 * Get parent Message.
918 *
919 * Returns the parent HttpMessage on success, or NULL if there's none.
920 */
921 PHP_METHOD(HttpMessage, getParentMessage)
922 {
923 NO_ARGS;
924
925 IF_RETVAL_USED {
926 getObject(http_message_object, obj);
927
928 if (obj->message->parent) {
929 RETVAL_OBJVAL(obj->parent);
930 } else {
931 RETVAL_NULL();
932 }
933 }
934 }
935 /* }}} */
936
937 /* {{{ proto bool HttpMessage::send()
938 *
939 * Send the Message according to its type as Response or Request.
940 * This provides limited functionality compared to HttpRequest and HttpResponse.
941 *
942 * Returns TRUE on success, or FALSE on failure.
943 */
944 PHP_METHOD(HttpMessage, send)
945 {
946 getObject(http_message_object, obj);
947
948 NO_ARGS;
949
950 RETURN_SUCCESS(http_message_send(obj->message));
951 }
952 /* }}} */
953
954 /* {{{ proto string HttpMessage::toString([bool include_parent = false])
955 *
956 * Get the string representation of the Message.
957 *
958 * Accepts a bool parameter which specifies whether the returned string
959 * should also contain any parent messages.
960 *
961 * Returns the full message as string.
962 */
963 PHP_METHOD(HttpMessage, toString)
964 {
965 IF_RETVAL_USED {
966 char *string;
967 size_t length;
968 zend_bool include_parent = 0;
969 getObject(http_message_object, obj);
970
971 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
972 RETURN_FALSE;
973 }
974
975 if (include_parent) {
976 http_message_serialize(obj->message, &string, &length);
977 } else {
978 http_message_tostring(obj->message, &string, &length);
979 }
980 RETURN_STRINGL(string, length, 0);
981 }
982 }
983 /* }}} */
984
985 #endif /* ZEND_ENGINE_2 */
986
987 /*
988 * Local variables:
989 * tab-width: 4
990 * c-basic-offset: 4
991 * End:
992 * vim600: noet sw=4 ts=4 fdm=marker
993 * vim<600: noet sw=4 ts=4
994 */
995