xref: /PHP-5.3/main/output.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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    +----------------------------------------------------------------------+
19 */
20 
21 /* $Id$ */
22 
23 #include "php.h"
24 #include "ext/standard/head.h"
25 #include "ext/standard/basic_functions.h"
26 #include "ext/standard/url_scanner_ex.h"
27 #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
28 #include "ext/zlib/php_zlib.h"
29 #endif
30 #include "SAPI.h"
31 
32 #define OB_DEFAULT_HANDLER_NAME "default output handler"
33 
34 /* output functions */
35 static int php_b_body_write(const char *str, uint str_length TSRMLS_DC);
36 
37 static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC);
38 static void php_ob_append(const char *text, uint text_length TSRMLS_DC);
39 #if 0
40 static void php_ob_prepend(const char *text, uint text_length);
41 #endif
42 
43 #ifdef ZTS
44 int output_globals_id;
45 #else
46 php_output_globals output_globals;
47 #endif
48 
49 /* {{{ php_default_output_func */
php_default_output_func(const char * str,uint str_len TSRMLS_DC)50 PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC)
51 {
52 	fwrite(str, 1, str_len, stderr);
53 /* See http://support.microsoft.com/kb/190351 */
54 #ifdef PHP_WIN32
55 	fflush(stderr);
56 #endif
57 	return str_len;
58 }
59 /* }}} */
60 
61 /* {{{ php_output_init_globals */
php_output_init_globals(php_output_globals * output_globals_p TSRMLS_DC)62 static void php_output_init_globals(php_output_globals *output_globals_p TSRMLS_DC)
63 {
64 	OG(php_body_write) = php_default_output_func;
65 	OG(php_header_write) = php_default_output_func;
66 	OG(implicit_flush) = 0;
67 	OG(output_start_filename) = NULL;
68 	OG(output_start_lineno) = 0;
69 }
70 /* }}} */
71 
72 /* {{{ php_output_startup
73  * Start output layer */
php_output_startup(void)74 PHPAPI void php_output_startup(void)
75 {
76 #ifdef ZTS
77 	ts_allocate_id(&output_globals_id, sizeof(php_output_globals), (ts_allocate_ctor) php_output_init_globals, NULL);
78 #else
79 	php_output_init_globals(&output_globals TSRMLS_CC);
80 #endif
81 }
82 /* }}} */
83 
84 /* {{{ php_output_activate
85  * Initilize output global for activation */
php_output_activate(TSRMLS_D)86 PHPAPI void php_output_activate(TSRMLS_D)
87 {
88 	OG(php_body_write) = php_ub_body_write;
89 	OG(php_header_write) = sapi_module.ub_write;
90 	OG(ob_nesting_level) = 0;
91 	OG(ob_lock) = 0;
92 	OG(disable_output) = 0;
93 	OG(output_start_filename) = NULL;
94 	OG(output_start_lineno) = 0;
95 }
96 /* }}} */
97 
98 /* {{{ php_output_register_constants */
php_output_register_constants(TSRMLS_D)99 void php_output_register_constants(TSRMLS_D)
100 {
101 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT);
102 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_CONT, CONST_CS | CONST_PERSISTENT);
103 	REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_END, CONST_CS | CONST_PERSISTENT);
104 }
105 /* }}} */
106 
107 /* {{{ php_output_set_status
108  * Toggle output status.  Do NOT use in application code, only in SAPIs where appropriate. */
php_output_set_status(zend_bool status TSRMLS_DC)109 PHPAPI void php_output_set_status(zend_bool status TSRMLS_DC)
110 {
111 	OG(disable_output) = !status;
112 }
113 /* }}} */
114 
115 /* {{{ php_body_write
116  * Write body part */
php_body_write(const char * str,uint str_length TSRMLS_DC)117 PHPAPI int php_body_write(const char *str, uint str_length TSRMLS_DC)
118 {
119 	return OG(php_body_write)(str, str_length TSRMLS_CC);
120 }
121 /* }}} */
122 
123 /* {{{ php_header_write
124  * Write HTTP header */
php_header_write(const char * str,uint str_length TSRMLS_DC)125 PHPAPI int php_header_write(const char *str, uint str_length TSRMLS_DC)
126 {
127 	if (OG(disable_output)) {
128 		return 0;
129 	} else {
130 		return OG(php_header_write)(str, str_length TSRMLS_CC);
131 	}
132 }
133 /* }}} */
134 
135 /* {{{ php_start_ob_buffer
136  * Start output buffering */
php_start_ob_buffer(zval * output_handler,uint chunk_size,zend_bool erase TSRMLS_DC)137 PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
138 {
139 	uint initial_size, block_size;
140 
141 	if (OG(ob_lock)) {
142 		if (SG(headers_sent) && !SG(request_info).headers_only) {
143 			OG(php_body_write) = php_ub_body_write_no_header;
144 		} else {
145 			OG(php_body_write) = php_ub_body_write;
146 		}
147 		OG(ob_nesting_level) = 0;
148 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
149 		return FAILURE;
150 	}
151 	if (chunk_size > 0) {
152 		if (chunk_size==1) {
153 			chunk_size = 4096;
154 		}
155 		initial_size = (chunk_size*3/2);
156 		block_size = chunk_size/2;
157 	} else {
158 		initial_size = 40*1024;
159 		block_size = 10*1024;
160 	}
161 	return php_ob_init(initial_size, block_size, output_handler, chunk_size, erase TSRMLS_CC);
162 }
163 /* }}} */
164 
165 /* {{{ php_start_ob_buffer_named
166  * Start output buffering */
php_start_ob_buffer_named(const char * output_handler_name,uint chunk_size,zend_bool erase TSRMLS_DC)167 PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC)
168 {
169 	zval *output_handler;
170 	int result;
171 
172 	ALLOC_INIT_ZVAL(output_handler);
173 	Z_STRLEN_P(output_handler) = strlen(output_handler_name);	/* this can be optimized */
174 	Z_STRVAL_P(output_handler) = estrndup(output_handler_name, Z_STRLEN_P(output_handler));
175 	Z_TYPE_P(output_handler) = IS_STRING;
176 	result = php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC);
177 	zval_dtor(output_handler);
178 	FREE_ZVAL(output_handler);
179 	return result;
180 }
181 /* }}} */
182 
183 /* {{{ php_end_ob_buffer
184  * End output buffering (one level) */
php_end_ob_buffer(zend_bool send_buffer,zend_bool just_flush TSRMLS_DC)185 PHPAPI void php_end_ob_buffer(zend_bool send_buffer, zend_bool just_flush TSRMLS_DC)
186 {
187 	char *final_buffer=NULL;
188 	unsigned int final_buffer_length=0;
189 	zval *alternate_buffer=NULL;
190 	char *to_be_destroyed_buffer, *to_be_destroyed_handler_name;
191 	char *to_be_destroyed_handled_output[2] = { 0, 0 };
192 	int status;
193 	php_ob_buffer *prev_ob_buffer_p=NULL;
194 	php_ob_buffer orig_ob_buffer;
195 
196 	if (OG(ob_nesting_level)==0) {
197 		return;
198 	}
199 	status = 0;
200 	if (!OG(active_ob_buffer).status & PHP_OUTPUT_HANDLER_START) {
201 		/* our first call */
202 		status |= PHP_OUTPUT_HANDLER_START;
203 	}
204 	if (just_flush) {
205 		status |= PHP_OUTPUT_HANDLER_CONT;
206 	} else {
207 		status |= PHP_OUTPUT_HANDLER_END;
208 	}
209 
210 #if 0
211  {
212 	 FILE *fp;
213 	 fp = fopen("/tmp/ob_log", "a");
214 	 fprintf(fp, "NestLevel: %d  ObStatus: %d  HandlerName: %s\n", OG(ob_nesting_level), status, OG(active_ob_buffer).handler_name);
215 	 fclose(fp);
216  }
217 #endif
218 
219 	if (OG(active_ob_buffer).internal_output_handler) {
220 		final_buffer = OG(active_ob_buffer).internal_output_handler_buffer;
221 		final_buffer_length = OG(active_ob_buffer).internal_output_handler_buffer_size;
222 		OG(active_ob_buffer).internal_output_handler(OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, &final_buffer, &final_buffer_length, status TSRMLS_CC);
223 	} else if (OG(active_ob_buffer).output_handler) {
224 		zval **params[2];
225 		zval *orig_buffer;
226 		zval *z_status;
227 
228 		if(OG(ob_lock)) {
229 			if (SG(headers_sent) && !SG(request_info).headers_only) {
230 				OG(php_body_write) = php_ub_body_write_no_header;
231 			} else {
232 				OG(php_body_write) = php_ub_body_write;
233 			}
234 			OG(ob_nesting_level) = 0;
235 			php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
236 			return;
237 		}
238 
239 		ALLOC_INIT_ZVAL(orig_buffer);
240 		ZVAL_STRINGL(orig_buffer, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1);
241 
242 		ALLOC_INIT_ZVAL(z_status);
243 		ZVAL_LONG(z_status, status);
244 
245 		params[0] = &orig_buffer;
246 		params[1] = &z_status;
247 		OG(ob_lock) = 1;
248 
249 		if (call_user_function_ex(CG(function_table), NULL, OG(active_ob_buffer).output_handler, &alternate_buffer, 2, params, 1, NULL TSRMLS_CC)==SUCCESS) {
250 			if (alternate_buffer && !(Z_TYPE_P(alternate_buffer)==IS_BOOL && Z_BVAL_P(alternate_buffer)==0)) {
251 				convert_to_string_ex(&alternate_buffer);
252 				final_buffer = Z_STRVAL_P(alternate_buffer);
253 				final_buffer_length = Z_STRLEN_P(alternate_buffer);
254 			}
255 		}
256 		OG(ob_lock) = 0;
257 		if (!just_flush) {
258 			zval_ptr_dtor(&OG(active_ob_buffer).output_handler);
259 		}
260 		zval_ptr_dtor(&orig_buffer);
261 		zval_ptr_dtor(&z_status);
262 	}
263 
264 	if (!final_buffer) {
265 		final_buffer = OG(active_ob_buffer).buffer;
266 		final_buffer_length = OG(active_ob_buffer).text_length;
267 	}
268 
269 	if (OG(ob_nesting_level)==1) { /* end buffering */
270 		if (SG(headers_sent) && !SG(request_info).headers_only) {
271 			OG(php_body_write) = php_ub_body_write_no_header;
272 		} else {
273 			OG(php_body_write) = php_ub_body_write;
274 		}
275 	}
276 
277 	to_be_destroyed_buffer = OG(active_ob_buffer).buffer;
278 	to_be_destroyed_handler_name = OG(active_ob_buffer).handler_name;
279 	if (OG(active_ob_buffer).internal_output_handler
280 		&& (final_buffer != OG(active_ob_buffer).internal_output_handler_buffer)
281 		&& (final_buffer != OG(active_ob_buffer).buffer)) {
282 		to_be_destroyed_handled_output[0] = final_buffer;
283 	}
284 
285 	if (!just_flush) {
286 		if (OG(active_ob_buffer).internal_output_handler) {
287 			to_be_destroyed_handled_output[1] = OG(active_ob_buffer).internal_output_handler_buffer;
288 		}
289 	}
290 	if (OG(ob_nesting_level)>1) { /* restore previous buffer */
291 		zend_stack_top(&OG(ob_buffers), (void **) &prev_ob_buffer_p);
292 		orig_ob_buffer = OG(active_ob_buffer);
293 		OG(active_ob_buffer) = *prev_ob_buffer_p;
294 		zend_stack_del_top(&OG(ob_buffers));
295 		if (!just_flush && OG(ob_nesting_level)==2) { /* destroy the stack */
296 			zend_stack_destroy(&OG(ob_buffers));
297 		}
298 	}
299 	OG(ob_nesting_level)--;
300 
301 	if (send_buffer) {
302 		if (just_flush) { /* if flush is called prior to proper end, ensure presence of NUL */
303 			final_buffer[final_buffer_length] = '\0';
304 		}
305 		OG(php_body_write)(final_buffer, final_buffer_length TSRMLS_CC);
306 	}
307 
308 	if (just_flush) { /* we restored the previous ob, return to the current */
309 		if (prev_ob_buffer_p) {
310 			zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer));
311 			OG(active_ob_buffer) = orig_ob_buffer;
312 		}
313 		OG(ob_nesting_level)++;
314 	}
315 
316 	if (alternate_buffer) {
317 		zval_ptr_dtor(&alternate_buffer);
318 	}
319 
320 	if (status & PHP_OUTPUT_HANDLER_END) {
321 		efree(to_be_destroyed_handler_name);
322 	}
323 	if (!just_flush) {
324 		efree(to_be_destroyed_buffer);
325 	} else {
326 		OG(active_ob_buffer).text_length = 0;
327 		OG(active_ob_buffer).status |= PHP_OUTPUT_HANDLER_START;
328 		OG(php_body_write) = php_b_body_write;
329 	}
330 	if (to_be_destroyed_handled_output[0]) {
331 		efree(to_be_destroyed_handled_output[0]);
332 	}
333 	if (to_be_destroyed_handled_output[1]) {
334 		efree(to_be_destroyed_handled_output[1]);
335 	}
336 }
337 /* }}} */
338 
339 /* {{{ php_end_ob_buffers
340  * End output buffering (all buffers) */
php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC)341 PHPAPI void php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC)
342 {
343 	while (OG(ob_nesting_level)!=0) {
344 		php_end_ob_buffer(send_buffer, 0 TSRMLS_CC);
345 	}
346 }
347 /* }}} */
348 
349 /* {{{ php_start_implicit_flush
350  */
php_start_implicit_flush(TSRMLS_D)351 PHPAPI void php_start_implicit_flush(TSRMLS_D)
352 {
353 	OG(implicit_flush) = 1;
354 }
355 /* }}} */
356 
357 /* {{{ php_end_implicit_flush
358  */
php_end_implicit_flush(TSRMLS_D)359 PHPAPI void php_end_implicit_flush(TSRMLS_D)
360 {
361 	OG(implicit_flush) = 0;
362 }
363 /* }}} */
364 
365 /* {{{ char *php_get_output_start_filename(TSRMLS_D)
366  *  Return filename start output something */
php_get_output_start_filename(TSRMLS_D)367 PHPAPI char *php_get_output_start_filename(TSRMLS_D)
368 {
369 	return OG(output_start_filename);
370 }
371 /* }}} */
372 
373 /* {{{ char *php_get_output_start_lineno(TSRMLS_D)
374  * Return line number start output something */
php_get_output_start_lineno(TSRMLS_D)375 PHPAPI int php_get_output_start_lineno(TSRMLS_D)
376 {
377 	return OG(output_start_lineno);
378 }
379 /* }}} */
380 
381 /* {{{ php_ob_set_internal_handler
382  */
php_ob_set_internal_handler(php_output_handler_func_t internal_output_handler,uint buffer_size,char * handler_name,zend_bool erase TSRMLS_DC)383 PHPAPI void php_ob_set_internal_handler(php_output_handler_func_t internal_output_handler, uint buffer_size, char *handler_name, zend_bool erase TSRMLS_DC)
384 {
385 	if (OG(ob_nesting_level) == 0 || OG(active_ob_buffer).internal_output_handler || strcmp(OG(active_ob_buffer).handler_name, OB_DEFAULT_HANDLER_NAME)) {
386 		php_start_ob_buffer(NULL, buffer_size, erase TSRMLS_CC);
387 	}
388 
389 	OG(active_ob_buffer).internal_output_handler = internal_output_handler;
390 	OG(active_ob_buffer).internal_output_handler_buffer = (char *) emalloc(buffer_size);
391 	OG(active_ob_buffer).internal_output_handler_buffer_size = buffer_size;
392 	if (OG(active_ob_buffer).handler_name) {
393 		efree(OG(active_ob_buffer).handler_name);
394 	}
395 	OG(active_ob_buffer).handler_name = estrdup(handler_name);
396 	OG(active_ob_buffer).erase = erase;
397 }
398 /* }}} */
399 
400 /*
401  * Output buffering - implementation
402  */
403 
404 /* {{{ php_ob_allocate
405  */
php_ob_allocate(uint text_length TSRMLS_DC)406 static inline void php_ob_allocate(uint text_length TSRMLS_DC)
407 {
408 	uint new_len = OG(active_ob_buffer).text_length + text_length;
409 
410 	if (OG(active_ob_buffer).size < new_len) {
411 		uint buf_size = OG(active_ob_buffer).size;
412 		while (buf_size <= new_len) {
413 			buf_size += OG(active_ob_buffer).block_size;
414 		}
415 
416 		OG(active_ob_buffer).buffer = (char *) erealloc(OG(active_ob_buffer).buffer, buf_size+1);
417 		OG(active_ob_buffer).size = buf_size;
418 	}
419 	OG(active_ob_buffer).text_length = new_len;
420 }
421 /* }}} */
422 
423 /* {{{ php_ob_init_conflict
424  * Returns 1 if handler_set is already used and generates error message */
php_ob_init_conflict(char * handler_new,char * handler_set TSRMLS_DC)425 PHPAPI int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC)
426 {
427 	if (php_ob_handler_used(handler_set TSRMLS_CC)) {
428 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set);
429 		return 1;
430 	}
431 	return 0;
432 }
433 /* }}} */
434 
435 /* {{{ php_ob_init_named
436  */
php_ob_init_named(uint initial_size,uint block_size,char * handler_name,zval * output_handler,uint chunk_size,zend_bool erase TSRMLS_DC)437 static int php_ob_init_named(uint initial_size, uint block_size, char *handler_name, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
438 {
439 	php_ob_buffer tmp_buf;
440 
441 	if (output_handler && !zend_is_callable(output_handler, 0, NULL TSRMLS_CC)) {
442 		return FAILURE;
443 	}
444 
445 	tmp_buf.block_size = block_size;
446 	tmp_buf.size = initial_size;
447 	tmp_buf.buffer = (char *) emalloc(initial_size+1);
448 	tmp_buf.text_length = 0;
449 	tmp_buf.output_handler = output_handler;
450 	tmp_buf.chunk_size = chunk_size;
451 	tmp_buf.status = 0;
452 	tmp_buf.internal_output_handler = NULL;
453 	tmp_buf.internal_output_handler_buffer = NULL;
454 	tmp_buf.internal_output_handler_buffer_size = 0;
455 	tmp_buf.handler_name = estrdup(handler_name&&handler_name[0]?handler_name:OB_DEFAULT_HANDLER_NAME);
456 	tmp_buf.erase = erase;
457 
458 	if (OG(ob_nesting_level)>0) {
459 #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
460 		if (!strncmp(handler_name, "ob_gzhandler", sizeof("ob_gzhandler")) && php_ob_gzhandler_check(TSRMLS_C)) {
461 			return FAILURE;
462 		}
463 #endif
464 		if (OG(ob_nesting_level)==1) { /* initialize stack */
465 			zend_stack_init(&OG(ob_buffers));
466 		}
467 		zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer));
468 	}
469 	OG(ob_nesting_level)++;
470 	OG(active_ob_buffer) = tmp_buf;
471 	OG(php_body_write) = php_b_body_write;
472 	return SUCCESS;
473 }
474 /* }}} */
475 
476 /* {{{ php_ob_handler_from_string
477  * Create zval output handler from string */
php_ob_handler_from_string(const char * handler_name,int len TSRMLS_DC)478 static zval* php_ob_handler_from_string(const char *handler_name, int len TSRMLS_DC)
479 {
480 	zval *output_handler;
481 
482 	ALLOC_INIT_ZVAL(output_handler);
483 	Z_STRLEN_P(output_handler) = len;
484 	Z_STRVAL_P(output_handler) = estrndup(handler_name, len);
485 	Z_TYPE_P(output_handler) = IS_STRING;
486 	return output_handler;
487 }
488 /* }}} */
489 
490 /* {{{ php_ob_init
491  */
php_ob_init(uint initial_size,uint block_size,zval * output_handler,uint chunk_size,zend_bool erase TSRMLS_DC)492 static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC)
493 {
494 	int result = FAILURE, handler_len, len;
495 	char *handler_name, *next_handler_name;
496 	HashPosition pos;
497 	zval **tmp;
498 	zval *handler_zval;
499 
500 	if (output_handler && output_handler->type == IS_STRING) {
501 		handler_name = Z_STRVAL_P(output_handler);
502 		handler_len  = Z_STRLEN_P(output_handler);
503 
504 		result = SUCCESS;
505 		if (handler_len && handler_name[0] != '\0') {
506 			while ((next_handler_name=strchr(handler_name, ',')) != NULL) {
507 				len = next_handler_name-handler_name;
508 				next_handler_name = estrndup(handler_name, len);
509 				handler_zval = php_ob_handler_from_string(next_handler_name, len TSRMLS_CC);
510 				result = php_ob_init_named(initial_size, block_size, next_handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
511 				if (result != SUCCESS) {
512 					zval_dtor(handler_zval);
513 					FREE_ZVAL(handler_zval);
514 				}
515 				handler_name += len+1;
516 				handler_len -= len+1;
517 				efree(next_handler_name);
518 			}
519 		}
520 		if (result == SUCCESS) {
521 			handler_zval = php_ob_handler_from_string(handler_name, handler_len TSRMLS_CC);
522 			result = php_ob_init_named(initial_size, block_size, handler_name, handler_zval, chunk_size, erase TSRMLS_CC);
523 			if (result != SUCCESS) {
524 				zval_dtor(handler_zval);
525 				FREE_ZVAL(handler_zval);
526 			}
527 		}
528 	} else if (output_handler && output_handler->type == IS_ARRAY) {
529 		/* do we have array(object,method) */
530 		if (zend_is_callable(output_handler, 0, &handler_name TSRMLS_CC)) {
531 			SEPARATE_ZVAL(&output_handler);
532 			Z_ADDREF_P(output_handler);
533 			result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC);
534 			efree(handler_name);
535 		} else {
536 			efree(handler_name);
537 			/* init all array elements recursively */
538 			zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(output_handler), &pos);
539 			while (zend_hash_get_current_data_ex(Z_ARRVAL_P(output_handler), (void **)&tmp, &pos) == SUCCESS) {
540 				result = php_ob_init(initial_size, block_size, *tmp, chunk_size, erase TSRMLS_CC);
541 				if (result == FAILURE) {
542 					break;
543 				}
544 				zend_hash_move_forward_ex(Z_ARRVAL_P(output_handler), &pos);
545 			}
546 		}
547 	} else if (output_handler && output_handler->type == IS_OBJECT) {
548 		/* do we have callable object */
549 		if (zend_is_callable(output_handler, 0, &handler_name TSRMLS_CC)) {
550 			SEPARATE_ZVAL(&output_handler);
551 			Z_ADDREF_P(output_handler);
552 			result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC);
553 			efree(handler_name);
554 		} else {
555 			efree(handler_name);
556 			php_error_docref(NULL TSRMLS_CC, E_ERROR, "No method name given: use ob_start(array($object,'method')) to specify instance $object and the name of a method of class %s to use as output handler", Z_OBJCE_P(output_handler)->name);
557 			result = FAILURE;
558 		}
559 	} else {
560 		result = php_ob_init_named(initial_size, block_size, OB_DEFAULT_HANDLER_NAME, NULL, chunk_size, erase TSRMLS_CC);
561 	}
562 	return result;
563 }
564 /* }}} */
565 
566 /* {{{ php_ob_list_each
567  */
php_ob_list_each(php_ob_buffer * ob_buffer,zval * ob_handler_array)568 static int php_ob_list_each(php_ob_buffer *ob_buffer, zval *ob_handler_array)
569 {
570 	add_next_index_string(ob_handler_array, ob_buffer->handler_name, 1);
571 	return 0;
572 }
573 /* }}} */
574 
575 /* {{{ php_ob_used_each
576    Sets handler_name to NULL is found */
php_ob_handler_used_each(php_ob_buffer * ob_buffer,char ** handler_name)577 static int php_ob_handler_used_each(php_ob_buffer *ob_buffer, char **handler_name)
578 {
579 	if (!strcmp(ob_buffer->handler_name, *handler_name)) {
580 		*handler_name = NULL;
581 		return 1;
582 	}
583 	return 0;
584 }
585 /* }}} */
586 
587 /* {{{ php_ob_used
588    returns 1 if given handler_name is used as output_handler */
php_ob_handler_used(char * handler_name TSRMLS_DC)589 PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC)
590 {
591 	char *tmp = handler_name;
592 
593 	if (OG(ob_nesting_level)) {
594 		if (!strcmp(OG(active_ob_buffer).handler_name, handler_name)) {
595 			return 1;
596 		}
597 		if (OG(ob_nesting_level)>1) {
598 			zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_handler_used_each, &tmp);
599 		}
600 	}
601 	return tmp ? 0 : 1;
602 }
603 /* }}} */
604 
605 /* {{{ php_ob_append
606  */
php_ob_append(const char * text,uint text_length TSRMLS_DC)607 static inline void php_ob_append(const char *text, uint text_length TSRMLS_DC)
608 {
609 	char *target;
610 	uint original_ob_text_length;
611 
612 	original_ob_text_length=OG(active_ob_buffer).text_length;
613 
614 	php_ob_allocate(text_length TSRMLS_CC);
615 	target = OG(active_ob_buffer).buffer+original_ob_text_length;
616 	memcpy(target, text, text_length);
617 	target[text_length]=0;
618 
619 	/* If implicit_flush is On or chunked buffering, send contents to next buffer and return. */
620 	if (OG(active_ob_buffer).chunk_size
621 		&& OG(active_ob_buffer).text_length >= OG(active_ob_buffer).chunk_size) {
622 
623 		php_end_ob_buffer(1, 1 TSRMLS_CC);
624 		return;
625 	}
626 }
627 /* }}} */
628 
629 #if 0
630 static inline void php_ob_prepend(const char *text, uint text_length)
631 {
632 	char *p, *start;
633 	TSRMLS_FETCH();
634 
635 	php_ob_allocate(text_length TSRMLS_CC);
636 
637 	/* php_ob_allocate() may change OG(ob_buffer), so we can't initialize p&start earlier */
638 	p = OG(ob_buffer)+OG(ob_text_length);
639 	start = OG(ob_buffer);
640 
641 	while (--p>=start) {
642 		p[text_length] = *p;
643 	}
644 	memcpy(OG(ob_buffer), text, text_length);
645 	OG(ob_buffer)[OG(active_ob_buffer).text_length]=0;
646 }
647 #endif
648 
649 /* {{{ php_ob_get_buffer
650  * Return the current output buffer */
php_ob_get_buffer(zval * p TSRMLS_DC)651 PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC)
652 {
653 	if (OG(ob_nesting_level)==0) {
654 		return FAILURE;
655 	}
656 	ZVAL_STRINGL(p, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1);
657 	return SUCCESS;
658 }
659 /* }}} */
660 
661 /* {{{ php_ob_get_length
662  * Return the size of the current output buffer */
php_ob_get_length(zval * p TSRMLS_DC)663 PHPAPI int php_ob_get_length(zval *p TSRMLS_DC)
664 {
665 	if (OG(ob_nesting_level) == 0) {
666 		return FAILURE;
667 	}
668 	ZVAL_LONG(p, OG(active_ob_buffer).text_length);
669 	return SUCCESS;
670 }
671 /* }}} */
672 
673 /*
674  * Wrapper functions - implementation
675  */
676 
677 /* buffered output function */
php_b_body_write(const char * str,uint str_length TSRMLS_DC)678 static int php_b_body_write(const char *str, uint str_length TSRMLS_DC)
679 {
680 	php_ob_append(str, str_length TSRMLS_CC);
681 	return str_length;
682 }
683 
684 /* {{{ php_ub_body_write_no_header
685  */
php_ub_body_write_no_header(const char * str,uint str_length TSRMLS_DC)686 PHPAPI int php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC)
687 {
688 	int result;
689 
690 	if (OG(disable_output)) {
691 		return 0;
692 	}
693 
694 	result = OG(php_header_write)(str, str_length TSRMLS_CC);
695 
696 	if (OG(implicit_flush)) {
697 		sapi_flush(TSRMLS_C);
698 	}
699 
700 	return result;
701 }
702 /* }}} */
703 
704 /* {{{ php_ub_body_write
705  */
php_ub_body_write(const char * str,uint str_length TSRMLS_DC)706 PHPAPI int php_ub_body_write(const char *str, uint str_length TSRMLS_DC)
707 {
708 	int result = 0;
709 
710 	if (SG(request_info).headers_only) {
711 		if(SG(headers_sent)) {
712 			return 0;
713 		}
714 		php_header(TSRMLS_C);
715 		zend_bailout();
716 	}
717 	if (php_header(TSRMLS_C)) {
718 		if (zend_is_compiling(TSRMLS_C)) {
719 			OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C);
720 			OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C);
721 		} else if (zend_is_executing(TSRMLS_C)) {
722 			OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C);
723 			OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C);
724 		}
725 
726 		OG(php_body_write) = php_ub_body_write_no_header;
727 		result = php_ub_body_write_no_header(str, str_length TSRMLS_CC);
728 	}
729 
730 	return result;
731 }
732 /* }}} */
733 
734 /* {{{ int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result)
735  */
php_ob_buffer_status(php_ob_buffer * ob_buffer,zval * result)736 static int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result)
737 {
738 	zval *elem;
739 
740 	MAKE_STD_ZVAL(elem);
741 	array_init(elem);
742 
743 	add_assoc_long(elem, "chunk_size", ob_buffer->chunk_size);
744 	if (!ob_buffer->chunk_size) {
745 		add_assoc_long(elem, "size", ob_buffer->size);
746 		add_assoc_long(elem, "block_size", ob_buffer->block_size);
747 	}
748 	if (ob_buffer->internal_output_handler) {
749 		add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_INTERNAL);
750 		add_assoc_long(elem, "buffer_size", ob_buffer->internal_output_handler_buffer_size);
751 	} else {
752 		add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_USER);
753 	}
754 	add_assoc_long(elem, "status", ob_buffer->status);
755 	add_assoc_string(elem, "name", ob_buffer->handler_name, 1);
756 	add_assoc_bool(elem, "del", ob_buffer->erase);
757 	add_next_index_zval(result, elem);
758 
759 	return SUCCESS;
760 }
761 /* }}} */
762 
763 /*
764  * USERLAND (nearly 1:1 of old output.c)
765  */
766 
767 /* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, bool erase]]])
768    Turn on Output Buffering (specifying an optional output handler). */
PHP_FUNCTION(ob_start)769 PHP_FUNCTION(ob_start)
770 {
771 	zval *output_handler = NULL;
772 	long chunk_size = 0;
773 	zend_bool erase = 1;
774 
775 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/lb", &output_handler, &chunk_size, &erase) == FAILURE) {
776 		return;
777 	}
778 
779 	if (chunk_size < 0) {
780 		chunk_size = 0;
781 	}
782 
783 	if (php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC) == FAILURE) {
784 		RETURN_FALSE;
785 	}
786 	RETURN_TRUE;
787 }
788 /* }}} */
789 
790 /* {{{ proto bool ob_flush(void)
791    Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */
PHP_FUNCTION(ob_flush)792 PHP_FUNCTION(ob_flush)
793 {
794 	if (zend_parse_parameters_none() == FAILURE) {
795 		return;
796 	}
797 
798 	if (!OG(ob_nesting_level)) {
799 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush");
800 		RETURN_FALSE;
801 	}
802 
803 	if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
804 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer %s", OG(active_ob_buffer).handler_name);
805 		RETURN_FALSE;
806 	}
807 
808 	php_end_ob_buffer(1, 1 TSRMLS_CC);
809 	RETURN_TRUE;
810 }
811 /* }}} */
812 
813 /* {{{ proto bool ob_clean(void)
814    Clean (delete) the current output buffer */
PHP_FUNCTION(ob_clean)815 PHP_FUNCTION(ob_clean)
816 {
817 	if (zend_parse_parameters_none() == FAILURE) {
818 		return;
819 	}
820 
821 	if (!OG(ob_nesting_level)) {
822 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
823 		RETURN_FALSE;
824 	}
825 
826 	if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
827 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
828 		RETURN_FALSE;
829 	}
830 
831 	php_end_ob_buffer(0, 1 TSRMLS_CC);
832 	RETURN_TRUE;
833 }
834 /* }}} */
835 
836 /* {{{ proto bool ob_end_flush(void)
837    Flush (send) the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_end_flush)838 PHP_FUNCTION(ob_end_flush)
839 {
840 	if (zend_parse_parameters_none() == FAILURE) {
841 		return;
842 	}
843 
844 	if (!OG(ob_nesting_level)) {
845 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
846 		RETURN_FALSE;
847 	}
848 
849 	if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
850 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
851 		RETURN_FALSE;
852 	}
853 
854 	php_end_ob_buffer(1, 0 TSRMLS_CC);
855 	RETURN_TRUE;
856 }
857 /* }}} */
858 
859 /* {{{ proto bool ob_end_clean(void)
860    Clean the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_end_clean)861 PHP_FUNCTION(ob_end_clean)
862 {
863 	if (zend_parse_parameters_none() == FAILURE) {
864 		return;
865 	}
866 
867 	if (!OG(ob_nesting_level)) {
868 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
869 		RETURN_FALSE;
870 	}
871 
872 	if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
873 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
874 		RETURN_FALSE;
875 	}
876 
877 	php_end_ob_buffer(0, 0 TSRMLS_CC);
878 	RETURN_TRUE;
879 }
880 /* }}} */
881 
882 /* {{{ proto bool ob_get_flush(void)
883    Get current buffer contents, flush (send) the output buffer, and delete current output buffer */
PHP_FUNCTION(ob_get_flush)884 PHP_FUNCTION(ob_get_flush)
885 {
886 	if (zend_parse_parameters_none() == FAILURE) {
887 		return;
888 	}
889 
890 	if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) {
891 		RETURN_FALSE;
892 	}
893 
894 	if (!OG(ob_nesting_level)) {
895 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
896 		zval_dtor(return_value);
897 		RETURN_FALSE;
898 	}
899 
900 	if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
901 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
902 		zval_dtor(return_value);
903 		RETURN_FALSE;
904 	}
905 
906 	php_end_ob_buffer(1, 0 TSRMLS_CC);
907 }
908 /* }}} */
909 
910 /* {{{ proto bool ob_get_clean(void)
911    Get current buffer contents and delete current output buffer */
PHP_FUNCTION(ob_get_clean)912 PHP_FUNCTION(ob_get_clean)
913 {
914 	if (zend_parse_parameters_none() == FAILURE) {
915 		return;
916 	}
917 
918 	if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) {
919 		RETURN_FALSE;
920 	}
921 
922 	if (!OG(ob_nesting_level)) {
923 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
924 		zval_dtor(return_value);
925 		RETURN_FALSE;
926 	}
927 	if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
928 		php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
929 		zval_dtor(return_value);
930 		RETURN_FALSE;
931 	}
932 
933 	php_end_ob_buffer(0, 0 TSRMLS_CC);
934 }
935 /* }}} */
936 
937 /* {{{ proto string ob_get_contents(void)
938    Return the contents of the output buffer */
PHP_FUNCTION(ob_get_contents)939 PHP_FUNCTION(ob_get_contents)
940 {
941 	if (zend_parse_parameters_none() == FAILURE) {
942 		return;
943 	}
944 
945 	if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) {
946 		RETURN_FALSE;
947 	}
948 }
949 /* }}} */
950 
951 /* {{{ proto int ob_get_level(void)
952    Return the nesting level of the output buffer */
PHP_FUNCTION(ob_get_level)953 PHP_FUNCTION(ob_get_level)
954 {
955 	if (zend_parse_parameters_none() == FAILURE) {
956 		return;
957 	}
958 
959 	RETURN_LONG(OG(ob_nesting_level));
960 }
961 /* }}} */
962 
963 /* {{{ proto int ob_get_length(void)
964    Return the length of the output buffer */
PHP_FUNCTION(ob_get_length)965 PHP_FUNCTION(ob_get_length)
966 {
967 	if (zend_parse_parameters_none() == FAILURE) {
968 		return;
969 	}
970 
971 	if (php_ob_get_length(return_value TSRMLS_CC) == FAILURE) {
972 		RETURN_FALSE;
973 	}
974 }
975 /* }}} */
976 
977 /* {{{ proto false|array ob_list_handlers()
978    List all output_buffers in an array */
PHP_FUNCTION(ob_list_handlers)979 PHP_FUNCTION(ob_list_handlers)
980 {
981 	if (zend_parse_parameters_none() == FAILURE) {
982 		return;
983 	}
984 
985 	array_init(return_value);
986 
987 	if (OG(ob_nesting_level)) {
988 		if (OG(ob_nesting_level) > 1) {
989 			zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_list_each, return_value);
990 		}
991 		php_ob_list_each(&OG(active_ob_buffer), return_value);
992 	}
993 }
994 /* }}} */
995 
996 /* {{{ proto false|array ob_get_status([bool full_status])
997    Return the status of the active or all output buffers */
PHP_FUNCTION(ob_get_status)998 PHP_FUNCTION(ob_get_status)
999 {
1000 	zend_bool full_status = 0;
1001 
1002 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status) == FAILURE) {
1003 		return;
1004 	}
1005 
1006 	array_init(return_value);
1007 
1008 	if (full_status) {
1009 		if (OG(ob_nesting_level) > 1) {
1010 			zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *elem, void *))php_ob_buffer_status, return_value);
1011 		}
1012 		if (OG(ob_nesting_level) > 0 && php_ob_buffer_status(&OG(active_ob_buffer), return_value) == FAILURE) {
1013 			RETURN_FALSE;
1014 		}
1015 	} else if (OG(ob_nesting_level) > 0) {
1016 		add_assoc_long(return_value, "level", OG(ob_nesting_level));
1017 		if (OG(active_ob_buffer).internal_output_handler) {
1018 			add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_INTERNAL);
1019 		} else {
1020 			add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_USER);
1021 		}
1022 		add_assoc_long(return_value, "status", OG(active_ob_buffer).status);
1023 		add_assoc_string(return_value, "name", OG(active_ob_buffer).handler_name, 1);
1024 		add_assoc_bool(return_value, "del", OG(active_ob_buffer).erase);
1025 	}
1026 }
1027 /* }}} */
1028 
1029 /* {{{ proto void ob_implicit_flush([int flag])
1030    Turn implicit flush on/off and is equivalent to calling flush() after every output call */
PHP_FUNCTION(ob_implicit_flush)1031 PHP_FUNCTION(ob_implicit_flush)
1032 {
1033 	long flag = 1;
1034 
1035 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag) == FAILURE) {
1036 		return;
1037 	}
1038 
1039 	if (flag) {
1040 		php_start_implicit_flush(TSRMLS_C);
1041 	} else {
1042 		php_end_implicit_flush(TSRMLS_C);
1043 	}
1044 }
1045 /* }}} */
1046 
1047 /* {{{ proto bool output_reset_rewrite_vars(void)
1048    Reset(clear) URL rewriter values */
PHP_FUNCTION(output_reset_rewrite_vars)1049 PHP_FUNCTION(output_reset_rewrite_vars)
1050 {
1051 	if (php_url_scanner_reset_vars(TSRMLS_C) == SUCCESS) {
1052 		RETURN_TRUE;
1053 	} else {
1054 		RETURN_FALSE;
1055 	}
1056 }
1057 /* }}} */
1058 
1059 /* {{{ proto bool output_add_rewrite_var(string name, string value)
1060    Add URL rewriter values */
PHP_FUNCTION(output_add_rewrite_var)1061 PHP_FUNCTION(output_add_rewrite_var)
1062 {
1063 	char *name, *value;
1064 	int name_len, value_len;
1065 
1066 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) {
1067 		return;
1068 	}
1069 
1070 	if (php_url_scanner_add_var(name, name_len, value, value_len, 1 TSRMLS_CC) == SUCCESS) {
1071 		RETURN_TRUE;
1072 	} else {
1073 		RETURN_FALSE;
1074 	}
1075 }
1076 /* }}} */
1077 
1078 /*
1079  * Local variables:
1080  * tab-width: 4
1081  * c-basic-offset: 4
1082  * End:
1083  * vim600: sw=4 ts=4 fdm=marker
1084  * vim<600: sw=4 ts=4
1085  */
1086