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