1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2016 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Zeev Suraski <zeev@zend.com> |
16 | Thies C. Arntzen <thies@thieso.net> |
17 | Marcus Boerger <helly@php.net> |
18 | New API: Michael Wallner <mike@php.net> |
19 +----------------------------------------------------------------------+
20 */
21
22 /* $Id$ */
23
24 #ifndef PHP_OUTPUT_DEBUG
25 # define PHP_OUTPUT_DEBUG 0
26 #endif
27 #ifndef PHP_OUTPUT_NOINLINE
28 # define PHP_OUTPUT_NOINLINE 0
29 #endif
30
31 #include "php.h"
32 #include "ext/standard/head.h"
33 #include "ext/standard/url_scanner_ex.h"
34 #include "SAPI.h"
35 #include "zend_stack.h"
36 #include "php_output.h"
37
38 PHPAPI ZEND_DECLARE_MODULE_GLOBALS(output);
39
40 const char php_output_default_handler_name[sizeof("default output handler")] = "default output handler";
41 const char php_output_devnull_handler_name[sizeof("null output handler")] = "null output handler";
42
43 #if PHP_OUTPUT_NOINLINE || PHP_OUTPUT_DEBUG
44 # undef inline
45 # define inline
46 #endif
47
48 /* {{{ aliases, conflict and reverse conflict hash tables */
49 static HashTable php_output_handler_aliases;
50 static HashTable php_output_handler_conflicts;
51 static HashTable php_output_handler_reverse_conflicts;
52 /* }}} */
53
54 /* {{{ forward declarations */
55 static inline int php_output_lock_error(int op TSRMLS_DC);
56 static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC);
57
58 static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC);
59 static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context);
60 static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC);
61 static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry);
62
63 static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC);
64 static inline void php_output_context_reset(php_output_context *context);
65 static inline void php_output_context_swap(php_output_context *context);
66 static inline void php_output_context_dtor(php_output_context *context);
67
68 static inline int php_output_stack_pop(int flags TSRMLS_DC);
69
70 static int php_output_stack_apply_op(void *h, void *c);
71 static int php_output_stack_apply_clean(void *h, void *c);
72 static int php_output_stack_apply_list(void *h, void *z);
73 static int php_output_stack_apply_status(void *h, void *z);
74
75 static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context);
76 static int php_output_handler_default_func(void **handler_context, php_output_context *output_context);
77 static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context);
78 /* }}} */
79
80 /* {{{ static void php_output_init_globals(zend_output_globals *G)
81 * Initialize the module globals on MINIT */
php_output_init_globals(zend_output_globals * G)82 static inline void php_output_init_globals(zend_output_globals *G)
83 {
84 memset(G, 0, sizeof(*G));
85 }
86 /* }}} */
87
88 /* {{{ stderr/stdout writer if not PHP_OUTPUT_ACTIVATED */
php_output_stdout(const char * str,size_t str_len)89 static int php_output_stdout(const char *str, size_t str_len)
90 {
91 fwrite(str, 1, str_len, stdout);
92 return str_len;
93 }
php_output_stderr(const char * str,size_t str_len)94 static int php_output_stderr(const char *str, size_t str_len)
95 {
96 fwrite(str, 1, str_len, stderr);
97 /* See http://support.microsoft.com/kb/190351 */
98 #ifdef PHP_WIN32
99 fflush(stderr);
100 #endif
101 return str_len;
102 }
103 static int (*php_output_direct)(const char *str, size_t str_len) = php_output_stderr;
104 /* }}} */
105
106 /* {{{ void php_output_header(TSRMLS_D) */
php_output_header(TSRMLS_D)107 static void php_output_header(TSRMLS_D)
108 {
109 if (!SG(headers_sent)) {
110 if (!OG(output_start_filename)) {
111 if (zend_is_compiling(TSRMLS_C)) {
112 OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C);
113 OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C);
114 } else if (zend_is_executing(TSRMLS_C)) {
115 OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C);
116 OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C);
117 }
118 #if PHP_OUTPUT_DEBUG
119 fprintf(stderr, "!!! output started at: %s (%d)\n", OG(output_start_filename), OG(output_start_lineno));
120 #endif
121 }
122 if (!php_header(TSRMLS_C)) {
123 OG(flags) |= PHP_OUTPUT_DISABLED;
124 }
125 }
126 }
127 /* }}} */
128
129 /* {{{ void php_output_startup(void)
130 * Set up module globals and initalize the conflict and reverse conflict hash tables */
php_output_startup(void)131 PHPAPI void php_output_startup(void)
132 {
133 ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL);
134 zend_hash_init(&php_output_handler_aliases, 0, NULL, NULL, 1);
135 zend_hash_init(&php_output_handler_conflicts, 0, NULL, NULL, 1);
136 zend_hash_init(&php_output_handler_reverse_conflicts, 0, NULL, (void (*)(void *)) zend_hash_destroy, 1);
137 php_output_direct = php_output_stdout;
138 }
139 /* }}} */
140
141 /* {{{ void php_output_shutdown(void)
142 * Destroy module globals and the conflict and reverse conflict hash tables */
php_output_shutdown(void)143 PHPAPI void php_output_shutdown(void)
144 {
145 php_output_direct = php_output_stderr;
146 zend_hash_destroy(&php_output_handler_aliases);
147 zend_hash_destroy(&php_output_handler_conflicts);
148 zend_hash_destroy(&php_output_handler_reverse_conflicts);
149 }
150 /* }}} */
151
152 /* {{{ SUCCESS|FAILURE php_output_activate(TSRMLS_D)
153 * Reset output globals and setup the output handler stack */
php_output_activate(TSRMLS_D)154 PHPAPI int php_output_activate(TSRMLS_D)
155 {
156 #ifdef ZTS
157 memset((*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals));
158 #else
159 memset(&output_globals, 0, sizeof(zend_output_globals));
160 #endif
161
162 zend_stack_init(&OG(handlers));
163 OG(flags) |= PHP_OUTPUT_ACTIVATED;
164
165 return SUCCESS;
166 }
167 /* }}} */
168
169 /* {{{ void php_output_deactivate(TSRMLS_D)
170 * Destroy the output handler stack */
php_output_deactivate(TSRMLS_D)171 PHPAPI void php_output_deactivate(TSRMLS_D)
172 {
173 php_output_handler **handler = NULL;
174
175 if ((OG(flags) & PHP_OUTPUT_ACTIVATED)) {
176 php_output_header(TSRMLS_C);
177
178 OG(flags) ^= PHP_OUTPUT_ACTIVATED;
179 OG(active) = NULL;
180 OG(running) = NULL;
181
182 /* release all output handlers */
183 if (OG(handlers).elements) {
184 while (SUCCESS == zend_stack_top(&OG(handlers), (void *) &handler)) {
185 php_output_handler_free(handler TSRMLS_CC);
186 zend_stack_del_top(&OG(handlers));
187 }
188 zend_stack_destroy(&OG(handlers));
189 }
190 }
191 }
192 /* }}} */
193
194 /* {{{ void php_output_register_constants() */
php_output_register_constants(TSRMLS_D)195 PHPAPI void php_output_register_constants(TSRMLS_D)
196 {
197 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT);
198 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_WRITE", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT);
199 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSH", PHP_OUTPUT_HANDLER_FLUSH, CONST_CS | CONST_PERSISTENT);
200 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEAN", PHP_OUTPUT_HANDLER_CLEAN, CONST_CS | CONST_PERSISTENT);
201 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FINAL", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT);
202 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT);
203 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT);
204
205 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEANABLE", PHP_OUTPUT_HANDLER_CLEANABLE, CONST_CS | CONST_PERSISTENT);
206 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSHABLE", PHP_OUTPUT_HANDLER_FLUSHABLE, CONST_CS | CONST_PERSISTENT);
207 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_REMOVABLE", PHP_OUTPUT_HANDLER_REMOVABLE, CONST_CS | CONST_PERSISTENT);
208 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STDFLAGS", PHP_OUTPUT_HANDLER_STDFLAGS, CONST_CS | CONST_PERSISTENT);
209 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STARTED", PHP_OUTPUT_HANDLER_STARTED, CONST_CS | CONST_PERSISTENT);
210 REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_DISABLED", PHP_OUTPUT_HANDLER_DISABLED, CONST_CS | CONST_PERSISTENT);
211 }
212 /* }}} */
213
214 /* {{{ void php_output_set_status(int status TSRMLS_DC)
215 * Used by SAPIs to disable output */
php_output_set_status(int status TSRMLS_DC)216 PHPAPI void php_output_set_status(int status TSRMLS_DC)
217 {
218 OG(flags) = (OG(flags) & ~0xf) | (status & 0xf);
219 }
220 /* }}} */
221
222 /* {{{ int php_output_get_status(TSRMLS_C)
223 * Get output control status */
php_output_get_status(TSRMLS_D)224 PHPAPI int php_output_get_status(TSRMLS_D)
225 {
226 return (
227 OG(flags)
228 | (OG(active) ? PHP_OUTPUT_ACTIVE : 0)
229 | (OG(running)? PHP_OUTPUT_LOCKED : 0)
230 ) & 0xff;
231 }
232 /* }}} */
233
234 /* {{{ int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC)
235 * Unbuffered write */
php_output_write_unbuffered(const char * str,size_t len TSRMLS_DC)236 PHPAPI int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC)
237 {
238 #if PHP_DEBUG
239 if (len > UINT_MAX) {
240 php_error(E_WARNING, "Attempt to output more than UINT_MAX bytes at once; "
241 "output will be truncated %lu => %lu",
242 (unsigned long) len, (unsigned long) (len % UINT_MAX));
243 }
244 #endif
245 if (OG(flags) & PHP_OUTPUT_DISABLED) {
246 return 0;
247 }
248 if (OG(flags) & PHP_OUTPUT_ACTIVATED) {
249 return sapi_module.ub_write(str, len TSRMLS_CC);
250 }
251 return php_output_direct(str, len);
252 }
253 /* }}} */
254
255 /* {{{ int php_output_write(const char *str, size_t len TSRMLS_DC)
256 * Buffered write */
php_output_write(const char * str,size_t len TSRMLS_DC)257 PHPAPI int php_output_write(const char *str, size_t len TSRMLS_DC)
258 {
259 #if PHP_DEBUG
260 if (len > UINT_MAX) {
261 php_error(E_WARNING, "Attempt to output more than UINT_MAX bytes at once; "
262 "output will be truncated %lu => %lu",
263 (unsigned long) len, (unsigned long) (len % UINT_MAX));
264 }
265 #endif
266 if (OG(flags) & PHP_OUTPUT_DISABLED) {
267 return 0;
268 }
269 if (OG(flags) & PHP_OUTPUT_ACTIVATED) {
270 php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len TSRMLS_CC);
271 return (int) len;
272 }
273 return php_output_direct(str, len);
274 }
275 /* }}} */
276
277 /* {{{ SUCCESS|FAILURE php_output_flush(TSRMLS_D)
278 * Flush the most recent output handlers buffer */
php_output_flush(TSRMLS_D)279 PHPAPI int php_output_flush(TSRMLS_D)
280 {
281 php_output_context context;
282
283 if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
284 php_output_context_init(&context, PHP_OUTPUT_HANDLER_FLUSH TSRMLS_CC);
285 php_output_handler_op(OG(active), &context);
286 if (context.out.data && context.out.used) {
287 zend_stack_del_top(&OG(handlers));
288 php_output_write(context.out.data, context.out.used TSRMLS_CC);
289 zend_stack_push(&OG(handlers), &OG(active), sizeof(php_output_handler *));
290 }
291 php_output_context_dtor(&context);
292 return SUCCESS;
293 }
294 return FAILURE;
295 }
296 /* }}} */
297
298 /* {{{ void php_output_flush_all(TSRMLS_C)
299 * Flush all output buffers subsequently */
php_output_flush_all(TSRMLS_D)300 PHPAPI void php_output_flush_all(TSRMLS_D)
301 {
302 if (OG(active)) {
303 php_output_op(PHP_OUTPUT_HANDLER_FLUSH, NULL, 0 TSRMLS_CC);
304 }
305 }
306 /* }}} */
307
308 /* {{{ SUCCESS|FAILURE php_output_clean(TSRMLS_D)
309 * Cleans the most recent output handlers buffer if the handler is cleanable */
php_output_clean(TSRMLS_D)310 PHPAPI int php_output_clean(TSRMLS_D)
311 {
312 php_output_context context;
313
314 if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_CLEANABLE)) {
315 php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC);
316 php_output_handler_op(OG(active), &context);
317 php_output_context_dtor(&context);
318 return SUCCESS;
319 }
320 return FAILURE;
321 }
322 /* }}} */
323
324 /* {{{ void php_output_clean_all(TSRMLS_D)
325 * Cleans all output handler buffers, without regard whether the handler is cleanable */
php_output_clean_all(TSRMLS_D)326 PHPAPI void php_output_clean_all(TSRMLS_D)
327 {
328 php_output_context context;
329
330 if (OG(active)) {
331 php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC);
332 zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_clean, &context);
333 }
334 }
335
336 /* {{{ SUCCESS|FAILURE php_output_end(TSRMLS_D)
337 * Finalizes the most recent output handler at pops it off the stack if the handler is removable */
php_output_end(TSRMLS_D)338 PHPAPI int php_output_end(TSRMLS_D)
339 {
340 if (php_output_stack_pop(PHP_OUTPUT_POP_TRY TSRMLS_CC)) {
341 return SUCCESS;
342 }
343 return FAILURE;
344 }
345 /* }}} */
346
347 /* {{{ void php_output_end_all(TSRMLS_D)
348 * Finalizes all output handlers and ends output buffering without regard whether a handler is removable */
php_output_end_all(TSRMLS_D)349 PHPAPI void php_output_end_all(TSRMLS_D)
350 {
351 while (OG(active) && php_output_stack_pop(PHP_OUTPUT_POP_FORCE TSRMLS_CC));
352 }
353 /* }}} */
354
355 /* {{{ SUCCESS|FAILURE php_output_discard(TSRMLS_D)
356 * Discards the most recent output handlers buffer and pops it off the stack if the handler is removable */
php_output_discard(TSRMLS_D)357 PHPAPI int php_output_discard(TSRMLS_D)
358 {
359 if (php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_TRY TSRMLS_CC)) {
360 return SUCCESS;
361 }
362 return FAILURE;
363 }
364 /* }}} */
365
366 /* {{{ void php_output_discard_all(TSRMLS_D)
367 * Discard all output handlers and buffers without regard whether a handler is removable */
php_output_discard_all(TSRMLS_D)368 PHPAPI void php_output_discard_all(TSRMLS_D)
369 {
370 while (OG(active)) {
371 php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_FORCE TSRMLS_CC);
372 }
373 }
374 /* }}} */
375
376 /* {{{ int php_output_get_level(TSRMLS_D)
377 * Get output buffering level, ie. how many output handlers the stack contains */
php_output_get_level(TSRMLS_D)378 PHPAPI int php_output_get_level(TSRMLS_D)
379 {
380 return OG(active) ? zend_stack_count(&OG(handlers)) : 0;
381 }
382 /* }}} */
383
384 /* {{{ SUCCESS|FAILURE php_output_get_contents(zval *z TSRMLS_DC)
385 * Get the contents of the active output handlers buffer */
php_output_get_contents(zval * p TSRMLS_DC)386 PHPAPI int php_output_get_contents(zval *p TSRMLS_DC)
387 {
388 if (OG(active)) {
389 ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used, 1);
390 return SUCCESS;
391 } else {
392 ZVAL_NULL(p);
393 return FAILURE;
394 }
395 }
396
397 /* {{{ SUCCESS|FAILURE php_output_get_length(zval *z TSRMLS_DC)
398 * Get the length of the active output handlers buffer */
php_output_get_length(zval * p TSRMLS_DC)399 PHPAPI int php_output_get_length(zval *p TSRMLS_DC)
400 {
401 if (OG(active)) {
402 ZVAL_LONG(p, OG(active)->buffer.used);
403 return SUCCESS;
404 } else {
405 ZVAL_NULL(p);
406 return FAILURE;
407 }
408 }
409 /* }}} */
410
411 /* {{{ php_output_handler* php_output_get_active_handler(TSRMLS_D)
412 * Get active output handler */
php_output_get_active_handler(TSRMLS_D)413 PHPAPI php_output_handler* php_output_get_active_handler(TSRMLS_D)
414 {
415 return OG(active);
416 }
417 /* }}} */
418
419 /* {{{ SUCCESS|FAILURE php_output_handler_start_default(TSRMLS_D)
420 * Start a "default output handler" */
php_output_start_default(TSRMLS_D)421 PHPAPI int php_output_start_default(TSRMLS_D)
422 {
423 php_output_handler *handler;
424
425 handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC);
426 if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
427 return SUCCESS;
428 }
429 php_output_handler_free(&handler TSRMLS_CC);
430 return FAILURE;
431 }
432 /* }}} */
433
434 /* {{{ SUCCESS|FAILURE php_output_handler_start_devnull(TSRMLS_D)
435 * Start a "null output handler" */
php_output_start_devnull(TSRMLS_D)436 PHPAPI int php_output_start_devnull(TSRMLS_D)
437 {
438 php_output_handler *handler;
439
440 handler = php_output_handler_create_internal(ZEND_STRL(php_output_devnull_handler_name), php_output_handler_devnull_func, PHP_OUTPUT_HANDLER_DEFAULT_SIZE, 0 TSRMLS_CC);
441 if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
442 return SUCCESS;
443 }
444 php_output_handler_free(&handler TSRMLS_CC);
445 return FAILURE;
446 }
447 /* }}} */
448
449 /* {{{ SUCCESS|FAILURE php_output_start_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC)
450 * Start a user level output handler */
php_output_start_user(zval * output_handler,size_t chunk_size,int flags TSRMLS_DC)451 PHPAPI int php_output_start_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC)
452 {
453 php_output_handler *handler;
454
455 if (output_handler) {
456 handler = php_output_handler_create_user(output_handler, chunk_size, flags TSRMLS_CC);
457 } else {
458 handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC);
459 }
460 if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
461 return SUCCESS;
462 }
463 php_output_handler_free(&handler TSRMLS_CC);
464 return FAILURE;
465 }
466 /* }}} */
467
468 /* {{{ SUCCESS|FAILURE php_output_start_internal(zval *name, php_output_handler_func_t handler, size_t chunk_size, int flags TSRMLS_DC)
469 * Start an internal output handler that does not have to maintain a non-global state */
php_output_start_internal(const char * name,size_t name_len,php_output_handler_func_t output_handler,size_t chunk_size,int flags TSRMLS_DC)470 PHPAPI int php_output_start_internal(const char *name, size_t name_len, php_output_handler_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC)
471 {
472 php_output_handler *handler;
473
474 handler = php_output_handler_create_internal(name, name_len, php_output_handler_compat_func, chunk_size, flags TSRMLS_CC);
475 php_output_handler_set_context(handler, output_handler, NULL TSRMLS_CC);
476 if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
477 return SUCCESS;
478 }
479 php_output_handler_free(&handler TSRMLS_CC);
480 return FAILURE;
481 }
482 /* }}} */
483
484 /* {{{ php_output_handler *php_output_handler_create_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC)
485 * Create a user level output handler */
php_output_handler_create_user(zval * output_handler,size_t chunk_size,int flags TSRMLS_DC)486 PHPAPI php_output_handler *php_output_handler_create_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC)
487 {
488 char *handler_name = NULL, *error = NULL;
489 php_output_handler *handler = NULL;
490 php_output_handler_alias_ctor_t *alias = NULL;
491 php_output_handler_user_func_t *user = NULL;
492
493 switch (Z_TYPE_P(output_handler)) {
494 case IS_NULL:
495 handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC);
496 break;
497 case IS_STRING:
498 if (Z_STRLEN_P(output_handler) && (alias = php_output_handler_alias(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler) TSRMLS_CC))) {
499 handler = (*alias)(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler), chunk_size, flags TSRMLS_CC);
500 break;
501 }
502 default:
503 user = ecalloc(1, sizeof(php_output_handler_user_func_t));
504 if (SUCCESS == zend_fcall_info_init(output_handler, 0, &user->fci, &user->fcc, &handler_name, &error TSRMLS_CC)) {
505 handler = php_output_handler_init(handler_name, strlen(handler_name), chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_USER TSRMLS_CC);
506 Z_ADDREF_P(output_handler);
507 user->zoh = output_handler;
508 handler->func.user = user;
509 } else {
510 efree(user);
511 }
512 if (error) {
513 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "%s", error);
514 efree(error);
515 }
516 if (handler_name) {
517 efree(handler_name);
518 }
519 }
520
521 return handler;
522 }
523 /* }}} */
524
525 /* {{{ php_output_handler *php_output_handler_create_internal(zval *name, php_output_handler_context_func_t handler, size_t chunk_size, int flags TSRMLS_DC)
526 * Create an internal output handler that can maintain a non-global state */
php_output_handler_create_internal(const char * name,size_t name_len,php_output_handler_context_func_t output_handler,size_t chunk_size,int flags TSRMLS_DC)527 PHPAPI php_output_handler *php_output_handler_create_internal(const char *name, size_t name_len, php_output_handler_context_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC)
528 {
529 php_output_handler *handler;
530
531 handler = php_output_handler_init(name, name_len, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_INTERNAL TSRMLS_CC);
532 handler->func.internal = output_handler;
533
534 return handler;
535 }
536 /* }}} */
537
538 /* {{{ void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC)
539 * Set the context/state of an output handler. Calls the dtor of the previous context if there is one */
php_output_handler_set_context(php_output_handler * handler,void * opaq,void (* dtor)(void * TSRMLS_DC)TSRMLS_DC)540 PHPAPI void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC)
541 {
542 if (handler->dtor && handler->opaq) {
543 handler->dtor(handler->opaq TSRMLS_CC);
544 }
545 handler->dtor = dtor;
546 handler->opaq = opaq;
547 }
548 /* }}} */
549
550 /* {{{ SUCCESS|FAILURE php_output_handler_start(php_output_handler *handler TSRMLS_DC)
551 * Starts the set up output handler and pushes it on top of the stack. Checks for any conflicts regarding the output handler to start */
php_output_handler_start(php_output_handler * handler TSRMLS_DC)552 PHPAPI int php_output_handler_start(php_output_handler *handler TSRMLS_DC)
553 {
554 HashPosition pos;
555 HashTable *rconflicts;
556 php_output_handler_conflict_check_t *conflict;
557
558 if (php_output_lock_error(PHP_OUTPUT_HANDLER_START TSRMLS_CC) || !handler) {
559 return FAILURE;
560 }
561 if (SUCCESS == zend_hash_find(&php_output_handler_conflicts, handler->name, handler->name_len+1, (void *) &conflict)) {
562 if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) {
563 return FAILURE;
564 }
565 }
566 if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, handler->name, handler->name_len+1, (void *) &rconflicts)) {
567 for (zend_hash_internal_pointer_reset_ex(rconflicts, &pos);
568 zend_hash_get_current_data_ex(rconflicts, (void *) &conflict, &pos) == SUCCESS;
569 zend_hash_move_forward_ex(rconflicts, &pos)
570 ) {
571 if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) {
572 return FAILURE;
573 }
574 }
575 }
576 /* zend_stack_push never returns SUCCESS but FAILURE or stack level */
577 if (FAILURE == (handler->level = zend_stack_push(&OG(handlers), &handler, sizeof(php_output_handler *)))) {
578 return FAILURE;
579 }
580 OG(active) = handler;
581 return SUCCESS;
582 }
583 /* }}} */
584
585 /* {{{ int php_output_handler_started(zval *name TSRMLS_DC)
586 * Check whether a certain output handler is in use */
php_output_handler_started(const char * name,size_t name_len TSRMLS_DC)587 PHPAPI int php_output_handler_started(const char *name, size_t name_len TSRMLS_DC)
588 {
589 php_output_handler ***handlers;
590 int i, count = php_output_get_level(TSRMLS_C);
591
592 if (count) {
593 handlers = (php_output_handler ***) zend_stack_base(&OG(handlers));
594
595 for (i = 0; i < count; ++i) {
596 if (name_len == (*(handlers[i]))->name_len && !memcmp((*(handlers[i]))->name, name, name_len)) {
597 return 1;
598 }
599 }
600 }
601
602 return 0;
603 }
604 /* }}} */
605
606 /* {{{ int php_output_handler_conflict(zval *handler_new, zval *handler_old TSRMLS_DC)
607 * Check whether a certain handler is in use and issue a warning that the new handler would conflict with the already used one */
php_output_handler_conflict(const char * handler_new,size_t handler_new_len,const char * handler_set,size_t handler_set_len TSRMLS_DC)608 PHPAPI int php_output_handler_conflict(const char *handler_new, size_t handler_new_len, const char *handler_set, size_t handler_set_len TSRMLS_DC)
609 {
610 if (php_output_handler_started(handler_set, handler_set_len TSRMLS_CC)) {
611 if (handler_new_len != handler_set_len || memcmp(handler_new, handler_set, handler_set_len)) {
612 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set);
613 } else {
614 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' cannot be used twice", handler_new);
615 }
616 return 1;
617 }
618 return 0;
619 }
620 /* }}} */
621
622 /* {{{ SUCCESS|FAILURE php_output_handler_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC)
623 * Register a conflict checking function on MINIT */
php_output_handler_conflict_register(const char * name,size_t name_len,php_output_handler_conflict_check_t check_func TSRMLS_DC)624 PHPAPI int php_output_handler_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC)
625 {
626 if (!EG(current_module)) {
627 zend_error(E_ERROR, "Cannot register an output handler conflict outside of MINIT");
628 return FAILURE;
629 }
630 return zend_hash_update(&php_output_handler_conflicts, name, name_len+1, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL);
631 }
632 /* }}} */
633
634 /* {{{ SUCCESS|FAILURE php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC)
635 * Register a reverse conflict checking function on MINIT */
php_output_handler_reverse_conflict_register(const char * name,size_t name_len,php_output_handler_conflict_check_t check_func TSRMLS_DC)636 PHPAPI int php_output_handler_reverse_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC)
637 {
638 HashTable rev, *rev_ptr = NULL;
639
640 if (!EG(current_module)) {
641 zend_error(E_ERROR, "Cannot register a reverse output handler conflict outside of MINIT");
642 return FAILURE;
643 }
644
645 if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, name, name_len+1, (void *) &rev_ptr)) {
646 return zend_hash_next_index_insert(rev_ptr, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL);
647 } else {
648 zend_hash_init(&rev, 1, NULL, NULL, 1);
649 if (SUCCESS != zend_hash_next_index_insert(&rev, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL)) {
650 zend_hash_destroy(&rev);
651 return FAILURE;
652 }
653 if (SUCCESS != zend_hash_update(&php_output_handler_reverse_conflicts, name, name_len+1, &rev, sizeof(HashTable), NULL)) {
654 zend_hash_destroy(&rev);
655 return FAILURE;
656 }
657 return SUCCESS;
658 }
659 }
660 /* }}} */
661
662 /* {{{ php_output_handler_alias_ctor_t php_output_handler_alias(zval *name TSRMLS_DC)
663 * Get an internal output handler for a user handler if it exists */
php_output_handler_alias(const char * name,size_t name_len TSRMLS_DC)664 PHPAPI php_output_handler_alias_ctor_t *php_output_handler_alias(const char *name, size_t name_len TSRMLS_DC)
665 {
666 php_output_handler_alias_ctor_t *func = NULL;
667
668 zend_hash_find(&php_output_handler_aliases, name, name_len+1, (void *) &func);
669 return func;
670 }
671 /* }}} */
672
673 /* {{{ SUCCESS|FAILURE php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func TSRMLS_DC)
674 * Registers an internal output handler as alias for a user handler */
php_output_handler_alias_register(const char * name,size_t name_len,php_output_handler_alias_ctor_t func TSRMLS_DC)675 PHPAPI int php_output_handler_alias_register(const char *name, size_t name_len, php_output_handler_alias_ctor_t func TSRMLS_DC)
676 {
677 if (!EG(current_module)) {
678 zend_error(E_ERROR, "Cannot register an output handler alias outside of MINIT");
679 return FAILURE;
680 }
681 return zend_hash_update(&php_output_handler_aliases, name, name_len+1, &func, sizeof(php_output_handler_alias_ctor_t *), NULL);
682 }
683 /* }}} */
684
685 /* {{{ SUCCESS|FAILURE php_output_handler_hook(php_output_handler_hook_t type, void *arg TSMRLS_DC)
686 * Output handler hook for output handler functions to check/modify the current handlers abilities */
php_output_handler_hook(php_output_handler_hook_t type,void * arg TSRMLS_DC)687 PHPAPI int php_output_handler_hook(php_output_handler_hook_t type, void *arg TSRMLS_DC)
688 {
689 if (OG(running)) {
690 switch (type) {
691 case PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ:
692 *(void ***) arg = &OG(running)->opaq;
693 return SUCCESS;
694 case PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS:
695 *(int *) arg = OG(running)->flags;
696 return SUCCESS;
697 case PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL:
698 *(int *) arg = OG(running)->level;
699 return SUCCESS;
700 case PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE:
701 OG(running)->flags &= ~(PHP_OUTPUT_HANDLER_REMOVABLE|PHP_OUTPUT_HANDLER_CLEANABLE);
702 return SUCCESS;
703 case PHP_OUTPUT_HANDLER_HOOK_DISABLE:
704 OG(running)->flags |= PHP_OUTPUT_HANDLER_DISABLED;
705 return SUCCESS;
706 default:
707 break;
708 }
709 }
710 return FAILURE;
711 }
712 /* }}} */
713
714 /* {{{ void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC)
715 * Destroy an output handler */
php_output_handler_dtor(php_output_handler * handler TSRMLS_DC)716 PHPAPI void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC)
717 {
718 STR_FREE(handler->name);
719 STR_FREE(handler->buffer.data);
720 if (handler->flags & PHP_OUTPUT_HANDLER_USER) {
721 zval_ptr_dtor(&handler->func.user->zoh);
722 efree(handler->func.user);
723 }
724 if (handler->dtor && handler->opaq) {
725 handler->dtor(handler->opaq TSRMLS_CC);
726 }
727 memset(handler, 0, sizeof(*handler));
728 }
729 /* }}} */
730
731 /* {{{ void php_output_handler_free(php_output_handler **handler TSMRLS_DC)
732 * Destroy and free an output handler */
php_output_handler_free(php_output_handler ** h TSRMLS_DC)733 PHPAPI void php_output_handler_free(php_output_handler **h TSRMLS_DC)
734 {
735 if (*h) {
736 php_output_handler_dtor(*h TSRMLS_CC);
737 efree(*h);
738 *h = NULL;
739 }
740 }
741 /* }}} */
742
743 /* void php_output_set_implicit_flush(int enabled TSRMLS_DC)
744 * Enable or disable implicit flush */
php_output_set_implicit_flush(int flush TSRMLS_DC)745 PHPAPI void php_output_set_implicit_flush(int flush TSRMLS_DC)
746 {
747 if (flush) {
748 OG(flags) |= PHP_OUTPUT_IMPLICITFLUSH;
749 } else {
750 OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH;
751 }
752 }
753 /* }}} */
754
755 /* {{{ char *php_output_get_start_filename(TSRMLS_D)
756 * Get the file name where output has started */
php_output_get_start_filename(TSRMLS_D)757 PHPAPI const char *php_output_get_start_filename(TSRMLS_D)
758 {
759 return OG(output_start_filename);
760 }
761 /* }}} */
762
763 /* {{{ int php_output_get_start_lineno(TSRMLS_D)
764 * Get the line number where output has started */
php_output_get_start_lineno(TSRMLS_D)765 PHPAPI int php_output_get_start_lineno(TSRMLS_D)
766 {
767 return OG(output_start_lineno);
768 }
769 /* }}} */
770
771 /* {{{ static int php_output_lock_error(int op TSRMLS_DC)
772 * Checks whether an unallowed operation is attempted from within the output handler and issues a fatal error */
php_output_lock_error(int op TSRMLS_DC)773 static inline int php_output_lock_error(int op TSRMLS_DC)
774 {
775 /* if there's no ob active, ob has been stopped */
776 if (op && OG(active) && OG(running)) {
777 /* fatal error */
778 php_output_deactivate(TSRMLS_C);
779 php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
780 return 1;
781 }
782 return 0;
783 }
784 /* }}} */
785
786 /* {{{ static php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC)
787 * Initialize a new output context */
php_output_context_init(php_output_context * context,int op TSRMLS_DC)788 static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC)
789 {
790 if (!context) {
791 context = emalloc(sizeof(php_output_context));
792 }
793
794 memset(context, 0, sizeof(php_output_context));
795 TSRMLS_SET_CTX(context->tsrm_ls);
796 context->op = op;
797
798 return context;
799 }
800 /* }}} */
801
802 /* {{{ static void php_output_context_reset(php_output_context *context)
803 * Reset an output context */
php_output_context_reset(php_output_context * context)804 static inline void php_output_context_reset(php_output_context *context)
805 {
806 int op = context->op;
807 php_output_context_dtor(context);
808 memset(context, 0, sizeof(php_output_context));
809 context->op = op;
810 }
811 /* }}} */
812
813 /* {{{ static void php_output_context_feed(php_output_context *context, char *, size_t, size_t)
814 * Feed output contexts input buffer */
php_output_context_feed(php_output_context * context,char * data,size_t size,size_t used,zend_bool free)815 static inline void php_output_context_feed(php_output_context *context, char *data, size_t size, size_t used, zend_bool free)
816 {
817 if (context->in.free && context->in.data) {
818 efree(context->in.data);
819 }
820 context->in.data = data;
821 context->in.used = used;
822 context->in.free = free;
823 context->in.size = size;
824 }
825 /* }}} */
826
827 /* {{{ static void php_output_context_swap(php_output_context *context)
828 * Swap output contexts buffers */
php_output_context_swap(php_output_context * context)829 static inline void php_output_context_swap(php_output_context *context)
830 {
831 if (context->in.free && context->in.data) {
832 efree(context->in.data);
833 }
834 context->in.data = context->out.data;
835 context->in.used = context->out.used;
836 context->in.free = context->out.free;
837 context->in.size = context->out.size;
838 context->out.data = NULL;
839 context->out.used = 0;
840 context->out.free = 0;
841 context->out.size = 0;
842 }
843 /* }}} */
844
845 /* {{{ static void php_output_context_pass(php_output_context *context)
846 * Pass input to output buffer */
php_output_context_pass(php_output_context * context)847 static inline void php_output_context_pass(php_output_context *context)
848 {
849 context->out.data = context->in.data;
850 context->out.used = context->in.used;
851 context->out.size = context->in.size;
852 context->out.free = context->in.free;
853 context->in.data = NULL;
854 context->in.used = 0;
855 context->in.free = 0;
856 context->in.size = 0;
857 }
858 /* }}} */
859
860 /* {{{ static void php_output_context_dtor(php_output_context *context)
861 * Destroy the contents of an output context */
php_output_context_dtor(php_output_context * context)862 static inline void php_output_context_dtor(php_output_context *context)
863 {
864 if (context->in.free && context->in.data) {
865 efree(context->in.data);
866 context->in.data = NULL;
867 }
868 if (context->out.free && context->out.data) {
869 efree(context->out.data);
870 context->out.data = NULL;
871 }
872 }
873 /* }}} */
874
875 /* {{{ static php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags TSRMLS_DC)
876 * Allocates and initializes a php_output_handler structure */
php_output_handler_init(const char * name,size_t name_len,size_t chunk_size,int flags TSRMLS_DC)877 static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC)
878 {
879 php_output_handler *handler;
880
881 handler = ecalloc(1, sizeof(php_output_handler));
882 handler->name = estrndup(name, name_len);
883 handler->name_len = name_len;
884 handler->size = chunk_size;
885 handler->flags = flags;
886 handler->buffer.size = PHP_OUTPUT_HANDLER_INITBUF_SIZE(chunk_size);
887 handler->buffer.data = emalloc(handler->buffer.size);
888
889 return handler;
890 }
891 /* }}} */
892
893 /* {{{ static int php_output_handler_appen(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC)
894 * Appends input to the output handlers buffer and indicates whether the buffer does not have to be processed by the output handler */
php_output_handler_append(php_output_handler * handler,const php_output_buffer * buf TSRMLS_DC)895 static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC)
896 {
897 if (buf->used) {
898 OG(flags) |= PHP_OUTPUT_WRITTEN;
899 /* store it away */
900 if ((handler->buffer.size - handler->buffer.used) <= buf->used) {
901 size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size);
902 size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used));
903 size_t grow_max = MAX(grow_int, grow_buf);
904
905 handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max);
906 handler->buffer.size += grow_max;
907 }
908 memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used);
909 handler->buffer.used += buf->used;
910
911 /* chunked buffering */
912 if (handler->size && (handler->buffer.used >= handler->size)) {
913 /* store away errors and/or any intermediate output */
914 return OG(running) ? 1 : 0;
915 }
916 }
917 return 1;
918 }
919 /* }}} */
920
921 /* {{{ static php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context)
922 * Output handler operation dispatcher, applying context op to the php_output_handler handler */
php_output_handler_op(php_output_handler * handler,php_output_context * context)923 static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context)
924 {
925 php_output_handler_status_t status;
926 int original_op = context->op;
927 PHP_OUTPUT_TSRMLS(context);
928
929 #if PHP_OUTPUT_DEBUG
930 fprintf(stderr, ">>> op(%d, "
931 "handler=%p, "
932 "name=%s, "
933 "flags=%d, "
934 "buffer.data=%s, "
935 "buffer.used=%zu, "
936 "buffer.size=%zu, "
937 "in.data=%s, "
938 "in.used=%zu)\n",
939 context->op,
940 handler,
941 handler->name,
942 handler->flags,
943 handler->buffer.used?handler->buffer.data:"",
944 handler->buffer.used,
945 handler->buffer.size,
946 context->in.used?context->in.data:"",
947 context->in.used
948 );
949 #endif
950
951 if (php_output_lock_error(context->op TSRMLS_CC)) {
952 /* fatal error */
953 return PHP_OUTPUT_HANDLER_FAILURE;
954 }
955
956 /* storable? */
957 if (php_output_handler_append(handler, &context->in TSRMLS_CC) && !context->op) {
958 context->op = original_op;
959 return PHP_OUTPUT_HANDLER_NO_DATA;
960 } else {
961 /* need to start? */
962 if (!(handler->flags & PHP_OUTPUT_HANDLER_STARTED)) {
963 context->op |= PHP_OUTPUT_HANDLER_START;
964 }
965
966 OG(running) = handler;
967 if (handler->flags & PHP_OUTPUT_HANDLER_USER) {
968 zval *retval = NULL, *ob_data, *ob_mode;
969
970 MAKE_STD_ZVAL(ob_data);
971 ZVAL_STRINGL(ob_data, handler->buffer.data, handler->buffer.used, 1);
972 MAKE_STD_ZVAL(ob_mode);
973 ZVAL_LONG(ob_mode, (long) context->op);
974 zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 2, &ob_data, &ob_mode);
975
976 #define PHP_OUTPUT_USER_SUCCESS(retval) (retval && !(Z_TYPE_P(retval) == IS_BOOL && Z_BVAL_P(retval)==0))
977 if (SUCCESS == zend_fcall_info_call(&handler->func.user->fci, &handler->func.user->fcc, &retval, NULL TSRMLS_CC) && PHP_OUTPUT_USER_SUCCESS(retval)) {
978 /* user handler may have returned TRUE */
979 status = PHP_OUTPUT_HANDLER_NO_DATA;
980 if (Z_TYPE_P(retval) != IS_BOOL) {
981 convert_to_string_ex(&retval);
982 if (Z_STRLEN_P(retval)) {
983 context->out.data = estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
984 context->out.used = Z_STRLEN_P(retval);
985 context->out.free = 1;
986 status = PHP_OUTPUT_HANDLER_SUCCESS;
987 }
988 }
989 } else {
990 /* call failed, pass internal buffer along */
991 status = PHP_OUTPUT_HANDLER_FAILURE;
992 }
993
994 zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 0);
995 zval_ptr_dtor(&ob_data);
996 zval_ptr_dtor(&ob_mode);
997 if (retval) {
998 zval_ptr_dtor(&retval);
999 }
1000
1001 } else {
1002
1003 php_output_context_feed(context, handler->buffer.data, handler->buffer.size, handler->buffer.used, 0);
1004
1005 if (SUCCESS == handler->func.internal(&handler->opaq, context)) {
1006 if (context->out.used) {
1007 status = PHP_OUTPUT_HANDLER_SUCCESS;
1008 } else {
1009 status = PHP_OUTPUT_HANDLER_NO_DATA;
1010 }
1011 } else {
1012 status = PHP_OUTPUT_HANDLER_FAILURE;
1013 }
1014 }
1015 handler->flags |= PHP_OUTPUT_HANDLER_STARTED;
1016 OG(running) = NULL;
1017 }
1018
1019 switch (status) {
1020 case PHP_OUTPUT_HANDLER_FAILURE:
1021 /* disable this handler */
1022 handler->flags |= PHP_OUTPUT_HANDLER_DISABLED;
1023 /* discard any output */
1024 if (context->out.data && context->out.free) {
1025 efree(context->out.data);
1026 }
1027 /* returns handlers buffer */
1028 context->out.data = handler->buffer.data;
1029 context->out.used = handler->buffer.used;
1030 context->out.free = 1;
1031 handler->buffer.data = NULL;
1032 handler->buffer.used = 0;
1033 handler->buffer.size = 0;
1034 break;
1035 case PHP_OUTPUT_HANDLER_NO_DATA:
1036 /* handler ate all */
1037 php_output_context_reset(context);
1038 /* no break */
1039 case PHP_OUTPUT_HANDLER_SUCCESS:
1040 /* no more buffered data */
1041 handler->buffer.used = 0;
1042 handler->flags |= PHP_OUTPUT_HANDLER_PROCESSED;
1043 break;
1044 }
1045
1046 context->op = original_op;
1047 return status;
1048 }
1049 /* }}} */
1050
1051
1052 /* {{{ static void php_output_op(int op, const char *str, size_t len TSRMLS_DC)
1053 * Output op dispatcher, passes input and output handlers output through the output handler stack until it gets written to the SAPI */
php_output_op(int op,const char * str,size_t len TSRMLS_DC)1054 static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC)
1055 {
1056 php_output_context context;
1057 php_output_handler **active;
1058 int obh_cnt;
1059
1060 if (php_output_lock_error(op TSRMLS_CC)) {
1061 return;
1062 }
1063
1064 php_output_context_init(&context, op TSRMLS_CC);
1065
1066 /*
1067 * broken up for better performance:
1068 * - apply op to the one active handler; note that OG(active) might be popped off the stack on a flush
1069 * - or apply op to the handler stack
1070 */
1071 if (OG(active) && (obh_cnt = zend_stack_count(&OG(handlers)))) {
1072 context.in.data = (char *) str;
1073 context.in.used = len;
1074
1075 if (obh_cnt > 1) {
1076 zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_op, &context);
1077 } else if ((SUCCESS == zend_stack_top(&OG(handlers), (void *) &active)) && (!((*active)->flags & PHP_OUTPUT_HANDLER_DISABLED))) {
1078 php_output_handler_op(*active, &context);
1079 } else {
1080 php_output_context_pass(&context);
1081 }
1082 } else {
1083 context.out.data = (char *) str;
1084 context.out.used = len;
1085 }
1086
1087 if (context.out.data && context.out.used) {
1088 php_output_header(TSRMLS_C);
1089
1090 if (!(OG(flags) & PHP_OUTPUT_DISABLED)) {
1091 #if PHP_OUTPUT_DEBUG
1092 fprintf(stderr, "::: sapi_write('%s', %zu)\n", context.out.data, context.out.used);
1093 #endif
1094 sapi_module.ub_write(context.out.data, context.out.used TSRMLS_CC);
1095
1096 if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) {
1097 sapi_flush(TSRMLS_C);
1098 }
1099
1100 OG(flags) |= PHP_OUTPUT_SENT;
1101 }
1102 }
1103 php_output_context_dtor(&context);
1104 }
1105 /* }}} */
1106
1107 /* {{{ static int php_output_stack_apply_op(void *h, void *c)
1108 * Operation callback for the stack apply function */
php_output_stack_apply_op(void * h,void * c)1109 static int php_output_stack_apply_op(void *h, void *c)
1110 {
1111 int was_disabled;
1112 php_output_handler_status_t status;
1113 php_output_handler *handler = *(php_output_handler **) h;
1114 php_output_context *context = (php_output_context *) c;
1115
1116 if ((was_disabled = (handler->flags & PHP_OUTPUT_HANDLER_DISABLED))) {
1117 status = PHP_OUTPUT_HANDLER_FAILURE;
1118 } else {
1119 status = php_output_handler_op(handler, context);
1120 }
1121
1122 /*
1123 * handler ate all => break
1124 * handler returned data or failed resp. is disabled => continue
1125 */
1126 switch (status) {
1127 case PHP_OUTPUT_HANDLER_NO_DATA:
1128 return 1;
1129
1130 case PHP_OUTPUT_HANDLER_SUCCESS:
1131 /* swap contexts buffers, unless this is the last handler in the stack */
1132 if (handler->level) {
1133 php_output_context_swap(context);
1134 }
1135 return 0;
1136
1137 case PHP_OUTPUT_HANDLER_FAILURE:
1138 default:
1139 if (was_disabled) {
1140 /* pass input along, if it's the last handler in the stack */
1141 if (!handler->level) {
1142 php_output_context_pass(context);
1143 }
1144 } else {
1145 /* swap buffers, unless this is the last handler */
1146 if (handler->level) {
1147 php_output_context_swap(context);
1148 }
1149 }
1150 return 0;
1151 }
1152 }
1153 /* }}} */
1154
1155 /* {{{ static int php_output_stack_apply_clean(void *h, void *c)
1156 * Clean callback for the stack apply function */
php_output_stack_apply_clean(void * h,void * c)1157 static int php_output_stack_apply_clean(void *h, void *c)
1158 {
1159 php_output_handler *handler = *(php_output_handler **) h;
1160 php_output_context *context = (php_output_context *) c;
1161
1162 handler->buffer.used = 0;
1163 php_output_handler_op(handler, context);
1164 php_output_context_reset(context);
1165 return 0;
1166 }
1167 /* }}} */
1168
1169 /* {{{ static int php_output_stack_apply_list(void *h, void *z)
1170 * List callback for the stack apply function */
php_output_stack_apply_list(void * h,void * z)1171 static int php_output_stack_apply_list(void *h, void *z)
1172 {
1173 php_output_handler *handler = *(php_output_handler **) h;
1174 zval *array = (zval *) z;
1175
1176 add_next_index_stringl(array, handler->name, handler->name_len, 1);
1177 return 0;
1178 }
1179 /* }}} */
1180
1181 /* {{{ static int php_output_stack_apply_status(void *h, void *z)
1182 * Status callback for the stack apply function */
php_output_stack_apply_status(void * h,void * z)1183 static int php_output_stack_apply_status(void *h, void *z)
1184 {
1185 php_output_handler *handler = *(php_output_handler **) h;
1186 zval *array = (zval *) z;
1187
1188 add_next_index_zval(array, php_output_handler_status(handler, NULL));
1189
1190 return 0;
1191 }
1192
1193 /* {{{ static zval *php_output_handler_status(php_output_handler *handler, zval *entry)
1194 * Returns an array with the status of the output handler */
php_output_handler_status(php_output_handler * handler,zval * entry)1195 static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry)
1196 {
1197 if (!entry) {
1198 MAKE_STD_ZVAL(entry);
1199 array_init(entry);
1200 }
1201
1202 add_assoc_stringl(entry, "name", handler->name, handler->name_len, 1);
1203 add_assoc_long(entry, "type", (long) (handler->flags & 0xf));
1204 add_assoc_long(entry, "flags", (long) handler->flags);
1205 add_assoc_long(entry, "level", (long) handler->level);
1206 add_assoc_long(entry, "chunk_size", (long) handler->size);
1207 add_assoc_long(entry, "buffer_size", (long) handler->buffer.size);
1208 add_assoc_long(entry, "buffer_used", (long) handler->buffer.used);
1209
1210 return entry;
1211 }
1212 /* }}} */
1213
1214 /* {{{ static int php_output_stack_pop(int flags TSRMLS_DC)
1215 * Pops an output handler off the stack */
php_output_stack_pop(int flags TSRMLS_DC)1216 static inline int php_output_stack_pop(int flags TSRMLS_DC)
1217 {
1218 php_output_context context;
1219 php_output_handler **current, *orphan = OG(active);
1220
1221 if (!orphan) {
1222 if (!(flags & PHP_OUTPUT_POP_SILENT)) {
1223 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer. No buffer to %s", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send");
1224 }
1225 return 0;
1226 } else if (!(flags & PHP_OUTPUT_POP_FORCE) && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) {
1227 if (!(flags & PHP_OUTPUT_POP_SILENT)) {
1228 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer of %s (%d)", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", orphan->name, orphan->level);
1229 }
1230 return 0;
1231 } else {
1232 php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL TSRMLS_CC);
1233
1234 /* don't run the output handler if it's disabled */
1235 if (!(orphan->flags & PHP_OUTPUT_HANDLER_DISABLED)) {
1236 /* didn't it start yet? */
1237 if (!(orphan->flags & PHP_OUTPUT_HANDLER_STARTED)) {
1238 context.op |= PHP_OUTPUT_HANDLER_START;
1239 }
1240 /* signal that we're cleaning up */
1241 if (flags & PHP_OUTPUT_POP_DISCARD) {
1242 context.op |= PHP_OUTPUT_HANDLER_CLEAN;
1243 }
1244 php_output_handler_op(orphan, &context);
1245 }
1246
1247 /* pop it off the stack */
1248 zend_stack_del_top(&OG(handlers));
1249 if (SUCCESS == zend_stack_top(&OG(handlers), (void *) ¤t)) {
1250 OG(active) = *current;
1251 } else {
1252 OG(active) = NULL;
1253 }
1254
1255 /* pass output along */
1256 if (context.out.data && context.out.used && !(flags & PHP_OUTPUT_POP_DISCARD)) {
1257 php_output_write(context.out.data, context.out.used TSRMLS_CC);
1258 }
1259
1260 /* destroy the handler (after write!) */
1261 php_output_handler_free(&orphan TSRMLS_CC);
1262 php_output_context_dtor(&context);
1263
1264 return 1;
1265 }
1266 }
1267 /* }}} */
1268
1269 /* {{{ static SUCCESS|FAILURE php_output_handler_compat_func(void *ctx, php_output_context *)
1270 * php_output_handler_context_func_t for php_output_handler_func_t output handlers */
php_output_handler_compat_func(void ** handler_context,php_output_context * output_context)1271 static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context)
1272 {
1273 php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context;
1274 PHP_OUTPUT_TSRMLS(output_context);
1275
1276 if (func) {
1277 char *out_str = NULL;
1278 uint out_len = 0;
1279
1280 func(output_context->in.data, output_context->in.used, &out_str, &out_len, output_context->op TSRMLS_CC);
1281
1282 if (out_str) {
1283 output_context->out.data = out_str;
1284 output_context->out.used = out_len;
1285 output_context->out.free = 1;
1286 } else {
1287 php_output_context_pass(output_context);
1288 }
1289
1290 return SUCCESS;
1291 }
1292 return FAILURE;
1293 }
1294 /* }}} */
1295
1296 /* {{{ static SUCCESS|FAILURE php_output_handler_default_func(void *ctx, php_output_context *)
1297 * Default output handler */
php_output_handler_default_func(void ** handler_context,php_output_context * output_context)1298 static int php_output_handler_default_func(void **handler_context, php_output_context *output_context)
1299 {
1300 php_output_context_pass(output_context);
1301 return SUCCESS;
1302 }
1303 /* }}} */
1304
1305 /* {{{ static SUCCESS|FAILURE php_output_handler_devnull_func(void *ctx, php_output_context *)
1306 * Null output handler */
php_output_handler_devnull_func(void ** handler_context,php_output_context * output_context)1307 static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context)
1308 {
1309 return SUCCESS;
1310 }
1311 /* }}} */
1312
1313 /*
1314 * USERLAND (nearly 1:1 of old output.c)
1315 */
1316
1317 /* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, int flags]]])
1318 Turn on Output Buffering (specifying an optional output handler). */
PHP_FUNCTION(ob_start)1319 PHP_FUNCTION(ob_start)
1320 {
1321 zval *output_handler = NULL;
1322 long chunk_size = 0;
1323 long flags = PHP_OUTPUT_HANDLER_STDFLAGS;
1324
1325 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/ll", &output_handler, &chunk_size, &flags) == FAILURE) {
1326 return;
1327 }
1328
1329 if (chunk_size < 0) {
1330 chunk_size = 0;
1331 }
1332
1333 if (php_output_start_user(output_handler, chunk_size, flags TSRMLS_CC) == FAILURE) {
1334 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to create buffer");
1335 RETURN_FALSE;
1336 }
1337 RETURN_TRUE;
1338 }
1339 /* }}} */
1340
1341 /* {{{ proto bool ob_flush(void)
1342 Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */
PHP_FUNCTION(ob_flush)1343 PHP_FUNCTION(ob_flush)
1344 {
1345 if (zend_parse_parameters_none() == FAILURE) {
1346 return;
1347 }
1348
1349 if (!OG(active)) {
1350 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush");
1351 RETURN_FALSE;
1352 }
1353
1354 if (SUCCESS != php_output_flush(TSRMLS_C)) {
1355 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer of %s (%d)", OG(active)->name, OG(active)->level);
1356 RETURN_FALSE;
1357 }
1358 RETURN_TRUE;
1359 }
1360 /* }}} */
1361
1362 /* {{{ proto bool ob_clean(void)
1363 Clean (delete) the current output buffer */
PHP_FUNCTION(ob_clean)1364 PHP_FUNCTION(ob_clean)
1365 {
1366 if (zend_parse_parameters_none() == FAILURE) {
1367 return;
1368 }
1369
1370 if (!OG(active)) {
1371 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
1372 RETURN_FALSE;
1373 }
1374
1375 if (SUCCESS != php_output_clean(TSRMLS_C)) {
1376 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
1377 RETURN_FALSE;
1378 }
1379 RETURN_TRUE;
1380 }
1381 /* }}} */
1382
1383 /* {{{ proto bool ob_end_flush(void)
1384 Flush (send) the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_end_flush)1385 PHP_FUNCTION(ob_end_flush)
1386 {
1387 if (zend_parse_parameters_none() == FAILURE) {
1388 return;
1389 }
1390
1391 if (!OG(active)) {
1392 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
1393 RETURN_FALSE;
1394 }
1395
1396 RETURN_BOOL(SUCCESS == php_output_end(TSRMLS_C));
1397 }
1398 /* }}} */
1399
1400 /* {{{ proto bool ob_end_clean(void)
1401 Clean the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_end_clean)1402 PHP_FUNCTION(ob_end_clean)
1403 {
1404 if (zend_parse_parameters_none() == FAILURE) {
1405 return;
1406 }
1407
1408 if (!OG(active)) {
1409 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
1410 RETURN_FALSE;
1411 }
1412
1413 RETURN_BOOL(SUCCESS == php_output_discard(TSRMLS_C));
1414 }
1415 /* }}} */
1416
1417 /* {{{ proto bool ob_get_flush(void)
1418 Get current buffer contents, flush (send) the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_get_flush)1419 PHP_FUNCTION(ob_get_flush)
1420 {
1421 if (zend_parse_parameters_none() == FAILURE) {
1422 return;
1423 }
1424
1425 if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
1426 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
1427 RETURN_FALSE;
1428 }
1429
1430 if (SUCCESS != php_output_end(TSRMLS_C)) {
1431 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
1432 }
1433 }
1434 /* }}} */
1435
1436 /* {{{ proto bool ob_get_clean(void)
1437 Get current buffer contents and delete current output buffer */
PHP_FUNCTION(ob_get_clean)1438 PHP_FUNCTION(ob_get_clean)
1439 {
1440 if (zend_parse_parameters_none() == FAILURE) {
1441 return;
1442 }
1443
1444 if(!OG(active)) {
1445 RETURN_FALSE;
1446 }
1447
1448 if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
1449 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
1450 RETURN_FALSE;
1451 }
1452
1453 if (SUCCESS != php_output_discard(TSRMLS_C)) {
1454 php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
1455 }
1456 }
1457 /* }}} */
1458
1459 /* {{{ proto string ob_get_contents(void)
1460 Return the contents of the output buffer */
PHP_FUNCTION(ob_get_contents)1461 PHP_FUNCTION(ob_get_contents)
1462 {
1463 if (zend_parse_parameters_none() == FAILURE) {
1464 return;
1465 }
1466
1467 if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
1468 RETURN_FALSE;
1469 }
1470 }
1471 /* }}} */
1472
1473 /* {{{ proto int ob_get_level(void)
1474 Return the nesting level of the output buffer */
PHP_FUNCTION(ob_get_level)1475 PHP_FUNCTION(ob_get_level)
1476 {
1477 if (zend_parse_parameters_none() == FAILURE) {
1478 return;
1479 }
1480
1481 RETURN_LONG(php_output_get_level(TSRMLS_C));
1482 }
1483 /* }}} */
1484
1485 /* {{{ proto int ob_get_length(void)
1486 Return the length of the output buffer */
PHP_FUNCTION(ob_get_length)1487 PHP_FUNCTION(ob_get_length)
1488 {
1489 if (zend_parse_parameters_none() == FAILURE) {
1490 return;
1491 }
1492
1493 if (php_output_get_length(return_value TSRMLS_CC) == FAILURE) {
1494 RETURN_FALSE;
1495 }
1496 }
1497 /* }}} */
1498
1499 /* {{{ proto false|array ob_list_handlers()
1500 List all output_buffers in an array */
PHP_FUNCTION(ob_list_handlers)1501 PHP_FUNCTION(ob_list_handlers)
1502 {
1503 if (zend_parse_parameters_none() == FAILURE) {
1504 return;
1505 }
1506
1507 array_init(return_value);
1508
1509 if (!OG(active)) {
1510 return;
1511 }
1512
1513 zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value);
1514 }
1515 /* }}} */
1516
1517 /* {{{ proto false|array ob_get_status([bool full_status])
1518 Return the status of the active or all output buffers */
PHP_FUNCTION(ob_get_status)1519 PHP_FUNCTION(ob_get_status)
1520 {
1521 zend_bool full_status = 0;
1522
1523 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status) == FAILURE) {
1524 return;
1525 }
1526
1527 array_init(return_value);
1528
1529 if (!OG(active)) {
1530 return;
1531 }
1532
1533 if (full_status) {
1534 zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_status, return_value);
1535 } else {
1536 php_output_handler_status(OG(active), return_value);
1537 }
1538 }
1539 /* }}} */
1540
1541 /* {{{ proto void ob_implicit_flush([int flag])
1542 Turn implicit flush on/off and is equivalent to calling flush() after every output call */
PHP_FUNCTION(ob_implicit_flush)1543 PHP_FUNCTION(ob_implicit_flush)
1544 {
1545 long flag = 1;
1546
1547 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag) == FAILURE) {
1548 return;
1549 }
1550
1551 php_output_set_implicit_flush(flag TSRMLS_CC);
1552 }
1553 /* }}} */
1554
1555 /* {{{ proto bool output_reset_rewrite_vars(void)
1556 Reset(clear) URL rewriter values */
PHP_FUNCTION(output_reset_rewrite_vars)1557 PHP_FUNCTION(output_reset_rewrite_vars)
1558 {
1559 if (php_url_scanner_reset_vars(TSRMLS_C) == SUCCESS) {
1560 RETURN_TRUE;
1561 } else {
1562 RETURN_FALSE;
1563 }
1564 }
1565 /* }}} */
1566
1567 /* {{{ proto bool output_add_rewrite_var(string name, string value)
1568 Add URL rewriter values */
PHP_FUNCTION(output_add_rewrite_var)1569 PHP_FUNCTION(output_add_rewrite_var)
1570 {
1571 char *name, *value;
1572 int name_len, value_len;
1573
1574 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) {
1575 return;
1576 }
1577
1578 if (php_url_scanner_add_var(name, name_len, value, value_len, 1 TSRMLS_CC) == SUCCESS) {
1579 RETURN_TRUE;
1580 } else {
1581 RETURN_FALSE;
1582 }
1583 }
1584 /* }}} */
1585
1586 /*
1587 * Local variables:
1588 * tab-width: 4
1589 * c-basic-offset: 4
1590 * End:
1591 * vim600: sw=4 ts=4 fdm=marker
1592 * vim<600: sw=4 ts=4
1593 */
1594