xref: /PHP-5.4/main/output.c (revision d08b4dbf)
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 *) &current)) {
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