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