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