- added missing HttpMessage::setBody()
[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 = GET_STATIC_PROP(headers);
255
256 if (Z_TYPE_P(headers) != IS_ARRAY) {
257 convert_to_array(headers);
258 }
259
260 /* delete header if value == null */
261 if (!value || Z_TYPE_P(value) == IS_NULL) {
262 RETURN_SUCCESS(zend_hash_del(Z_ARRVAL_P(headers), name, name_len + 1));
263 }
264
265 if (Z_TYPE_P(value) != IS_STRING) {
266 convert_to_string_ex(&value);
267 }
268
269 /* convert old header to an array and add new one if header exists and replace == false */
270 if (replace || (SUCCESS != zend_hash_find(Z_ARRVAL_P(headers), name, name_len + 1, (void **) &header))) {
271 RETURN_SUCCESS(add_assoc_stringl_ex(headers, name, name_len + 1, Z_STRVAL_P(value), Z_STRLEN_P(value), 1));
272 } else {
273 convert_to_array(*header);
274 RETURN_SUCCESS(add_next_index_stringl(*header, Z_STRVAL_P(value), Z_STRLEN_P(value), 1));
275 }
276 }
277 /* }}} */
278
279 /* {{{ proto static mixed HttpResponse::getHeader([string name])
280 */
281 PHP_METHOD(HttpResponse, getHeader)
282 {
283 char *name = NULL;
284 int name_len = 0;
285 zval *headers, **header;
286
287 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len)) {
288 RETURN_FALSE;
289 }
290
291 headers = GET_STATIC_PROP(headers);
292 if (Z_TYPE_P(headers) != IS_ARRAY) {
293 convert_to_array(headers);
294 }
295
296 if (!name || !name_len) {
297 array_init(return_value);
298 array_copy(headers, return_value);
299 } else if (SUCCESS == zend_hash_find(Z_ARRVAL_P(headers), name, name_len + 1, (void **) &header)) {
300 RETURN_ZVAL(*header, 1, 0);
301 } else {
302 RETURN_NULL();
303 }
304 }
305 /* }}} */
306
307 /* {{{ proto static bool HttpResponse::setCache(bool cache)
308 *
309 * Whether it sould be attempted to cache the entitity.
310 * This will result in necessary caching headers and checks of clients
311 * "If-Modified-Since" and "If-None-Match" headers. If one of those headers
312 * matches a "304 Not Modified" status code will be issued.
313 *
314 * NOTE: If you're using sessions, be shure that you set session.cache_limiter
315 * to something more appropriate than "no-cache"!
316 */
317 PHP_METHOD(HttpResponse, setCache)
318 {
319 zend_bool do_cache = 0;
320
321 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) {
322 RETURN_FALSE;
323 }
324
325 RETURN_SUCCESS(UPD_STATIC_PROP(bool, cache, do_cache));
326 }
327 /* }}} */
328
329 /* {{{ proto static bool HttpResponse::getCache()
330 *
331 * Get current caching setting.
332 */
333 PHP_METHOD(HttpResponse, getCache)
334 {
335 NO_ARGS;
336
337 IF_RETVAL_USED {
338 RETURN_BOOL(Z_LVAL_P(GET_STATIC_PROP(cache)));
339 }
340 }
341 /* }}}*/
342
343 /* {{{ proto static bool HttpResponse::setGzip(bool gzip)
344 *
345 * Enable on-thy-fly gzipping of the sent entity.
346 */
347 PHP_METHOD(HttpResponse, setGzip)
348 {
349 zend_bool do_gzip = 0;
350
351 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) {
352 RETURN_FALSE;
353 }
354
355 RETURN_SUCCESS(UPD_STATIC_PROP(bool, gzip, do_gzip));
356 }
357 /* }}} */
358
359 /* {{{ proto static bool HttpResponse::getGzip()
360 *
361 * Get current gzipping setting.
362 */
363 PHP_METHOD(HttpResponse, getGzip)
364 {
365 NO_ARGS;
366
367 IF_RETVAL_USED {
368 RETURN_BOOL(Z_LVAL_P(GET_STATIC_PROP(gzip)));
369 }
370 }
371 /* }}} */
372
373 /* {{{ proto static bool HttpResponse::setCacheControl(string control[, long max_age = 0])
374 *
375 * Set a custom cache-control header, usually being "private" or "public";
376 * The max_age parameter controls how long the cache entry is valid on the client side.
377 */
378 PHP_METHOD(HttpResponse, setCacheControl)
379 {
380 char *ccontrol, *cctl;
381 int cc_len;
382 long max_age = 0;
383
384 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ccontrol, &cc_len, &max_age)) {
385 RETURN_FALSE;
386 }
387
388 if (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache")) {
389 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol);
390 RETURN_FALSE;
391 } else {
392 size_t cctl_len = spprintf(&cctl, 0, "%s, must-revalidate, max_age=%ld", ccontrol, max_age);
393 RETVAL_SUCCESS(UPD_STATIC_STRL(cacheControl, cctl, cctl_len));
394 efree(cctl);
395 }
396 }
397 /* }}} */
398
399 /* {{{ proto static string HttpResponse::getCacheControl()
400 *
401 * Get current Cache-Control header setting.
402 */
403 PHP_METHOD(HttpResponse, getCacheControl)
404 {
405 NO_ARGS;
406
407 IF_RETVAL_USED {
408 zval *ccontrol = GET_STATIC_PROP(cacheControl);
409 RETURN_STRINGL(Z_STRVAL_P(ccontrol), Z_STRLEN_P(ccontrol), 1);
410 }
411 }
412 /* }}} */
413
414 /* {{{ proto static bool HttpResponse::setContentType(string content_type)
415 *
416 * Set the content-type of the sent entity.
417 */
418 PHP_METHOD(HttpResponse, setContentType)
419 {
420 char *ctype;
421 int ctype_len;
422
423 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) {
424 RETURN_FALSE;
425 }
426
427 if (!strchr(ctype, '/')) {
428 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Content type '%s' doesn't seem to contain a primary and a secondary part", ctype);
429 RETURN_FALSE;
430 }
431
432 RETURN_SUCCESS(UPD_STATIC_STRL(contentType, ctype, ctype_len));
433 }
434 /* }}} */
435
436 /* {{{ proto static string HttpResponse::getContentType()
437 *
438 * Get current Content-Type header setting.
439 */
440 PHP_METHOD(HttpResponse, getContentType)
441 {
442 NO_ARGS;
443
444 IF_RETVAL_USED {
445 zval *ctype = GET_STATIC_PROP(contentType);
446 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
447 }
448 }
449 /* }}} */
450
451 /* {{{ proto static string HttpResponse::guessContentType(string magic_file[, long magic_mode])
452 *
453 * Attempts to guess the content type of supplied payload through libmagic.
454 * See docs/KnownIssues.txt!
455 */
456 PHP_METHOD(HttpResponse, guessContentType)
457 {
458 char *magic_file, *ct = NULL;
459 int magic_file_len;
460 long magic_mode = 0;
461
462 RETVAL_NULL();
463
464 SET_EH_THROW_HTTP();
465 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) {
466 switch (Z_LVAL_P(GET_STATIC_PROP(mode))) {
467 case SEND_DATA:
468 {
469 zval *data = GET_STATIC_PROP(data);
470 ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(data), Z_STRLEN_P(data), SEND_DATA);
471 }
472 break;
473
474 case SEND_RSRC:
475 {
476 php_stream *s;
477 zval *z = GET_STATIC_PROP(stream);
478 z->type = IS_RESOURCE;
479 php_stream_from_zval(s, &z);
480 ct = http_guess_content_type(magic_file, magic_mode, s, 0, SEND_RSRC);
481 }
482 break;
483
484 default:
485 ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(GET_STATIC_PROP(file)), 0, -1);
486 break;
487 }
488 if (ct) {
489 UPD_STATIC_PROP(string, contentType, ct);
490 RETVAL_STRING(ct, 0);
491 }
492 }
493 SET_EH_NORMAL();
494 }
495 /* }}} */
496
497 /* {{{ proto static bool HttpResponse::setContentDisposition(string filename[, bool inline = false])
498 *
499 * Set the Content-Disposition of the sent entity. This setting aims to suggest
500 * the receiveing user agent how to handle the sent entity; usually the client
501 * will show the user a "Save As..." popup.
502 */
503 PHP_METHOD(HttpResponse, setContentDisposition)
504 {
505 char *file, *cd;
506 int file_len;
507 size_t cd_len;
508 zend_bool send_inline = 0;
509
510 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &send_inline)) {
511 RETURN_FALSE;
512 }
513
514 cd_len = spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file);
515 RETVAL_SUCCESS(UPD_STATIC_STRL(contentDisposition, cd, cd_len));
516 efree(cd);
517 }
518 /* }}} */
519
520 /* {{{ proto static string HttpResponse::getContentDisposition()
521 *
522 * Get current Content-Disposition setting.
523 */
524 PHP_METHOD(HttpResponse, getContentDisposition)
525 {
526 NO_ARGS;
527
528 IF_RETVAL_USED {
529 zval *cd = GET_STATIC_PROP(contentDisposition);
530 RETURN_STRINGL(Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1);
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 = GET_STATIC_PROP(eTag);
562 RETURN_STRINGL(Z_STRVAL_P(etag), Z_STRLEN_P(etag), 1);
563 }
564 }
565 /* }}} */
566
567 /* {{{ proto static bool HttpResponse::setLastModified(long timestamp)
568 *
569 * Set a custom Last-Modified date.
570 */
571 PHP_METHOD(HttpResponse, setLastModified)
572 {
573 long lm;
574
575 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &lm)) {
576 RETURN_FALSE;
577 }
578
579 RETURN_SUCCESS(UPD_STATIC_PROP(long, lastModified, lm));
580 }
581 /* }}} */
582
583 /* {{{ proto static HttpResponse::getLastModified()
584 *
585 * Get calculated or previously set custom Last-Modified date.
586 */
587 PHP_METHOD(HttpResponse, getLastModified)
588 {
589 NO_ARGS;
590
591 IF_RETVAL_USED {
592 RETURN_LONG(Z_LVAL_P(GET_STATIC_PROP(lastModified)));
593 }
594 }
595 /* }}} */
596
597 /* {{{ proto static bool HttpResponse::setThrottleDelay(double seconds)
598 *
599 */
600 PHP_METHOD(HttpResponse, setThrottleDelay)
601 {
602 double seconds;
603
604 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &seconds)) {
605 RETURN_FALSE;
606 }
607 RETURN_SUCCESS(UPD_STATIC_PROP(double, throttleDelay, seconds));
608 }
609 /* }}} */
610
611 /* {{{ proto static double HttpResponse::getThrottleDelay()
612 *
613 */
614 PHP_METHOD(HttpResponse, getThrottleDelay)
615 {
616 NO_ARGS;
617
618 IF_RETVAL_USED {
619 RETURN_DOUBLE(Z_DVAL_P(GET_STATIC_PROP(throttleDelay)));
620 }
621 }
622 /* }}} */
623
624 /* {{{ proto static bool HttpResponse::setBufferSize(long bytes)
625 *
626 */
627 PHP_METHOD(HttpResponse, setBufferSize)
628 {
629 long bytes;
630
631 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &bytes)) {
632 RETURN_FALSE;
633 }
634 RETURN_SUCCESS(UPD_STATIC_PROP(long, bufferSize, bytes));
635 }
636 /* }}} */
637
638 /* {{{ proto static long HttpResponse::getBufferSize()
639 *
640 */
641 PHP_METHOD(HttpResponse, getBufferSize)
642 {
643 NO_ARGS;
644
645 IF_RETVAL_USED {
646 RETURN_LONG(Z_LVAL_P(GET_STATIC_PROP(bufferSize)));
647 }
648 }
649 /* }}} */
650
651 /* {{{ proto static bool HttpResponse::setData(string data)
652 *
653 * Set the data to be sent.
654 */
655 PHP_METHOD(HttpResponse, setData)
656 {
657 zval *the_data;
658
659 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) {
660 RETURN_FALSE;
661 }
662 if (Z_TYPE_P(the_data) != IS_STRING) {
663 convert_to_string_ex(&the_data);
664 }
665
666 if ( (SUCCESS != SET_STATIC_PROP(data, the_data)) ||
667 (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_DATA))) {
668 RETURN_FALSE;
669 }
670
671 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)) > 0)) {
672 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_data, SEND_DATA));
673 }
674 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
675 char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA);
676 UPD_STATIC_PROP(string, eTag, etag);
677 efree(etag);
678 }
679
680 RETURN_TRUE;
681 }
682 /* }}} */
683
684 /* {{{ proto static string HttpResponse::getData()
685 *
686 * Get the previously set data to be sent.
687 */
688 PHP_METHOD(HttpResponse, getData)
689 {
690 NO_ARGS;
691
692 IF_RETVAL_USED {
693 zval *the_data = GET_STATIC_PROP(data);
694 RETURN_STRINGL(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), 1);
695 }
696 }
697 /* }}} */
698
699 /* {{{ proto static bool HttpResponse::setStream(resource stream)
700 *
701 * Set the resource to be sent.
702 */
703 PHP_METHOD(HttpResponse, setStream)
704 {
705 zval *the_stream;
706 php_stream *the_real_stream;
707 php_stream_statbuf ssb;
708
709 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) {
710 RETURN_FALSE;
711 }
712
713 php_stream_from_zval(the_real_stream, &the_stream);
714 if (php_stream_stat(the_real_stream, &ssb)) {
715 RETURN_FALSE;
716 }
717
718 if ( (SUCCESS != UPD_STATIC_PROP(long, stream, Z_LVAL_P(the_stream))) ||
719 (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_RSRC))) {
720 RETURN_FALSE;
721 }
722 zend_list_addref(Z_LVAL_P(the_stream));
723
724 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)) > 0)) {
725 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_real_stream, SEND_RSRC));
726 }
727 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
728 char *etag = http_etag(the_real_stream, 0, SEND_RSRC);
729 UPD_STATIC_PROP(string, eTag, etag);
730 efree(etag);
731 }
732
733 RETURN_TRUE;
734 }
735 /* }}} */
736
737 /* {{{ proto static resource HttpResponse::getStream()
738 *
739 * Get the previously set resource to be sent.
740 */
741 PHP_METHOD(HttpResponse, getStream)
742 {
743 NO_ARGS;
744
745 IF_RETVAL_USED {
746 RETURN_RESOURCE(Z_LVAL_P(GET_STATIC_PROP(stream)));
747 }
748 }
749 /* }}} */
750
751 /* {{{ proto static bool HttpResponse::setFile(string file)
752 *
753 * Set the file to be sent.
754 */
755 PHP_METHOD(HttpResponse, setFile)
756 {
757 char *the_file;
758 int file_len;
759 php_stream_statbuf ssb;
760
761 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &the_file, &file_len)) {
762 RETURN_FALSE;
763 }
764
765 if (php_stream_stat_path(the_file, &ssb)) {
766 RETURN_FALSE;
767 }
768
769 if ( (SUCCESS != UPD_STATIC_STRL(file, the_file, file_len)) ||
770 (SUCCESS != UPD_STATIC_PROP(long, mode, -1))) {
771 RETURN_FALSE;
772 }
773
774 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)))) {
775 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_file, -1));
776 }
777 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
778 char *etag = http_etag(the_file, 0, -1);
779 UPD_STATIC_PROP(string, eTag, etag);
780 efree(etag);
781 }
782
783 RETURN_TRUE;
784 }
785 /* }}} */
786
787 /* {{{ proto static string HttpResponse::getFile()
788 *
789 * Get the previously set file to be sent.
790 */
791 PHP_METHOD(HttpResponse, getFile)
792 {
793 NO_ARGS;
794
795 IF_RETVAL_USED {
796 zval *the_file = GET_STATIC_PROP(file);
797 RETURN_STRINGL(Z_STRVAL_P(the_file), Z_STRLEN_P(the_file), 1);
798 }
799 }
800 /* }}} */
801
802 /* {{{ proto static bool HttpResponse::send([bool clean_ob = true])
803 *
804 * Finally send the entity.
805 *
806 * Example:
807 * <pre>
808 * <?php
809 * HttpResponse::setCache(true);
810 * HttpResponse::setContentType('application/pdf');
811 * HttpResponse::setContentDisposition("$user.pdf", false);
812 * HttpResponse::setFile('sheet.pdf');
813 * HttpResponse::send();
814 * ?>
815 * </pre>
816 */
817 PHP_METHOD(HttpResponse, send)
818 {
819 zval *sent, *headers;
820 zend_bool clean_ob = 1;
821
822 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean_ob)) {
823 RETURN_FALSE;
824 }
825 if (SG(headers_sent)) {
826 http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, headers have already been sent");
827 RETURN_FALSE;
828 }
829
830 sent = GET_STATIC_PROP(sent);
831 if (Z_LVAL_P(sent)) {
832 http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, response has already been sent");
833 RETURN_FALSE;
834 } else {
835 Z_LVAL_P(sent) = 1;
836 }
837
838 /* capture mode */
839 if (Z_BVAL_P(GET_STATIC_PROP(catch))) {
840 zval *the_data;
841
842 MAKE_STD_ZVAL(the_data);
843 php_ob_get_buffer(the_data TSRMLS_CC);
844 SET_STATIC_PROP(data, the_data);
845 ZVAL_LONG(GET_STATIC_PROP(mode), SEND_DATA);
846
847 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
848 char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA);
849 UPD_STATIC_PROP(string, eTag, etag);
850 efree(etag);
851 }
852 zval_ptr_dtor(&the_data);
853
854 clean_ob = 1;
855 }
856
857 if (clean_ob) {
858 /* interrupt on-the-fly etag generation */
859 HTTP_G(etag).started = 0;
860 /* discard previous output buffers */
861 php_end_ob_buffers(0 TSRMLS_CC);
862 }
863
864 /* custom headers */
865 headers = GET_STATIC_PROP(headers);
866 if (Z_TYPE_P(headers) == IS_ARRAY) {
867 char *name = NULL;
868 ulong idx = 0;
869 zval **value;
870
871 FOREACH_KEYVAL(headers, name, idx, value) {
872 if (name) {
873 if (Z_TYPE_PP(value) == IS_ARRAY) {
874 zend_bool first = 1;
875 zval **data;
876
877 FOREACH_VAL(*value, data) {
878 http_send_header_ex(name, strlen(name), Z_STRVAL_PP(data), Z_STRLEN_PP(data), first, NULL);
879 first = 0;
880 }
881 } else {
882 http_send_header_ex(name, strlen(name), Z_STRVAL_PP(value), Z_STRLEN_PP(value), 1, NULL);
883 }
884 name = NULL;
885 }
886 }
887 }
888
889 /* gzip */
890 if (Z_LVAL_P(GET_STATIC_PROP(gzip))) {
891 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC);
892 } else {
893 php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC);
894 }
895
896 /* caching */
897 if (Z_LVAL_P(GET_STATIC_PROP(cache))) {
898 zval *cctl, *etag, *lmod;
899
900 etag = GET_STATIC_PROP(eTag);
901 lmod = GET_STATIC_PROP(lastModified);
902 cctl = GET_STATIC_PROP(cacheControl);
903
904 http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), Z_STRVAL_P(cctl), Z_STRLEN_P(cctl));
905 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));
906 }
907
908 /* content type */
909 {
910 zval *ctype = GET_STATIC_PROP(contentType);
911 if (Z_STRLEN_P(ctype)) {
912 http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype));
913 } else {
914 char *ctypes = INI_STR("default_mimetype");
915 size_t ctlen = ctypes ? strlen(ctypes) : 0;
916
917 if (ctlen) {
918 http_send_content_type(ctypes, ctlen);
919 } else {
920 http_send_content_type("application/x-octetstream", lenof("application/x-octetstream"));
921 }
922 }
923 }
924
925 /* content disposition */
926 {
927 zval *cd = GET_STATIC_PROP(contentDisposition);
928 if (Z_STRLEN_P(cd)) {
929 http_send_header_ex("Content-Disposition", lenof("Content-Disposition"), Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1, NULL);
930 }
931 }
932
933 /* throttling */
934 {
935 HTTP_G(send).buffer_size = Z_LVAL_P(GET_STATIC_PROP(bufferSize));
936 HTTP_G(send).throttle_delay = Z_DVAL_P(GET_STATIC_PROP(throttleDelay));
937 }
938
939 /* send */
940 {
941 switch (Z_LVAL_P(GET_STATIC_PROP(mode)))
942 {
943 case SEND_DATA:
944 {
945 zval *zdata = GET_STATIC_PROP(data);
946 RETURN_SUCCESS(http_send_data_ex(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata), 1));
947 }
948
949 case SEND_RSRC:
950 {
951 php_stream *the_real_stream;
952 zval *the_stream = GET_STATIC_PROP(stream);
953 the_stream->type = IS_RESOURCE;
954 php_stream_from_zval(the_real_stream, &the_stream);
955 RETURN_SUCCESS(http_send_stream_ex(the_real_stream, 0, 1));
956 }
957
958 default:
959 {
960 RETURN_SUCCESS(http_send_file_ex(Z_STRVAL_P(GET_STATIC_PROP(file)), 1));
961 }
962 }
963 }
964 }
965 /* }}} */
966
967 /* {{{ proto static void HttpResponse::capture()
968 *
969 * Capture script output.
970 *
971 * Example:
972 * <pre>
973 * <?php
974 * HttpResponse::setCache(true);
975 * HttpResponse::capture();
976 * // script follows
977 * ?>
978 * </pre>
979 */
980 PHP_METHOD(HttpResponse, capture)
981 {
982 zval do_catch;
983
984 NO_ARGS;
985
986 INIT_PZVAL(&do_catch);
987 ZVAL_LONG(&do_catch, 1);
988
989 SET_STATIC_PROP(catch, &do_catch);
990
991 php_end_ob_buffers(0 TSRMLS_CC);
992 php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC);
993
994 #ifndef WONKY
995 /* register shutdown function --
996 messing around with ob and headers only works in PHP-5.1 or greater */
997 {
998 zval func, retval, arg, *argp[1];
999
1000 INIT_PZVAL(&arg);
1001 INIT_PZVAL(&func);
1002 INIT_PZVAL(&retval);
1003 ZVAL_STRINGL(&func, "register_shutdown_function", lenof("register_shutdown_function"), 0);
1004
1005 array_init(&arg);
1006 add_next_index_stringl(&arg, "HttpResponse", lenof("HttpResponse"), 1);
1007 add_next_index_stringl(&arg, "send", lenof("send"), 1);
1008 argp[0] = &arg;
1009 call_user_function(EG(function_table), NULL, &func, &retval, 1, argp TSRMLS_CC);
1010 zval_dtor(&arg);
1011 }
1012 #endif
1013 }
1014 /* }}} */
1015
1016 #endif /* ZEND_ENGINE_2 && !WONKY */
1017
1018 /*
1019 * Local variables:
1020 * tab-width: 4
1021 * c-basic-offset: 4
1022 * End:
1023 * vim600: noet sw=4 ts=4 fdm=marker
1024 * vim<600: noet sw=4 ts=4
1025 */
1026