fix doublefree with certinfo
[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-2010, 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 THIS_CE http_response_object_ce
136 zend_class_entry *http_response_object_ce;
137 zend_function_entry http_response_object_fe[] = {
138
139 HTTP_RESPONSE_ME(setHeader, ZEND_ACC_PUBLIC)
140 HTTP_RESPONSE_ME(getHeader, ZEND_ACC_PUBLIC)
141
142 HTTP_RESPONSE_ME(setETag, ZEND_ACC_PUBLIC)
143 HTTP_RESPONSE_ME(getETag, ZEND_ACC_PUBLIC)
144
145 HTTP_RESPONSE_ME(setLastModified, ZEND_ACC_PUBLIC)
146 HTTP_RESPONSE_ME(getLastModified, ZEND_ACC_PUBLIC)
147
148 HTTP_RESPONSE_ME(setContentDisposition, ZEND_ACC_PUBLIC)
149 HTTP_RESPONSE_ME(getContentDisposition, ZEND_ACC_PUBLIC)
150
151 HTTP_RESPONSE_ME(setContentType, ZEND_ACC_PUBLIC)
152 HTTP_RESPONSE_ME(getContentType, ZEND_ACC_PUBLIC)
153
154 HTTP_RESPONSE_ME(guessContentType, ZEND_ACC_PUBLIC)
155
156 HTTP_RESPONSE_ME(setCache, ZEND_ACC_PUBLIC)
157 HTTP_RESPONSE_ME(getCache, ZEND_ACC_PUBLIC)
158
159 HTTP_RESPONSE_ME(setCacheControl, ZEND_ACC_PUBLIC)
160 HTTP_RESPONSE_ME(getCacheControl, ZEND_ACC_PUBLIC)
161
162 HTTP_RESPONSE_ME(setGzip, ZEND_ACC_PUBLIC)
163 HTTP_RESPONSE_ME(getGzip, ZEND_ACC_PUBLIC)
164
165 HTTP_RESPONSE_ME(setThrottleDelay, ZEND_ACC_PUBLIC)
166 HTTP_RESPONSE_ME(getThrottleDelay, ZEND_ACC_PUBLIC)
167
168 HTTP_RESPONSE_ME(setBufferSize, ZEND_ACC_PUBLIC)
169 HTTP_RESPONSE_ME(getBufferSize, ZEND_ACC_PUBLIC)
170
171 HTTP_RESPONSE_ME(setData, ZEND_ACC_PUBLIC)
172 HTTP_RESPONSE_ME(getData, ZEND_ACC_PUBLIC)
173
174 HTTP_RESPONSE_ME(setFile, ZEND_ACC_PUBLIC)
175 HTTP_RESPONSE_ME(getFile, ZEND_ACC_PUBLIC)
176
177 HTTP_RESPONSE_ME(setStream, ZEND_ACC_PUBLIC)
178 HTTP_RESPONSE_ME(getStream, ZEND_ACC_PUBLIC)
179
180 HTTP_RESPONSE_ME(send, ZEND_ACC_PUBLIC)
181 HTTP_RESPONSE_ME(capture, ZEND_ACC_PUBLIC)
182
183 HTTP_RESPONSE_ALIAS(redirect, http_redirect)
184 HTTP_RESPONSE_ALIAS(status, http_send_status)
185 HTTP_RESPONSE_ALIAS(getRequestHeaders, http_get_request_headers)
186 HTTP_RESPONSE_ALIAS(getRequestBody, http_get_request_body)
187 HTTP_RESPONSE_ALIAS(getRequestBodyStream, http_get_request_body_stream)
188
189 EMPTY_FUNCTION_ENTRY
190 };
191
192 PHP_MINIT_FUNCTION(http_response_object)
193 {
194 HTTP_REGISTER_CLASS(HttpResponse, http_response_object, NULL, 0);
195
196 zend_declare_property_bool(THIS_CE, ZEND_STRS("sent")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC);
197 zend_declare_property_bool(THIS_CE, ZEND_STRS("catch")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC);
198 zend_declare_property_long(THIS_CE, ZEND_STRS("mode")-1, -1, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC);
199 zend_declare_property_long(THIS_CE, ZEND_STRS("stream")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC);
200 zend_declare_property_null(THIS_CE, ZEND_STRS("file")-1, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC);
201 zend_declare_property_null(THIS_CE, ZEND_STRS("data")-1, (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC);
202 zend_declare_property_bool(THIS_CE, ZEND_STRS("cache")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
203 zend_declare_property_bool(THIS_CE, ZEND_STRS("gzip")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
204 zend_declare_property_null(THIS_CE, ZEND_STRS("eTag")-1, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
205 zend_declare_property_long(THIS_CE, ZEND_STRS("lastModified")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
206 zend_declare_property_null(THIS_CE, ZEND_STRS("cacheControl")-1, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
207 zend_declare_property_null(THIS_CE, ZEND_STRS("contentType")-1, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
208 zend_declare_property_null(THIS_CE, ZEND_STRS("contentDisposition")-1, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
209 zend_declare_property_long(THIS_CE, ZEND_STRS("bufferSize")-1, 0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
210 zend_declare_property_double(THIS_CE, ZEND_STRS("throttleDelay")-1, 0.0, (ZEND_ACC_STATIC|ZEND_ACC_PROTECTED) TSRMLS_CC);
211
212 #ifndef WONKY
213 zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT")-1, HTTP_REDIRECT TSRMLS_CC);
214 zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_PERM")-1, HTTP_REDIRECT_PERM TSRMLS_CC);
215 zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_FOUND")-1, HTTP_REDIRECT_FOUND TSRMLS_CC);
216 zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_POST")-1, HTTP_REDIRECT_POST TSRMLS_CC);
217 zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_PROXY")-1, HTTP_REDIRECT_PROXY TSRMLS_CC);
218 zend_declare_class_constant_long(THIS_CE, ZEND_STRS("REDIRECT_TEMP")-1, HTTP_REDIRECT_TEMP TSRMLS_CC);
219 #endif /* WONKY */
220
221 return SUCCESS;
222 }
223
224 /* ### USERLAND ### */
225
226 /* {{{ proto static bool HttpResponse::setHeader(string name[, mixed value[, bool replace = true]])
227 Send an HTTP header. */
228 PHP_METHOD(HttpResponse, setHeader)
229 {
230 zend_bool replace = 1;
231 char *name;
232 int name_len = 0;
233 zval *value = NULL;
234
235 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/!b", &name, &name_len, &value, &replace)) {
236 RETURN_FALSE;
237 }
238 if (SG(headers_sent)) {
239 http_error(HE_WARNING, HTTP_E_HEADER, "Cannot add another header when headers have already been sent");
240 RETURN_FALSE;
241 }
242 if (!name_len) {
243 http_error(HE_WARNING, HTTP_E_HEADER, "Cannot send anonymous headers");
244 RETURN_FALSE;
245 }
246 http_send_header_zval_ex(name, name_len, &value, replace);
247 RETURN_TRUE;
248 }
249 /* }}} */
250
251 /* {{{ proto static mixed HttpResponse::getHeader([string name])
252 Get header(s) about to be sent. */
253 PHP_METHOD(HttpResponse, getHeader)
254 {
255 char *name = NULL;
256 int name_len = 0;
257
258 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len)) {
259 RETURN_FALSE;
260 }
261
262 if (name && name_len) {
263 zval **header;
264 HashTable headers_ht;
265
266 zend_hash_init(&headers_ht, sizeof(zval *), NULL, ZVAL_PTR_DTOR, 0);
267 if ( (SUCCESS == http_get_response_headers(&headers_ht)) &&
268 (SUCCESS == zend_hash_find(&headers_ht, name, name_len + 1, (void *) &header))) {
269 RETVAL_ZVAL(*header, 1, 0);
270 } else {
271 RETVAL_NULL();
272 }
273 zend_hash_destroy(&headers_ht);
274 } else {
275 array_init(return_value);
276 http_get_response_headers(Z_ARRVAL_P(return_value));
277 }
278 }
279 /* }}} */
280
281 /* {{{ proto static bool HttpResponse::setCache(bool cache)
282 Whether it should be attempted to cache the entity. */
283 PHP_METHOD(HttpResponse, setCache)
284 {
285 zend_bool do_cache = 0;
286
287 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) {
288 RETURN_FALSE;
289 }
290
291 RETURN_SUCCESS(zend_update_static_property_bool(THIS_CE, ZEND_STRS("cache")-1, do_cache TSRMLS_CC));
292 }
293 /* }}} */
294
295 /* {{{ proto static bool HttpResponse::getCache()
296 Get current caching setting. */
297 PHP_METHOD(HttpResponse, getCache)
298 {
299 NO_ARGS;
300
301 if (return_value_used) {
302 zval *cache = http_zsep(IS_BOOL, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("cache")-1, 0 TSRMLS_CC)));
303 RETVAL_ZVAL(cache, 1, 1);
304 }
305 }
306 /* }}}*/
307
308 /* {{{ proto static bool HttpResponse::setGzip(bool gzip)
309 Enable on-thy-fly gzipping of the sent entity. */
310 PHP_METHOD(HttpResponse, setGzip)
311 {
312 zend_bool do_gzip = 0;
313
314 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) {
315 RETURN_FALSE;
316 }
317
318 RETURN_SUCCESS(zend_update_static_property_bool(THIS_CE, ZEND_STRS("gzip")-1, do_gzip TSRMLS_CC));
319 }
320 /* }}} */
321
322 /* {{{ proto static bool HttpResponse::getGzip()
323 Get current gzipping setting. */
324 PHP_METHOD(HttpResponse, getGzip)
325 {
326 NO_ARGS;
327
328 if (return_value_used) {
329 zval *gzip = http_zsep(IS_BOOL, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("gzip")-1, 0 TSRMLS_CC)));
330 RETVAL_ZVAL(gzip, 1, 1);
331 }
332 }
333 /* }}} */
334
335 /* {{{ proto static bool HttpResponse::setCacheControl(string control[, int max_age = 0[, bool must_revalidate = true]])
336 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. */
337 PHP_METHOD(HttpResponse, setCacheControl)
338 {
339 char *ccontrol, *cctl;
340 int cc_len;
341 long max_age = 0;
342 zend_bool must_revalidate = 1;
343
344 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lb", &ccontrol, &cc_len, &max_age, &must_revalidate)) {
345 RETURN_FALSE;
346 }
347
348 if (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache")) {
349 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol);
350 RETURN_FALSE;
351 } else {
352 size_t cctl_len = spprintf(&cctl, 0, "%s,%s max-age=%ld", ccontrol, must_revalidate?" must-revalidate,":"", max_age);
353 RETVAL_SUCCESS(zend_update_static_property_stringl(THIS_CE, ZEND_STRS("cacheControl")-1, cctl, cctl_len TSRMLS_CC));
354 efree(cctl);
355 }
356 }
357 /* }}} */
358
359 /* {{{ proto static string HttpResponse::getCacheControl()
360 Get current Cache-Control header setting. */
361 PHP_METHOD(HttpResponse, getCacheControl)
362 {
363 NO_ARGS;
364
365 if (return_value_used) {
366 zval *cctl = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("cacheControl")-1, 0 TSRMLS_CC)));
367 RETVAL_ZVAL(cctl, 1, 1);
368 }
369 }
370 /* }}} */
371
372 /* {{{ proto static bool HttpResponse::setContentType(string content_type)
373 Set the content-type of the sent entity. */
374 PHP_METHOD(HttpResponse, setContentType)
375 {
376 char *ctype;
377 int ctype_len;
378
379 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) {
380 RETURN_FALSE;
381 }
382
383 HTTP_CHECK_CONTENT_TYPE(ctype, RETURN_FALSE);
384 RETURN_SUCCESS(zend_update_static_property_stringl(THIS_CE, ZEND_STRS("contentType")-1, ctype, ctype_len TSRMLS_CC));
385 }
386 /* }}} */
387
388 /* {{{ proto static string HttpResponse::getContentType()
389 Get current Content-Type header setting. */
390 PHP_METHOD(HttpResponse, getContentType)
391 {
392 NO_ARGS;
393
394 if (return_value_used) {
395 zval *ctype = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("contentType")-1, 0 TSRMLS_CC)));
396 RETVAL_ZVAL(ctype, 1, 1);
397 }
398 }
399 /* }}} */
400
401 /* {{{ proto static string HttpResponse::guessContentType(string magic_file[, int magic_mode = MAGIC_MIME])
402 Attempts to guess the content type of supplied payload through libmagic. */
403 PHP_METHOD(HttpResponse, guessContentType)
404 {
405 #ifdef HTTP_HAVE_MAGIC
406 char *magic_file, *ct = NULL;
407 int magic_file_len;
408 long magic_mode = MAGIC_MIME;
409
410 RETVAL_FALSE;
411 SET_EH_THROW_HTTP();
412 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) {
413 switch (Z_LVAL_P(*zend_std_get_static_property(THIS_CE, ZEND_STRS("mode")-1, 0 TSRMLS_CC))) {
414 case SEND_DATA:
415 {
416 zval *data = *zend_std_get_static_property(THIS_CE, ZEND_STRS("data")-1, 0 TSRMLS_CC);
417 ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(data), Z_STRLEN_P(data), SEND_DATA);
418 break;
419 }
420
421 case SEND_RSRC:
422 {
423 php_stream *s;
424 zval *z = *zend_std_get_static_property(THIS_CE, ZEND_STRS("stream")-1, 0 TSRMLS_CC);
425 z->type = IS_RESOURCE;
426 php_stream_from_zval(s, &z);
427 ct = http_guess_content_type(magic_file, magic_mode, s, 0, SEND_RSRC);
428 break;
429 }
430
431 default:
432 ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(*zend_std_get_static_property(THIS_CE, ZEND_STRS("file")-1, 0 TSRMLS_CC)), 0, -1);
433 break;
434 }
435 if (ct) {
436 zend_update_static_property_string(THIS_CE, ZEND_STRS("contentType")-1, ct TSRMLS_CC);
437 RETVAL_STRING(ct, 0);
438 }
439 }
440 SET_EH_NORMAL();
441 #else
442 http_error(HE_THROW, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available");
443 RETURN_FALSE;
444 #endif
445 }
446 /* }}} */
447
448 /* {{{ proto static bool HttpResponse::setContentDisposition(string filename[, bool inline = false])
449 Set the Content-Disposition. */
450 PHP_METHOD(HttpResponse, setContentDisposition)
451 {
452 char *file, *cd;
453 int file_len;
454 size_t cd_len;
455 zend_bool send_inline = 0;
456
457 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &send_inline)) {
458 RETURN_FALSE;
459 }
460
461 cd_len = spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file);
462 RETVAL_SUCCESS(zend_update_static_property_stringl(THIS_CE, ZEND_STRS("contentDisposition")-1, cd, cd_len TSRMLS_CC));
463 efree(cd);
464 }
465 /* }}} */
466
467 /* {{{ proto static string HttpResponse::getContentDisposition()
468 Get current Content-Disposition setting. */
469 PHP_METHOD(HttpResponse, getContentDisposition)
470 {
471 NO_ARGS;
472
473 if (return_value_used) {
474 zval *cdisp = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("contentDisposition")-1, 0 TSRMLS_CC)));
475 RETVAL_ZVAL(cdisp, 1, 1);
476 }
477 }
478 /* }}} */
479
480 /* {{{ proto static bool HttpResponse::setETag(string etag)
481 Set a custom ETag. Use this only if you know what you're doing. */
482 PHP_METHOD(HttpResponse, setETag)
483 {
484 char *etag;
485 int etag_len;
486
487 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) {
488 RETURN_FALSE;
489 }
490
491 RETURN_SUCCESS(zend_update_static_property_stringl(THIS_CE, ZEND_STRS("eTag")-1, etag, etag_len TSRMLS_CC));
492 }
493 /* }}} */
494
495 /* {{{ proto static string HttpResponse::getETag()
496 Get calculated or previously set custom ETag. */
497 PHP_METHOD(HttpResponse, getETag)
498 {
499 NO_ARGS;
500
501 if (return_value_used) {
502 zval *etag = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("eTag")-1, 0 TSRMLS_CC)));
503 RETVAL_ZVAL(etag, 1, 1);
504 }
505 }
506 /* }}} */
507
508 /* {{{ proto static bool HttpResponse::setLastModified(int timestamp)
509 Set a custom Last-Modified date. */
510 PHP_METHOD(HttpResponse, setLastModified)
511 {
512 long lm;
513
514 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &lm)) {
515 RETURN_FALSE;
516 }
517
518 RETURN_SUCCESS(zend_update_static_property_long(THIS_CE, ZEND_STRS("lastModified")-1, lm TSRMLS_CC));
519 }
520 /* }}} */
521
522 /* {{{ proto static int HttpResponse::getLastModified()
523 Get calculated or previously set custom Last-Modified date. */
524 PHP_METHOD(HttpResponse, getLastModified)
525 {
526 NO_ARGS;
527
528 if (return_value_used) {
529 zval *lmod = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("lastModified")-1, 0 TSRMLS_CC)));
530 RETVAL_ZVAL(lmod, 1, 1);
531 }
532 }
533 /* }}} */
534
535 /* {{{ proto static bool HttpResponse::setThrottleDelay(double seconds)
536 Sets the throttle delay for use with HttpResponse::setBufferSize(). */
537 PHP_METHOD(HttpResponse, setThrottleDelay)
538 {
539 double seconds;
540
541 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &seconds)) {
542 RETURN_FALSE;
543 }
544 RETURN_SUCCESS(zend_update_static_property_double(THIS_CE, ZEND_STRS("throttleDelay")-1, seconds TSRMLS_CC));
545 }
546 /* }}} */
547
548 /* {{{ proto static double HttpResponse::getThrottleDelay()
549 Get the current throttle delay. */
550 PHP_METHOD(HttpResponse, getThrottleDelay)
551 {
552 NO_ARGS;
553
554 if (return_value_used) {
555 zval *tdel = http_zsep(IS_DOUBLE, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("throttleDelay")-1, 0 TSRMLS_CC)));
556 RETVAL_ZVAL(tdel, 1, 1);
557 }
558 }
559 /* }}} */
560
561 /* {{{ proto static bool HttpResponse::setBufferSize(int bytes)
562 Sets the send buffer size for use with HttpResponse::setThrottleDelay(). */
563 PHP_METHOD(HttpResponse, setBufferSize)
564 {
565 long bytes;
566
567 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &bytes)) {
568 RETURN_FALSE;
569 }
570 RETURN_SUCCESS(zend_update_static_property_long(THIS_CE, ZEND_STRS("bufferSize")-1, bytes TSRMLS_CC));
571 }
572 /* }}} */
573
574 /* {{{ proto static int HttpResponse::getBufferSize()
575 Get current buffer size. */
576 PHP_METHOD(HttpResponse, getBufferSize)
577 {
578 NO_ARGS;
579
580 if (return_value_used) {
581 zval *bsize = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("bufferSize")-1, 0 TSRMLS_CC)));
582 RETVAL_ZVAL(bsize, 1, 1);
583 }
584 }
585 /* }}} */
586
587 /* {{{ proto static bool HttpResponse::setData(mixed data)
588 Set the data to be sent. */
589 PHP_METHOD(HttpResponse, setData)
590 {
591 char *etag;
592 zval *the_data;
593
594 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &the_data)) {
595 RETURN_FALSE;
596 }
597 if (Z_TYPE_P(the_data) != IS_STRING) {
598 convert_to_string(the_data);
599 }
600
601 if ( (SUCCESS != zend_update_static_property(THIS_CE, ZEND_STRS("data")-1, the_data TSRMLS_CC)) ||
602 (SUCCESS != zend_update_static_property_long(THIS_CE, ZEND_STRS("mode")-1, SEND_DATA TSRMLS_CC))) {
603 RETURN_FALSE;
604 }
605
606 zend_update_static_property_long(THIS_CE, ZEND_STRS("lastModified")-1, http_last_modified(the_data, SEND_DATA) TSRMLS_CC);
607 if ((etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA))) {
608 zend_update_static_property_string(THIS_CE, ZEND_STRS("eTag")-1, etag TSRMLS_CC);
609 efree(etag);
610 }
611
612 RETURN_TRUE;
613 }
614 /* }}} */
615
616 /* {{{ proto static string HttpResponse::getData()
617 Get the previously set data to be sent. */
618 PHP_METHOD(HttpResponse, getData)
619 {
620 NO_ARGS;
621
622 if (return_value_used) {
623 zval *the_data = *zend_std_get_static_property(THIS_CE, ZEND_STRS("data")-1, 0 TSRMLS_CC);
624
625 RETURN_ZVAL(the_data, 1, 0);
626 }
627 }
628 /* }}} */
629
630 /* {{{ proto static bool HttpResponse::setStream(resource stream)
631 Set the resource to be sent. */
632 PHP_METHOD(HttpResponse, setStream)
633 {
634 char *etag;
635 zval *the_stream;
636 php_stream *the_real_stream;
637 php_stream_statbuf ssb;
638
639 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) {
640 RETURN_FALSE;
641 }
642
643 php_stream_from_zval(the_real_stream, &the_stream);
644 if (php_stream_stat(the_real_stream, &ssb)) {
645 RETURN_FALSE;
646 }
647
648 if ( (SUCCESS != zend_update_static_property_long(THIS_CE, ZEND_STRS("stream")-1, Z_LVAL_P(the_stream) TSRMLS_CC)) ||
649 (SUCCESS != zend_update_static_property_long(THIS_CE, ZEND_STRS("mode")-1, SEND_RSRC TSRMLS_CC))) {
650 RETURN_FALSE;
651 }
652 zend_list_addref(Z_LVAL_P(the_stream));
653
654 zend_update_static_property_long(THIS_CE, ZEND_STRS("lastModified")-1, http_last_modified(the_real_stream, SEND_RSRC) TSRMLS_CC);
655 if ((etag = http_etag(the_real_stream, 0, SEND_RSRC))) {
656 zend_update_static_property_string(THIS_CE, ZEND_STRS("eTag")-1, etag TSRMLS_CC);
657 efree(etag);
658 }
659
660 RETURN_TRUE;
661 }
662 /* }}} */
663
664 /* {{{ proto static resource HttpResponse::getStream()
665 Get the previously set resource to be sent. */
666 PHP_METHOD(HttpResponse, getStream)
667 {
668 NO_ARGS;
669
670 if (return_value_used) {
671 zval *stream = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("stream")-1, 0 TSRMLS_CC)));
672 RETVAL_RESOURCE(Z_LVAL_P(stream));
673 zval_ptr_dtor(&stream);
674 }
675 }
676 /* }}} */
677
678 /* {{{ proto static bool HttpResponse::setFile(string file)
679 Set the file to be sent. */
680 PHP_METHOD(HttpResponse, setFile)
681 {
682 char *the_file, *etag;
683 int file_len;
684 php_stream_statbuf ssb;
685
686 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &the_file, &file_len)) {
687 RETURN_FALSE;
688 }
689
690 if (php_stream_stat_path(the_file, &ssb)) {
691 RETURN_FALSE;
692 }
693
694 if ( (SUCCESS != zend_update_static_property_stringl(THIS_CE, ZEND_STRS("file")-1, the_file, file_len TSRMLS_CC)) ||
695 (SUCCESS != zend_update_static_property_long(THIS_CE, ZEND_STRS("mode")-1, -1 TSRMLS_CC))) {
696 RETURN_FALSE;
697 }
698
699 zend_update_static_property_long(THIS_CE, ZEND_STRS("lastModified")-1, http_last_modified(the_file, -1) TSRMLS_CC);
700 if ((etag = http_etag(the_file, 0, -1))) {
701 zend_update_static_property_string(THIS_CE, ZEND_STRS("eTag")-1, etag TSRMLS_CC);
702 efree(etag);
703 }
704
705 RETURN_TRUE;
706 }
707 /* }}} */
708
709 /* {{{ proto static string HttpResponse::getFile()
710 Get the previously set file to be sent. */
711 PHP_METHOD(HttpResponse, getFile)
712 {
713 NO_ARGS;
714
715 if (return_value_used) {
716 zval *file = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("file")-1, 0 TSRMLS_CC)));
717 RETVAL_ZVAL(file, 1, 1);
718 }
719 }
720 /* }}} */
721
722 /* {{{ proto static bool HttpResponse::send([bool clean_ob = true])
723 Finally send the entity. */
724 PHP_METHOD(HttpResponse, send)
725 {
726 zval *sent;
727 zend_bool clean_ob = 1;
728
729 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean_ob)) {
730 RETURN_FALSE;
731 }
732
733 HTTP_CHECK_HEADERS_SENT(RETURN_FALSE);
734
735 sent = *zend_std_get_static_property(THIS_CE, ZEND_STRS("sent")-1, 0 TSRMLS_CC);
736 if (Z_LVAL_P(sent)) {
737 http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, response has already been sent");
738 RETURN_FALSE;
739 } else {
740 Z_LVAL_P(sent) = 1;
741 }
742
743 /* capture mode */
744 if (i_zend_is_true(*zend_std_get_static_property(THIS_CE, ZEND_STRS("catch")-1, 0 TSRMLS_CC))) {
745 zval *zetag, *the_data;
746
747 MAKE_STD_ZVAL(the_data);
748 php_ob_get_buffer(the_data TSRMLS_CC);
749 zend_update_static_property(THIS_CE, ZEND_STRS("data")-1, the_data TSRMLS_CC);
750 ZVAL_LONG(*zend_std_get_static_property(THIS_CE, ZEND_STRS("mode")-1, 0 TSRMLS_CC), SEND_DATA);
751
752 zetag = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("eTag")-1, 0 TSRMLS_CC)));
753 if (!Z_STRLEN_P(zetag)) {
754 char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA);
755 if (etag) {
756 zend_update_static_property_string(THIS_CE, ZEND_STRS("eTag")-1, etag TSRMLS_CC);
757 efree(etag);
758 }
759 }
760 zval_ptr_dtor(&the_data);
761 zval_ptr_dtor(&zetag);
762
763 clean_ob = 1;
764 }
765
766 if (clean_ob) {
767 /* interrupt on-the-fly etag generation */
768 HTTP_G->etag.started = 0;
769 /* discard previous output buffers */
770 php_end_ob_buffers(0 TSRMLS_CC);
771 }
772
773 /* caching */
774 if (i_zend_is_true(*zend_std_get_static_property(THIS_CE, ZEND_STRS("cache")-1, 0 TSRMLS_CC))) {
775 zval *cctl, *etag, *lmod;
776
777 lmod = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("lastModified")-1, 0 TSRMLS_CC)));
778 etag = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("eTag")-1, 0 TSRMLS_CC)));
779 cctl = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("cacheControl")-1, 0 TSRMLS_CC)));
780
781 if (Z_LVAL_P(lmod) || Z_STRLEN_P(etag)) {
782 if (Z_STRLEN_P(cctl)) {
783 http_send_cache_control(Z_STRVAL_P(cctl), Z_STRLEN_P(cctl));
784 } else {
785 http_send_cache_control(HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL));
786 }
787 if (Z_STRLEN_P(etag)) {
788 http_send_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag));
789 }
790 if (Z_LVAL_P(lmod)) {
791 http_send_last_modified(Z_LVAL_P(lmod));
792 }
793 }
794
795 zval_ptr_dtor(&etag);
796 zval_ptr_dtor(&lmod);
797 zval_ptr_dtor(&cctl);
798 }
799
800 /* content type */
801 {
802 zval *ctype = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("contentType")-1, 0 TSRMLS_CC)));
803 if (Z_STRLEN_P(ctype)) {
804 http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype));
805 } else {
806 char *ctypes = INI_STR("default_mimetype");
807 size_t ctlen = ctypes ? strlen(ctypes) : 0;
808
809 if (ctlen) {
810 http_send_content_type(ctypes, ctlen);
811 } else {
812 http_send_content_type("application/x-octetstream", lenof("application/x-octetstream"));
813 }
814 }
815 zval_ptr_dtor(&ctype);
816 }
817
818 /* content disposition */
819 {
820 zval *cd = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("contentDisposition")-1, 0 TSRMLS_CC)));
821 if (Z_STRLEN_P(cd)) {
822 http_send_header_ex("Content-Disposition", lenof("Content-Disposition"), Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1, NULL);
823 }
824 zval_ptr_dtor(&cd);
825 }
826
827 /* throttling */
828 {
829 zval *bsize = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("bufferSize")-1, 0 TSRMLS_CC)));
830 zval *delay = http_zsep(IS_DOUBLE, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("throttleDelay")-1, 0 TSRMLS_CC)));
831 HTTP_G->send.buffer_size = Z_LVAL_P(bsize);
832 HTTP_G->send.throttle_delay = Z_DVAL_P(delay);
833 zval_ptr_dtor(&bsize);
834 zval_ptr_dtor(&delay);
835 }
836
837 /* gzip */
838 HTTP_G->send.deflate.response = i_zend_is_true(*zend_std_get_static_property(THIS_CE, ZEND_STRS("gzip")-1, 0 TSRMLS_CC));
839
840 /* send */
841 switch (Z_LVAL_P(*zend_std_get_static_property(THIS_CE, ZEND_STRS("mode")-1, 0 TSRMLS_CC))) {
842 case SEND_DATA:
843 {
844 zval *zdata = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("data")-1, 0 TSRMLS_CC)));
845 RETVAL_SUCCESS(http_send_data(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)));
846 zval_ptr_dtor(&zdata);
847 return;
848 }
849
850 case SEND_RSRC:
851 {
852 php_stream *the_real_stream;
853 zval *the_stream = http_zsep(IS_LONG, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("stream")-1, 0 TSRMLS_CC)));
854 the_stream->type = IS_RESOURCE;
855 php_stream_from_zval(the_real_stream, &the_stream);
856 RETVAL_SUCCESS(http_send_stream(the_real_stream));
857 zval_ptr_dtor(&the_stream);
858 return;
859 }
860
861 default:
862 {
863 zval *file = http_zsep(IS_STRING, *(zend_std_get_static_property(THIS_CE, ZEND_STRS("file")-1, 0 TSRMLS_CC)));
864 RETVAL_SUCCESS(http_send_file(Z_STRVAL_P(file)));
865 zval_ptr_dtor(&file);
866 return;
867 }
868 }
869 }
870 /* }}} */
871
872 /* {{{ proto static void HttpResponse::capture()
873 Capture script output.
874 */
875 PHP_METHOD(HttpResponse, capture)
876 {
877 NO_ARGS;
878
879 HTTP_CHECK_HEADERS_SENT(RETURN_FALSE);
880
881 zend_update_static_property_long(THIS_CE, ZEND_STRS("catch")-1, 1 TSRMLS_CC);
882
883 php_end_ob_buffers(0 TSRMLS_CC);
884 php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC);
885
886 /* register shutdown function */
887 {
888 zval func, retval, arg, *argp[1];
889
890 INIT_PZVAL(&arg);
891 INIT_PZVAL(&func);
892 INIT_PZVAL(&retval);
893 ZVAL_STRINGL(&func, "register_shutdown_function", lenof("register_shutdown_function"), 0);
894
895 array_init(&arg);
896 add_next_index_stringl(&arg, "HttpResponse", lenof("HttpResponse"), 1);
897 add_next_index_stringl(&arg, "send", lenof("send"), 1);
898 argp[0] = &arg;
899 call_user_function(EG(function_table), NULL, &func, &retval, 1, argp TSRMLS_CC);
900 zval_dtor(&arg);
901 }
902 }
903 /* }}} */
904
905 #endif /* ZEND_ENGINE_2 && !WONKY */
906
907 /*
908 * Local variables:
909 * tab-width: 4
910 * c-basic-offset: 4
911 * End:
912 * vim600: noet sw=4 ts=4 fdm=marker
913 * vim<600: noet sw=4 ts=4
914 */
915