- fixing ridiculous default transfer timeout of 3 seconds (set to 0 now)
[m6w6/ext-http] / http_response_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 #include "missing.h"
25
26 /* broken static properties in PHP 5.0 */
27 #if defined(ZEND_ENGINE_2) && !defined(WONKY)
28
29 #include "SAPI.h"
30 #include "php_ini.h"
31
32 #include "php_http.h"
33 #include "php_http_api.h"
34 #include "php_http_std_defs.h"
35 #include "php_http_response_object.h"
36 #include "php_http_exception_object.h"
37 #include "php_http_send_api.h"
38 #include "php_http_cache_api.h"
39
40 ZEND_EXTERN_MODULE_GLOBALS(http);
41
42 #define GET_STATIC_PROP(n) *GET_STATIC_PROP_EX(http_response_object_ce, n)
43 #define UPD_STATIC_PROP(t, n, v) UPD_STATIC_PROP_EX(http_response_object_ce, t, n, v)
44 #define SET_STATIC_PROP(n, v) SET_STATIC_PROP_EX(http_response_object_ce, n, v)
45
46 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpResponse, method, 0, req_args)
47 #define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpResponse, method, ret_ref)
48 #define HTTP_RESPONSE_ME(method, visibility) PHP_ME(HttpResponse, method, HTTP_ARGS(HttpResponse, method), visibility|ZEND_ACC_STATIC)
49 #define HTTP_RESPONSE_ALIAS(method, func) HTTP_STATIC_ME_ALIAS(method, func, HTTP_ARGS(HttpResponse, method))
50
51 HTTP_BEGIN_ARGS(setHeader, 2)
52 HTTP_ARG_VAL(name, 0)
53 HTTP_ARG_VAL(value, 0)
54 HTTP_ARG_VAL(replace, 0)
55 HTTP_END_ARGS;
56
57 HTTP_BEGIN_ARGS(getHeader, 0)
58 HTTP_ARG_VAL(name, 0)
59 HTTP_END_ARGS;
60
61 HTTP_EMPTY_ARGS(getETag, 0);
62 HTTP_BEGIN_ARGS(setETag, 1)
63 HTTP_ARG_VAL(etag, 0)
64 HTTP_END_ARGS;
65
66 HTTP_EMPTY_ARGS(getLastModified, 0);
67 HTTP_BEGIN_ARGS(setLastModified, 1)
68 HTTP_ARG_VAL(timestamp, 0)
69 HTTP_END_ARGS;
70
71 HTTP_EMPTY_ARGS(getCache, 0);
72 HTTP_BEGIN_ARGS(setCache, 1)
73 HTTP_ARG_VAL(cache, 0)
74 HTTP_END_ARGS;
75
76 HTTP_EMPTY_ARGS(getGzip, 0);
77 HTTP_BEGIN_ARGS(setGzip, 1)
78 HTTP_ARG_VAL(gzip, 0)
79 HTTP_END_ARGS;
80
81 HTTP_EMPTY_ARGS(getCacheControl, 0);
82 HTTP_BEGIN_ARGS(setCacheControl, 1)
83 HTTP_ARG_VAL(cache_control, 0)
84 HTTP_ARG_VAL(max_age, 0)
85 HTTP_END_ARGS;
86
87 HTTP_EMPTY_ARGS(getContentType, 0);
88 HTTP_BEGIN_ARGS(setContentType, 1)
89 HTTP_ARG_VAL(content_type, 0)
90 HTTP_END_ARGS;
91
92 HTTP_EMPTY_ARGS(getContentDisposition, 0);
93 HTTP_BEGIN_ARGS(setContentDisposition, 1)
94 HTTP_ARG_VAL(filename, 0)
95 HTTP_ARG_VAL(send_inline, 0)
96 HTTP_END_ARGS;
97
98 HTTP_EMPTY_ARGS(getThrottleDelay, 0);
99 HTTP_BEGIN_ARGS(setThrottleDelay, 1)
100 HTTP_ARG_VAL(seconds, 0)
101 HTTP_END_ARGS;
102
103 HTTP_EMPTY_ARGS(getBufferSize, 0);
104 HTTP_BEGIN_ARGS(setBufferSize, 1)
105 HTTP_ARG_VAL(bytes, 0)
106 HTTP_END_ARGS;
107
108 HTTP_EMPTY_ARGS(getData, 0);
109 HTTP_BEGIN_ARGS(setData, 1)
110 HTTP_ARG_VAL(data, 0)
111 HTTP_END_ARGS;
112
113 HTTP_EMPTY_ARGS(getStream, 0);
114 HTTP_BEGIN_ARGS(setStream, 1)
115 HTTP_ARG_VAL(stream, 0)
116 HTTP_END_ARGS;
117
118 HTTP_EMPTY_ARGS(getFile, 0);
119 HTTP_BEGIN_ARGS(setFile, 1)
120 HTTP_ARG_VAL(filepath, 0)
121 HTTP_END_ARGS;
122
123 HTTP_BEGIN_ARGS(send, 0)
124 HTTP_ARG_VAL(clean_ob, 0)
125 HTTP_END_ARGS;
126
127 HTTP_EMPTY_ARGS(capture, 0);
128
129 HTTP_BEGIN_ARGS(redirect, 0)
130 HTTP_ARG_VAL(url, 0)
131 HTTP_ARG_VAL(params, 0)
132 HTTP_ARG_VAL(session, 0)
133 HTTP_ARG_VAL(permanent, 0)
134 HTTP_END_ARGS;
135
136 HTTP_BEGIN_ARGS(status, 1)
137 HTTP_ARG_VAL(code, 0)
138 HTTP_END_ARGS;
139
140 HTTP_EMPTY_ARGS(getRequestHeaders, 0);
141 HTTP_EMPTY_ARGS(getRequestBody, 0);
142
143 #define http_response_object_declare_default_properties() _http_response_object_declare_default_properties(TSRMLS_C)
144 static inline void _http_response_object_declare_default_properties(TSRMLS_D);
145
146 zend_class_entry *http_response_object_ce;
147 zend_function_entry http_response_object_fe[] = {
148
149 HTTP_RESPONSE_ME(setHeader, ZEND_ACC_PUBLIC)
150 HTTP_RESPONSE_ME(getHeader, ZEND_ACC_PUBLIC)
151
152 HTTP_RESPONSE_ME(setETag, ZEND_ACC_PUBLIC)
153 HTTP_RESPONSE_ME(getETag, ZEND_ACC_PUBLIC)
154
155 HTTP_RESPONSE_ME(setLastModified, ZEND_ACC_PUBLIC)
156 HTTP_RESPONSE_ME(getLastModified, ZEND_ACC_PUBLIC)
157
158 HTTP_RESPONSE_ME(setContentDisposition, ZEND_ACC_PUBLIC)
159 HTTP_RESPONSE_ME(getContentDisposition, ZEND_ACC_PUBLIC)
160
161 HTTP_RESPONSE_ME(setContentType, ZEND_ACC_PUBLIC)
162 HTTP_RESPONSE_ME(getContentType, ZEND_ACC_PUBLIC)
163
164 HTTP_RESPONSE_ME(setCache, ZEND_ACC_PUBLIC)
165 HTTP_RESPONSE_ME(getCache, ZEND_ACC_PUBLIC)
166
167 HTTP_RESPONSE_ME(setCacheControl, ZEND_ACC_PUBLIC)
168 HTTP_RESPONSE_ME(getCacheControl, ZEND_ACC_PUBLIC)
169
170 HTTP_RESPONSE_ME(setGzip, ZEND_ACC_PUBLIC)
171 HTTP_RESPONSE_ME(getGzip, ZEND_ACC_PUBLIC)
172
173 HTTP_RESPONSE_ME(setThrottleDelay, ZEND_ACC_PUBLIC)
174 HTTP_RESPONSE_ME(getThrottleDelay, ZEND_ACC_PUBLIC)
175
176 HTTP_RESPONSE_ME(setBufferSize, ZEND_ACC_PUBLIC)
177 HTTP_RESPONSE_ME(getBufferSize, ZEND_ACC_PUBLIC)
178
179 HTTP_RESPONSE_ME(setData, ZEND_ACC_PUBLIC)
180 HTTP_RESPONSE_ME(getData, ZEND_ACC_PUBLIC)
181
182 HTTP_RESPONSE_ME(setFile, ZEND_ACC_PUBLIC)
183 HTTP_RESPONSE_ME(getFile, ZEND_ACC_PUBLIC)
184
185 HTTP_RESPONSE_ME(setStream, ZEND_ACC_PUBLIC)
186 HTTP_RESPONSE_ME(getStream, ZEND_ACC_PUBLIC)
187
188 HTTP_RESPONSE_ME(send, ZEND_ACC_PUBLIC)
189 HTTP_RESPONSE_ME(capture, ZEND_ACC_PUBLIC)
190
191 HTTP_RESPONSE_ALIAS(redirect, http_redirect)
192 HTTP_RESPONSE_ALIAS(status, http_send_status)
193 HTTP_RESPONSE_ALIAS(getRequestHeaders, http_get_request_headers)
194 HTTP_RESPONSE_ALIAS(getRequestBody, http_get_request_body)
195
196 {NULL, NULL, NULL}
197 };
198
199 void _http_response_object_init(INIT_FUNC_ARGS)
200 {
201 HTTP_REGISTER_CLASS(HttpResponse, http_response_object, NULL, 0);
202 http_response_object_declare_default_properties();
203 }
204
205 static inline void _http_response_object_declare_default_properties(TSRMLS_D)
206 {
207 zend_class_entry *ce = http_response_object_ce;
208
209 DCL_STATIC_PROP(PRIVATE, bool, sent, 0);
210 DCL_STATIC_PROP(PRIVATE, bool, catch, 0);
211 DCL_STATIC_PROP(PRIVATE, long, mode, -1);
212 DCL_STATIC_PROP(PROTECTED, bool, cache, 0);
213 DCL_STATIC_PROP(PROTECTED, bool, gzip, 0);
214 DCL_STATIC_PROP(PROTECTED, long, stream, 0);
215 DCL_STATIC_PROP_N(PROTECTED, file);
216 DCL_STATIC_PROP_N(PROTECTED, data);
217 DCL_STATIC_PROP_N(PROTECTED, eTag);
218 DCL_STATIC_PROP(PROTECTED, long, lastModified, 0);
219 DCL_STATIC_PROP_N(PROTECTED, cacheControl);
220 DCL_STATIC_PROP_N(PROTECTED, contentType);
221 DCL_STATIC_PROP_N(PROTECTED, contentDisposition);
222 DCL_STATIC_PROP(PROTECTED, long, bufferSize, HTTP_SENDBUF_SIZE);
223 DCL_STATIC_PROP(PROTECTED, double, throttleDelay, 0.0);
224 DCL_STATIC_PROP_N(PROTECTED, headers);
225 }
226
227 /* ### USERLAND ### */
228
229 /* {{{ proto static bool HttpResponse::setHeader(string name, mixed value[, bool replace = true)
230 */
231 PHP_METHOD(HttpResponse, setHeader)
232 {
233 zend_bool replace = 1;
234 char *name;
235 int name_len = 0;
236 zval *value = NULL, *headers, **header;
237
238 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/!|b", &name, &name_len, &value, &replace)) {
239 RETURN_FALSE;
240 }
241 if (!name_len) {
242 http_error(HE_WARNING, HTTP_E_HEADER, "Cannot send anonymous headers");
243 RETURN_FALSE;
244 }
245
246 headers = GET_STATIC_PROP(headers);
247
248 if (Z_TYPE_P(headers) != IS_ARRAY) {
249 convert_to_array(headers);
250 }
251
252 /* delete header if value == null */
253 if (!value || Z_TYPE_P(value) == IS_NULL) {
254 RETURN_SUCCESS(zend_hash_del(Z_ARRVAL_P(headers), name, name_len + 1));
255 }
256
257 if (Z_TYPE_P(value) != IS_STRING) {
258 convert_to_string_ex(&value);
259 }
260
261 /* convert old header to an array and add new one if header exists and replace == false */
262 if (replace || (SUCCESS != zend_hash_find(Z_ARRVAL_P(headers), name, name_len + 1, (void **) &header))) {
263 RETURN_SUCCESS(add_assoc_stringl_ex(headers, name, name_len + 1, Z_STRVAL_P(value), Z_STRLEN_P(value), 1));
264 } else {
265 convert_to_array(*header);
266 RETURN_SUCCESS(add_next_index_stringl(*header, Z_STRVAL_P(value), Z_STRLEN_P(value), 1));
267 }
268 }
269 /* }}} */
270
271 /* {{{ proto static mixed HttpResponse::getHeader([string name])
272 */
273 PHP_METHOD(HttpResponse, getHeader)
274 {
275 char *name = NULL;
276 int name_len = 0;
277 zval *headers, **header;
278
279 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len)) {
280 RETURN_FALSE;
281 }
282
283 headers = GET_STATIC_PROP(headers);
284 if (Z_TYPE_P(headers) != IS_ARRAY) {
285 convert_to_array(headers);
286 }
287
288 if (!name || !name_len) {
289 array_init(return_value);
290 array_copy(headers, return_value);
291 } else if (SUCCESS == zend_hash_find(Z_ARRVAL_P(headers), name, name_len + 1, (void **) &header)) {
292 RETURN_ZVAL(*header, ZVAL_PTR_DTOR, 1);
293 } else {
294 RETURN_NULL();
295 }
296 }
297 /* }}} */
298
299 /* {{{ proto static bool HttpResponse::setCache(bool cache)
300 *
301 * Whether it sould be attempted to cache the entitity.
302 * This will result in necessary caching headers and checks of clients
303 * "If-Modified-Since" and "If-None-Match" headers. If one of those headers
304 * matches a "304 Not Modified" status code will be issued.
305 *
306 * NOTE: If you're using sessions, be shure that you set session.cache_limiter
307 * to something more appropriate than "no-cache"!
308 */
309 PHP_METHOD(HttpResponse, setCache)
310 {
311 zend_bool do_cache = 0;
312
313 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) {
314 RETURN_FALSE;
315 }
316
317 RETURN_SUCCESS(UPD_STATIC_PROP(bool, cache, do_cache));
318 }
319 /* }}} */
320
321 /* {{{ proto static bool HttpResponse::getCache()
322 *
323 * Get current caching setting.
324 */
325 PHP_METHOD(HttpResponse, getCache)
326 {
327 NO_ARGS;
328
329 IF_RETVAL_USED {
330 RETURN_BOOL(Z_LVAL_P(GET_STATIC_PROP(cache)));
331 }
332 }
333 /* }}}*/
334
335 /* {{{ proto static bool HttpResponse::setGzip(bool gzip)
336 *
337 * Enable on-thy-fly gzipping of the sent entity.
338 */
339 PHP_METHOD(HttpResponse, setGzip)
340 {
341 zend_bool do_gzip = 0;
342
343 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) {
344 RETURN_FALSE;
345 }
346
347 RETURN_SUCCESS(UPD_STATIC_PROP(bool, gzip, do_gzip));
348 }
349 /* }}} */
350
351 /* {{{ proto static bool HttpResponse::getGzip()
352 *
353 * Get current gzipping setting.
354 */
355 PHP_METHOD(HttpResponse, getGzip)
356 {
357 NO_ARGS;
358
359 IF_RETVAL_USED {
360 RETURN_BOOL(Z_LVAL_P(GET_STATIC_PROP(gzip)));
361 }
362 }
363 /* }}} */
364
365 /* {{{ proto static bool HttpResponse::setCacheControl(string control[, long max_age = 0])
366 *
367 * Set a custom cache-control header, usually being "private" or "public";
368 * The max_age parameter controls how long the cache entry is valid on the client side.
369 */
370 PHP_METHOD(HttpResponse, setCacheControl)
371 {
372 char *ccontrol, *cctl;
373 int cc_len;
374 long max_age = 0;
375
376 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ccontrol, &cc_len, &max_age)) {
377 RETURN_FALSE;
378 }
379
380 if (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache")) {
381 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol);
382 RETURN_FALSE;
383 } else {
384 spprintf(&cctl, 0, "%s, must-revalidate, max_age=%ld", ccontrol, max_age);
385 RETVAL_SUCCESS(UPD_STATIC_PROP(string, cacheControl, cctl));
386 efree(cctl);
387 }
388 }
389 /* }}} */
390
391 /* {{{ proto static string HttpResponse::getCacheControl()
392 *
393 * Get current Cache-Control header setting.
394 */
395 PHP_METHOD(HttpResponse, getCacheControl)
396 {
397 NO_ARGS;
398
399 IF_RETVAL_USED {
400 zval *ccontrol = GET_STATIC_PROP(cacheControl);
401 RETURN_STRINGL(Z_STRVAL_P(ccontrol), Z_STRLEN_P(ccontrol), 1);
402 }
403 }
404 /* }}} */
405
406 /* {{{ proto static bool HttpResponse::setContentType(string content_type)
407 *
408 * Set the content-type of the sent entity.
409 */
410 PHP_METHOD(HttpResponse, setContentType)
411 {
412 char *ctype;
413 int ctype_len;
414
415 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) {
416 RETURN_FALSE;
417 }
418
419 if (!strchr(ctype, '/')) {
420 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Content type '%s' doesn't seem to contain a primary and a secondary part", ctype);
421 RETURN_FALSE;
422 }
423
424 RETURN_SUCCESS(UPD_STATIC_PROP(string, contentType, ctype));
425 }
426 /* }}} */
427
428 /* {{{ proto static string HttpResponse::getContentType()
429 *
430 * Get current Content-Type header setting.
431 */
432 PHP_METHOD(HttpResponse, getContentType)
433 {
434 NO_ARGS;
435
436 IF_RETVAL_USED {
437 zval *ctype = GET_STATIC_PROP(contentType);
438 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
439 }
440 }
441 /* }}} */
442
443 /* {{{ proto static bool HttpResponse::setContentDisposition(string filename[, bool inline = false])
444 *
445 * Set the Content-Disposition of the sent entity. This setting aims to suggest
446 * the receiveing user agent how to handle the sent entity; usually the client
447 * will show the user a "Save As..." popup.
448 */
449 PHP_METHOD(HttpResponse, setContentDisposition)
450 {
451 char *file, *cd;
452 int file_len;
453 zend_bool send_inline = 0;
454
455 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &send_inline)) {
456 RETURN_FALSE;
457 }
458
459 spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file);
460 RETVAL_SUCCESS(UPD_STATIC_PROP(string, contentDisposition, cd));
461 efree(cd);
462 }
463 /* }}} */
464
465 /* {{{ proto static string HttpResponse::getContentDisposition()
466 *
467 * Get current Content-Disposition setting.
468 */
469 PHP_METHOD(HttpResponse, getContentDisposition)
470 {
471 NO_ARGS;
472
473 IF_RETVAL_USED {
474 zval *cd = GET_STATIC_PROP(contentDisposition);
475 RETURN_STRINGL(Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1);
476 }
477 }
478 /* }}} */
479
480 /* {{{ proto static bool HttpResponse::setETag(string etag)
481 *
482 * Set a custom ETag. Use this only if you know what you're doing.
483 */
484 PHP_METHOD(HttpResponse, setETag)
485 {
486 char *etag;
487 int etag_len;
488
489 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) {
490 RETURN_FALSE;
491 }
492
493 RETURN_SUCCESS(UPD_STATIC_PROP(string, eTag, etag));
494 }
495 /* }}} */
496
497 /* {{{ proto static string HttpResponse::getETag()
498 *
499 * Get calculated or previously set custom ETag.
500 */
501 PHP_METHOD(HttpResponse, getETag)
502 {
503 NO_ARGS;
504
505 IF_RETVAL_USED {
506 zval *etag = GET_STATIC_PROP(eTag);
507 RETURN_STRINGL(Z_STRVAL_P(etag), Z_STRLEN_P(etag), 1);
508 }
509 }
510 /* }}} */
511
512 /* {{{ proto static bool HttpResponse::setLastModified(long timestamp)
513 *
514 * Set a custom Last-Modified date.
515 */
516 PHP_METHOD(HttpResponse, setLastModified)
517 {
518 long lm;
519
520 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &lm)) {
521 RETURN_FALSE;
522 }
523
524 RETURN_SUCCESS(UPD_STATIC_PROP(long, lastModified, lm));
525 }
526 /* }}} */
527
528 /* {{{ proto static HttpResponse::getLastModified()
529 *
530 * Get calculated or previously set custom Last-Modified date.
531 */
532 PHP_METHOD(HttpResponse, getLastModified)
533 {
534 NO_ARGS;
535
536 IF_RETVAL_USED {
537 RETURN_LONG(Z_LVAL_P(GET_STATIC_PROP(lastModified)));
538 }
539 }
540 /* }}} */
541
542 /* {{{ proto static bool HttpResponse::setThrottleDelay(double seconds)
543 *
544 */
545 PHP_METHOD(HttpResponse, setThrottleDelay)
546 {
547 double seconds;
548
549 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &seconds)) {
550 RETURN_FALSE;
551 }
552 RETURN_SUCCESS(UPD_STATIC_PROP(double, throttleDelay, seconds));
553 }
554 /* }}} */
555
556 /* {{{ proto static double HttpResponse::getThrottleDelay()
557 *
558 */
559 PHP_METHOD(HttpResponse, getThrottleDelay)
560 {
561 NO_ARGS;
562
563 IF_RETVAL_USED {
564 RETURN_DOUBLE(Z_DVAL_P(GET_STATIC_PROP(throttleDelay)));
565 }
566 }
567 /* }}} */
568
569 /* {{{ proto static bool HttpResponse::setBufferSize(long bytes)
570 *
571 */
572 PHP_METHOD(HttpResponse, setBufferSize)
573 {
574 long bytes;
575
576 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &bytes)) {
577 RETURN_FALSE;
578 }
579 RETURN_SUCCESS(UPD_STATIC_PROP(long, bufferSize, bytes));
580 }
581 /* }}} */
582
583 /* {{{ proto static long HttpResponse::getBufferSize()
584 *
585 */
586 PHP_METHOD(HttpResponse, getBufferSize)
587 {
588 NO_ARGS;
589
590 IF_RETVAL_USED {
591 RETURN_LONG(Z_LVAL_P(GET_STATIC_PROP(bufferSize)));
592 }
593 }
594 /* }}} */
595
596 /* {{{ proto static bool HttpResponse::setData(string data)
597 *
598 * Set the data to be sent.
599 */
600 PHP_METHOD(HttpResponse, setData)
601 {
602 zval *the_data;
603
604 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) {
605 RETURN_FALSE;
606 }
607 if (Z_TYPE_P(the_data) != IS_STRING) {
608 convert_to_string_ex(&the_data);
609 }
610
611 if ( (SUCCESS != SET_STATIC_PROP(data, the_data)) ||
612 (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_DATA))) {
613 RETURN_FALSE;
614 }
615
616 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)) > 0)) {
617 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_data, SEND_DATA));
618 }
619 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
620 char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA);
621 UPD_STATIC_PROP(string, eTag, etag);
622 efree(etag);
623 }
624
625 RETURN_TRUE;
626 }
627 /* }}} */
628
629 /* {{{ proto static string HttpResponse::getData()
630 *
631 * Get the previously set data to be sent.
632 */
633 PHP_METHOD(HttpResponse, getData)
634 {
635 NO_ARGS;
636
637 IF_RETVAL_USED {
638 zval *the_data = GET_STATIC_PROP(data);
639 RETURN_STRINGL(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), 1);
640 }
641 }
642 /* }}} */
643
644 /* {{{ proto static bool HttpResponse::setStream(resource stream)
645 *
646 * Set the resource to be sent.
647 */
648 PHP_METHOD(HttpResponse, setStream)
649 {
650 zval *the_stream;
651 php_stream *the_real_stream;
652
653 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) {
654 RETURN_FALSE;
655 }
656 zend_list_addref(Z_LVAL_P(the_stream));
657 php_stream_from_zval(the_real_stream, &the_stream);
658
659 if ( (SUCCESS != UPD_STATIC_PROP(long, stream, Z_LVAL_P(the_stream))) ||
660 (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_RSRC))) {
661 RETURN_FALSE;
662 }
663
664 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)) > 0)) {
665 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_real_stream, SEND_RSRC));
666 }
667 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
668 char *etag = http_etag(the_real_stream, 0, SEND_RSRC);
669 UPD_STATIC_PROP(string, eTag, etag);
670 efree(etag);
671 }
672
673 RETURN_TRUE;
674 }
675 /* }}} */
676
677 /* {{{ proto static resource HttpResponse::getStream()
678 *
679 * Get the previously set resource to be sent.
680 */
681 PHP_METHOD(HttpResponse, getStream)
682 {
683 NO_ARGS;
684
685 IF_RETVAL_USED {
686 RETURN_RESOURCE(Z_LVAL_P(GET_STATIC_PROP(stream)));
687 }
688 }
689 /* }}} */
690
691 /* {{{ proto static bool HttpResponse::setFile(string file)
692 *
693 * Set the file to be sent.
694 */
695 PHP_METHOD(HttpResponse, setFile)
696 {
697 char *the_file;
698 int file_len;
699
700 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &the_file, &file_len)) {
701 RETURN_FALSE;
702 }
703
704 if ( (SUCCESS != UPD_STATIC_PROP(string, file, the_file)) ||
705 (SUCCESS != UPD_STATIC_PROP(long, mode, -1))) {
706 RETURN_FALSE;
707 }
708
709 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)))) {
710 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_file, -1));
711 }
712 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
713 char *etag = http_etag(the_file, 0, -1);
714 UPD_STATIC_PROP(string, eTag, etag);
715 efree(etag);
716 }
717
718 RETURN_TRUE;
719 }
720 /* }}} */
721
722 /* {{{ proto static string HttpResponse::getFile()
723 *
724 * Get the previously set file to be sent.
725 */
726 PHP_METHOD(HttpResponse, getFile)
727 {
728 NO_ARGS;
729
730 IF_RETVAL_USED {
731 zval *the_file = GET_STATIC_PROP(file);
732 RETURN_STRINGL(Z_STRVAL_P(the_file), Z_STRLEN_P(the_file), 1);
733 }
734 }
735 /* }}} */
736
737 /* {{{ proto static bool HttpResponse::send([bool clean_ob = true])
738 *
739 * Finally send the entity.
740 *
741 * Example:
742 * <pre>
743 * <?php
744 * HttpResponse::setCache(true);
745 * HttpResponse::setContentType('application/pdf');
746 * HttpResponse::setContentDisposition("$user.pdf", false);
747 * HttpResponse::setFile('sheet.pdf');
748 * HttpResponse::send();
749 * ?>
750 * </pre>
751 */
752 PHP_METHOD(HttpResponse, send)
753 {
754 zval *sent, *headers;
755 zend_bool clean_ob = 1;
756
757 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean_ob)) {
758 RETURN_FALSE;
759 }
760 if (SG(headers_sent)) {
761 http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, headers have already been sent");
762 RETURN_FALSE;
763 }
764
765 sent = GET_STATIC_PROP(sent);
766 if (Z_LVAL_P(sent)) {
767 http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, response has already been sent");
768 RETURN_FALSE;
769 } else {
770 Z_LVAL_P(sent) = 1;
771 }
772
773 /* capture mode */
774 if (Z_BVAL_P(GET_STATIC_PROP(catch))) {
775 zval *the_data;
776
777 MAKE_STD_ZVAL(the_data);
778 php_ob_get_buffer(the_data TSRMLS_CC);
779 SET_STATIC_PROP(data, the_data);
780 ZVAL_LONG(GET_STATIC_PROP(mode), SEND_DATA);
781
782 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
783 char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA);
784 UPD_STATIC_PROP(string, eTag, etag);
785 efree(etag);
786 }
787 zval_ptr_dtor(&the_data);
788
789 clean_ob = 1;
790 }
791
792 if (clean_ob) {
793 /* interrupt on-the-fly etag generation */
794 HTTP_G(etag).started = 0;
795 /* discard previous output buffers */
796 php_end_ob_buffers(0 TSRMLS_CC);
797 }
798
799 /* custom headers */
800 headers = GET_STATIC_PROP(headers);
801 if (Z_TYPE_P(headers) == IS_ARRAY) {
802 char *name = NULL;
803 ulong idx = 0;
804 zval **value;
805
806 FOREACH_KEYVAL(headers, name, idx, value) {
807 if (name) {
808 if (Z_TYPE_PP(value) == IS_ARRAY) {
809 zend_bool first = 1;
810 zval **data;
811
812 FOREACH_VAL(*value, data) {
813 http_send_header_ex(name, strlen(name), Z_STRVAL_PP(data), Z_STRLEN_PP(data), first, NULL);
814 first = 0;
815 }
816 } else {
817 http_send_header_ex(name, strlen(name), Z_STRVAL_PP(value), Z_STRLEN_PP(value), 1, NULL);
818 }
819 name = NULL;
820 }
821 }
822 }
823
824 /* gzip */
825 if (Z_LVAL_P(GET_STATIC_PROP(gzip))) {
826 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC);
827 } else {
828 php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC);
829 }
830
831 /* caching */
832 if (Z_LVAL_P(GET_STATIC_PROP(cache))) {
833 zval *cctl, *etag, *lmod;
834
835 etag = GET_STATIC_PROP(eTag);
836 lmod = GET_STATIC_PROP(lastModified);
837 cctl = GET_STATIC_PROP(cacheControl);
838
839 http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), Z_STRVAL_P(cctl), Z_STRLEN_P(cctl));
840 http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), Z_STRVAL_P(cctl), Z_STRLEN_P(cctl));
841 }
842
843 /* content type */
844 {
845 zval *ctype = GET_STATIC_PROP(contentType);
846 if (Z_STRLEN_P(ctype)) {
847 http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype));
848 } else {
849 char *ctypes = INI_STR("default_mimetype");
850 size_t ctlen = ctypes ? strlen(ctypes) : 0;
851
852 if (ctlen) {
853 http_send_content_type(ctypes, ctlen);
854 } else {
855 http_send_content_type("application/x-octetstream", lenof("application/x-octetstream"));
856 }
857 }
858 }
859
860 /* content disposition */
861 {
862 zval *cd = GET_STATIC_PROP(contentDisposition);
863 if (Z_STRLEN_P(cd)) {
864 http_send_header_ex("Content-Disposition", lenof("Content-Disposition"), Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1, NULL);
865 }
866 }
867
868 /* throttling */
869 {
870 HTTP_G(send).buffer_size = Z_LVAL_P(GET_STATIC_PROP(bufferSize));
871 HTTP_G(send).throttle_delay = Z_DVAL_P(GET_STATIC_PROP(throttleDelay));
872 }
873
874 /* send */
875 {
876 switch (Z_LVAL_P(GET_STATIC_PROP(mode)))
877 {
878 case SEND_DATA:
879 {
880 zval *zdata = GET_STATIC_PROP(data);
881 RETURN_SUCCESS(http_send_data_ex(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata), 1));
882 }
883
884 case SEND_RSRC:
885 {
886 php_stream *the_real_stream;
887 zval *the_stream = GET_STATIC_PROP(stream);
888 the_stream->type = IS_RESOURCE;
889 php_stream_from_zval(the_real_stream, &the_stream);
890 RETURN_SUCCESS(http_send_stream_ex(the_real_stream, 0, 1));
891 }
892
893 default:
894 {
895 RETURN_SUCCESS(http_send_file_ex(Z_STRVAL_P(GET_STATIC_PROP(file)), 1));
896 }
897 }
898 }
899 }
900 /* }}} */
901
902 /* {{{ proto static void HttpResponse::capture()
903 *
904 * Capture script output.
905 *
906 * Example:
907 * <pre>
908 * <?php
909 * HttpResponse::setCache(true);
910 * HttpResponse::capture();
911 * // script follows
912 * ?>
913 * </pre>
914 */
915 PHP_METHOD(HttpResponse, capture)
916 {
917 zval do_catch;
918
919 NO_ARGS;
920
921 INIT_PZVAL(&do_catch);
922 ZVAL_LONG(&do_catch, 1);
923
924 SET_STATIC_PROP(catch, &do_catch);
925
926 php_end_ob_buffers(0 TSRMLS_CC);
927 php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC);
928
929 #ifndef WONKY
930 /* register shutdown function --
931 messing around with ob and headers only works in PHP-5.1 or greater */
932 {
933 zval func, retval, arg, *argp[1];
934
935 INIT_PZVAL(&arg);
936 INIT_PZVAL(&func);
937 INIT_PZVAL(&retval);
938 ZVAL_STRINGL(&func, "register_shutdown_function", lenof("register_shutdown_function"), 0);
939
940 array_init(&arg);
941 add_next_index_stringl(&arg, "HttpResponse", lenof("HttpResponse"), 1);
942 add_next_index_stringl(&arg, "send", lenof("send"), 1);
943 argp[0] = &arg;
944 call_user_function(EG(function_table), NULL, &func, &retval, 1, argp TSRMLS_CC);
945 zval_dtor(&arg);
946 }
947 #endif
948 }
949 /* }}} */
950
951 #endif /* ZEND_ENGINE_2 && !WONKY */
952
953 /*
954 * Local variables:
955 * tab-width: 4
956 * c-basic-offset: 4
957 * End:
958 * vim600: noet sw=4 ts=4 fdm=marker
959 * vim<600: noet sw=4 ts=4
960 */
961