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