diff options
Diffstat (limited to 'main')
34 files changed, 2783 insertions, 2428 deletions
diff --git a/main/SAPI.c b/main/SAPI.c index 05644df11d..fc4b7bee84 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -37,6 +37,8 @@ #endif #ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#elif defined(PHP_WIN32) +#include "win32/time.h" #endif #include "rfc1867.h" @@ -73,6 +75,10 @@ SAPI_API sapi_module_struct sapi_module; SAPI_API void sapi_startup(sapi_module_struct *sf) { +#ifdef ZEND_SIGNALS + zend_signal_startup(); +#endif + sf->ini_entries = NULL; sapi_module = *sf; @@ -117,6 +123,58 @@ SAPI_API void sapi_free_header(sapi_header_struct *sapi_header) efree(sapi_header->header); } +/* {{{ proto bool header_register_callback(mixed callback) + call a header function */ +PHP_FUNCTION(header_register_callback) +{ + zval *callback_func; + char *callback_name; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &callback_func) == FAILURE) { + return; + } + + if (!zend_is_callable(callback_func, 0, &callback_name TSRMLS_CC)) { + efree(callback_name); + RETURN_FALSE; + } + efree(callback_name); + + if (SG(callback_func)) { + zval_ptr_dtor(&SG(callback_func)); + SG(fci_cache) = empty_fcall_info_cache; + } + + Z_ADDREF_P(callback_func); + + SG(callback_func) = callback_func; + + RETURN_TRUE; +} +/* }}} */ + +static void sapi_run_header_callback(TSRMLS_D) +{ + int error; + zend_fcall_info fci; + zval *retval_ptr = NULL; + + fci.size = sizeof(fci); + fci.function_table = EG(function_table); + fci.object_ptr = NULL; + fci.function_name = SG(callback_func); + fci.retval_ptr_ptr = &retval_ptr; + fci.param_count = 0; + fci.params = NULL; + fci.no_separation = 0; + fci.symbol_table = NULL; + + error = zend_call_function(&fci, &SG(fci_cache) TSRMLS_CC); + if (error == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not call the sapi_header_callback"); + } else if (retval_ptr) { + zval_ptr_dtor(&retval_ptr); + } +} SAPI_API void sapi_handle_post(void *arg TSRMLS_DC) { @@ -227,35 +285,61 @@ SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data) } -SAPI_API char *sapi_get_default_content_type(TSRMLS_D) +static inline char *get_default_content_type(uint prefix_len, uint *len TSRMLS_DC) { char *mimetype, *charset, *content_type; + uint mimetype_len, charset_len; - mimetype = SG(default_mimetype) ? SG(default_mimetype) : SAPI_DEFAULT_MIMETYPE; - charset = SG(default_charset) ? SG(default_charset) : SAPI_DEFAULT_CHARSET; + if (SG(default_mimetype)) { + mimetype = SG(default_mimetype); + mimetype_len = strlen(SG(default_mimetype)); + } else { + mimetype = SAPI_DEFAULT_MIMETYPE; + mimetype_len = sizeof(SAPI_DEFAULT_MIMETYPE) - 1; + } + if (SG(default_charset)) { + charset = SG(default_charset); + charset_len = strlen(SG(default_charset)); + } else { + charset = SAPI_DEFAULT_CHARSET; + charset_len = sizeof(SAPI_DEFAULT_CHARSET) - 1; + } - if (strncasecmp(mimetype, "text/", 5) == 0 && *charset) { - int len = strlen(mimetype) + sizeof("; charset=") + strlen(charset); /* sizeof() includes \0 */ - content_type = emalloc(len); - snprintf(content_type, len, "%s; charset=%s", mimetype, charset); + if (*charset && strncasecmp(mimetype, "text/", 5) == 0) { + char *p; + + *len = prefix_len + mimetype_len + sizeof("; charset=") - 1 + charset_len; + content_type = (char*)emalloc(*len + 1); + p = content_type + prefix_len; + memcpy(p, mimetype, mimetype_len); + p += mimetype_len; + memcpy(p, "; charset=", sizeof("; charset=") - 1); + p += sizeof("; charset=") - 1; + memcpy(p, charset, charset_len + 1); } else { - content_type = estrdup(mimetype); + *len = prefix_len + mimetype_len; + content_type = (char*)emalloc(*len + 1); + memcpy(content_type + prefix_len, mimetype, mimetype_len + 1); } return content_type; } +SAPI_API char *sapi_get_default_content_type(TSRMLS_D) +{ + uint len; + + return get_default_content_type(0, &len TSRMLS_CC); +} + + SAPI_API void sapi_get_default_content_type_header(sapi_header_struct *default_header TSRMLS_DC) { - char *default_content_type = sapi_get_default_content_type(TSRMLS_C); - int default_content_type_len = strlen(default_content_type); - - default_header->header_len = (sizeof("Content-type: ")-1) + default_content_type_len; - default_header->header = emalloc(default_header->header_len+1); - memcpy(default_header->header, "Content-type: ", sizeof("Content-type: ")); - memcpy(default_header->header+sizeof("Content-type: ")-1, default_content_type, default_content_type_len); - default_header->header[default_header->header_len] = 0; - efree(default_content_type); + uint len; + + default_header->header = get_default_content_type(sizeof("Content-type: ")-1, &len TSRMLS_CC); + default_header->header_len = len; + memcpy(default_header->header, "Content-type: ", sizeof("Content-type: ") - 1); } /* @@ -346,6 +430,8 @@ SAPI_API void sapi_activate(TSRMLS_D) SG(sapi_headers).http_status_line = NULL; SG(sapi_headers).mimetype = NULL; SG(headers_sent) = 0; + SG(callback_run) = 0; + SG(callback_func) = NULL; SG(read_post_bytes) = 0; SG(request_info).post_data = NULL; SG(request_info).raw_post_data = NULL; @@ -356,9 +442,7 @@ SAPI_API void sapi_activate(TSRMLS_D) SG(request_info).proto_num = 1000; /* Default to HTTP 1.0 */ SG(global_request_time) = 0; - /* It's possible to override this general case in the activate() callback, if - * necessary. - */ + /* It's possible to override this general case in the activate() callback, if necessary. */ if (SG(request_info).request_method && !strcmp(SG(request_info).request_method, "HEAD")) { SG(request_info).headers_only = 1; } else { @@ -366,22 +450,19 @@ SAPI_API void sapi_activate(TSRMLS_D) } SG(rfc1867_uploaded_files) = NULL; - /* handle request mehtod */ + /* Handle request method */ if (SG(server_context)) { - if ( SG(request_info).request_method) { - if(!strcmp(SG(request_info).request_method, "POST") - && (SG(request_info).content_type)) { - /* HTTP POST -> may contain form data to be read into variables - depending on content type given - */ + if (PG(enable_post_data_reading) && SG(request_info).request_method) { + if (SG(request_info).content_type && !strcmp(SG(request_info).request_method, "POST")) { + /* HTTP POST may contain form data to be processed into variables + * depending on given content type */ sapi_read_post_data(TSRMLS_C); } else { - /* any other method with content payload will fill - $HTTP_RAW_POST_DATA if enabled by always_populate_raw_post_data - it is up to the webserver to decide whether to allow a method or not - */ + /* Any other method with content payload will fill $HTTP_RAW_POST_DATA + * if it is enabled by always_populate_raw_post_data. + * It's up to the webserver to decide whether to allow a method or not. */ SG(request_info).content_type_dup = NULL; - if(sapi_module.default_post_reader) { + if (sapi_module.default_post_reader) { sapi_module.default_post_reader(TSRMLS_C); } } @@ -391,11 +472,12 @@ SAPI_API void sapi_activate(TSRMLS_D) /* Cookies */ SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C); + if (sapi_module.activate) { sapi_module.activate(TSRMLS_C); } } - if (sapi_module.input_filter_init ) { + if (sapi_module.input_filter_init) { sapi_module.input_filter_init(TSRMLS_C); } } @@ -456,6 +538,10 @@ SAPI_API void sapi_deactivate(TSRMLS_D) sapi_send_headers_free(TSRMLS_C); SG(sapi_started) = 0; SG(headers_sent) = 0; + SG(callback_run) = 0; + if (SG(callback_func)) { + zval_ptr_dtor(&SG(callback_func)); + } SG(request_info).headers_read = 0; SG(global_request_time) = 0; } @@ -550,19 +636,38 @@ SAPI_API int sapi_add_header_ex(char *header_line, uint header_line_len, zend_bo return r; } +static void sapi_header_add_op(sapi_header_op_enum op, sapi_header_struct *sapi_header TSRMLS_DC) +{ + if (!sapi_module.header_handler || + (SAPI_HEADER_ADD & sapi_module.header_handler(sapi_header, op, &SG(sapi_headers) TSRMLS_CC))) { + if (op == SAPI_HEADER_REPLACE) { + char *colon_offset = strchr(sapi_header->header, ':'); + + if (colon_offset) { + char sav = *colon_offset; + + *colon_offset = 0; + sapi_remove_header(&SG(sapi_headers).headers, sapi_header->header, strlen(sapi_header->header)); + *colon_offset = sav; + } + } + zend_llist_add_element(&SG(sapi_headers).headers, (void *) sapi_header); + } else { + sapi_free_header(sapi_header); + } +} + SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC) { - int retval; sapi_header_struct sapi_header; char *colon_offset; - long myuid = 0L; char *header_line; uint header_line_len; int http_response_code; - + if (SG(headers_sent) && !SG(request_info).no_headers) { - char *output_start_filename = php_get_output_start_filename(TSRMLS_C); - int output_start_lineno = php_get_output_start_lineno(TSRMLS_C); + const char *output_start_filename = php_output_get_start_filename(TSRMLS_C); + int output_start_lineno = php_output_get_start_lineno(TSRMLS_C); if (output_start_filename) { sapi_module.sapi_error(E_WARNING, "Cannot modify header information - headers already sent by (output started at %s:%d)", @@ -606,8 +711,12 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC) header_line = estrndup(header_line, header_line_len); /* cut of trailing spaces, linefeeds and carriage-returns */ - while(header_line_len && isspace(header_line[header_line_len-1])) - header_line[--header_line_len]='\0'; + if (header_line_len && isspace(header_line[header_line_len-1])) { + do { + header_line_len--; + } while(header_line_len && isspace(header_line[header_line_len-1])); + header_line[header_line_len]='\0'; + } if (op == SAPI_HEADER_DELETE) { if (strchr(header_line, ':')) { @@ -615,6 +724,14 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC) sapi_module.sapi_error(E_WARNING, "Header to delete may not contain colon."); return FAILURE; } + if (sapi_module.header_handler) { + sapi_header.header = header_line; + sapi_header.header_len = header_line_len; + sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC); + } + sapi_remove_header(&SG(sapi_headers).headers, header_line, header_line_len); + efree(header_line); + return SUCCESS; } else { /* new line/NUL character safety check */ int i; @@ -642,15 +759,6 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC) sapi_header.header = header_line; sapi_header.header_len = header_line_len; - if (op == SAPI_HEADER_DELETE) { - if (sapi_module.header_handler) { - sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC); - } - sapi_remove_header(&SG(sapi_headers).headers, sapi_header.header, sapi_header.header_len); - sapi_free_header(&sapi_header); - return SUCCESS; - } - /* Check the header for a few cases that we have special support for in SAPI */ if (header_line_len>=5 && !strncasecmp(header_line, "HTTP/", 5)) { @@ -696,6 +804,14 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC) } efree(mimetype); SG(sapi_headers).send_default_content_type = 0; + } else if (!STRCASECMP(header_line, "Content-Length")) { + /* Script is setting Content-length. The script cannot reasonably + * know the size of the message body after compression, so it's best + * do disable compression altogether. This contributes to making scripts + * portable between setups that have and don't have zlib compression + * enabled globally. See req #44164 */ + zend_alter_ini_entry("zlib.output_compression", sizeof("zlib.output_compression"), + "0", sizeof("0") - 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); } else if (!STRCASECMP(header_line, "Location")) { if ((SG(sapi_headers).http_response_code < 300 || SG(sapi_headers).http_response_code > 307) && @@ -713,75 +829,7 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC) } } } else if (!STRCASECMP(header_line, "WWW-Authenticate")) { /* HTTP Authentication */ - sapi_update_response_code(401 TSRMLS_CC); /* authentication-required */ - - if(PG(safe_mode)) -#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) - { - zval *repl_temp; - char *ptr = colon_offset+1, *result, *newheader; - int ptr_len=0, result_len = 0, newlen = 0; - - /* skip white space */ - while (isspace(*ptr)) { - ptr++; - } - - myuid = php_getuid(); - - ptr_len = strlen(ptr); - MAKE_STD_ZVAL(repl_temp); - Z_TYPE_P(repl_temp) = IS_STRING; - Z_STRLEN_P(repl_temp) = spprintf(&Z_STRVAL_P(repl_temp), 0, "realm=\"\\1-%ld\"", myuid); - /* Modify quoted realm value */ - result = php_pcre_replace("/realm=\"(.*?)\"/i", 16, - ptr, ptr_len, - repl_temp, - 0, &result_len, -1, NULL TSRMLS_CC); - if(result_len==ptr_len) { - efree(result); - efree(Z_STRVAL_P(repl_temp)); - Z_STRLEN_P(repl_temp) = spprintf(&Z_STRVAL_P(repl_temp), 0, "realm=\\1-%ld\\2", myuid); - /* modify unquoted realm value */ - result = php_pcre_replace("/realm=([^\\s]+)(.*)/i", 21, - ptr, ptr_len, - repl_temp, - 0, &result_len, -1, NULL TSRMLS_CC); - if(result_len==ptr_len) { - char *lower_temp = estrdup(ptr); - char conv_temp[32]; - int conv_len; - - php_strtolower(lower_temp,strlen(lower_temp)); - /* If there is no realm string at all, append one */ - if(!strstr(lower_temp,"realm")) { - efree(result); - conv_len = slprintf(conv_temp, sizeof(conv_temp), " realm=\"%ld\"",myuid); - result = emalloc(ptr_len+conv_len+1); - result_len = ptr_len+conv_len; - memcpy(result, ptr, ptr_len); - memcpy(result+ptr_len, conv_temp, conv_len); - *(result+ptr_len+conv_len) = '\0'; - } - efree(lower_temp); - } - } - newlen = spprintf(&newheader, 0, "WWW-Authenticate: %s", result); - efree(header_line); - sapi_header.header = newheader; - sapi_header.header_len = newlen; - efree(result); - efree(Z_STRVAL_P(repl_temp)); - efree(repl_temp); - } -#else - { - myuid = php_getuid(); - efree(header_line); - sapi_header.header_len = spprintf(&sapi_header.header, 0, "WWW-Authenticate: Basic realm=\"%ld\"", myuid); - } -#endif } if (sapi_header.header==header_line) { *colon_offset = ':'; @@ -791,28 +839,7 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC) if (http_response_code) { sapi_update_response_code(http_response_code TSRMLS_CC); } - if (sapi_module.header_handler) { - retval = sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC); - } else { - retval = SAPI_HEADER_ADD; - } - if (retval & SAPI_HEADER_ADD) { - /* in replace mode first remove the header if it already exists in the headers llist */ - if (op == SAPI_HEADER_REPLACE) { - colon_offset = strchr(sapi_header.header, ':'); - if (colon_offset) { - char sav; - sav = *colon_offset; - *colon_offset = 0; - sapi_remove_header(&SG(sapi_headers).headers, sapi_header.header, strlen(sapi_header.header)); - *colon_offset = sav; - } - } - - zend_llist_add_element(&SG(sapi_headers).headers, (void *) &sapi_header); - } else { - sapi_free_header(&sapi_header); - } + sapi_header_add_op(op, &sapi_header TSRMLS_CC); return SUCCESS; } @@ -822,7 +849,7 @@ SAPI_API int sapi_send_headers(TSRMLS_D) int retval; int ret = FAILURE; - if (SG(headers_sent) || SG(request_info).no_headers) { + if (SG(headers_sent) || SG(request_info).no_headers || SG(callback_run)) { return SUCCESS; } @@ -831,8 +858,20 @@ SAPI_API int sapi_send_headers(TSRMLS_D) */ if (SG(sapi_headers).send_default_content_type && sapi_module.send_headers) { sapi_header_struct default_header; - sapi_get_default_content_type_header(&default_header TSRMLS_CC); - sapi_add_header_ex(default_header.header, default_header.header_len, 0, 0 TSRMLS_CC); + uint len; + + SG(sapi_headers).mimetype = get_default_content_type(0, &len TSRMLS_CC); + default_header.header_len = sizeof("Content-type: ") - 1 + len; + default_header.header = emalloc(default_header.header_len + 1); + memcpy(default_header.header, "Content-type: ", sizeof("Content-type: ") - 1); + memcpy(default_header.header + sizeof("Content-type: ") - 1, SG(sapi_headers).mimetype, len + 1); + sapi_header_add_op(SAPI_HEADER_ADD, &default_header TSRMLS_CC); + SG(sapi_headers).send_default_content_type = 0; + } + + if (SG(callback_func) && !SG(callback_run)) { + SG(callback_run) = 1; + sapi_run_header_callback(TSRMLS_C); } SG(headers_sent) = 1; @@ -917,9 +956,8 @@ SAPI_API void sapi_unregister_post_entry(sapi_post_entry *post_entry TSRMLS_DC) } -SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D)) +SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D) TSRMLS_DC) { - TSRMLS_FETCH(); if (SG(sapi_started) && EG(in_execution)) { return FAILURE; } @@ -928,9 +966,8 @@ SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRML } -SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC)) +SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC) TSRMLS_DC) { - TSRMLS_FETCH(); if (SG(sapi_started) && EG(in_execution)) { return FAILURE; } @@ -938,9 +975,8 @@ SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zva return SUCCESS; } -SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC), unsigned int (*input_filter_init)(TSRMLS_D)) +SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC), unsigned int (*input_filter_init)(TSRMLS_D) TSRMLS_DC) { - TSRMLS_FETCH(); if (SG(sapi_started) && EG(in_execution)) { return FAILURE; } @@ -1025,14 +1061,19 @@ SAPI_API int sapi_get_target_gid(gid_t *obj TSRMLS_DC) } } -SAPI_API time_t sapi_get_request_time(TSRMLS_D) +SAPI_API double sapi_get_request_time(TSRMLS_D) { if(SG(global_request_time)) return SG(global_request_time); if (sapi_module.get_request_time && SG(server_context)) { SG(global_request_time) = sapi_module.get_request_time(TSRMLS_C); } else { - SG(global_request_time) = time(0); + struct timeval tp = {0}; + if (!gettimeofday(&tp, NULL)) { + SG(global_request_time) = (double)(tp.tv_sec + tp.tv_usec / 1000000.00); + } else { + SG(global_request_time) = (double)time(0); + } } return SG(global_request_time); } diff --git a/main/SAPI.h b/main/SAPI.h index 9a063d396a..8f2536b84f 100644 --- a/main/SAPI.h +++ b/main/SAPI.h @@ -128,8 +128,11 @@ typedef struct _sapi_globals_struct { long post_max_size; int options; zend_bool sapi_started; - time_t global_request_time; + double global_request_time; HashTable known_post_content_types; + zval *callback_func; + zend_fcall_info_cache fci_cache; + zend_bool callback_run; } sapi_globals_struct; @@ -189,9 +192,9 @@ SAPI_API void sapi_handle_post(void *arg TSRMLS_DC); SAPI_API int sapi_register_post_entries(sapi_post_entry *post_entry TSRMLS_DC); SAPI_API int sapi_register_post_entry(sapi_post_entry *post_entry TSRMLS_DC); SAPI_API void sapi_unregister_post_entry(sapi_post_entry *post_entry TSRMLS_DC); -SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D)); -SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC)); -SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC), unsigned int (*input_filter_init)(TSRMLS_D)); +SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D) TSRMLS_DC); +SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC) TSRMLS_DC); +SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC), unsigned int (*input_filter_init)(TSRMLS_D) TSRMLS_DC); SAPI_API int sapi_flush(TSRMLS_D); SAPI_API struct stat *sapi_get_stat(TSRMLS_D); @@ -207,7 +210,7 @@ SAPI_API int sapi_force_http_10(TSRMLS_D); SAPI_API int sapi_get_target_uid(uid_t * TSRMLS_DC); SAPI_API int sapi_get_target_gid(gid_t * TSRMLS_DC); -SAPI_API time_t sapi_get_request_time(TSRMLS_D); +SAPI_API double sapi_get_request_time(TSRMLS_D); SAPI_API void sapi_terminate_process(TSRMLS_D); END_EXTERN_C() @@ -236,8 +239,8 @@ struct _sapi_module_struct { char *(*read_cookies)(TSRMLS_D); void (*register_server_variables)(zval *track_vars_array TSRMLS_DC); - void (*log_message)(char *message); - time_t (*get_request_time)(TSRMLS_D); + void (*log_message)(char *message TSRMLS_DC); + double (*get_request_time)(TSRMLS_D); void (*terminate_process)(TSRMLS_D); char *php_ini_path_override; @@ -250,6 +253,7 @@ struct _sapi_module_struct { char *executable_location; int php_ini_ignore; + int php_ini_ignore_cwd; /* don't look for php.ini in the current directory */ int (*get_fd)(int *fd TSRMLS_DC); diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c index 1ea07e1f13..9d4a8a85c2 100644 --- a/main/fopen_wrappers.c +++ b/main/fopen_wrappers.c @@ -39,7 +39,6 @@ #include <sys/param.h> #endif -#include "safe_mode.h" #include "ext/standard/head.h" #include "ext/standard/php_standard.h" #include "zend_compile.h" @@ -338,55 +337,6 @@ PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC) } /* }}} */ -/* {{{ php_check_safe_mode_include_dir - */ -PHPAPI int php_check_safe_mode_include_dir(const char *path TSRMLS_DC) -{ - if (PG(safe_mode)) { - if (PG(safe_mode_include_dir) && *PG(safe_mode_include_dir)) { - char *pathbuf; - char *ptr; - char *end; - char resolved_name[MAXPATHLEN]; - - /* Resolve the real path into resolved_name */ - if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) { - return -1; - } - pathbuf = estrdup(PG(safe_mode_include_dir)); - ptr = pathbuf; - - while (ptr && *ptr) { - end = strchr(ptr, DEFAULT_DIR_SEPARATOR); - if (end != NULL) { - *end = '\0'; - end++; - } - - /* Check the path */ -#ifdef PHP_WIN32 - if (strncasecmp(ptr, resolved_name, strlen(ptr)) == 0) -#else - if (strncmp(ptr, resolved_name, strlen(ptr)) == 0) -#endif - { - /* File is in the right directory */ - efree(pathbuf); - return 0; - } - - ptr = end; - } - efree(pathbuf); - } - return -1; - } - - /* Nothing to check... */ - return 0; -} -/* }}} */ - /* {{{ php_fopen_and_set_opened_path */ static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, char **opened_path TSRMLS_DC) @@ -398,7 +348,7 @@ static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, c } fp = VCWD_FOPEN(path, mode); if (fp && opened_path) { - *opened_path = expand_filepath(path, NULL TSRMLS_CC); + *opened_path = expand_filepath_with_mode(path, NULL, NULL, 0, CWD_EXPAND TSRMLS_CC); } return fp; } @@ -408,14 +358,11 @@ static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, c */ PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC) { - FILE *fp; -#ifndef PHP_WIN32 - struct stat st; -#endif char *path_info; char *filename = NULL; char *resolved_path = NULL; int length; + zend_bool orig_display_errors; path_info = SG(request_info).request_uri; #if HAVE_PWD_H @@ -496,17 +443,12 @@ PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC) SG(request_info).path_translated = NULL; return FAILURE; } - fp = VCWD_FOPEN(resolved_path, "rb"); + efree(resolved_path); -#ifndef PHP_WIN32 - /* refuse to open anything that is not a regular file */ - if (fp && (0 > fstat(fileno(fp), &st) || !S_ISREG(st.st_mode))) { - fclose(fp); - fp = NULL; - } -#endif - - if (!fp) { + orig_display_errors = PG(display_errors); + PG(display_errors) = 0; + if (zend_stream_open(filename, file_handle TSRMLS_CC) == FAILURE) { + PG(display_errors) = orig_display_errors; if (SG(request_info).path_translated != filename) { STR_FREE(filename); } @@ -514,19 +456,13 @@ PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC) SG(request_info).path_translated = NULL; return FAILURE; } - - file_handle->opened_path = resolved_path; + PG(display_errors) = orig_display_errors; if (SG(request_info).path_translated != filename) { STR_FREE(SG(request_info).path_translated); /* for same reason as above */ SG(request_info).path_translated = filename; } - file_handle->filename = SG(request_info).path_translated; - file_handle->free_filename = 0; - file_handle->handle.fp = fp; - file_handle->type = ZEND_HANDLE_FP; - return SUCCESS; } /* }}} */ @@ -542,11 +478,7 @@ PHPAPI char *php_resolve_path(const char *filename, int filename_length, const c char *actual_path; php_stream_wrapper *wrapper; - if (!filename) { - return NULL; - } - - if (strlen(filename) != filename_length) { + if (!filename || CHECK_NULL_PATH(filename, filename_length)) { return NULL; } @@ -633,7 +565,7 @@ PHPAPI char *php_resolve_path(const char *filename, int filename_length, const c /* check in calling scripts' current working directory as a fall back case */ if (zend_is_executing(TSRMLS_C)) { - char *exec_fname = zend_get_executed_filename(TSRMLS_C); + const char *exec_fname = zend_get_executed_filename(TSRMLS_C); int exec_fname_length = strlen(exec_fname); while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); @@ -679,9 +611,8 @@ PHPAPI char *php_resolve_path(const char *filename, int filename_length, const c PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, char **opened_path TSRMLS_DC) { char *pathbuf, *ptr, *end; - char *exec_fname; + const char *exec_fname; char trypath[MAXPATHLEN]; - struct stat sb; FILE *fp; int path_length; int filename_length; @@ -698,34 +629,11 @@ PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const c filename_length = strlen(filename); /* Relative path open */ - if (*filename == '.') { - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } - return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC); - } - - /* - * files in safe_mode_include_dir (or subdir) are excluded from - * safe mode GID/UID checks - */ - + if ((*filename == '.') /* Absolute path open */ - if (IS_ABSOLUTE_PATH(filename, filename_length)) { - if (php_check_safe_mode_include_dir(filename TSRMLS_CC) == 0) { - /* filename is in safe_mode_include_dir (or subdir) */ - return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC); - } - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } - return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC); - } - - if (!path || (path && !*path)) { - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } + || IS_ABSOLUTE_PATH(filename, filename_length) + || (!path || (path && !*path)) + ) { return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC); } @@ -764,21 +672,6 @@ PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const c if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); } - if (PG(safe_mode)) { - if (VCWD_STAT(trypath, &sb) == 0) { - /* file exists ... check permission */ - if (php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0 || - php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM) - ) { - /* UID ok, or trypath is in safe_mode_include_dir */ - fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC); - } else { - fp = NULL; - } - efree(pathbuf); - return fp; - } - } fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC); if (fp) { efree(pathbuf); @@ -844,6 +737,14 @@ PHPAPI char *expand_filepath(const char *filepath, char *real_path TSRMLS_DC) */ PHPAPI char *expand_filepath_ex(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len TSRMLS_DC) { + return expand_filepath_with_mode(filepath, real_path, relative_to, relative_to_len, CWD_FILEPATH TSRMLS_CC); +} +/* }}} */ + +/* {{{ expand_filepath_use_realpath + */ +PHPAPI char *expand_filepath_with_mode(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len, int realpath_mode TSRMLS_DC) +{ cwd_state new_state; char cwd[MAXPATHLEN]; int copy_len; @@ -888,7 +789,7 @@ PHPAPI char *expand_filepath_ex(const char *filepath, char *real_path, const cha new_state.cwd = strdup(cwd); new_state.cwd_length = strlen(cwd); - if (virtual_file_ex(&new_state, filepath, NULL, CWD_FILEPATH)) { + if (virtual_file_ex(&new_state, filepath, NULL, realpath_mode TSRMLS_CC)) { free(new_state.cwd); return NULL; } diff --git a/main/fopen_wrappers.h b/main/fopen_wrappers.h index 8f48a1c2f5..6dcc6e1ccc 100644 --- a/main/fopen_wrappers.h +++ b/main/fopen_wrappers.h @@ -28,13 +28,14 @@ BEGIN_EXTERN_C() PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC); PHPAPI char *expand_filepath(const char *filepath, char *real_path TSRMLS_DC); PHPAPI char *expand_filepath_ex(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len TSRMLS_DC); +PHPAPI char *expand_filepath_with_mode(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len, int realpath_mode TSRMLS_DC); PHPAPI int php_check_open_basedir(const char *path TSRMLS_DC); PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC); PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path TSRMLS_DC); /* {{{ OPENBASEDIR_CHECKPATH(filename) to ease merge between 6.x and 5.x */ -#if (PHP_MAJOR_VERSION < 6) +#if PHP_API_VERSION < 20100412 # define OPENBASEDIR_CHECKPATH(filename) \ (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename TSRMLS_CC) #else diff --git a/main/main.c b/main/main.c index 631a6a9fea..44c61c6005 100644 --- a/main/main.c +++ b/main/main.c @@ -82,6 +82,7 @@ #include "zend_indent.h" #include "zend_extensions.h" #include "zend_ini.h" +#include "zend_dtrace.h" #include "php_content_types.h" #include "php_ticks.h" @@ -125,9 +126,9 @@ PHPAPI int core_globals_id; #ifdef PHP_WIN32 #include "win32_internal_function_disabled.h" -static php_win32_disable_functions() { +static php_win32_disable_functions(TSRMLS_D) +{ int i; - TSRMLS_FETCH(); if (EG(windows_version_info).dwMajorVersion < 5) { for (i = 0; i < function_name_cnt_5; i++) { @@ -254,6 +255,57 @@ static void php_disable_classes(TSRMLS_D) } /* }}} */ +/* {{{ php_binary_init + */ +static void php_binary_init(TSRMLS_D) +{ + char *binary_location; +#ifdef PHP_WIN32 + binary_location = (char *)malloc(MAXPATHLEN); + if (GetModuleFileName(0, binary_location, MAXPATHLEN) == 0) { + free(binary_location); + PG(php_binary) = NULL; + } +#else + if (sapi_module.executable_location) { + binary_location = (char *)malloc(MAXPATHLEN); + if (!strchr(sapi_module.executable_location, '/')) { + char *envpath, *path; + int found = 0; + + if ((envpath = getenv("PATH")) != NULL) { + char *search_dir, search_path[MAXPATHLEN]; + char *last = NULL; + + path = estrdup(envpath); + search_dir = php_strtok_r(path, ":", &last); + + while (search_dir) { + snprintf(search_path, MAXPATHLEN, "%s/%s", search_dir, sapi_module.executable_location); + if (VCWD_REALPATH(search_path, binary_location) && !VCWD_ACCESS(binary_location, X_OK)) { + found = 1; + break; + } + search_dir = php_strtok_r(NULL, ":", &last); + } + efree(path); + } + if (!found) { + free(binary_location); + binary_location = NULL; + } + } else if (!VCWD_REALPATH(sapi_module.executable_location, binary_location) || VCWD_ACCESS(binary_location, X_OK)) { + free(binary_location); + binary_location = NULL; + } + } else { + binary_location = NULL; + } +#endif + PG(php_binary) = binary_location; +} +/* }}} */ + /* {{{ PHP_INI_MH */ static PHP_INI_MH(OnUpdateTimeout) @@ -365,14 +417,9 @@ static PHP_INI_MH(OnUpdateErrorLog) { /* Only do the safemode/open_basedir check at runtime */ if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && new_value && strcmp(new_value, "syslog")) { - if (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { - return FAILURE; - } - if (PG(open_basedir) && php_check_open_basedir(new_value TSRMLS_CC)) { return FAILURE; } - } OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); return SUCCESS; @@ -385,14 +432,9 @@ static PHP_INI_MH(OnUpdateMailLog) { /* Only do the safemode/open_basedir check at runtime */ if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && new_value) { - if (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { - return FAILURE; - } - if (PG(open_basedir) && php_check_open_basedir(new_value TSRMLS_CC)) { return FAILURE; } - } OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); return SUCCESS; @@ -414,10 +456,8 @@ static PHP_INI_MH(OnChangeMailForceExtra) /* defined in browscap.c */ PHP_INI_MH(OnChangeBrowscap); -/* Need to convert to strings and make use of: - * PHP_SAFE_MODE - * - * Need to be read from the environment (?): + +/* Need to be read from the environment (?): * PHP_AUTO_PREPEND_FILE * PHP_AUTO_APPEND_FILE * PHP_DOCUMENT_ROOT @@ -425,10 +465,6 @@ PHP_INI_MH(OnChangeBrowscap); * PHP_INCLUDE_PATH */ -#ifndef PHP_SAFE_MODE_EXEC_DIR -# define PHP_SAFE_MODE_EXEC_DIR "" -#endif - /* Windows and Netware use the internal mail */ #if defined(PHP_WIN32) || defined(NETWARE) # define DEFAULT_SENDMAIL_PATH NULL @@ -441,15 +477,12 @@ PHP_INI_MH(OnChangeBrowscap); /* {{{ PHP_INI */ PHP_INI_BEGIN() - PHP_INI_ENTRY_EX("define_syslog_variables", "0", PHP_INI_ALL, NULL, php_ini_boolean_displayer_cb) - PHP_INI_ENTRY_EX("highlight.bg", HL_BG_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) PHP_INI_ENTRY_EX("highlight.comment", HL_COMMENT_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) PHP_INI_ENTRY_EX("highlight.default", HL_DEFAULT_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) PHP_INI_ENTRY_EX("highlight.html", HL_HTML_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) PHP_INI_ENTRY_EX("highlight.keyword", HL_KEYWORD_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) PHP_INI_ENTRY_EX("highlight.string", HL_STRING_COLOR, PHP_INI_ALL, NULL, php_ini_color_displayer_cb) - STD_PHP_INI_BOOLEAN("allow_call_time_pass_reference", "1", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, allow_call_time_pass_reference, zend_compiler_globals, compiler_globals) STD_PHP_INI_BOOLEAN("asp_tags", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, asp_tags, zend_compiler_globals, compiler_globals) STD_PHP_INI_ENTRY_EX("display_errors", "1", PHP_INI_ALL, OnUpdateDisplayErrors, display_errors, php_core_globals, core_globals, display_errors_mode) STD_PHP_INI_BOOLEAN("display_startup_errors", "0", PHP_INI_ALL, OnUpdateBool, display_startup_errors, php_core_globals, core_globals) @@ -469,26 +502,13 @@ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("ignore_repeated_source", "0", PHP_INI_ALL, OnUpdateBool, ignore_repeated_source, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("report_memleaks", "1", PHP_INI_ALL, OnUpdateBool, report_memleaks, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("report_zend_debug", "1", PHP_INI_ALL, OnUpdateBool, report_zend_debug, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("magic_quotes_gpc", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, magic_quotes_gpc, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("magic_quotes_runtime", "0", PHP_INI_ALL, OnUpdateBool, magic_quotes_runtime, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("magic_quotes_sybase", "0", PHP_INI_ALL, OnUpdateBool, magic_quotes_sybase, php_core_globals, core_globals) STD_PHP_INI_ENTRY("output_buffering", "0", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateLong, output_buffering, php_core_globals, core_globals) STD_PHP_INI_ENTRY("output_handler", NULL, PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateString, output_handler, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("register_argc_argv", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_argc_argv, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("register_globals", "0", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_globals, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("register_long_arrays", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, register_long_arrays, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("auto_globals_jit", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, auto_globals_jit, php_core_globals, core_globals) -#if PHP_SAFE_MODE - STD_PHP_INI_BOOLEAN("safe_mode", "1", PHP_INI_SYSTEM, OnUpdateBool, safe_mode, php_core_globals, core_globals) -#else - STD_PHP_INI_BOOLEAN("safe_mode", "0", PHP_INI_SYSTEM, OnUpdateBool, safe_mode, php_core_globals, core_globals) -#endif - STD_PHP_INI_ENTRY("safe_mode_include_dir", NULL, PHP_INI_SYSTEM, OnUpdateString, safe_mode_include_dir, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("safe_mode_gid", "0", PHP_INI_SYSTEM, OnUpdateBool, safe_mode_gid, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("short_open_tag", DEFAULT_SHORT_OPEN_TAG, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, short_tags, zend_compiler_globals, compiler_globals) STD_PHP_INI_BOOLEAN("sql.safe_mode", "0", PHP_INI_SYSTEM, OnUpdateBool, sql_safe_mode, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("track_errors", "0", PHP_INI_ALL, OnUpdateBool, track_errors, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("y2k_compliance", "1", PHP_INI_ALL, OnUpdateBool, y2k_compliance, php_core_globals, core_globals) STD_PHP_INI_ENTRY("unserialize_callback_func", NULL, PHP_INI_ALL, OnUpdateString, unserialize_callback_func, php_core_globals, core_globals) STD_PHP_INI_ENTRY("serialize_precision", "17", PHP_INI_ALL, OnUpdateLongGEZero, serialize_precision, php_core_globals, core_globals) @@ -505,7 +525,6 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("include_path", PHP_INCLUDE_PATH, PHP_INI_ALL, OnUpdateStringUnempty, include_path, php_core_globals, core_globals) PHP_INI_ENTRY("max_execution_time", "30", PHP_INI_ALL, OnUpdateTimeout) STD_PHP_INI_ENTRY("open_basedir", NULL, PHP_INI_ALL, OnUpdateBaseDir, open_basedir, php_core_globals, core_globals) - STD_PHP_INI_ENTRY("safe_mode_exec_dir", PHP_SAFE_MODE_EXEC_DIR, PHP_INI_SYSTEM, OnUpdateString, safe_mode_exec_dir, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("file_uploads", "1", PHP_INI_SYSTEM, OnUpdateBool, file_uploads, php_core_globals, core_globals) STD_PHP_INI_ENTRY("upload_max_filesize", "2M", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, upload_max_filesize, php_core_globals, core_globals) @@ -533,10 +552,11 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra) PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL) PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL) - PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL) STD_PHP_INI_BOOLEAN("allow_url_fopen", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_url_fopen, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("allow_url_include", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_url_include, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("enable_post_data_reading", "1", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, enable_post_data_reading, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("always_populate_raw_post_data", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, always_populate_raw_post_data, php_core_globals, core_globals) STD_PHP_INI_ENTRY("realpath_cache_size", "16K", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_size_limit, virtual_cwd_globals, cwd_globals) @@ -571,6 +591,14 @@ static int php_during_module_shutdown(void) } /* }}} */ +/* {{{ php_get_module_initialized + */ +PHPAPI int php_get_module_initialized(void) +{ + return module_initialized; +} +/* }}} */ + /* {{{ php_log_err */ PHPAPI void php_log_err(char *log_message TSRMLS_DC) @@ -613,7 +641,7 @@ PHPAPI void php_log_err(char *log_message TSRMLS_DC) #ifdef PHP_WIN32 php_flock(fd, 2); #endif - write(fd, tmp, len); + php_ignore_value(write(fd, tmp, len)); efree(tmp); efree(error_time_str); close(fd); @@ -625,7 +653,7 @@ PHPAPI void php_log_err(char *log_message TSRMLS_DC) /* Otherwise fall back to the default logging location, if we have one */ if (sapi_module.log_message) { - sapi_module.log_message(log_message); + sapi_module.log_message(log_message TSRMLS_CC); } PG(in_error_log) = 0; } @@ -671,9 +699,9 @@ PHPAPI void php_verror(const char *docref, const char *params, int type, const c char *docref_target = "", *docref_root = ""; char *p; int buffer_len = 0; - char *space = ""; - char *class_name = ""; - char *function; + const char *space = ""; + const char *class_name = ""; + const char *function; int origin_len; char *origin; char *message; @@ -681,8 +709,9 @@ PHPAPI void php_verror(const char *docref, const char *params, int type, const c /* get error text into buffer and escape for html if necessary */ buffer_len = vspprintf(&buffer, 0, format, args); + if (PG(html_errors)) { - int len; + size_t len; char *replace = php_escape_html_entities(buffer, buffer_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); efree(buffer); buffer = replace; @@ -698,7 +727,7 @@ PHPAPI void php_verror(const char *docref, const char *params, int type, const c EG(current_execute_data)->opline && EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL ) { - switch (EG(current_execute_data)->opline->op2.u.constant.value.lval) { + switch (EG(current_execute_data)->opline->extended_value) { case ZEND_EVAL: function = "eval"; is_function = 1; @@ -740,7 +769,7 @@ PHPAPI void php_verror(const char *docref, const char *params, int type, const c } if (PG(html_errors)) { - int len; + size_t len; char *replace = php_escape_html_entities(origin, origin_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); efree(origin); origin = replace; @@ -767,10 +796,10 @@ PHPAPI void php_verror(const char *docref, const char *params, int type, const c } /* we have a docref for a function AND - * - we show erroes in html mode OR - * - the user wants to see the links anyway + * - we show errors in html mode AND + * - the user wants to see the links */ - if (docref && is_function && (PG(html_errors) || strlen(PG(docref_root)))) { + if (docref && is_function && PG(html_errors) && strlen(PG(docref_root))) { if (strncmp(docref, "http://", 7)) { /* We don't have 'http://' so we use docref_root */ @@ -932,6 +961,9 @@ static void php_error_cb(int type, const char *error_filename, const uint error_ /* store the error if it has changed */ if (display) { +#ifdef ZEND_SIGNALS + HANDLE_BLOCK_INTERRUPTIONS(); +#endif if (PG(last_error_message)) { free(PG(last_error_message)); PG(last_error_message) = NULL; @@ -940,6 +972,9 @@ static void php_error_cb(int type, const char *error_filename, const uint error_ free(PG(last_error_file)); PG(last_error_file) = NULL; } +#ifdef ZEND_SIGNALS + HANDLE_UNBLOCK_INTERRUPTIONS(); +#endif if (!error_filename) { error_filename = "Unknown"; } @@ -1032,14 +1067,7 @@ static void php_error_cb(int type, const char *error_filename, const uint error_ efree(log_buffer); } - if (PG(display_errors) - && ((module_initialized && !PG(during_request_startup)) - || (PG(display_startup_errors) - && (OG(php_body_write)==php_default_output_func || OG(php_body_write)==php_ub_body_write_no_header || OG(php_body_write)==php_ub_body_write) - ) - ) - ) { - + if (PG(display_errors) && ((module_initialized && !PG(during_request_startup)) || (PG(display_startup_errors)))) { if (PG(xmlrpc_errors)) { php_printf("<?xml version=\"1.0\"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><int>%ld</int></value></member><member><name>faultString</name><value><string>%s:%s in %s on line %d</string></value></member></struct></value></fault></methodResponse>", PG(xmlrpc_error_number), error_type_str, buffer, error_filename, error_lineno); } else { @@ -1047,8 +1075,8 @@ static void php_error_cb(int type, const char *error_filename, const uint error_ char *append_string = INI_STR("error_append_string"); if (PG(html_errors)) { - if (type == E_ERROR) { - int len; + if (type == E_ERROR || type == E_PARSE) { + size_t len; char *buf = php_escape_html_entities(buffer, buffer_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); php_printf("%s<br />\n<b>%s</b>: %s in <b>%s</b> on line <b>%d</b><br />\n%s", STR_PRINT(prepend_string), error_type_str, buf, error_filename, error_lineno, STR_PRINT(append_string)); efree(buf); @@ -1061,7 +1089,7 @@ static void php_error_cb(int type, const char *error_filename, const uint error_ PG(display_errors) == PHP_DISPLAY_ERRORS_STDERR ) { #ifdef PHP_WIN32 - fprintf(stderr, "%s: %s in %s on line%d\n", error_type_str, buffer, error_filename, error_lineno); + fprintf(stderr, "%s: %s in %s on line %d\n", error_type_str, buffer, error_filename, error_lineno); fflush(stderr); #else fprintf(stderr, "%s: %s in %s on line %d\n", error_type_str, buffer, error_filename, error_lineno); @@ -1114,11 +1142,13 @@ static void php_error_cb(int type, const char *error_filename, const uint error_ sapi_header_line ctr = {0}; ctr.line = "HTTP/1.0 500 Internal Server Error"; - ctr.line_len = strlen(ctr.line); + ctr.line_len = sizeof("HTTP/1.0 500 Internal Server Error") - 1; sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC); } /* the parser would return 1 (failure), we can bail out nicely */ - if (type != E_PARSE) { + if (type == E_PARSE) { + CG(parse_error) = 0; + } else { /* restore memory limit */ zend_set_memory_limit(PG(memory_limit)); efree(buffer); @@ -1152,6 +1182,69 @@ static void php_error_cb(int type, const char *error_filename, const uint error_ } /* }}} */ +/* {{{ php_get_current_user + */ +PHPAPI char *php_get_current_user(TSRMLS_D) +{ + struct stat *pstat; + + if (SG(request_info).current_user) { + return SG(request_info).current_user; + } + + /* FIXME: I need to have this somehow handled if + USE_SAPI is defined, because cgi will also be + interfaced in USE_SAPI */ + + pstat = sapi_get_stat(TSRMLS_C); + + if (!pstat) { + return ""; + } else { +#ifdef PHP_WIN32 + char name[256]; + DWORD len = sizeof(name)-1; + + if (!GetUserName(name, &len)) { + return ""; + } + name[len] = '\0'; + SG(request_info).current_user_length = len; + SG(request_info).current_user = estrndup(name, len); + return SG(request_info).current_user; +#else + struct passwd *pwd; +#if defined(ZTS) && defined(HAVE_GETPWUID_R) && defined(_SC_GETPW_R_SIZE_MAX) + struct passwd _pw; + struct passwd *retpwptr = NULL; + int pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); + char *pwbuf; + + if (pwbuflen < 1) { + return ""; + } + pwbuf = emalloc(pwbuflen); + if (getpwuid_r(pstat->st_uid, &_pw, pwbuf, pwbuflen, &retpwptr) != 0) { + efree(pwbuf); + return ""; + } + pwd = &_pw; +#else + if ((pwd=getpwuid(pstat->st_uid))==NULL) { + return ""; + } +#endif + SG(request_info).current_user_length = strlen(pwd->pw_name); + SG(request_info).current_user = estrndup(pwd->pw_name, SG(request_info).current_user_length); +#if defined(ZTS) && defined(HAVE_GETPWUID_R) && defined(_SC_GETPW_R_SIZE_MAX) + efree(pwbuf); +#endif + return SG(request_info).current_user; +#endif + } +} +/* }}} */ + /* {{{ proto bool set_time_limit(int seconds) Sets the maximum time a script can run */ PHP_FUNCTION(set_time_limit) @@ -1160,11 +1253,6 @@ PHP_FUNCTION(set_time_limit) char *new_timeout_str; int new_timeout_strlen; - if (PG(safe_mode)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot set time limit in safe mode"); - RETURN_FALSE; - } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &new_timeout) == FAILURE) { return; } @@ -1184,7 +1272,7 @@ PHP_FUNCTION(set_time_limit) */ static FILE *php_fopen_wrapper_for_zend(const char *filename, char **opened_path TSRMLS_DC) { - return php_stream_open_wrapper_as_file((char *)filename, "rb", ENFORCE_SAFE_MODE|USE_PATH|IGNORE_URL_WIN|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE, opened_path); + return php_stream_open_wrapper_as_file((char *)filename, "rb", USE_PATH|IGNORE_URL_WIN|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE, opened_path); } /* }}} */ @@ -1213,7 +1301,7 @@ static size_t php_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */ static int php_stream_open_for_zend(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */ { - return php_stream_open_for_zend_ex(filename, handle, ENFORCE_SAFE_MODE|USE_PATH|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); + return php_stream_open_for_zend_ex(filename, handle, USE_PATH|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); } /* }}} */ @@ -1283,7 +1371,7 @@ static int php_get_configuration_directive_for_zend(const char *name, uint name_ /* {{{ php_message_handler_for_zend */ -static void php_message_handler_for_zend(long message, void *data TSRMLS_DC) +static void php_message_handler_for_zend(long message, const void *data TSRMLS_DC) { switch (message) { case ZMSG_FAILED_INCLUDE_FOPEN: @@ -1377,8 +1465,12 @@ void php_on_timeout(int seconds TSRMLS_DC) */ static void sigchld_handler(int apar) { + int errno_save = errno; + while (waitpid(-1, NULL, WNOHANG) > 0); signal(SIGCHLD, sigchld_handler); + + errno = errno_save; } /* }}} */ #endif @@ -1420,6 +1512,10 @@ int php_request_startup(TSRMLS_D) { int retval = SUCCESS; +#ifdef HAVE_DTRACE + DTRACE_REQUEST_STARTUP(SAFE_FILENAME(SG(request_info).path_translated), SAFE_FILENAME(SG(request_info).request_uri), SAFE_FILENAME(SG(request_info).request_method)); +#endif /* HAVE_DTRACE */ + #ifdef PHP_WIN32 PG(com_initialized) = 0; #endif @@ -1443,14 +1539,18 @@ int php_request_startup(TSRMLS_D) zend_activate(TSRMLS_C); sapi_activate(TSRMLS_C); +#ifdef ZEND_SIGNALS + zend_signal_activate(TSRMLS_C); +#endif + if (PG(max_input_time) == -1) { zend_set_timeout(EG(timeout_seconds), 1); } else { zend_set_timeout(PG(max_input_time), 1); } - /* Disable realpath cache if safe_mode or open_basedir are set */ - if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) { + /* Disable realpath cache if an open_basedir is set */ + if (PG(open_basedir) && *PG(open_basedir)) { CWDG(realpath_cache_size_limit) = 0; } @@ -1459,15 +1559,16 @@ int php_request_startup(TSRMLS_D) } if (PG(output_handler) && PG(output_handler)[0]) { - php_start_ob_buffer_named(PG(output_handler), 0, 1 TSRMLS_CC); + zval *oh; + + MAKE_STD_ZVAL(oh); + ZVAL_STRING(oh, PG(output_handler), 1); + php_output_start_user(oh, 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC); + zval_ptr_dtor(&oh); } else if (PG(output_buffering)) { - if (PG(output_buffering)>1) { - php_start_ob_buffer(NULL, PG(output_buffering), 1 TSRMLS_CC); - } else { - php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC); - } + php_output_start_user(NULL, PG(output_buffering) > 1 ? PG(output_buffering) : 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC); } else if (PG(implicit_flush)) { - php_start_implicit_flush(TSRMLS_C); + php_output_set_implicit_flush(1 TSRMLS_CC); } /* We turn this off in php_execute_script() */ @@ -1503,7 +1604,6 @@ int php_request_startup(TSRMLS_D) zend_try { PG(during_request_startup) = 1; - php_output_activate(TSRMLS_C); if (PG(expose_php)) { sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); } @@ -1547,6 +1647,7 @@ void php_request_shutdown_for_exec(void *dummy) /* used to close fd's in the 3..255 range here, but it's problematic */ shutdown_memory_manager(1, 1 TSRMLS_CC); + zend_interned_strings_restore(TSRMLS_C); } /* }}} */ @@ -1566,6 +1667,10 @@ void php_request_shutdown_for_hook(void *dummy) } zend_try { + zend_unset_timeout(TSRMLS_C); + } zend_end_try(); + + zend_try { int i; for (i = 0; i < NUM_TRACK_VARS; i++) { @@ -1589,9 +1694,13 @@ void php_request_shutdown_for_hook(void *dummy) shutdown_memory_manager(CG(unclean_shutdown), 0 TSRMLS_CC); } zend_end_try(); + zend_interned_strings_restore(TSRMLS_C); + +#ifdef ZEND_SIGNALS zend_try { - zend_unset_timeout(TSRMLS_C); + zend_signal_deactivate(TSRMLS_C); } zend_end_try(); +#endif } /* }}} */ @@ -1626,16 +1735,23 @@ void php_request_shutdown(void *dummy) /* 3. Flush all output buffers */ zend_try { zend_bool send_buffer = SG(request_info).headers_only ? 0 : 1; + if (CG(unclean_shutdown) && PG(last_error_type) == E_ERROR && - OG(ob_nesting_level) && !OG(active_ob_buffer).chunk_size && PG(memory_limit) < zend_memory_usage(1 TSRMLS_CC)) { + (size_t)PG(memory_limit) < zend_memory_usage(1 TSRMLS_CC) + ) { send_buffer = 0; } - php_end_ob_buffers(send_buffer TSRMLS_CC); + + if (!send_buffer) { + php_output_discard_all(TSRMLS_C); + } else { + php_output_end_all(TSRMLS_C); + } } zend_end_try(); - /* 4. Send the set HTTP headers (note: This must be done AFTER php_end_ob_buffers() !!) */ + /* 4. Reset max_execution_time (no longer executing php code after response sent) */ zend_try { - sapi_send_headers(TSRMLS_C); + zend_unset_timeout(TSRMLS_C); } zend_end_try(); /* 5. Call all extensions RSHUTDOWN functions */ @@ -1644,7 +1760,12 @@ void php_request_shutdown(void *dummy) php_free_shutdown_functions(TSRMLS_C); } - /* 6. Destroy super-globals */ + /* 6. Shutdown output layer (send the set HTTP headers, cleanup output handlers, etc.) */ + zend_try { + php_output_deactivate(TSRMLS_C); + } zend_end_try(); + + /* 7. Destroy super-globals */ zend_try { int i; @@ -1655,7 +1776,7 @@ void php_request_shutdown(void *dummy) } } zend_end_try(); - /* 6.5 free last error information */ + /* 7.5 free last error information */ if (PG(last_error_message)) { free(PG(last_error_message)); PG(last_error_message) = NULL; @@ -1687,6 +1808,7 @@ void php_request_shutdown(void *dummy) zend_try { shutdown_memory_manager(CG(unclean_shutdown) || !report_memleaks, 0 TSRMLS_CC); } zend_end_try(); + zend_interned_strings_restore(TSRMLS_C); /* 12. Reset max_execution_time */ zend_try { @@ -1699,6 +1821,10 @@ void php_request_shutdown(void *dummy) PG(com_initialized) = 0; } #endif + +#ifdef HAVE_DTRACE + DTRACE_REQUEST_SHUTDOWN(SAFE_FILENAME(SG(request_info).path_translated), SAFE_FILENAME(SG(request_info).request_uri), SAFE_FILENAME(SG(request_info).request_method)); +#endif /* HAVE_DTRACE */ } /* }}} */ @@ -1716,12 +1842,12 @@ PHPAPI void php_com_initialize(TSRMLS_D) } /* }}} */ -/* {{{ php_body_write_wrapper +/* {{{ php_output_wrapper */ -static int php_body_write_wrapper(const char *str, uint str_length) +static int php_output_wrapper(const char *str, uint str_length) { TSRMLS_FETCH(); - return php_body_write(str, str_length TSRMLS_CC); + return php_output_write(str, str_length TSRMLS_CC); } /* }}} */ @@ -1753,6 +1879,9 @@ static void core_globals_dtor(php_core_globals *core_globals TSRMLS_DC) if (core_globals->disable_classes) { free(core_globals->disable_classes); } + if (core_globals->php_binary) { + free(core_globals->php_binary); + } php_shutdown_ticks(TSRMLS_C); } @@ -1784,7 +1913,7 @@ int php_register_extensions(zend_module_entry **ptr, int count TSRMLS_DC) } /* }}} */ -#if defined(PHP_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1400) +#if defined(PHP_WIN32) && _MSC_VER >= 1400 static _invalid_parameter_handler old_invalid_parameter_handler; void dummy_invalid_parameter_handler( @@ -1824,7 +1953,7 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod { zend_utility_functions zuf; zend_utility_values zuv; - int module_number=0; /* for REGISTER_INI_ENTRIES() */ + int retval = SUCCESS, module_number=0; /* for REGISTER_INI_ENTRIES() */ char *php_os; zend_module_entry *module; #ifdef ZTS @@ -1832,19 +1961,14 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod void ***tsrm_ls; php_core_globals *core_globals; #endif + #if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) WORD wVersionRequested = MAKEWORD(2, 0); WSADATA wsaData; #endif #ifdef PHP_WIN32 - DWORD dwVersion = GetVersion(); - /* Get build numbers for Windows NT or Win95 */ - if (dwVersion < 0x80000000){ - php_os="WINNT"; - } else { - php_os="WIN32"; - } -#if defined(_MSC_VER) && (_MSC_VER >= 1400) + php_os = "WINNT"; +#if _MSC_VER >= 1400 old_invalid_parameter_handler = _set_invalid_parameter_handler(dummy_invalid_parameter_handler); if (old_invalid_parameter_handler != NULL) { @@ -1881,7 +2005,7 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod zuf.error_function = php_error_cb; zuf.printf_function = php_printf; - zuf.write_function = php_body_write_wrapper; + zuf.write_function = php_output_wrapper; zuf.fopen_function = php_fopen_wrapper_for_zend; zuf.message_handler = php_message_handler_for_zend; zuf.block_interruptions = sapi_module.block_interruptions; @@ -1991,11 +2115,6 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod REGISTER_MAIN_LONG_CONSTANT("PHP_MAXPATHLEN", MAXPATHLEN, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_INT_MAX", LONG_MAX, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_INT_SIZE", sizeof(long), CONST_PERSISTENT | CONST_CS); -#ifdef ZEND_MULTIBYTE - REGISTER_MAIN_LONG_CONSTANT("ZEND_MULTIBYTE", 1, CONST_PERSISTENT | CONST_CS); -#else - REGISTER_MAIN_LONG_CONSTANT("ZEND_MULTIBYTE", 0, CONST_PERSISTENT | CONST_CS); -#endif #ifdef PHP_WIN32 REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MAJOR", EG(windows_version_info).dwMajorVersion, CONST_PERSISTENT | CONST_CS); @@ -2011,6 +2130,13 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_WORKSTATION", VER_NT_WORKSTATION, CONST_PERSISTENT | CONST_CS); #endif + php_binary_init(TSRMLS_C); + if (PG(php_binary)) { + REGISTER_MAIN_STRINGL_CONSTANT("PHP_BINARY", PG(php_binary), strlen(PG(php_binary)), CONST_PERSISTENT | CONST_CS); + } else { + REGISTER_MAIN_STRINGL_CONSTANT("PHP_BINARY", "", 0, CONST_PERSISTENT | CONST_CS); + } + php_output_register_constants(TSRMLS_C); php_rfc1867_register_constants(TSRMLS_C); @@ -2027,8 +2153,8 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod /* Register Zend ini entries */ zend_register_standard_ini_entries(TSRMLS_C); - /* Disable realpath cache if safe_mode or open_basedir are set */ - if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) { + /* Disable realpath cache if an open_basedir is set */ + if (PG(open_basedir) && *PG(open_basedir)) { CWDG(realpath_cache_size_limit) = 0; } @@ -2076,6 +2202,8 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod /* start Zend extensions */ zend_startup_extensions(); + zend_collect_module_handlers(TSRMLS_C); + /* register additional functions */ if (sapi_module.additional_functions) { if (zend_hash_find(&module_registry, "standard", sizeof("standard"), (void**)&module)==SUCCESS) { @@ -2098,7 +2226,7 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod #ifdef PHP_WIN32 /* Disable incompatible functions for the running platform */ - if (php_win32_disable_functions() == FAILURE) { + if (php_win32_disable_functions(TSRMLS_C) == FAILURE) { php_printf("Unable to disable unsupported functions\n"); return FAILURE; } @@ -2111,41 +2239,74 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod module_initialized = 1; /* Check for deprecated directives */ - /* NOTE: If you add anything here, remember to add it also in Makefile.global! */ + /* NOTE: If you add anything here, remember to add it to Makefile.global! */ { - static const char *directives[] = { - "define_syslog_variables", - "register_globals", - "register_long_arrays", - "safe_mode", - "magic_quotes_gpc", - "magic_quotes_runtime", - "magic_quotes_sybase", - NULL + struct { + const long error_level; + const char *phrase; + const char *directives[16]; /* Remember to change this if the number of directives change */ + } directives[2] = { + { + E_DEPRECATED, + "Directive '%s' is deprecated in PHP 5.3 and greater", + { + NULL + } + }, + { + E_CORE_ERROR, + "Directive '%s' is no longer available in PHP", + { + "allow_call_time_pass_reference", + "define_syslog_variables", + "highlight.bg", + "magic_quotes_gpc", + "magic_quotes_runtime", + "magic_quotes_sybase", + "register_globals", + "register_long_arrays", + "safe_mode", + "safe_mode_gid", + "safe_mode_include_dir", + "safe_mode_exec_dir", + "safe_mode_allowed_env_vars", + "safe_mode_protected_env_vars", + "zend.ze1_compatibility_mode", + NULL + } + } }; - const char **p = directives; - long val; - while (*p) { - if (cfg_get_long((char*)*p, &val) == SUCCESS && val) { - zend_error(E_DEPRECATED, "Directive '%s' is deprecated in PHP 5.3 and greater", *p); - } - ++p; - } + unsigned int i; + + zend_try { + /* 2 = Count of deprecation structs */ + for (i = 0; i < 2; i++) { + const char **p = directives[i].directives; - /* This is not too nice, but since its the only one theres no need for extra stuff here */ - if (cfg_get_long("zend.ze1_compatibility_mode", &val) == SUCCESS && val) { - zend_error(E_CORE_ERROR, "zend.ze1_compatibility_mode is no longer supported in PHP 5.3 and greater"); - } + while(*p) { + long value; + + if (cfg_get_long((char*)*p, &value) == SUCCESS && value) { + zend_error(directives[i].error_level, directives[i].phrase, *p); + } + + ++p; + } + } + } zend_catch { + retval = FAILURE; + } zend_end_try(); } sapi_deactivate(TSRMLS_C); module_startup = 0; shutdown_memory_manager(1, 0 TSRMLS_CC); + zend_interned_strings_snapshot(TSRMLS_C); /* we're done */ - return SUCCESS; + return retval; } /* }}} */ @@ -2205,17 +2366,22 @@ void php_module_shutdown(TSRMLS_D) #ifndef ZTS zend_ini_shutdown(TSRMLS_C); shutdown_memory_manager(CG(unclean_shutdown), 1 TSRMLS_CC); - core_globals_dtor(&core_globals TSRMLS_CC); - gc_globals_dtor(TSRMLS_C); #else zend_ini_global_shutdown(TSRMLS_C); - ts_free_id(core_globals_id); #endif + php_output_shutdown(); php_shutdown_temporary_directory(); module_initialized = 0; +#ifndef ZTS + core_globals_dtor(&core_globals TSRMLS_CC); + gc_globals_dtor(TSRMLS_C); +#else + ts_free_id(core_globals_id); +#endif + #if defined(PHP_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1400) if (old_invalid_parameter_handler == NULL) { _set_invalid_parameter_handler(old_invalid_parameter_handler); @@ -2265,7 +2431,7 @@ PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC) /* this looks nasty to me */ old_cwd_fd = open(".", 0); #else - VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1); + php_ignore_value(VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1)); #endif VCWD_CHDIR_FILE(primary_file->filename); } @@ -2324,7 +2490,7 @@ PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC) } #else if (old_cwd[0] != '\0') { - VCWD_CHDIR(old_cwd); + php_ignore_value(VCWD_CHDIR(old_cwd)); } free_alloca(old_cwd, use_heap); #endif @@ -2354,14 +2520,14 @@ PHPAPI int php_execute_simple_script(zend_file_handle *primary_file, zval **ret PG(during_request_startup) = 0; if (primary_file->filename && !(SG(options) & SAPI_OPTION_NO_CHDIR)) { - VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1); + php_ignore_value(VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1)); VCWD_CHDIR_FILE(primary_file->filename); } zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, ret, 1, primary_file); } zend_end_try(); if (old_cwd[0] != '\0') { - VCWD_CHDIR(old_cwd); + php_ignore_value(VCWD_CHDIR(old_cwd)); } free_alloca(old_cwd, use_heap); @@ -2376,7 +2542,7 @@ PHPAPI void php_handle_aborted_connection(void) TSRMLS_FETCH(); PG(connection_status) = PHP_CONNECTION_ABORTED; - php_output_set_status(0 TSRMLS_CC); + php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC); if (!PG(ignore_user_abort)) { zend_bailout(); diff --git a/main/network.c b/main/network.c index 56b08978a7..6c7031f983 100644 --- a/main/network.c +++ b/main/network.c @@ -148,7 +148,7 @@ static const char *php_gai_strerror(int code) /* {{{ php_network_freeaddresses */ -static void php_network_freeaddresses(struct sockaddr **sal) +PHPAPI void php_network_freeaddresses(struct sockaddr **sal) { struct sockaddr **sap; @@ -163,7 +163,7 @@ static void php_network_freeaddresses(struct sockaddr **sal) /* {{{ php_network_getaddresses * Returns number of addresses, 0 for none/error */ -static int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, char **error_string TSRMLS_DC) +PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, char **error_string TSRMLS_DC) { struct sockaddr **sap; int n; @@ -1058,7 +1058,7 @@ PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short p reslen = spprintf(&res, 0, "tcp://%s:%d", host, port); - stream = php_stream_xport_create(res, reslen, ENFORCE_SAFE_MODE | REPORT_ERRORS, + stream = php_stream_xport_create(res, reslen, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL); efree(res); diff --git a/main/output.c b/main/output.c index 5a7ed7b1a2..e100057ca9 100644 --- a/main/output.c +++ b/main/output.c @@ -15,39 +15,83 @@ | Authors: Zeev Suraski <zeev@zend.com> | | Thies C. Arntzen <thies@thieso.net> | | Marcus Boerger <helly@php.net> | + | New API: Michael Wallner <mike@php.net> | +----------------------------------------------------------------------+ */ /* $Id$ */ +#ifndef PHP_OUTPUT_DEBUG +# define PHP_OUTPUT_DEBUG 0 +#endif +#ifndef PHP_OUTPUT_NOINLINE +# define PHP_OUTPUT_NOINLINE 0 +#endif + #include "php.h" #include "ext/standard/head.h" -#include "ext/standard/basic_functions.h" #include "ext/standard/url_scanner_ex.h" -#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB) -#include "ext/zlib/php_zlib.h" -#endif #include "SAPI.h" +#include "zend_stack.h" +#include "php_output.h" -#define OB_DEFAULT_HANDLER_NAME "default output handler" +ZEND_DECLARE_MODULE_GLOBALS(output); -/* output functions */ -static int php_b_body_write(const char *str, uint str_length TSRMLS_DC); +const char php_output_default_handler_name[sizeof("default output handler")] = "default output handler"; +const char php_output_devnull_handler_name[sizeof("null output handler")] = "null output handler"; -static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC); -static void php_ob_append(const char *text, uint text_length TSRMLS_DC); -#if 0 -static void php_ob_prepend(const char *text, uint text_length); +#if PHP_OUTPUT_NOINLINE || PHP_OUTPUT_DEBUG +# undef inline +# define inline #endif -#ifdef ZTS -int output_globals_id; -#else -php_output_globals output_globals; -#endif +/* {{{ aliases, conflict and reverse conflict hash tables */ +static HashTable php_output_handler_aliases; +static HashTable php_output_handler_conflicts; +static HashTable php_output_handler_reverse_conflicts; +/* }}} */ + +/* {{{ forward declarations */ +static inline int php_output_lock_error(int op TSRMLS_DC); +static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC); + +static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC); +static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context); +static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC); +static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry); + +static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC); +static inline void php_output_context_reset(php_output_context *context); +static inline void php_output_context_swap(php_output_context *context); +static inline void php_output_context_dtor(php_output_context *context); + +static inline int php_output_stack_pop(int flags TSRMLS_DC); + +static int php_output_stack_apply_op(void *h, void *c); +static int php_output_stack_apply_clean(void *h, void *c); +static int php_output_stack_apply_list(void *h, void *z); +static int php_output_stack_apply_status(void *h, void *z); -/* {{{ php_default_output_func */ -PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC) +static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context); +static int php_output_handler_default_func(void **handler_context, php_output_context *output_context); +static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context); +/* }}} */ + +/* {{{ static void php_output_init_globals(zend_output_globals *G) + * Initialize the module globals on MINIT */ +static inline void php_output_init_globals(zend_output_globals *G) +{ + memset(G, 0, sizeof(*G)); +} +/* }}} */ + +/* {{{ stderr/stdout writer if not PHP_OUTPUT_ACTIVATED */ +static int php_output_stdout(const char *str, size_t str_len) +{ + fwrite(str, 1, str_len, stdout); + return str_len; +} +static int php_output_stderr(const char *str, size_t str_len) { fwrite(str, 1, str_len, stderr); /* See http://support.microsoft.com/kb/190351 */ @@ -56,706 +100,1199 @@ PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC) #endif return str_len; } +static int (*php_output_direct)(const char *str, size_t str_len) = php_output_stderr; /* }}} */ -/* {{{ php_output_init_globals */ -static void php_output_init_globals(php_output_globals *output_globals_p TSRMLS_DC) +/* {{{ void php_output_header(TSRMLS_D) */ +static void php_output_header(TSRMLS_D) { - OG(php_body_write) = php_default_output_func; - OG(php_header_write) = php_default_output_func; - OG(implicit_flush) = 0; - OG(output_start_filename) = NULL; - OG(output_start_lineno) = 0; + if (!SG(headers_sent)) { + if (!OG(output_start_filename)) { + if (zend_is_compiling(TSRMLS_C)) { + OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C); + OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C); + } else if (zend_is_executing(TSRMLS_C)) { + OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C); + OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C); + } +#if PHP_OUTPUT_DEBUG + fprintf(stderr, "!!! output started at: %s (%d)\n", OG(output_start_filename), OG(output_start_lineno)); +#endif + } + if (!php_header(TSRMLS_C)) { + OG(flags) |= PHP_OUTPUT_DISABLED; + } + } } /* }}} */ -/* {{{ php_output_startup - * Start output layer */ +/* {{{ void php_output_startup(void) + * Set up module globals and initalize the conflict and reverse conflict hash tables */ PHPAPI void php_output_startup(void) { + ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL); + zend_hash_init(&php_output_handler_aliases, 0, NULL, NULL, 1); + zend_hash_init(&php_output_handler_conflicts, 0, NULL, NULL, 1); + zend_hash_init(&php_output_handler_reverse_conflicts, 0, NULL, (void (*)(void *)) zend_hash_destroy, 1); + php_output_direct = php_output_stdout; +} +/* }}} */ + +/* {{{ void php_output_shutdown(void) + * Destroy module globals and the conflict and reverse conflict hash tables */ +PHPAPI void php_output_shutdown(void) +{ + php_output_direct = php_output_stderr; + zend_hash_destroy(&php_output_handler_aliases); + zend_hash_destroy(&php_output_handler_conflicts); + zend_hash_destroy(&php_output_handler_reverse_conflicts); +} +/* }}} */ + +/* {{{ SUCCESS|FAILURE php_output_activate(TSRMLS_D) + * Reset output globals and setup the output handler stack */ +PHPAPI int php_output_activate(TSRMLS_D) +{ #ifdef ZTS - ts_allocate_id(&output_globals_id, sizeof(php_output_globals), (ts_allocate_ctor) php_output_init_globals, NULL); + memset((*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals)); #else - php_output_init_globals(&output_globals TSRMLS_CC); + memset(&output_globals, 0, sizeof(zend_output_globals)); #endif + + zend_stack_init(&OG(handlers)); + OG(flags) |= PHP_OUTPUT_ACTIVATED; + + return SUCCESS; } /* }}} */ -/* {{{ php_output_activate - * Initilize output global for activation */ -PHPAPI void php_output_activate(TSRMLS_D) +/* {{{ void php_output_deactivate(TSRMLS_D) + * Destroy the output handler stack */ +PHPAPI void php_output_deactivate(TSRMLS_D) { - OG(php_body_write) = php_ub_body_write; - OG(php_header_write) = sapi_module.ub_write; - OG(ob_nesting_level) = 0; - OG(ob_lock) = 0; - OG(disable_output) = 0; - OG(output_start_filename) = NULL; - OG(output_start_lineno) = 0; + php_output_handler **handler = NULL; + + php_output_header(TSRMLS_C); + + OG(flags) ^= PHP_OUTPUT_ACTIVATED; + OG(active) = NULL; + OG(running) = NULL; + + /* release all output handlers */ + if (OG(handlers).elements) { + while (SUCCESS == zend_stack_top(&OG(handlers), (void *) &handler)) { + php_output_handler_free(handler TSRMLS_CC); + zend_stack_del_top(&OG(handlers)); + } + zend_stack_destroy(&OG(handlers)); + } + } /* }}} */ -/* {{{ php_output_register_constants */ -void php_output_register_constants(TSRMLS_D) +/* {{{ void php_output_register_constants() */ +PHPAPI void php_output_register_constants(TSRMLS_D) { REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_CONT, CONST_CS | CONST_PERSISTENT); - REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_END, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_WRITE", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSH", PHP_OUTPUT_HANDLER_FLUSH, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEAN", PHP_OUTPUT_HANDLER_CLEAN, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FINAL", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT); + + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEANABLE", PHP_OUTPUT_HANDLER_CLEANABLE, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSHABLE", PHP_OUTPUT_HANDLER_FLUSHABLE, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_REMOVABLE", PHP_OUTPUT_HANDLER_REMOVABLE, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STDFLAGS", PHP_OUTPUT_HANDLER_STDFLAGS, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STARTED", PHP_OUTPUT_HANDLER_STARTED, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_DISABLED", PHP_OUTPUT_HANDLER_DISABLED, CONST_CS | CONST_PERSISTENT); } /* }}} */ -/* {{{ php_output_set_status - * Toggle output status. Do NOT use in application code, only in SAPIs where appropriate. */ -PHPAPI void php_output_set_status(zend_bool status TSRMLS_DC) +/* {{{ void php_output_set_status(int status TSRMLS_DC) + * Used by SAPIs to disable output */ +PHPAPI void php_output_set_status(int status TSRMLS_DC) { - OG(disable_output) = !status; + OG(flags) = (OG(flags) & ~0xf) | (status & 0xf); } /* }}} */ -/* {{{ php_body_write - * Write body part */ -PHPAPI int php_body_write(const char *str, uint str_length TSRMLS_DC) +/* {{{ int php_output_get_status(TSRMLS_C) + * Get output control status */ +PHPAPI int php_output_get_status(TSRMLS_D) { - return OG(php_body_write)(str, str_length TSRMLS_CC); + return ( + OG(flags) + | (OG(active) ? PHP_OUTPUT_ACTIVE : 0) + | (OG(running)? PHP_OUTPUT_LOCKED : 0) + ) & 0xff; } /* }}} */ -/* {{{ php_header_write - * Write HTTP header */ -PHPAPI int php_header_write(const char *str, uint str_length TSRMLS_DC) +/* {{{ int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC) + * Unbuffered write */ +PHPAPI int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC) { - if (OG(disable_output)) { + if (OG(flags) & PHP_OUTPUT_DISABLED) { return 0; - } else { - return OG(php_header_write)(str, str_length TSRMLS_CC); } + if (OG(flags) & PHP_OUTPUT_ACTIVATED) { + return sapi_module.ub_write(str, len TSRMLS_CC); + } + return php_output_direct(str, len); } /* }}} */ -/* {{{ php_start_ob_buffer - * Start output buffering */ -PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC) +/* {{{ int php_output_write(const char *str, size_t len TSRMLS_DC) + * Buffered write */ +PHPAPI int php_output_write(const char *str, size_t len TSRMLS_DC) { - uint initial_size, block_size; + if (OG(flags) & PHP_OUTPUT_DISABLED) { + return 0; + } + if (OG(flags) & PHP_OUTPUT_ACTIVATED) { + php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len TSRMLS_CC); + return (int) len; + } + return php_output_direct(str, len); +} +/* }}} */ - if (OG(ob_lock)) { - if (SG(headers_sent) && !SG(request_info).headers_only) { - OG(php_body_write) = php_ub_body_write_no_header; - } else { - OG(php_body_write) = php_ub_body_write; +/* {{{ SUCCESS|FAILURE php_output_flush(TSRMLS_D) + * Flush the most recent output handlers buffer */ +PHPAPI int php_output_flush(TSRMLS_D) +{ + php_output_context context; + + if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_FLUSHABLE)) { + php_output_context_init(&context, PHP_OUTPUT_HANDLER_FLUSH TSRMLS_CC); + php_output_handler_op(OG(active), &context); + if (context.out.data && context.out.used) { + zend_stack_del_top(&OG(handlers)); + php_output_write(context.out.data, context.out.used TSRMLS_CC); + zend_stack_push(&OG(handlers), &OG(active), sizeof(php_output_handler *)); } - OG(ob_nesting_level) = 0; - php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers"); - return FAILURE; + php_output_context_dtor(&context); + return SUCCESS; } - if (chunk_size > 0) { - if (chunk_size==1) { - chunk_size = 4096; - } - initial_size = (chunk_size*3/2); - block_size = chunk_size/2; - } else { - initial_size = 40*1024; - block_size = 10*1024; + return FAILURE; +} +/* }}} */ + +/* {{{ void php_output_flush_all(TSRMLS_C) + * Flush all output buffers subsequently */ +PHPAPI void php_output_flush_all(TSRMLS_D) +{ + if (OG(active)) { + php_output_op(PHP_OUTPUT_HANDLER_FLUSH, NULL, 0 TSRMLS_CC); } - return php_ob_init(initial_size, block_size, output_handler, chunk_size, erase TSRMLS_CC); } /* }}} */ -/* {{{ php_start_ob_buffer_named - * Start output buffering */ -PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC) +/* {{{ SUCCESS|FAILURE php_output_clean(TSRMLS_D) + * Cleans the most recent output handlers buffer if the handler is cleanable */ +PHPAPI int php_output_clean(TSRMLS_D) { - zval *output_handler; - int result; + php_output_context context; - ALLOC_INIT_ZVAL(output_handler); - Z_STRLEN_P(output_handler) = strlen(output_handler_name); /* this can be optimized */ - Z_STRVAL_P(output_handler) = estrndup(output_handler_name, Z_STRLEN_P(output_handler)); - Z_TYPE_P(output_handler) = IS_STRING; - result = php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC); - zval_dtor(output_handler); - FREE_ZVAL(output_handler); - return result; + if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_CLEANABLE)) { + OG(active)->buffer.used = 0; + php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC); + php_output_handler_op(OG(active), &context); + php_output_context_dtor(&context); + return SUCCESS; + } + return FAILURE; } /* }}} */ -/* {{{ php_end_ob_buffer - * End output buffering (one level) */ -PHPAPI void php_end_ob_buffer(zend_bool send_buffer, zend_bool just_flush TSRMLS_DC) +/* {{{ void php_output_clean_all(TSRMLS_D) + * Cleans all output handler buffers, without regard whether the handler is cleanable */ +PHPAPI void php_output_clean_all(TSRMLS_D) { - char *final_buffer=NULL; - unsigned int final_buffer_length=0; - zval *alternate_buffer=NULL; - char *to_be_destroyed_buffer, *to_be_destroyed_handler_name; - char *to_be_destroyed_handled_output[2] = { 0, 0 }; - int status; - php_ob_buffer *prev_ob_buffer_p=NULL; - php_ob_buffer orig_ob_buffer; + php_output_context context; - if (OG(ob_nesting_level)==0) { - return; - } - status = 0; - if (!OG(active_ob_buffer).status & PHP_OUTPUT_HANDLER_START) { - /* our first call */ - status |= PHP_OUTPUT_HANDLER_START; - } - if (just_flush) { - status |= PHP_OUTPUT_HANDLER_CONT; - } else { - status |= PHP_OUTPUT_HANDLER_END; + if (OG(active)) { + php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC); + zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_clean, &context); } +} -#if 0 - { - FILE *fp; - fp = fopen("/tmp/ob_log", "a"); - fprintf(fp, "NestLevel: %d ObStatus: %d HandlerName: %s\n", OG(ob_nesting_level), status, OG(active_ob_buffer).handler_name); - fclose(fp); - } -#endif +/* {{{ SUCCESS|FAILURE php_output_end(TSRMLS_D) + * Finalizes the most recent output handler at pops it off the stack if the handler is removable */ +PHPAPI int php_output_end(TSRMLS_D) +{ + if (php_output_stack_pop(PHP_OUTPUT_POP_TRY TSRMLS_CC)) { + return SUCCESS; + } + return FAILURE; +} +/* }}} */ - if (OG(active_ob_buffer).internal_output_handler) { - final_buffer = OG(active_ob_buffer).internal_output_handler_buffer; - final_buffer_length = OG(active_ob_buffer).internal_output_handler_buffer_size; - 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); - } else if (OG(active_ob_buffer).output_handler) { - zval **params[2]; - zval *orig_buffer; - zval *z_status; - - if(OG(ob_lock)) { - if (SG(headers_sent) && !SG(request_info).headers_only) { - OG(php_body_write) = php_ub_body_write_no_header; - } else { - OG(php_body_write) = php_ub_body_write; - } - OG(ob_nesting_level) = 0; - php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers"); - return; - } +/* {{{ void php_output_end_all(TSRMLS_D) + * Finalizes all output handlers and ends output buffering without regard whether a handler is removable */ +PHPAPI void php_output_end_all(TSRMLS_D) +{ + while (OG(active) && php_output_stack_pop(PHP_OUTPUT_POP_FORCE TSRMLS_CC)); +} +/* }}} */ - ALLOC_INIT_ZVAL(orig_buffer); - ZVAL_STRINGL(orig_buffer, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1); +/* {{{ SUCCESS|FAILURE php_output_discard(TSRMLS_D) + * Discards the most recent output handlers buffer and pops it off the stack if the handler is removable */ +PHPAPI int php_output_discard(TSRMLS_D) +{ + if (php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_TRY TSRMLS_CC)) { + return SUCCESS; + } + return FAILURE; +} +/* }}} */ - ALLOC_INIT_ZVAL(z_status); - ZVAL_LONG(z_status, status); +/* {{{ void php_output_discard_all(TSRMLS_D) + * Discard all output handlers and buffers without regard whether a handler is removable */ +PHPAPI void php_output_discard_all(TSRMLS_D) +{ + while (OG(active)) { + php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_FORCE TSRMLS_CC); + } +} +/* }}} */ - params[0] = &orig_buffer; - params[1] = &z_status; - OG(ob_lock) = 1; +/* {{{ int php_output_get_level(TSRMLS_D) + * Get output buffering level, ie. how many output handlers the stack contains */ +PHPAPI int php_output_get_level(TSRMLS_D) +{ + return OG(active) ? zend_stack_count(&OG(handlers)) : 0; +} +/* }}} */ - if (call_user_function_ex(CG(function_table), NULL, OG(active_ob_buffer).output_handler, &alternate_buffer, 2, params, 1, NULL TSRMLS_CC)==SUCCESS) { - if (alternate_buffer && !(Z_TYPE_P(alternate_buffer)==IS_BOOL && Z_BVAL_P(alternate_buffer)==0)) { - convert_to_string_ex(&alternate_buffer); - final_buffer = Z_STRVAL_P(alternate_buffer); - final_buffer_length = Z_STRLEN_P(alternate_buffer); - } - } - OG(ob_lock) = 0; - if (!just_flush) { - zval_ptr_dtor(&OG(active_ob_buffer).output_handler); - } - zval_ptr_dtor(&orig_buffer); - zval_ptr_dtor(&z_status); +/* {{{ SUCCESS|FAILURE php_output_get_contents(zval *z TSRMLS_DC) + * Get the contents of the active output handlers buffer */ +PHPAPI int php_output_get_contents(zval *p TSRMLS_DC) +{ + if (OG(active)) { + ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used, 1); + return SUCCESS; + } else { + ZVAL_NULL(p); + return FAILURE; } +} - if (!final_buffer) { - final_buffer = OG(active_ob_buffer).buffer; - final_buffer_length = OG(active_ob_buffer).text_length; +/* {{{ SUCCESS|FAILURE php_output_get_length(zval *z TSRMLS_DC) + * Get the length of the active output handlers buffer */ +PHPAPI int php_output_get_length(zval *p TSRMLS_DC) +{ + if (OG(active)) { + ZVAL_LONG(p, OG(active)->buffer.used); + return SUCCESS; + } else { + ZVAL_NULL(p); + return FAILURE; } +} +/* }}} */ - if (OG(ob_nesting_level)==1) { /* end buffering */ - if (SG(headers_sent) && !SG(request_info).headers_only) { - OG(php_body_write) = php_ub_body_write_no_header; - } else { - OG(php_body_write) = php_ub_body_write; - } - } +/* {{{ php_output_handler* php_output_get_active_handler(TSRMLS_D) + * Get active output handler */ +PHPAPI php_output_handler* php_output_get_active_handler(TSRMLS_D) +{ + return OG(active); +} +/* }}} */ - to_be_destroyed_buffer = OG(active_ob_buffer).buffer; - to_be_destroyed_handler_name = OG(active_ob_buffer).handler_name; - if (OG(active_ob_buffer).internal_output_handler - && (final_buffer != OG(active_ob_buffer).internal_output_handler_buffer) - && (final_buffer != OG(active_ob_buffer).buffer)) { - to_be_destroyed_handled_output[0] = final_buffer; - } +/* {{{ SUCCESS|FAILURE php_output_handler_start_default(TSRMLS_D) + * Start a "default output handler" */ +PHPAPI int php_output_start_default(TSRMLS_D) +{ + php_output_handler *handler; - if (!just_flush) { - if (OG(active_ob_buffer).internal_output_handler) { - to_be_destroyed_handled_output[1] = OG(active_ob_buffer).internal_output_handler_buffer; - } + 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); + if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { + return SUCCESS; } - if (OG(ob_nesting_level)>1) { /* restore previous buffer */ - zend_stack_top(&OG(ob_buffers), (void **) &prev_ob_buffer_p); - orig_ob_buffer = OG(active_ob_buffer); - OG(active_ob_buffer) = *prev_ob_buffer_p; - zend_stack_del_top(&OG(ob_buffers)); - if (!just_flush && OG(ob_nesting_level)==2) { /* destroy the stack */ - zend_stack_destroy(&OG(ob_buffers)); - } - } - OG(ob_nesting_level)--; + php_output_handler_free(&handler TSRMLS_CC); + return FAILURE; +} +/* }}} */ - if (send_buffer) { - if (just_flush) { /* if flush is called prior to proper end, ensure presence of NUL */ - final_buffer[final_buffer_length] = '\0'; - } - OG(php_body_write)(final_buffer, final_buffer_length TSRMLS_CC); - } +/* {{{ SUCCESS|FAILURE php_output_handler_start_devnull(TSRMLS_D) + * Start a "null output handler" */ +PHPAPI int php_output_start_devnull(TSRMLS_D) +{ + php_output_handler *handler; - if (just_flush) { /* we restored the previous ob, return to the current */ - if (prev_ob_buffer_p) { - zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer)); - OG(active_ob_buffer) = orig_ob_buffer; - } - OG(ob_nesting_level)++; + 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); + if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { + return SUCCESS; } + php_output_handler_free(&handler TSRMLS_CC); + return FAILURE; +} +/* }}} */ - if (alternate_buffer) { - zval_ptr_dtor(&alternate_buffer); - } +/* {{{ SUCCESS|FAILURE php_output_start_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC) + * Start a user level output handler */ +PHPAPI int php_output_start_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC) +{ + php_output_handler *handler; - if (status & PHP_OUTPUT_HANDLER_END) { - efree(to_be_destroyed_handler_name); - } - if (!just_flush) { - efree(to_be_destroyed_buffer); + if (output_handler) { + handler = php_output_handler_create_user(output_handler, chunk_size, flags TSRMLS_CC); } else { - OG(active_ob_buffer).text_length = 0; - OG(active_ob_buffer).status |= PHP_OUTPUT_HANDLER_START; - OG(php_body_write) = php_b_body_write; - } - if (to_be_destroyed_handled_output[0]) { - efree(to_be_destroyed_handled_output[0]); + handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC); } - if (to_be_destroyed_handled_output[1]) { - efree(to_be_destroyed_handled_output[1]); + if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { + return SUCCESS; } + php_output_handler_free(&handler TSRMLS_CC); + return FAILURE; } /* }}} */ -/* {{{ php_end_ob_buffers - * End output buffering (all buffers) */ -PHPAPI void php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC) +/* {{{ SUCCESS|FAILURE php_output_start_internal(zval *name, php_output_handler_func_t handler, size_t chunk_size, int flags TSRMLS_DC) + * Start an internal output handler that does not have to maintain a non-global state */ +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) { - while (OG(ob_nesting_level)!=0) { - php_end_ob_buffer(send_buffer, 0 TSRMLS_CC); + php_output_handler *handler; + + handler = php_output_handler_create_internal(name, name_len, php_output_handler_compat_func, chunk_size, flags TSRMLS_CC); + php_output_handler_set_context(handler, output_handler, NULL TSRMLS_CC); + if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { + return SUCCESS; } + php_output_handler_free(&handler TSRMLS_CC); + return FAILURE; } /* }}} */ -/* {{{ php_start_implicit_flush - */ -PHPAPI void php_start_implicit_flush(TSRMLS_D) +/* {{{ php_output_handler *php_output_handler_create_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC) + * Create a user level output handler */ +PHPAPI php_output_handler *php_output_handler_create_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC) { - OG(implicit_flush) = 1; -} -/* }}} */ + char *handler_name = NULL, *error = NULL; + php_output_handler *handler = NULL; + php_output_handler_alias_ctor_t *alias = NULL; + php_output_handler_user_func_t *user = NULL; + + switch (Z_TYPE_P(output_handler)) { + case IS_NULL: + handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC); + break; + case IS_STRING: + if (Z_STRLEN_P(output_handler) && (alias = php_output_handler_alias(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler) TSRMLS_CC))) { + handler = (*alias)(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler), chunk_size, flags TSRMLS_CC); + break; + } + default: + user = ecalloc(1, sizeof(php_output_handler_user_func_t)); + if (SUCCESS == zend_fcall_info_init(output_handler, 0, &user->fci, &user->fcc, &handler_name, &error TSRMLS_CC)) { + handler = php_output_handler_init(handler_name, strlen(handler_name), chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_USER TSRMLS_CC); + Z_ADDREF_P(output_handler); + user->zoh = output_handler; + handler->func.user = user; + } else { + efree(user); + } + if (error) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "%s", error); + efree(error); + } + if (handler_name) { + efree(handler_name); + } + } -/* {{{ php_end_implicit_flush - */ -PHPAPI void php_end_implicit_flush(TSRMLS_D) -{ - OG(implicit_flush) = 0; + return handler; } /* }}} */ -/* {{{ char *php_get_output_start_filename(TSRMLS_D) - * Return filename start output something */ -PHPAPI char *php_get_output_start_filename(TSRMLS_D) +/* {{{ 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) + * Create an internal output handler that can maintain a non-global state */ +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) { - return OG(output_start_filename); + php_output_handler *handler; + + handler = php_output_handler_init(name, name_len, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_INTERNAL TSRMLS_CC); + handler->func.internal = output_handler; + + return handler; } /* }}} */ -/* {{{ char *php_get_output_start_lineno(TSRMLS_D) - * Return line number start output something */ -PHPAPI int php_get_output_start_lineno(TSRMLS_D) +/* {{{ void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC) + * Set the context/state of an output handler. Calls the dtor of the previous context if there is one */ +PHPAPI void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC) { - return OG(output_start_lineno); + if (handler->dtor && handler->opaq) { + handler->dtor(handler->opaq TSRMLS_CC); + } + handler->dtor = dtor; + handler->opaq = opaq; } /* }}} */ -/* {{{ php_ob_set_internal_handler - */ -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) +/* {{{ SUCCESS|FAILURE php_output_handler_start(php_output_handler *handler TSRMLS_DC) + * Starts the set up output handler and pushes it on top of the stack. Checks for any conflicts regarding the output handler to start */ +PHPAPI int php_output_handler_start(php_output_handler *handler TSRMLS_DC) { - if (OG(ob_nesting_level) == 0 || OG(active_ob_buffer).internal_output_handler || strcmp(OG(active_ob_buffer).handler_name, OB_DEFAULT_HANDLER_NAME)) { - php_start_ob_buffer(NULL, buffer_size, erase TSRMLS_CC); - } + HashPosition pos; + HashTable *rconflicts; + php_output_handler_conflict_check_t *conflict; - OG(active_ob_buffer).internal_output_handler = internal_output_handler; - OG(active_ob_buffer).internal_output_handler_buffer = (char *) emalloc(buffer_size); - OG(active_ob_buffer).internal_output_handler_buffer_size = buffer_size; - if (OG(active_ob_buffer).handler_name) { - efree(OG(active_ob_buffer).handler_name); + if (php_output_lock_error(PHP_OUTPUT_HANDLER_START TSRMLS_CC) || !handler) { + return FAILURE; } - OG(active_ob_buffer).handler_name = estrdup(handler_name); - OG(active_ob_buffer).erase = erase; + if (SUCCESS == zend_hash_find(&php_output_handler_conflicts, handler->name, handler->name_len+1, (void *) &conflict)) { + if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) { + return FAILURE; + } + } + if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, handler->name, handler->name_len+1, (void *) &rconflicts)) { + for (zend_hash_internal_pointer_reset_ex(rconflicts, &pos); + zend_hash_get_current_data_ex(rconflicts, (void *) &conflict, &pos) == SUCCESS; + zend_hash_move_forward_ex(rconflicts, &pos) + ) { + if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) { + return FAILURE; + } + } + } + /* zend_stack_push never returns SUCCESS but FAILURE or stack level */ + if (FAILURE == (handler->level = zend_stack_push(&OG(handlers), &handler, sizeof(php_output_handler *)))) { + return FAILURE; + } + OG(active) = handler; + return SUCCESS; } /* }}} */ -/* - * Output buffering - implementation - */ - -/* {{{ php_ob_allocate - */ -static inline void php_ob_allocate(uint text_length TSRMLS_DC) +/* {{{ int php_output_handler_started(zval *name TSRMLS_DC) + * Check whether a certain output handler is in use */ +PHPAPI int php_output_handler_started(const char *name, size_t name_len TSRMLS_DC) { - uint new_len = OG(active_ob_buffer).text_length + text_length; + php_output_handler ***handlers; + int i, count = php_output_get_level(TSRMLS_C); - if (OG(active_ob_buffer).size < new_len) { - uint buf_size = OG(active_ob_buffer).size; - while (buf_size <= new_len) { - buf_size += OG(active_ob_buffer).block_size; - } + if (count) { + handlers = (php_output_handler ***) zend_stack_base(&OG(handlers)); - OG(active_ob_buffer).buffer = (char *) erealloc(OG(active_ob_buffer).buffer, buf_size+1); - OG(active_ob_buffer).size = buf_size; + for (i = 0; i < count; ++i) { + if (name_len == (*(handlers[i]))->name_len && !memcmp((*(handlers[i]))->name, name, name_len)) { + return 1; + } + } } - OG(active_ob_buffer).text_length = new_len; + + return 0; } /* }}} */ -/* {{{ php_ob_init_conflict - * Returns 1 if handler_set is already used and generates error message */ -PHPAPI int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC) +/* {{{ int php_output_handler_conflict(zval *handler_new, zval *handler_old TSRMLS_DC) + * Check whether a certain handler is in use and issue a warning that the new handler would conflict with the already used one */ +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) { - if (php_ob_handler_used(handler_set TSRMLS_CC)) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set); + if (php_output_handler_started(handler_set, handler_set_len TSRMLS_CC)) { + if (handler_new_len != handler_set_len || memcmp(handler_new, handler_set, handler_set_len)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set); + } else { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' cannot be used twice", handler_new); + } return 1; } return 0; } /* }}} */ -/* {{{ php_ob_init_named - */ -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) +/* {{{ SUCCESS|FAILURE php_output_handler_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC) + * Register a conflict checking function on MINIT */ +PHPAPI int php_output_handler_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC) +{ + if (!EG(current_module)) { + zend_error(E_ERROR, "Cannot register an output handler conflict outside of MINIT"); + return FAILURE; + } + return zend_hash_update(&php_output_handler_conflicts, name, name_len+1, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL); +} +/* }}} */ + +/* {{{ SUCCESS|FAILURE php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC) + * Register a reverse conflict checking function on MINIT */ +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) { - php_ob_buffer tmp_buf; + HashTable rev, *rev_ptr = NULL; - if (output_handler && !zend_is_callable(output_handler, 0, NULL TSRMLS_CC)) { + if (!EG(current_module)) { + zend_error(E_ERROR, "Cannot register a reverse output handler conflict outside of MINIT"); return FAILURE; } - tmp_buf.block_size = block_size; - tmp_buf.size = initial_size; - tmp_buf.buffer = (char *) emalloc(initial_size+1); - tmp_buf.text_length = 0; - tmp_buf.output_handler = output_handler; - tmp_buf.chunk_size = chunk_size; - tmp_buf.status = 0; - tmp_buf.internal_output_handler = NULL; - tmp_buf.internal_output_handler_buffer = NULL; - tmp_buf.internal_output_handler_buffer_size = 0; - tmp_buf.handler_name = estrdup(handler_name&&handler_name[0]?handler_name:OB_DEFAULT_HANDLER_NAME); - tmp_buf.erase = erase; - - if (OG(ob_nesting_level)>0) { -#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB) - if (!strncmp(handler_name, "ob_gzhandler", sizeof("ob_gzhandler")) && php_ob_gzhandler_check(TSRMLS_C)) { + if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, name, name_len+1, (void *) &rev_ptr)) { + return zend_hash_next_index_insert(rev_ptr, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL); + } else { + zend_hash_init(&rev, 1, NULL, NULL, 1); + if (SUCCESS != zend_hash_next_index_insert(&rev, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL)) { + zend_hash_destroy(&rev); return FAILURE; } -#endif - if (OG(ob_nesting_level)==1) { /* initialize stack */ - zend_stack_init(&OG(ob_buffers)); + if (SUCCESS != zend_hash_update(&php_output_handler_reverse_conflicts, name, name_len+1, &rev, sizeof(HashTable), NULL)) { + zend_hash_destroy(&rev); + return FAILURE; } - zend_stack_push(&OG(ob_buffers), &OG(active_ob_buffer), sizeof(php_ob_buffer)); + return SUCCESS; } - OG(ob_nesting_level)++; - OG(active_ob_buffer) = tmp_buf; - OG(php_body_write) = php_b_body_write; - return SUCCESS; } /* }}} */ -/* {{{ php_ob_handler_from_string - * Create zval output handler from string */ -static zval* php_ob_handler_from_string(const char *handler_name, int len TSRMLS_DC) +/* {{{ php_output_handler_alias_ctor_t php_output_handler_alias(zval *name TSRMLS_DC) + * Get an internal output handler for a user handler if it exists */ +PHPAPI php_output_handler_alias_ctor_t *php_output_handler_alias(const char *name, size_t name_len TSRMLS_DC) { - zval *output_handler; + php_output_handler_alias_ctor_t *func = NULL; - ALLOC_INIT_ZVAL(output_handler); - Z_STRLEN_P(output_handler) = len; - Z_STRVAL_P(output_handler) = estrndup(handler_name, len); - Z_TYPE_P(output_handler) = IS_STRING; - return output_handler; + zend_hash_find(&php_output_handler_aliases, name, name_len+1, (void *) &func); + return func; } /* }}} */ -/* {{{ php_ob_init - */ -static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC) +/* {{{ SUCCESS|FAILURE php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func TSRMLS_DC) + * Registers an internal output handler as alias for a user handler */ +PHPAPI int php_output_handler_alias_register(const char *name, size_t name_len, php_output_handler_alias_ctor_t func TSRMLS_DC) { - int result = FAILURE, handler_len, len; - char *handler_name, *next_handler_name; - HashPosition pos; - zval **tmp; - zval *handler_zval; - - if (output_handler && output_handler->type == IS_STRING) { - handler_name = Z_STRVAL_P(output_handler); - handler_len = Z_STRLEN_P(output_handler); - - result = SUCCESS; - if (handler_len && handler_name[0] != '\0') { - while ((next_handler_name=strchr(handler_name, ',')) != NULL) { - len = next_handler_name-handler_name; - next_handler_name = estrndup(handler_name, len); - handler_zval = php_ob_handler_from_string(next_handler_name, len TSRMLS_CC); - result = php_ob_init_named(initial_size, block_size, next_handler_name, handler_zval, chunk_size, erase TSRMLS_CC); - if (result != SUCCESS) { - zval_dtor(handler_zval); - FREE_ZVAL(handler_zval); - } - handler_name += len+1; - handler_len -= len+1; - efree(next_handler_name); - } - } - if (result == SUCCESS) { - handler_zval = php_ob_handler_from_string(handler_name, handler_len TSRMLS_CC); - result = php_ob_init_named(initial_size, block_size, handler_name, handler_zval, chunk_size, erase TSRMLS_CC); - if (result != SUCCESS) { - zval_dtor(handler_zval); - FREE_ZVAL(handler_zval); - } - } - } else if (output_handler && output_handler->type == IS_ARRAY) { - /* do we have array(object,method) */ - if (zend_is_callable(output_handler, 0, &handler_name TSRMLS_CC)) { - SEPARATE_ZVAL(&output_handler); - Z_ADDREF_P(output_handler); - result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC); - efree(handler_name); - } else { - efree(handler_name); - /* init all array elements recursively */ - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(output_handler), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(output_handler), (void **)&tmp, &pos) == SUCCESS) { - result = php_ob_init(initial_size, block_size, *tmp, chunk_size, erase TSRMLS_CC); - if (result == FAILURE) { - break; - } - zend_hash_move_forward_ex(Z_ARRVAL_P(output_handler), &pos); - } - } - } else if (output_handler && output_handler->type == IS_OBJECT) { - /* do we have callable object */ - if (zend_is_callable(output_handler, 0, &handler_name TSRMLS_CC)) { - SEPARATE_ZVAL(&output_handler); - Z_ADDREF_P(output_handler); - result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC); - efree(handler_name); - } else { - efree(handler_name); - 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); - result = FAILURE; + if (!EG(current_module)) { + zend_error(E_ERROR, "Cannot register an output handler alias outside of MINIT"); + return FAILURE; + } + return zend_hash_update(&php_output_handler_aliases, name, name_len+1, &func, sizeof(php_output_handler_alias_ctor_t *), NULL); +} +/* }}} */ + +/* {{{ SUCCESS|FAILURE php_output_handler_hook(php_output_handler_hook_t type, void *arg TSMRLS_DC) + * Output handler hook for output handler functions to check/modify the current handlers abilities */ +PHPAPI int php_output_handler_hook(php_output_handler_hook_t type, void *arg TSRMLS_DC) +{ + if (OG(running)) { + switch (type) { + case PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ: + *(void ***) arg = &OG(running)->opaq; + return SUCCESS; + case PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS: + *(int *) arg = OG(running)->flags; + return SUCCESS; + case PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL: + *(int *) arg = OG(running)->level; + return SUCCESS; + case PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE: + OG(running)->flags &= ~(PHP_OUTPUT_HANDLER_REMOVABLE|PHP_OUTPUT_HANDLER_CLEANABLE); + return SUCCESS; + case PHP_OUTPUT_HANDLER_HOOK_DISABLE: + OG(running)->flags |= PHP_OUTPUT_HANDLER_DISABLED; + return SUCCESS; + default: + break; } + } + return FAILURE; +} +/* }}} */ + +/* {{{ void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC) + * Destroy an output handler */ +PHPAPI void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC) +{ + STR_FREE(handler->name); + STR_FREE(handler->buffer.data); + if (handler->flags & PHP_OUTPUT_HANDLER_USER) { + zval_ptr_dtor(&handler->func.user->zoh); + efree(handler->func.user); + } + if (handler->dtor && handler->opaq) { + handler->dtor(handler->opaq TSRMLS_CC); + } + memset(handler, 0, sizeof(*handler)); +} +/* }}} */ + +/* {{{ void php_output_handler_free(php_output_handler **handler TSMRLS_DC) + * Destroy and free an output handler */ +PHPAPI void php_output_handler_free(php_output_handler **h TSRMLS_DC) +{ + if (*h) { + php_output_handler_dtor(*h TSRMLS_CC); + efree(*h); + *h = NULL; + } +} +/* }}} */ + +/* void php_output_set_implicit_flush(int enabled TSRMLS_DC) + * Enable or disable implicit flush */ +PHPAPI void php_output_set_implicit_flush(int flush TSRMLS_DC) +{ + if (flush) { + OG(flags) |= PHP_OUTPUT_IMPLICITFLUSH; } else { - result = php_ob_init_named(initial_size, block_size, OB_DEFAULT_HANDLER_NAME, NULL, chunk_size, erase TSRMLS_CC); + OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH; } - return result; } /* }}} */ -/* {{{ php_ob_list_each - */ -static int php_ob_list_each(php_ob_buffer *ob_buffer, zval *ob_handler_array) +/* {{{ char *php_output_get_start_filename(TSRMLS_D) + * Get the file name where output has started */ +PHPAPI const char *php_output_get_start_filename(TSRMLS_D) { - add_next_index_string(ob_handler_array, ob_buffer->handler_name, 1); - return 0; + return OG(output_start_filename); } /* }}} */ -/* {{{ php_ob_used_each - Sets handler_name to NULL is found */ -static int php_ob_handler_used_each(php_ob_buffer *ob_buffer, char **handler_name) +/* {{{ int php_output_get_start_lineno(TSRMLS_D) + * Get the line number where output has started */ +PHPAPI int php_output_get_start_lineno(TSRMLS_D) { - if (!strcmp(ob_buffer->handler_name, *handler_name)) { - *handler_name = NULL; + return OG(output_start_lineno); +} +/* }}} */ + +/* {{{ static int php_output_lock_error(int op TSRMLS_DC) + * Checks whether an unallowed operation is attempted from within the output handler and issues a fatal error */ +static inline int php_output_lock_error(int op TSRMLS_DC) +{ + /* if there's no ob active, ob has been stopped */ + if (op && OG(active) && OG(running)) { + /* fatal error */ + php_output_deactivate(TSRMLS_C); + php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers"); return 1; } return 0; } /* }}} */ -/* {{{ php_ob_used - returns 1 if given handler_name is used as output_handler */ -PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC) +/* {{{ static php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC) + * Initialize a new output context */ +static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC) { - char *tmp = handler_name; + if (!context) { + context = emalloc(sizeof(php_output_context)); + } - if (OG(ob_nesting_level)) { - if (!strcmp(OG(active_ob_buffer).handler_name, handler_name)) { - return 1; + memset(context, 0, sizeof(php_output_context)); + TSRMLS_SET_CTX(context->tsrm_ls); + context->op = op; + + return context; +} +/* }}} */ + +/* {{{ static void php_output_context_reset(php_output_context *context) + * Reset an output context */ +static inline void php_output_context_reset(php_output_context *context) +{ + int op = context->op; + php_output_context_dtor(context); + memset(context, 0, sizeof(php_output_context)); + context->op = op; +} +/* }}} */ + +/* {{{ static void php_output_context_feed(php_output_context *context, char *, size_t, size_t) + * Feed output contexts input buffer */ +static inline void php_output_context_feed(php_output_context *context, char *data, size_t size, size_t used, zend_bool free) +{ + if (context->in.free && context->in.data) { + efree(context->in.data); + } + context->in.data = data; + context->in.used = used; + context->in.free = free; + context->in.size = size; +} +/* }}} */ + +/* {{{ static void php_output_context_swap(php_output_context *context) + * Swap output contexts buffers */ +static inline void php_output_context_swap(php_output_context *context) +{ + if (context->in.free && context->in.data) { + efree(context->in.data); + } + context->in.data = context->out.data; + context->in.used = context->out.used; + context->in.free = context->out.free; + context->in.size = context->out.size; + context->out.data = NULL; + context->out.used = 0; + context->out.free = 0; + context->out.size = 0; +} +/* }}} */ + +/* {{{ static void php_output_context_pass(php_output_context *context) + * Pass input to output buffer */ +static inline void php_output_context_pass(php_output_context *context) +{ + context->out.data = context->in.data; + context->out.used = context->in.used; + context->out.size = context->in.size; + context->out.free = context->in.free; + context->in.data = NULL; + context->in.used = 0; + context->in.free = 0; + context->in.size = 0; +} +/* }}} */ + +/* {{{ static void php_output_context_dtor(php_output_context *context) + * Destroy the contents of an output context */ +static inline void php_output_context_dtor(php_output_context *context) +{ + if (context->in.free && context->in.data) { + efree(context->in.data); + context->in.data = NULL; + } + if (context->out.free && context->out.data) { + efree(context->out.data); + context->out.data = NULL; + } +} +/* }}} */ + +/* {{{ static php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags TSRMLS_DC) + * Allocates and initializes a php_output_handler structure */ +static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC) +{ + php_output_handler *handler; + + handler = ecalloc(1, sizeof(php_output_handler)); + handler->name = estrndup(name, name_len); + handler->name_len = name_len; + handler->size = chunk_size; + handler->flags = flags; + handler->buffer.size = PHP_OUTPUT_HANDLER_INITBUF_SIZE(chunk_size); + handler->buffer.data = emalloc(handler->buffer.size); + + return handler; +} +/* }}} */ + +/* {{{ static int php_output_handler_appen(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC) + * Appends input to the output handlers buffer and indicates whether the buffer does not have to be processed by the output handler */ +static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC) +{ + if (buf->used) { + OG(flags) |= PHP_OUTPUT_WRITTEN; + /* store it away */ + if ((handler->buffer.size - handler->buffer.used) <= buf->used) { + size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size); + size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used)); + size_t grow_max = MAX(grow_int, grow_buf); + + handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max); + handler->buffer.size += grow_max; } - if (OG(ob_nesting_level)>1) { - zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_handler_used_each, &tmp); + memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used); + handler->buffer.used += buf->used; + + /* chunked buffering */ + if (handler->size && (handler->buffer.used >= handler->size)) { + /* store away errors and/or any intermediate output */ + return OG(running) ? 1 : 0; } } - return tmp ? 0 : 1; + return 1; } /* }}} */ -/* {{{ php_ob_append - */ -static inline void php_ob_append(const char *text, uint text_length TSRMLS_DC) +/* {{{ static php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context) + * Output handler operation dispatcher, applying context op to the php_output_handler handler */ +static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context) { - char *target; - int original_ob_text_length; + php_output_handler_status_t status; + int original_op = context->op; + PHP_OUTPUT_TSRMLS(context); + +#if PHP_OUTPUT_DEBUG + fprintf(stderr, ">>> op(%d, " + "handler=%p, " + "name=%s, " + "flags=%d, " + "buffer.data=%s, " + "buffer.used=%zu, " + "buffer.size=%zu, " + "in.data=%s, " + "in.used=%zu)\n", + context->op, + handler, + handler->name, + handler->flags, + handler->buffer.used?handler->buffer.data:"", + handler->buffer.used, + handler->buffer.size, + context->in.used?context->in.data:"", + context->in.used + ); +#endif - original_ob_text_length=OG(active_ob_buffer).text_length; + if (php_output_lock_error(context->op TSRMLS_CC)) { + /* fatal error */ + return PHP_OUTPUT_HANDLER_FAILURE; + } + + /* storable? */ + if (php_output_handler_append(handler, &context->in TSRMLS_CC) && !context->op) { + context->op = original_op; + return PHP_OUTPUT_HANDLER_NO_DATA; + } else { + /* need to start? */ + if (!(handler->flags & PHP_OUTPUT_HANDLER_STARTED)) { + context->op |= PHP_OUTPUT_HANDLER_START; + } - php_ob_allocate(text_length TSRMLS_CC); - target = OG(active_ob_buffer).buffer+original_ob_text_length; - memcpy(target, text, text_length); - target[text_length]=0; + OG(running) = handler; + if (handler->flags & PHP_OUTPUT_HANDLER_USER) { + zval *retval = NULL, *ob_data, *ob_mode; + + MAKE_STD_ZVAL(ob_data); + ZVAL_STRINGL(ob_data, handler->buffer.data, handler->buffer.used, 1); + MAKE_STD_ZVAL(ob_mode); + ZVAL_LONG(ob_mode, (long) context->op); + zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 2, &ob_data, &ob_mode); + +#define PHP_OUTPUT_USER_SUCCESS(retval) (retval && !(Z_TYPE_P(retval) == IS_BOOL && Z_BVAL_P(retval)==0)) + if (SUCCESS == zend_fcall_info_call(&handler->func.user->fci, &handler->func.user->fcc, &retval, NULL TSRMLS_CC) && PHP_OUTPUT_USER_SUCCESS(retval)) { + /* user handler may have returned TRUE */ + status = PHP_OUTPUT_HANDLER_NO_DATA; + if (Z_TYPE_P(retval) != IS_BOOL) { + convert_to_string_ex(&retval); + if (Z_STRLEN_P(retval)) { + context->out.data = estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval)); + context->out.used = Z_STRLEN_P(retval); + context->out.free = 1; + status = PHP_OUTPUT_HANDLER_SUCCESS; + } + } + } else { + /* call failed, pass internal buffer along */ + status = PHP_OUTPUT_HANDLER_FAILURE; + } - /* If implicit_flush is On or chunked buffering, send contents to next buffer and return. */ - if (OG(active_ob_buffer).chunk_size - && OG(active_ob_buffer).text_length >= OG(active_ob_buffer).chunk_size) { + zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 0); + zval_ptr_dtor(&ob_data); + zval_ptr_dtor(&ob_mode); + if (retval) { + zval_ptr_dtor(&retval); + } - php_end_ob_buffer(1, 1 TSRMLS_CC); - return; + } else { + + php_output_context_feed(context, handler->buffer.data, handler->buffer.size, handler->buffer.used, 0); + + if (SUCCESS == handler->func.internal(&handler->opaq, context)) { + if (context->out.used) { + status = PHP_OUTPUT_HANDLER_SUCCESS; + } else { + status = PHP_OUTPUT_HANDLER_NO_DATA; + } + } else { + status = PHP_OUTPUT_HANDLER_FAILURE; + } + } + handler->flags |= PHP_OUTPUT_HANDLER_STARTED; + OG(running) = NULL; } + + switch (status) { + case PHP_OUTPUT_HANDLER_FAILURE: + /* disable this handler */ + handler->flags |= PHP_OUTPUT_HANDLER_DISABLED; + /* discard any output */ + if (context->out.data && context->out.free) { + efree(context->out.data); + } + /* returns handlers buffer */ + context->out.data = handler->buffer.data; + context->out.used = handler->buffer.used; + context->out.free = 1; + handler->buffer.data = NULL; + handler->buffer.used = 0; + handler->buffer.size = 0; + break; + case PHP_OUTPUT_HANDLER_NO_DATA: + /* handler ate all */ + php_output_context_reset(context); + /* no break */ + case PHP_OUTPUT_HANDLER_SUCCESS: + /* no more buffered data */ + handler->buffer.used = 0; + handler->flags |= PHP_OUTPUT_HANDLER_PROCESSED; + break; + } + + context->op = original_op; + return status; } /* }}} */ -#if 0 -static inline void php_ob_prepend(const char *text, uint text_length) + +/* {{{ static void php_output_op(int op, const char *str, size_t len TSRMLS_DC) + * Output op dispatcher, passes input and output handlers output through the output handler stack until it gets written to the SAPI */ +static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC) { - char *p, *start; - TSRMLS_FETCH(); + php_output_context context; + php_output_handler **active; + int obh_cnt; + + if (php_output_lock_error(op TSRMLS_CC)) { + return; + } - php_ob_allocate(text_length TSRMLS_CC); + php_output_context_init(&context, op TSRMLS_CC); - /* php_ob_allocate() may change OG(ob_buffer), so we can't initialize p&start earlier */ - p = OG(ob_buffer)+OG(ob_text_length); - start = OG(ob_buffer); + /* + * broken up for better performance: + * - apply op to the one active handler; note that OG(active) might be popped off the stack on a flush + * - or apply op to the handler stack + */ + if (OG(active) && (obh_cnt = zend_stack_count(&OG(handlers)))) { + context.in.data = (char *) str; + context.in.used = len; - while (--p>=start) { - p[text_length] = *p; + if (obh_cnt > 1) { + zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_op, &context); + } else if ((SUCCESS == zend_stack_top(&OG(handlers), (void *) &active)) && (!((*active)->flags & PHP_OUTPUT_HANDLER_DISABLED))) { + php_output_handler_op(*active, &context); + } else { + php_output_context_pass(&context); + } + } else { + context.out.data = (char *) str; + context.out.used = len; } - memcpy(OG(ob_buffer), text, text_length); - OG(ob_buffer)[OG(active_ob_buffer).text_length]=0; -} + + if (context.out.data && context.out.used) { + php_output_header(TSRMLS_C); + + if (!(OG(flags) & PHP_OUTPUT_DISABLED)) { +#if PHP_OUTPUT_DEBUG + fprintf(stderr, "::: sapi_write('%s', %zu)\n", context.out.data, context.out.used); #endif + sapi_module.ub_write(context.out.data, context.out.used TSRMLS_CC); -/* {{{ php_ob_get_buffer - * Return the current output buffer */ -PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC) -{ - if (OG(ob_nesting_level)==0) { - return FAILURE; + if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) { + sapi_flush(TSRMLS_C); + } + + OG(flags) |= PHP_OUTPUT_SENT; + } } - ZVAL_STRINGL(p, OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length, 1); - return SUCCESS; + php_output_context_dtor(&context); } /* }}} */ -/* {{{ php_ob_get_length - * Return the size of the current output buffer */ -PHPAPI int php_ob_get_length(zval *p TSRMLS_DC) +/* {{{ static int php_output_stack_apply_op(void *h, void *c) + * Operation callback for the stack apply function */ +static int php_output_stack_apply_op(void *h, void *c) { - if (OG(ob_nesting_level) == 0) { - return FAILURE; + int was_disabled; + php_output_handler_status_t status; + php_output_handler *handler = *(php_output_handler **) h; + php_output_context *context = (php_output_context *) c; + + if ((was_disabled = (handler->flags & PHP_OUTPUT_HANDLER_DISABLED))) { + status = PHP_OUTPUT_HANDLER_FAILURE; + } else { + status = php_output_handler_op(handler, context); + } + + /* + * handler ate all => break + * handler returned data or failed resp. is disabled => continue + */ + switch (status) { + case PHP_OUTPUT_HANDLER_NO_DATA: + return 1; + + case PHP_OUTPUT_HANDLER_SUCCESS: + /* swap contexts buffers, unless this is the last handler in the stack */ + if (handler->level) { + php_output_context_swap(context); + } + return 0; + + case PHP_OUTPUT_HANDLER_FAILURE: + default: + if (was_disabled) { + /* pass input along, if it's the last handler in the stack */ + if (!handler->level) { + php_output_context_pass(context); + } + } else { + /* swap buffers, unless this is the last handler */ + if (handler->level) { + php_output_context_swap(context); + } + } + return 0; } - ZVAL_LONG(p, OG(active_ob_buffer).text_length); - return SUCCESS; } /* }}} */ -/* - * Wrapper functions - implementation - */ +/* {{{ static int php_output_stack_apply_clean(void *h, void *c) + * Clean callback for the stack apply function */ +static int php_output_stack_apply_clean(void *h, void *c) +{ + php_output_handler *handler = *(php_output_handler **) h; + php_output_context *context = (php_output_context *) c; + + handler->buffer.used = 0; + php_output_handler_op(handler, context); + php_output_context_reset(context); + return 0; +} +/* }}} */ -/* buffered output function */ -static int php_b_body_write(const char *str, uint str_length TSRMLS_DC) +/* {{{ static int php_output_stack_apply_list(void *h, void *z) + * List callback for the stack apply function */ +static int php_output_stack_apply_list(void *h, void *z) { - php_ob_append(str, str_length TSRMLS_CC); - return str_length; + php_output_handler *handler = *(php_output_handler **) h; + zval *array = (zval *) z; + + add_next_index_stringl(array, handler->name, handler->name_len, 1); + return 0; } +/* }}} */ -/* {{{ php_ub_body_write_no_header - */ -PHPAPI int php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC) +/* {{{ static int php_output_stack_apply_status(void *h, void *z) + * Status callback for the stack apply function */ +static int php_output_stack_apply_status(void *h, void *z) { - int result; + php_output_handler *handler = *(php_output_handler **) h; + zval *array = (zval *) z; - if (OG(disable_output)) { - return 0; - } + add_next_index_zval(array, php_output_handler_status(handler, NULL)); - result = OG(php_header_write)(str, str_length TSRMLS_CC); + return 0; +} - if (OG(implicit_flush)) { - sapi_flush(TSRMLS_C); +/* {{{ static zval *php_output_handler_status(php_output_handler *handler, zval *entry) + * Returns an array with the status of the output handler */ +static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry) +{ + if (!entry) { + MAKE_STD_ZVAL(entry); + array_init(entry); } - return result; + add_assoc_stringl(entry, "name", handler->name, handler->name_len, 1); + add_assoc_long(entry, "type", (long) (handler->flags & 0xf)); + add_assoc_long(entry, "flags", (long) handler->flags); + add_assoc_long(entry, "level", (long) handler->level); + add_assoc_long(entry, "chunk_size", (long) handler->size); + add_assoc_long(entry, "buffer_size", (long) handler->buffer.size); + add_assoc_long(entry, "buffer_used", (long) handler->buffer.used); + + return entry; } /* }}} */ -/* {{{ php_ub_body_write - */ -PHPAPI int php_ub_body_write(const char *str, uint str_length TSRMLS_DC) +/* {{{ static int php_output_stack_pop(int flags TSRMLS_DC) + * Pops an output handler off the stack */ +static inline int php_output_stack_pop(int flags TSRMLS_DC) { - int result = 0; + php_output_context context; + php_output_handler **current, *orphan = OG(active); - if (SG(request_info).headers_only) { - if(SG(headers_sent)) { - return 0; + if (!orphan) { + if (!(flags & PHP_OUTPUT_POP_SILENT)) { + 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"); + } + return 0; + } else if (!(flags & PHP_OUTPUT_POP_FORCE) && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) { + if (!(flags & PHP_OUTPUT_POP_SILENT)) { + 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); + } + return 0; + } else { + php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL TSRMLS_CC); + + /* don't run the output handler if it's disabled */ + if (!(orphan->flags & PHP_OUTPUT_HANDLER_DISABLED)) { + /* didn't it start yet? */ + if (!(orphan->flags & PHP_OUTPUT_HANDLER_STARTED)) { + context.op |= PHP_OUTPUT_HANDLER_START; + } + /* signal that we're cleaning up */ + if (flags & PHP_OUTPUT_POP_DISCARD) { + context.op |= PHP_OUTPUT_HANDLER_CLEAN; + orphan->buffer.used = 0; + } + php_output_handler_op(orphan, &context); + } + + /* pop it off the stack */ + zend_stack_del_top(&OG(handlers)); + if (SUCCESS == zend_stack_top(&OG(handlers), (void *) ¤t)) { + OG(active) = *current; + } else { + OG(active) = NULL; } - php_header(TSRMLS_C); - zend_bailout(); - } - if (php_header(TSRMLS_C)) { - if (zend_is_compiling(TSRMLS_C)) { - OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C); - OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C); - } else if (zend_is_executing(TSRMLS_C)) { - OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C); - OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C); + + /* pass output along */ + if (context.out.data && context.out.used && !(flags & PHP_OUTPUT_POP_DISCARD)) { + php_output_write(context.out.data, context.out.used TSRMLS_CC); } - OG(php_body_write) = php_ub_body_write_no_header; - result = php_ub_body_write_no_header(str, str_length TSRMLS_CC); - } + /* destroy the handler (after write!) */ + php_output_handler_free(&orphan TSRMLS_CC); + php_output_context_dtor(&context); - return result; + return 1; + } } /* }}} */ -/* {{{ int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) - */ -static int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) +/* {{{ static SUCCESS|FAILURE php_output_handler_compat_func(void *ctx, php_output_context *) + * php_output_handler_context_func_t for php_output_handler_func_t output handlers */ +static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context) { - zval *elem; + php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context; + PHP_OUTPUT_TSRMLS(output_context); - MAKE_STD_ZVAL(elem); - array_init(elem); + if (func) { + char *out_str = NULL; + uint out_len = 0; - add_assoc_long(elem, "chunk_size", ob_buffer->chunk_size); - if (!ob_buffer->chunk_size) { - add_assoc_long(elem, "size", ob_buffer->size); - add_assoc_long(elem, "block_size", ob_buffer->block_size); - } - if (ob_buffer->internal_output_handler) { - add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_INTERNAL); - add_assoc_long(elem, "buffer_size", ob_buffer->internal_output_handler_buffer_size); - } else { - add_assoc_long(elem, "type", PHP_OUTPUT_HANDLER_USER); + func(output_context->in.data, output_context->in.used, &out_str, &out_len, output_context->op TSRMLS_CC); + + if (out_str) { + output_context->out.data = out_str; + output_context->out.used = out_len; + output_context->out.free = 1; + } else { + php_output_context_pass(output_context); + } + + return SUCCESS; } - add_assoc_long(elem, "status", ob_buffer->status); - add_assoc_string(elem, "name", ob_buffer->handler_name, 1); - add_assoc_bool(elem, "del", ob_buffer->erase); - add_next_index_zval(result, elem); + return FAILURE; +} +/* }}} */ + +/* {{{ static SUCCESS|FAILURE php_output_handler_default_func(void *ctx, php_output_context *) + * Default output handler */ +static int php_output_handler_default_func(void **handler_context, php_output_context *output_context) +{ + php_output_context_pass(output_context); + return SUCCESS; +} +/* }}} */ +/* {{{ static SUCCESS|FAILURE php_output_handler_devnull_func(void *ctx, php_output_context *) + * Null output handler */ +static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context) +{ return SUCCESS; } /* }}} */ @@ -764,15 +1301,15 @@ static int php_ob_buffer_status(php_ob_buffer *ob_buffer, zval *result) * USERLAND (nearly 1:1 of old output.c) */ -/* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, bool erase]]]) +/* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, int flags]]]) Turn on Output Buffering (specifying an optional output handler). */ PHP_FUNCTION(ob_start) { zval *output_handler = NULL; long chunk_size = 0; - zend_bool erase = 1; + long flags = PHP_OUTPUT_HANDLER_STDFLAGS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/lb", &output_handler, &chunk_size, &erase) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/ll", &output_handler, &chunk_size, &flags) == FAILURE) { return; } @@ -780,7 +1317,8 @@ PHP_FUNCTION(ob_start) chunk_size = 0; } - if (php_start_ob_buffer(output_handler, chunk_size, erase TSRMLS_CC) == FAILURE) { + if (php_output_start_user(output_handler, chunk_size, flags TSRMLS_CC) == FAILURE) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to create buffer"); RETURN_FALSE; } RETURN_TRUE; @@ -795,17 +1333,15 @@ PHP_FUNCTION(ob_flush) return; } - if (!OG(ob_nesting_level)) { + if (!OG(active)) { php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush"); RETURN_FALSE; } - if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer %s", OG(active_ob_buffer).handler_name); + if (SUCCESS != php_output_flush(TSRMLS_C)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer of %s (%d)", OG(active)->name, OG(active)->level); RETURN_FALSE; } - - php_end_ob_buffer(1, 1 TSRMLS_CC); RETURN_TRUE; } /* }}} */ @@ -818,17 +1354,15 @@ PHP_FUNCTION(ob_clean) return; } - if (!OG(ob_nesting_level)) { + if (!OG(active)) { php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); RETURN_FALSE; } - if (!OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name); + if (SUCCESS != php_output_clean(TSRMLS_C)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level); RETURN_FALSE; } - - php_end_ob_buffer(0, 1 TSRMLS_CC); RETURN_TRUE; } /* }}} */ @@ -841,18 +1375,12 @@ PHP_FUNCTION(ob_end_flush) return; } - if (!OG(ob_nesting_level)) { + if (!OG(active)) { php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush"); RETURN_FALSE; } - if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name); - RETURN_FALSE; - } - - php_end_ob_buffer(1, 0 TSRMLS_CC); - RETURN_TRUE; + RETURN_BOOL(SUCCESS == php_output_end(TSRMLS_C)); } /* }}} */ @@ -864,18 +1392,12 @@ PHP_FUNCTION(ob_end_clean) return; } - if (!OG(ob_nesting_level)) { + if (!OG(active)) { php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); RETURN_FALSE; } - if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name); - RETURN_FALSE; - } - - php_end_ob_buffer(0, 0 TSRMLS_CC); - RETURN_TRUE; + RETURN_BOOL(SUCCESS == php_output_discard(TSRMLS_C)); } /* }}} */ @@ -887,23 +1409,14 @@ PHP_FUNCTION(ob_get_flush) return; } - if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) { - RETURN_FALSE; - } - - if (!OG(ob_nesting_level)) { + if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) { php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush"); - zval_dtor(return_value); RETURN_FALSE; } - if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name); - zval_dtor(return_value); - RETURN_FALSE; + if (SUCCESS != php_output_end(TSRMLS_C)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level); } - - php_end_ob_buffer(1, 0 TSRMLS_CC); } /* }}} */ @@ -915,22 +1428,18 @@ PHP_FUNCTION(ob_get_clean) return; } - if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) { + if(!OG(active)) { RETURN_FALSE; } - if (!OG(ob_nesting_level)) { + if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) { php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); - zval_dtor(return_value); - RETURN_FALSE; - } - if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) { - php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name); - zval_dtor(return_value); RETURN_FALSE; } - php_end_ob_buffer(0, 0 TSRMLS_CC); + if (SUCCESS != php_output_discard(TSRMLS_C)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level); + } } /* }}} */ @@ -942,7 +1451,7 @@ PHP_FUNCTION(ob_get_contents) return; } - if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) { + if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) { RETURN_FALSE; } } @@ -956,7 +1465,7 @@ PHP_FUNCTION(ob_get_level) return; } - RETURN_LONG(OG(ob_nesting_level)); + RETURN_LONG(php_output_get_level(TSRMLS_C)); } /* }}} */ @@ -968,7 +1477,7 @@ PHP_FUNCTION(ob_get_length) return; } - if (php_ob_get_length(return_value TSRMLS_CC) == FAILURE) { + if (php_output_get_length(return_value TSRMLS_CC) == FAILURE) { RETURN_FALSE; } } @@ -984,12 +1493,11 @@ PHP_FUNCTION(ob_list_handlers) array_init(return_value); - if (OG(ob_nesting_level)) { - if (OG(ob_nesting_level) > 1) { - zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_ob_list_each, return_value); - } - php_ob_list_each(&OG(active_ob_buffer), return_value); + if (!OG(active)) { + return; } + + zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value); } /* }}} */ @@ -1005,23 +1513,14 @@ PHP_FUNCTION(ob_get_status) array_init(return_value); + if (!OG(active)) { + return; + } + if (full_status) { - if (OG(ob_nesting_level) > 1) { - zend_stack_apply_with_argument(&OG(ob_buffers), ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *elem, void *))php_ob_buffer_status, return_value); - } - if (OG(ob_nesting_level) > 0 && php_ob_buffer_status(&OG(active_ob_buffer), return_value) == FAILURE) { - RETURN_FALSE; - } - } else if (OG(ob_nesting_level) > 0) { - add_assoc_long(return_value, "level", OG(ob_nesting_level)); - if (OG(active_ob_buffer).internal_output_handler) { - add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_INTERNAL); - } else { - add_assoc_long(return_value, "type", PHP_OUTPUT_HANDLER_USER); - } - add_assoc_long(return_value, "status", OG(active_ob_buffer).status); - add_assoc_string(return_value, "name", OG(active_ob_buffer).handler_name, 1); - add_assoc_bool(return_value, "del", OG(active_ob_buffer).erase); + zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_status, return_value); + } else { + php_output_handler_status(OG(active), return_value); } } /* }}} */ @@ -1036,11 +1535,7 @@ PHP_FUNCTION(ob_implicit_flush) return; } - if (flag) { - php_start_implicit_flush(TSRMLS_C); - } else { - php_end_implicit_flush(TSRMLS_C); - } + php_output_set_implicit_flush(flag TSRMLS_CC); } /* }}} */ diff --git a/main/php.h b/main/php.h index 497439d17a..2a93118ac6 100644 --- a/main/php.h +++ b/main/php.h @@ -26,7 +26,7 @@ #include <dmalloc.h> #endif -#define PHP_API_VERSION 20090626 +#define PHP_API_VERSION 20100412 #define PHP_HAVE_STREAMS #define YYDEBUG 0 @@ -60,6 +60,7 @@ # else # define PHPAPI # endif + # define THREAD_LS # define PHP_DIR_SEPARATOR '/' # define PHP_EOL "\n" @@ -142,7 +143,11 @@ END_EXTERN_C() #endif #ifndef HAVE_SOCKLEN_T +# if PHP_WIN32 +typedef int socklen_t; +# else typedef unsigned int socklen_t; +# endif #endif #define CREATE_MUTEX(a, b) @@ -177,7 +182,6 @@ typedef unsigned int socklen_t; #endif #include "zend_hash.h" -#include "php3_compat.h" #include "zend_alloc.h" #include "zend_stack.h" @@ -192,8 +196,6 @@ typedef unsigned int socklen_t; # endif #endif -#include "safe_mode.h" - #ifndef HAVE_STRERROR char *strerror(int); #endif @@ -253,6 +255,11 @@ END_EXTERN_C() # endif #endif +#if defined(__GNUC__) && __GNUC__ >= 4 +# define php_ignore_value(x) (({ __typeof__ (x) __x = (x); (void) __x; })) +#else +# define php_ignore_value(x) ((void) (x)) +#endif /* global variables */ #if !defined(PHP_WIN32) @@ -274,6 +281,7 @@ void phperror(char *error); PHPAPI int php_write(void *buf, uint size TSRMLS_DC); PHPAPI int php_printf(const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1, 2); +PHPAPI int php_get_module_initialized(void); PHPAPI void php_log_err(char *log_message TSRMLS_DC); int Debug(char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1, 2); int cfgparse(void); @@ -327,6 +335,7 @@ PHPAPI int php_register_internal_extensions(TSRMLS_D); PHPAPI int php_mergesort(void *base, size_t nmemb, register size_t size, int (*cmp)(const void *, const void * TSRMLS_DC) TSRMLS_DC); PHPAPI void php_register_pre_request_shutdown(void (*func)(void *), void *userdata); PHPAPI void php_com_initialize(TSRMLS_D); +PHPAPI char *php_get_current_user(TSRMLS_D); END_EXTERN_C() /* PHP-named Zend macro wrappers */ @@ -382,20 +391,7 @@ END_EXTERN_C() /* Output support */ #include "main/php_output.h" -#define PHPWRITE(str, str_len) php_body_write((str), (str_len) TSRMLS_CC) -#define PUTS(str) do { \ - const char *__str = (str); \ - php_body_write(__str, strlen(__str) TSRMLS_CC); \ -} while (0) - -#define PUTC(c) (php_body_write(&(c), 1 TSRMLS_CC), (c)) -#define PHPWRITE_H(str, str_len) php_header_write((str), (str_len) TSRMLS_CC) -#define PUTS_H(str) do { \ - const char *__str = (str); \ - php_header_write(__str, strlen(__str) TSRMLS_CC); \ -} while (0) - -#define PUTC_H(c) (php_header_write(&(c), 1 TSRMLS_CC), (c)) + #include "php_streams.h" #include "php_memory_streams.h" diff --git a/main/php3_compat.h b/main/php3_compat.h deleted file mode 100644 index 3b9e710385..0000000000 --- a/main/php3_compat.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 5 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2012 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: | - +----------------------------------------------------------------------+ -*/ - -/* $Id$ */ - -#ifndef PHP3_COMPAT_H -#define PHP3_COMPAT_H - -typedef zval pval; - -#define pval_copy_constructor zval_copy_ctor -#define pval_destructor zval_dtor - -#define _php3_hash_init zend_hash_init -#define _php3_hash_destroy zend_hash_destroy - -#define _php3_hash_clean zend_hash_clean - -#define _php3_hash_add_or_update zend_hash_add_or_update -#define _php3_hash_add zend_hash_add -#define _php3_hash_update zend_hash_update - -#define _php3_hash_quick_add_or_update zend_hash_quick_add_or_update -#define _php3_hash_quick_add zend_hash_quick_add -#define _php3_hash_quick_update zend_hash_quick_update - -#define _php3_hash_index_update_or_next_insert zend_hash_index_update_or_next_insert -#define _php3_hash_index_update zend_hash_index_update -#define _php3_hash_next_index_insert zend_hash_next_index_insert - -#define _php3_hash_pointer_update zend_hash_pointer_update - -#define _php3_hash_pointer_index_update_or_next_insert zend_hash_pointer_index_update_or_next_insert -#define _php3_hash_pointer_index_update zend_hash_pointer_index_update -#define _php3_hash_next_index_pointer_update zend_hash_next_index_pointer_update -#define _php3_hash_next_index_pointer_insert zend_hash_next_index_pointer_insert - -#define _php3_hash_del_key_or_index zend_hash_del_key_or_index -#define _php3_hash_del zend_hash_del -#define _php3_hash_index_del zend_hash_index_del - -#define _php3_hash_find zend_hash_find -#define _php3_hash_quick_find zend_hash_quick_find -#define _php3_hash_index_find zend_hash_index_find - -#define _php3_hash_exists zend_hash_exists -#define _php3_hash_index_exists zend_hash_index_exists -#define _php3_hash_is_pointer zend_hash_is_pointer -#define _php3_hash_index_is_pointer zend_hash_index_is_pointer -#define _php3_hash_next_free_element zend_hash_next_free_element - -#define _php3_hash_move_forward zend_hash_move_forward -#define _php3_hash_move_backwards zend_hash_move_backwards -#define _php3_hash_get_current_key zend_hash_get_current_key -#define _php3_hash_get_current_data zend_hash_get_current_data -#define _php3_hash_internal_pointer_reset zend_hash_internal_pointer_reset -#define _php3_hash_internal_pointer_end zend_hash_internal_pointer_end - -#define _php3_hash_copy zend_hash_copy -#define _php3_hash_merge zend_hash_merge -#define _php3_hash_sort zend_hash_sort -#define _php3_hash_minmax zend_hash_minmax - -#define _php3_hash_num_elements zend_hash_num_elements - -#define _php3_hash_apply zend_hash_apply -#define _php3_hash_apply_with_argument zend_hash_apply_with_argument - - -#define php3_error php_error - -#define php3_printf php_printf -#define _php3_sprintf php_sprintf - - - -#define php3_module_entry zend_module_entry - -#define php3_strndup zend_strndup -#define php3_str_tolower zend_str_tolower -#define php3_binary_strcmp zend_binary_strcmp - - -#define php3_list_insert zend_list_insert -#define php3_list_find zend_list_find -#define php3_list_delete zend_list_delete - -#define php3_plist_insert zend_plist_insert -#define php3_plist_find zend_plist_find -#define php3_plist_delete zend_plist_delete - -#define zend_print_pval zend_print_zval -#define zend_print_pval_r zend_print_zval_r - - -#define function_entry zend_function_entry - -#define _php3_addslashes php_addslashes -#define _php3_stripslashes php_stripslashes -#define php3_dl php_dl - -#define getParameters zend_get_parameters -#define getParametersArray zend_get_parameters_array - -#define list_entry zend_rsrc_list_entry - -#endif /* PHP3_COMPAT_H */ diff --git a/main/php_content_types.c b/main/php_content_types.c index 97f8022e17..cca006ad6a 100644 --- a/main/php_content_types.c +++ b/main/php_content_types.c @@ -73,9 +73,9 @@ SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader) */ int php_startup_sapi_content_types(TSRMLS_D) { - sapi_register_default_post_reader(php_default_post_reader); - sapi_register_treat_data(php_default_treat_data); - sapi_register_input_filter(php_default_input_filter, NULL); + sapi_register_default_post_reader(php_default_post_reader TSRMLS_CC); + sapi_register_treat_data(php_default_treat_data TSRMLS_CC); + sapi_register_input_filter(php_default_input_filter, NULL TSRMLS_CC); return SUCCESS; } /* }}} */ diff --git a/main/php_globals.h b/main/php_globals.h index 70f3bbfbbe..514c27869b 100644 --- a/main/php_globals.h +++ b/main/php_globals.h @@ -54,19 +54,10 @@ typedef struct _arg_separators { } arg_separators; struct _php_core_globals { - zend_bool magic_quotes_gpc; - zend_bool magic_quotes_runtime; - zend_bool magic_quotes_sybase; - - zend_bool safe_mode; - - zend_bool allow_call_time_pass_reference; zend_bool implicit_flush; long output_buffering; - char *safe_mode_include_dir; - zend_bool safe_mode_gid; zend_bool sql_safe_mode; zend_bool enable_dl; @@ -75,8 +66,6 @@ struct _php_core_globals { char *unserialize_callback_func; long serialize_precision; - char *safe_mode_exec_dir; - long memory_limit; long max_input_time; @@ -95,6 +84,7 @@ struct _php_core_globals { char *include_path; char *open_basedir; char *extension_dir; + char *php_binary; char *upload_tmp_dir; long upload_max_filesize; @@ -122,13 +112,9 @@ struct _php_core_globals { zend_bool expose_php; - zend_bool register_globals; - zend_bool register_long_arrays; zend_bool register_argc_argv; zend_bool auto_globals_jit; - zend_bool y2k_compliance; - char *docref_root; char *docref_ext; @@ -143,6 +129,7 @@ struct _php_core_globals { zend_bool file_uploads; zend_bool during_request_startup; zend_bool allow_url_fopen; + zend_bool enable_post_data_reading; zend_bool always_populate_raw_post_data; zend_bool report_zend_debug; @@ -159,6 +146,7 @@ struct _php_core_globals { zend_bool com_initialized; #endif long max_input_nesting_level; + long max_input_vars; zend_bool in_user_include; char *user_ini_filename; @@ -174,8 +162,6 @@ struct _php_core_globals { #ifdef PHP_WIN32 zend_bool windows_show_crt_warning; #endif - - long max_input_vars; }; diff --git a/main/php_ini.c b/main/php_ini.c index eed600f134..89a3d7e461 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -368,7 +368,6 @@ int php_init_config(TSRMLS_D) char *php_ini_file_name = NULL; char *php_ini_search_path = NULL; int php_ini_scanned_path_len; - int safe_mode_state; char *open_basedir; int free_ini_search_path = 0; zend_file_handle fh; @@ -384,7 +383,6 @@ int php_init_config(TSRMLS_D) zend_llist_init(&extension_lists.engine, sizeof(char *), (llist_dtor_func_t) free_estring, 1); zend_llist_init(&extension_lists.functions, sizeof(char *), (llist_dtor_func_t) free_estring, 1); - safe_mode_state = PG(safe_mode); open_basedir = PG(open_basedir); if (sapi_module.php_ini_path_override) { @@ -395,7 +393,6 @@ int php_init_config(TSRMLS_D) int search_path_size; char *default_location; char *env_location; - char *binary_location; static const char paths_separator[] = { ZEND_PATHS_SEPARATOR, 0 }; #ifdef PHP_WIN32 char *reg_location; @@ -423,7 +420,11 @@ int php_init_config(TSRMLS_D) env_location = ""; } else { size = GetEnvironmentVariableA("PHPRC", phprc_path, size); - env_location = phprc_path; + if (size == 0) { + env_location = ""; + } else { + env_location = phprc_path; + } } } } @@ -463,58 +464,18 @@ int php_init_config(TSRMLS_D) #endif /* Add cwd (not with CLI) */ - if (strcmp(sapi_module.name, "cli") != 0) { + if (!sapi_module.php_ini_ignore_cwd) { if (*php_ini_search_path) { strlcat(php_ini_search_path, paths_separator, search_path_size); } strlcat(php_ini_search_path, ".", search_path_size); } - /* Add binary directory */ -#ifdef PHP_WIN32 - binary_location = (char *) emalloc(MAXPATHLEN); - if (GetModuleFileName(0, binary_location, MAXPATHLEN) == 0) { - efree(binary_location); - binary_location = NULL; - } -#else - if (sapi_module.executable_location) { - binary_location = (char *)emalloc(MAXPATHLEN); - if (!strchr(sapi_module.executable_location, '/')) { - char *envpath, *path; - int found = 0; - - if ((envpath = getenv("PATH")) != NULL) { - char *search_dir, search_path[MAXPATHLEN]; - char *last = NULL; - - path = estrdup(envpath); - search_dir = php_strtok_r(path, ":", &last); - - while (search_dir) { - snprintf(search_path, MAXPATHLEN, "%s/%s", search_dir, sapi_module.executable_location); - if (VCWD_REALPATH(search_path, binary_location) && !VCWD_ACCESS(binary_location, X_OK)) { - found = 1; - break; - } - search_dir = php_strtok_r(NULL, ":", &last); - } - efree(path); - } - if (!found) { - efree(binary_location); - binary_location = NULL; - } - } else if (!VCWD_REALPATH(sapi_module.executable_location, binary_location) || VCWD_ACCESS(binary_location, X_OK)) { - efree(binary_location); - binary_location = NULL; - } - } else { - binary_location = NULL; - } -#endif - if (binary_location) { - char *separator_location = strrchr(binary_location, DEFAULT_SLASH); + if (PG(php_binary)) { + char *separator_location, *binary_location; + + binary_location = estrdup(PG(php_binary)); + separator_location = strrchr(binary_location, DEFAULT_SLASH); if (separator_location && separator_location != binary_location) { *(separator_location) = 0; @@ -557,7 +518,6 @@ int php_init_config(TSRMLS_D) #endif } - PG(safe_mode) = 0; PG(open_basedir) = NULL; /* @@ -610,7 +570,6 @@ int php_init_config(TSRMLS_D) efree(php_ini_search_path); } - PG(safe_mode) = safe_mode_state; PG(open_basedir) = open_basedir; if (fh.handle.fp) { diff --git a/main/php_logos.c b/main/php_logos.c index 55204aa816..3689f71e95 100644 --- a/main/php_logos.c +++ b/main/php_logos.c @@ -85,7 +85,7 @@ int php_info_logos(const char *logo_string TSRMLS_DC) content_header[len] = '\0'; sapi_add_header(content_header, len, 0); - PHPWRITE(logo_image->data, logo_image->size); + PHPWRITE((char*)logo_image->data, logo_image->size); return 1; } diff --git a/main/php_main.h b/main/php_main.h index 93ba5a32ac..124be2379a 100644 --- a/main/php_main.h +++ b/main/php_main.h @@ -35,6 +35,7 @@ PHPAPI void php_module_shutdown(TSRMLS_D); PHPAPI void php_module_shutdown_for_exec(void); PHPAPI int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals); PHPAPI int php_request_startup_for_hook(TSRMLS_D); +PHPAPI void php_request_shutdown_for_hook(void *dummy); PHPAPI int php_register_extensions(zend_module_entry **ptr, int count TSRMLS_DC); diff --git a/main/php_network.h b/main/php_network.h index 214a66077b..6078890049 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -194,10 +194,12 @@ PHPAPI void _php_emit_fd_setsize_warning(int max_fd); /* it is safe to FD_SET too many fd's under win32; the macro will simply ignore * descriptors that go beyond the default FD_SETSIZE */ # define PHP_SAFE_FD_SET(fd, set) FD_SET(fd, set) +# define PHP_SAFE_FD_CLR(fd, set) FD_CLR(fd, set) # define PHP_SAFE_FD_ISSET(fd, set) FD_ISSET(fd, set) # define PHP_SAFE_MAX_FD(m, n) do { if (n + 1 >= FD_SETSIZE) { _php_emit_fd_setsize_warning(n); }} while(0) #else # define PHP_SAFE_FD_SET(fd, set) do { if (fd < FD_SETSIZE) FD_SET(fd, set); } while(0) +# define PHP_SAFE_FD_CLR(fd, set) do { if (fd < FD_SETSIZE) FD_CLR(fd, set); } while(0) # define PHP_SAFE_FD_ISSET(fd, set) ((fd < FD_SETSIZE) && FD_ISSET(fd, set)) # define PHP_SAFE_MAX_FD(m, n) do { if (m >= FD_SETSIZE) { _php_emit_fd_setsize_warning(m); m = FD_SETSIZE - 1; }} while(0) #endif @@ -220,6 +222,9 @@ typedef struct { #endif BEGIN_EXTERN_C() +PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, char **error_string TSRMLS_DC); +PHPAPI void php_network_freeaddresses(struct sockaddr **sal); + PHPAPI php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short port, int socktype, int asynchronous, struct timeval *timeout, char **error_string, int *error_code, char *bindto, unsigned short bindport diff --git a/main/php_open_temporary_file.c b/main/php_open_temporary_file.c index f6fe0ff9fd..8d5c9e9ee7 100644 --- a/main/php_open_temporary_file.c +++ b/main/php_open_temporary_file.c @@ -127,7 +127,7 @@ static int php_do_open_temporary_file(const char *path, const char *pfx, char ** new_state.cwd = strdup(cwd); new_state.cwd_length = strlen(cwd); - if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) { + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)) { free(new_state.cwd); return -1; } diff --git a/main/php_output.h b/main/php_output.h index cb98839de8..d050a141a1 100644 --- a/main/php_output.h +++ b/main/php_output.h @@ -12,7 +12,7 @@ | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Zeev Suraski <zeev@zend.com> | + | Author: Michael Wallner <mike@php.net> | +----------------------------------------------------------------------+ */ @@ -21,35 +21,235 @@ #ifndef PHP_OUTPUT_H #define PHP_OUTPUT_H +#define PHP_OUTPUT_NEWAPI 1 + +/* handler ops */ +#define PHP_OUTPUT_HANDLER_WRITE 0x00 /* standard passthru */ +#define PHP_OUTPUT_HANDLER_START 0x01 /* start */ +#define PHP_OUTPUT_HANDLER_CLEAN 0x02 /* restart */ +#define PHP_OUTPUT_HANDLER_FLUSH 0x04 /* pass along as much as possible */ +#define PHP_OUTPUT_HANDLER_FINAL 0x08 /* finalize */ +#define PHP_OUTPUT_HANDLER_CONT PHP_OUTPUT_HANDLER_WRITE +#define PHP_OUTPUT_HANDLER_END PHP_OUTPUT_HANDLER_FINAL + +/* handler types */ +#define PHP_OUTPUT_HANDLER_INTERNAL 0x0000 +#define PHP_OUTPUT_HANDLER_USER 0x0001 + +/* handler ability flags */ +#define PHP_OUTPUT_HANDLER_CLEANABLE 0x0010 +#define PHP_OUTPUT_HANDLER_FLUSHABLE 0x0020 +#define PHP_OUTPUT_HANDLER_REMOVABLE 0x0040 +#define PHP_OUTPUT_HANDLER_STDFLAGS 0x0070 + +/* handler status flags */ +#define PHP_OUTPUT_HANDLER_STARTED 0x1000 +#define PHP_OUTPUT_HANDLER_DISABLED 0x2000 +#define PHP_OUTPUT_HANDLER_PROCESSED 0x4000 + +/* handler op return values */ +typedef enum _php_output_handler_status_t { + PHP_OUTPUT_HANDLER_FAILURE, + PHP_OUTPUT_HANDLER_SUCCESS, + PHP_OUTPUT_HANDLER_NO_DATA +} php_output_handler_status_t; + +/* php_output_stack_pop() flags */ +#define PHP_OUTPUT_POP_TRY 0x000 +#define PHP_OUTPUT_POP_FORCE 0x001 +#define PHP_OUTPUT_POP_DISCARD 0x010 +#define PHP_OUTPUT_POP_SILENT 0x100 + +/* real global flags */ +#define PHP_OUTPUT_IMPLICITFLUSH 0x01 +#define PHP_OUTPUT_DISABLED 0x02 +#define PHP_OUTPUT_WRITTEN 0x04 +#define PHP_OUTPUT_SENT 0x08 +/* supplementary flags for php_output_get_status() */ +#define PHP_OUTPUT_ACTIVE 0x10 +#define PHP_OUTPUT_LOCKED 0x20 +/* output layer is ready to use */ +#define PHP_OUTPUT_ACTIVATED 0x100000 + +/* handler hooks */ +typedef enum _php_output_handler_hook_t { + PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, + PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS, + PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL, + PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, + PHP_OUTPUT_HANDLER_HOOK_DISABLE, + /* unused */ + PHP_OUTPUT_HANDLER_HOOK_LAST +} php_output_handler_hook_t; + +#define PHP_OUTPUT_HANDLER_INITBUF_SIZE(s) \ +( ((s) > 1) ? \ + (s) + PHP_OUTPUT_HANDLER_ALIGNTO_SIZE - ((s) % (PHP_OUTPUT_HANDLER_ALIGNTO_SIZE)) : \ + PHP_OUTPUT_HANDLER_DEFAULT_SIZE \ +) +#define PHP_OUTPUT_HANDLER_ALIGNTO_SIZE 0x1000 +#define PHP_OUTPUT_HANDLER_DEFAULT_SIZE 0x4000 + +typedef struct _php_output_buffer { + char *data; + size_t size; + size_t used; + uint free:1; + uint _res:31; +} php_output_buffer; + +typedef struct _php_output_context { + int op; + php_output_buffer in; + php_output_buffer out; +#ifdef ZTS + void ***tsrm_ls; +#endif +} php_output_context; + +#define PHP_OUTPUT_TSRMLS(ctx) TSRMLS_FETCH_FROM_CTX((ctx)->tsrm_ls) + +/* old-style, stateless callback */ typedef void (*php_output_handler_func_t)(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC); +/* new-style, opaque context callback */ +typedef int (*php_output_handler_context_func_t)(void **handler_context, php_output_context *output_context); +/* output handler context dtor */ +typedef void (*php_output_handler_context_dtor_t)(void *opaq TSRMLS_DC); +/* conflict check callback */ +typedef int (*php_output_handler_conflict_check_t)(const char *handler_name, size_t handler_name_len TSRMLS_DC); +/* ctor for aliases */ +typedef struct _php_output_handler *(*php_output_handler_alias_ctor_t)(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags TSRMLS_DC); + +typedef struct _php_output_handler_user_func_t { + zend_fcall_info fci; + zend_fcall_info_cache fcc; + zval *zoh; +} php_output_handler_user_func_t; + +typedef struct _php_output_handler { + char *name; + size_t name_len; + int flags; + int level; + size_t size; + php_output_buffer buffer; + + void *opaq; + void (*dtor)(void *opaq TSRMLS_DC); + + union { + php_output_handler_user_func_t *user; + php_output_handler_context_func_t internal; + } func; +} php_output_handler; + +ZEND_BEGIN_MODULE_GLOBALS(output) + int flags; + zend_stack handlers; + php_output_handler *active; + php_output_handler *running; + const char *output_start_filename; + int output_start_lineno; +ZEND_END_MODULE_GLOBALS(output) + +/* there should not be a need to use OG() from outside of output.c */ +#ifdef ZTS +# define OG(v) TSRMG(output_globals_id, zend_output_globals *, v) +#else +# define OG(v) (output_globals.v) +#endif + +/* convenience macros */ +#define PHPWRITE(str, str_len) php_output_write((str), (str_len) TSRMLS_CC) +#define PHPWRITE_H(str, str_len) php_output_write_unbuffered((str), (str_len) TSRMLS_CC) + +#define PUTC(c) (php_output_write(&(c), 1 TSRMLS_CC), (c)) +#define PUTC_H(c) (php_output_write_unbuffered(&(c), 1 TSRMLS_CC), (c)) + +#define PUTS(str) do { \ + const char *__str = (str); \ + php_output_write(__str, strlen(__str) TSRMLS_CC); \ +} while (0) +#define PUTS_H(str) do { \ + const char *__str = (str); \ + php_output_write_unbuffered(__str, strlen(__str) TSRMLS_CC); \ +} while (0) + BEGIN_EXTERN_C() + +extern const char php_output_default_handler_name[sizeof("default output handler")]; +extern const char php_output_devnull_handler_name[sizeof("null output handler")]; + +#define php_output_tearup() \ + php_output_startup(); \ + php_output_activate(TSRMLS_C) +#define php_output_teardown() \ + php_output_end_all(TSRMLS_C); \ + php_output_deactivate(TSRMLS_C); \ + php_output_shutdown() + +/* MINIT */ PHPAPI void php_output_startup(void); -PHPAPI void php_output_activate(TSRMLS_D); -PHPAPI void php_output_set_status(zend_bool status TSRMLS_DC); +/* MSHUTDOWN */ +PHPAPI void php_output_shutdown(void); + PHPAPI void php_output_register_constants(TSRMLS_D); -PHPAPI int php_default_output_func(const char *str, uint str_len TSRMLS_DC); -PHPAPI int php_ub_body_write(const char *str, uint str_length TSRMLS_DC); -PHPAPI int php_ub_body_write_no_header(const char *str, uint str_length TSRMLS_DC); -PHPAPI int php_body_write(const char *str, uint str_length TSRMLS_DC); -PHPAPI int php_header_write(const char *str, uint str_length TSRMLS_DC); -PHPAPI int php_start_ob_buffer(zval *output_handler, uint chunk_size, zend_bool erase TSRMLS_DC); -PHPAPI int php_start_ob_buffer_named(const char *output_handler_name, uint chunk_size, zend_bool erase TSRMLS_DC); -PHPAPI void php_end_ob_buffer(zend_bool send_buffer, zend_bool just_flush TSRMLS_DC); -PHPAPI void php_end_ob_buffers(zend_bool send_buffer TSRMLS_DC); -PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC); -PHPAPI int php_ob_get_length(zval *p TSRMLS_DC); -PHPAPI void php_start_implicit_flush(TSRMLS_D); -PHPAPI void php_end_implicit_flush(TSRMLS_D); -PHPAPI char *php_get_output_start_filename(TSRMLS_D); -PHPAPI int php_get_output_start_lineno(TSRMLS_D); -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); -PHPAPI int php_ob_handler_used(char *handler_name TSRMLS_DC); -PHPAPI int php_ob_init_conflict(char *handler_new, char *handler_set TSRMLS_DC); -PHPAPI int php_ob_get_buffer(zval *p TSRMLS_DC); -PHPAPI int php_ob_get_length(zval *p TSRMLS_DC); + +/* RINIT */ +PHPAPI int php_output_activate(TSRMLS_D); +/* RSHUTDOWN */ +PHPAPI void php_output_deactivate(TSRMLS_D); + +PHPAPI void php_output_set_status(int status TSRMLS_DC); +PHPAPI int php_output_get_status(TSRMLS_D); +PHPAPI void php_output_set_implicit_flush(int flush TSRMLS_DC); +PHPAPI const char *php_output_get_start_filename(TSRMLS_D); +PHPAPI int php_output_get_start_lineno(TSRMLS_D); + +PHPAPI int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC); +PHPAPI int php_output_write(const char *str, size_t len TSRMLS_DC); + +PHPAPI int php_output_flush(TSRMLS_D); +PHPAPI void php_output_flush_all(TSRMLS_D); +PHPAPI int php_output_clean(TSRMLS_D); +PHPAPI void php_output_clean_all(TSRMLS_D); +PHPAPI int php_output_end(TSRMLS_D); +PHPAPI void php_output_end_all(TSRMLS_D); +PHPAPI int php_output_discard(TSRMLS_D); +PHPAPI void php_output_discard_all(TSRMLS_D); + +PHPAPI int php_output_get_contents(zval *p TSRMLS_DC); +PHPAPI int php_output_get_length(zval *p TSRMLS_DC); +PHPAPI int php_output_get_level(TSRMLS_D); +PHPAPI php_output_handler* php_output_get_active_handler(TSRMLS_D); + +PHPAPI int php_output_start_default(TSRMLS_D); +PHPAPI int php_output_start_devnull(TSRMLS_D); + +PHPAPI int php_output_start_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC); +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); + +PHPAPI php_output_handler *php_output_handler_create_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC); +PHPAPI php_output_handler *php_output_handler_create_internal(const char *name, size_t name_len, php_output_handler_context_func_t handler, size_t chunk_size, int flags TSRMLS_DC); + +PHPAPI void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC); +PHPAPI int php_output_handler_start(php_output_handler *handler TSRMLS_DC); +PHPAPI int php_output_handler_started(const char *name, size_t name_len TSRMLS_DC); +PHPAPI int php_output_handler_hook(php_output_handler_hook_t type, void *arg TSRMLS_DC); +PHPAPI void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC); +PHPAPI void php_output_handler_free(php_output_handler **handler TSRMLS_DC); + +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); +PHPAPI int php_output_handler_conflict_register(const char *handler_name, size_t handler_name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC); +PHPAPI int php_output_handler_reverse_conflict_register(const char *handler_name, size_t handler_name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC); + +PHPAPI php_output_handler_alias_ctor_t *php_output_handler_alias(const char *handler_name, size_t handler_name_len TSRMLS_DC); +PHPAPI int php_output_handler_alias_register(const char *handler_name, size_t handler_name_len, php_output_handler_alias_ctor_t func TSRMLS_DC); + END_EXTERN_C() + PHP_FUNCTION(ob_start); PHP_FUNCTION(ob_flush); PHP_FUNCTION(ob_clean); @@ -64,51 +264,16 @@ PHP_FUNCTION(ob_get_status); PHP_FUNCTION(ob_implicit_flush); PHP_FUNCTION(ob_list_handlers); -typedef struct _php_ob_buffer { - char *buffer; - uint size; - uint text_length; - int block_size; - uint chunk_size; - int status; - zval *output_handler; - php_output_handler_func_t internal_output_handler; - char *internal_output_handler_buffer; - uint internal_output_handler_buffer_size; - char *handler_name; - zend_bool erase; -} php_ob_buffer; - -typedef struct _php_output_globals { - int (*php_body_write)(const char *str, uint str_length TSRMLS_DC); /* string output */ - int (*php_header_write)(const char *str, uint str_length TSRMLS_DC); /* unbuffer string output */ - php_ob_buffer active_ob_buffer; - unsigned char implicit_flush; - char *output_start_filename; - int output_start_lineno; - zend_stack ob_buffers; - int ob_nesting_level; - zend_bool ob_lock; - zend_bool disable_output; -} php_output_globals; - -#ifdef ZTS -#define OG(v) TSRMG(output_globals_id, php_output_globals *, v) -ZEND_API extern int output_globals_id; -#else -#define OG(v) (output_globals.v) -ZEND_API extern php_output_globals output_globals; -#endif - -#define PHP_OUTPUT_HANDLER_START (1<<0) -#define PHP_OUTPUT_HANDLER_CONT (1<<1) -#define PHP_OUTPUT_HANDLER_END (1<<2) - -#define PHP_OUTPUT_HANDLER_INTERNAL 0 -#define PHP_OUTPUT_HANDLER_USER 1 - PHP_FUNCTION(output_add_rewrite_var); PHP_FUNCTION(output_reset_rewrite_vars); +#endif -#endif /* PHP_OUTPUT_H */ +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/main/php_streams.h b/main/php_streams.h index 83b18c008b..82498d6619 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -154,6 +154,8 @@ typedef struct _php_stream_wrapper_ops { /* Create/Remove directory */ int (*stream_mkdir)(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC); int (*stream_rmdir)(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC); + /* Metadata handling */ + int (*stream_metadata)(php_stream_wrapper *wrapper, char *url, int options, void *value, php_stream_context *context TSRMLS_DC); } php_stream_wrapper_ops; struct _php_stream_wrapper { @@ -225,9 +227,11 @@ struct _php_stream { int eof; #if ZEND_DEBUG - char *open_filename; + const char *open_filename; uint open_lineno; #endif + + struct _php_stream *enclosing_stream; /* this is a private stream owned by enclosing_stream */ }; /* php_stream */ /* state definitions when closing down; these are private to streams.c */ @@ -259,6 +263,10 @@ END_EXTERN_C() #define php_stream_from_zval_no_verify(xstr, ppzval) (xstr) = (php_stream*)zend_fetch_resource((ppzval) TSRMLS_CC, -1, "stream", NULL, 2, php_file_le_stream(), php_file_le_pstream()) BEGIN_EXTERN_C() +PHPAPI php_stream *php_stream_encloses(php_stream *enclosing, php_stream *enclosed); +#define php_stream_free_enclosed(stream_enclosed, close_options) _php_stream_free_enclosed((stream_enclosed), (close_options) TSRMLS_CC) +PHPAPI int _php_stream_free_enclosed(php_stream *stream_enclosed, int close_options TSRMLS_DC); + PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream **stream TSRMLS_DC); #define PHP_STREAM_PERSISTENT_SUCCESS 0 /* id exists */ #define PHP_STREAM_PERSISTENT_FAILURE 1 /* id exists but is not a stream! */ @@ -269,6 +277,7 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream * #define PHP_STREAM_FREE_PRESERVE_HANDLE 4 /* tell ops->close to not close it's underlying handle */ #define PHP_STREAM_FREE_RSRC_DTOR 8 /* called from the resource list dtor */ #define PHP_STREAM_FREE_PERSISTENT 16 /* manually freeing a persistent connection */ +#define PHP_STREAM_FREE_IGNORE_ENCLOSING 32 /* don't close the enclosing stream instead */ #define PHP_STREAM_FREE_CLOSE (PHP_STREAM_FREE_CALL_DTOR | PHP_STREAM_FREE_RELEASE_STREAM) #define PHP_STREAM_FREE_CLOSE_CASTED (PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_PRESERVE_HANDLE) #define PHP_STREAM_FREE_CLOSE_PERSISTENT (PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_PERSISTENT) @@ -429,7 +438,7 @@ BEGIN_EXTERN_C() ZEND_ATTRIBUTE_DEPRECATED PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC); #define php_stream_copy_to_stream(src, dest, maxlen) _php_stream_copy_to_stream((src), (dest), (maxlen) STREAMS_CC TSRMLS_CC) -PHPAPI size_t _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t *len STREAMS_DC TSRMLS_DC); +PHPAPI int _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t *len STREAMS_DC TSRMLS_DC); #define php_stream_copy_to_stream_ex(src, dest, maxlen, len) _php_stream_copy_to_stream_ex((src), (dest), (maxlen), (len) STREAMS_CC TSRMLS_CC) @@ -484,8 +493,8 @@ END_EXTERN_C() #define IGNORE_PATH 0x00000000 #define USE_PATH 0x00000001 #define IGNORE_URL 0x00000002 -#define ENFORCE_SAFE_MODE 0x00000004 #define REPORT_ERRORS 0x00000008 +#define ENFORCE_SAFE_MODE 0 /* for BC only */ /* If you don't need to write to the stream, but really need to * be able to seek, use this flag in your options. */ @@ -583,6 +592,15 @@ END_EXTERN_C() /* Definitions for user streams */ #define PHP_STREAM_IS_URL 1 + +/* Stream metadata definitions */ +/* Create if referred resource does not exist */ +#define PHP_STREAM_META_TOUCH 1 +#define PHP_STREAM_META_OWNER_NAME 2 +#define PHP_STREAM_META_OWNER 3 +#define PHP_STREAM_META_GROUP_NAME 4 +#define PHP_STREAM_META_GROUP 5 +#define PHP_STREAM_META_ACCESS 6 /* * Local variables: * tab-width: 4 diff --git a/main/php_variables.c b/main/php_variables.c index 4b97cbd85f..427966170c 100644 --- a/main/php_variables.c +++ b/main/php_variables.c @@ -47,11 +47,7 @@ PHPAPI void php_register_variable_safe(char *var, char *strval, int str_len, zva /* Prepare value */ Z_STRLEN(new_entry) = str_len; - if (PG(magic_quotes_gpc)) { - Z_STRVAL(new_entry) = php_addslashes(strval, Z_STRLEN(new_entry), &Z_STRLEN(new_entry), 0 TSRMLS_CC); - } else { - Z_STRVAL(new_entry) = estrndup(strval, Z_STRLEN(new_entry)); - } + Z_STRVAL(new_entry) = estrndup(strval, Z_STRLEN(new_entry)); Z_TYPE(new_entry) = IS_STRING; php_register_variable_ex(var, &new_entry, track_vars_array TSRMLS_CC); @@ -61,39 +57,38 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars { char *p = NULL; char *ip; /* index pointer */ - char *index, *escaped_index = NULL; + char *index; char *var, *var_orig; int var_len, index_len; zval *gpc_element, **gpc_element_p; zend_bool is_array = 0; HashTable *symtable1 = NULL; + ALLOCA_FLAG(use_heap) assert(var_name != NULL); if (track_vars_array) { symtable1 = Z_ARRVAL_P(track_vars_array); - } else if (PG(register_globals)) { - if (!EG(active_symbol_table)) { - zend_rebuild_symbol_table(TSRMLS_C); - } - symtable1 = EG(active_symbol_table); } + if (!symtable1) { /* Nothing to do */ zval_dtor(val); return; } - /* - * Prepare variable name - */ - var_orig = estrdup(var_name); - var = var_orig; /* ignore leading spaces in the variable name */ - while (*var && *var==' ') { - var++; + while (*var_name && *var_name==' ') { + var_name++; } + + /* + * Prepare variable name + */ + var_len = strlen(var_name); + var = var_orig = do_alloca(var_len + 1, use_heap); + memcpy(var_orig, var_name, var_len + 1); /* ensure that we don't have spaces or dots in the variable name (not binary safe) */ for (p = var; *p; p++) { @@ -110,7 +105,7 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars if (var_len==0) { /* empty variable name, or variable name with a space in it */ zval_dtor(val); - efree(var_orig); + free_alloca(var_orig, use_heap); return; } @@ -119,7 +114,7 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars var_len == sizeof("GLOBALS")-1 && !memcmp(var, "GLOBALS", sizeof("GLOBALS")-1)) { zval_dtor(val); - efree(var_orig); + free_alloca(var_orig, use_heap); return; } @@ -139,9 +134,6 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars if (track_vars_array) { ht = Z_ARRVAL_P(track_vars_array); zend_symtable_del(ht, var, var_len + 1); - } else if (PG(register_globals)) { - ht = EG(active_symbol_table); - zend_symtable_del(ht, var, var_len + 1); } zval_dtor(val); @@ -151,7 +143,7 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars if (!PG(display_errors)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variable nesting level exceeded %ld. To increase the limit change max_input_nesting_level in php.ini.", PG(max_input_nesting_level)); } - efree(var_orig); + free_alloca(var_orig, use_heap); return; } @@ -185,23 +177,15 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars if (zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p) == FAILURE) { zval_ptr_dtor(&gpc_element); zval_dtor(val); - efree(var_orig); + free_alloca(var_orig, use_heap); return; } } else { - if (PG(magic_quotes_gpc)) { - escaped_index = php_addslashes(index, index_len, &index_len, 0 TSRMLS_CC); - } else { - escaped_index = index; - } - if (zend_symtable_find(symtable1, escaped_index, index_len + 1, (void **) &gpc_element_p) == FAILURE + if (zend_symtable_find(symtable1, index, index_len + 1, (void **) &gpc_element_p) == FAILURE || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { MAKE_STD_ZVAL(gpc_element); array_init(gpc_element); - zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); - } - if (index != escaped_index) { - efree(escaped_index); + zend_symtable_update(symtable1, index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); } } symtable1 = Z_ARRVAL_PP(gpc_element_p); @@ -227,11 +211,6 @@ plain_var: zval_ptr_dtor(&gpc_element); } } else { - if (PG(magic_quotes_gpc)) { - escaped_index = php_addslashes(index, index_len, &index_len, 0 TSRMLS_CC); - } else { - escaped_index = index; - } /* * According to rfc2965, more specific paths are listed above the less specific ones. * If we encounter a duplicate cookie name, we should skip it, since it is not possible @@ -240,17 +219,14 @@ plain_var: */ if (PG(http_globals)[TRACK_VARS_COOKIE] && symtable1 == Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]) && - zend_symtable_exists(symtable1, escaped_index, index_len + 1)) { + zend_symtable_exists(symtable1, index, index_len + 1)) { zval_ptr_dtor(&gpc_element); } else { - zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); - } - if (escaped_index != index) { - efree(escaped_index); + zend_symtable_update(symtable1, index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); } } } - efree(var_orig); + free_alloca(var_orig, use_heap); } SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) @@ -447,13 +423,6 @@ void _php_import_environment_variables(zval *array_ptr TSRMLS_DC) size_t alloc_size = sizeof(buf); unsigned long nlen; /* ptrdiff_t is not portable */ - /* turn off magic_quotes while importing environment variables */ - int magic_quotes_gpc = PG(magic_quotes_gpc); - - if (magic_quotes_gpc) { - zend_alter_ini_entry_ex("magic_quotes_gpc", sizeof("magic_quotes_gpc"), "0", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_ACTIVATE, 1 TSRMLS_CC); - } - for (env = environ; env != NULL && *env != NULL; env++) { p = strchr(*env, '='); if (!p) { /* malformed entry? */ @@ -471,10 +440,6 @@ void _php_import_environment_variables(zval *array_ptr TSRMLS_DC) if (t != buf && t != NULL) { efree(t); } - - if (magic_quotes_gpc) { - zend_alter_ini_entry_ex("magic_quotes_gpc", sizeof("magic_quotes_gpc"), "1", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_ACTIVATE, 1 TSRMLS_CC); - } } zend_bool php_std_auto_global_callback(char *name, uint name_len TSRMLS_DC) @@ -491,7 +456,7 @@ static void php_build_argv(char *s, zval *track_vars_array TSRMLS_DC) int count = 0; char *ss, *space; - if (!(PG(register_globals) || SG(request_info).argc || track_vars_array)) { + if (!(SG(request_info).argc || track_vars_array)) { return; } @@ -550,7 +515,7 @@ static void php_build_argv(char *s, zval *track_vars_array TSRMLS_DC) } Z_TYPE_P(argc) = IS_LONG; - if (PG(register_globals) || SG(request_info).argc) { + if (SG(request_info).argc) { Z_ADDREF_P(arr); Z_ADDREF_P(argc); zend_hash_update(&EG(symbol_table), "argv", sizeof("argv"), &arr, sizeof(zval *), NULL); @@ -588,8 +553,6 @@ PHPAPI int php_handle_special_queries(TSRMLS_D) static inline void php_register_server_variables(TSRMLS_D) { zval *array_ptr = NULL; - /* turn off magic_quotes while importing server variables */ - int magic_quotes_gpc = PG(magic_quotes_gpc); ALLOC_ZVAL(array_ptr); array_init(array_ptr); @@ -598,9 +561,6 @@ static inline void php_register_server_variables(TSRMLS_D) zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_SERVER]); } PG(http_globals)[TRACK_VARS_SERVER] = array_ptr; - if (magic_quotes_gpc) { - zend_alter_ini_entry_ex("magic_quotes_gpc", sizeof("magic_quotes_gpc"), "0", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_ACTIVATE, 1 TSRMLS_CC); - } /* Server variables */ if (sapi_module.register_server_variables) { @@ -619,15 +579,15 @@ static inline void php_register_server_variables(TSRMLS_D) } /* store request init time */ { - zval new_entry; - Z_TYPE(new_entry) = IS_LONG; - Z_LVAL(new_entry) = sapi_get_request_time(TSRMLS_C); - php_register_variable_ex("REQUEST_TIME", &new_entry, array_ptr TSRMLS_CC); + zval request_time_float, request_time_long; + Z_TYPE(request_time_float) = IS_DOUBLE; + Z_DVAL(request_time_float) = sapi_get_request_time(TSRMLS_C); + php_register_variable_ex("REQUEST_TIME_FLOAT", &request_time_float, array_ptr TSRMLS_CC); + Z_TYPE(request_time_long) = IS_LONG; + Z_LVAL(request_time_long) = zend_dval_to_lval(Z_DVAL(request_time_float)); + php_register_variable_ex("REQUEST_TIME", &request_time_long, array_ptr TSRMLS_CC); } - if (magic_quotes_gpc) { - zend_alter_ini_entry_ex("magic_quotes_gpc", sizeof("magic_quotes_gpc"), "1", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_ACTIVATE, 1 TSRMLS_CC); - } } /* }}} */ @@ -641,7 +601,7 @@ static void php_autoglobal_merge(HashTable *dest, HashTable *src TSRMLS_DC) ulong num_key; HashPosition pos; int key_type; - int globals_check = (PG(register_globals) && (dest == (&EG(symbol_table)))); + int globals_check = (dest == (&EG(symbol_table))); zend_hash_internal_pointer_reset_ex(src, &pos); while (zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS) { @@ -650,10 +610,9 @@ static void php_autoglobal_merge(HashTable *dest, HashTable *src TSRMLS_DC) || (key_type == HASH_KEY_IS_STRING && zend_hash_find(dest, string_key, string_key_len, (void **) &dest_entry) != SUCCESS) || (key_type == HASH_KEY_IS_LONG && zend_hash_index_find(dest, num_key, (void **)&dest_entry) != SUCCESS) || Z_TYPE_PP(dest_entry) != IS_ARRAY - ) { + ) { Z_ADDREF_PP(src_entry); if (key_type == HASH_KEY_IS_STRING) { - /* if register_globals is on and working with main symbol table, prevent overwriting of GLOBALS */ if (!globals_check || string_key_len != sizeof("GLOBALS") || memcmp(string_key, "GLOBALS", sizeof("GLOBALS") - 1)) { zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL); } else { @@ -671,130 +630,116 @@ static void php_autoglobal_merge(HashTable *dest, HashTable *src TSRMLS_DC) } /* }}} */ -static zend_bool php_auto_globals_create_server(char *name, uint name_len TSRMLS_DC); -static zend_bool php_auto_globals_create_env(char *name, uint name_len TSRMLS_DC); -static zend_bool php_auto_globals_create_request(char *name, uint name_len TSRMLS_DC); +static zend_bool php_auto_globals_create_server(const char *name, uint name_len TSRMLS_DC); +static zend_bool php_auto_globals_create_env(const char *name, uint name_len TSRMLS_DC); +static zend_bool php_auto_globals_create_request(const char *name, uint name_len TSRMLS_DC); /* {{{ php_hash_environment */ int php_hash_environment(TSRMLS_D) { - char *p; - unsigned char _gpc_flags[5] = {0, 0, 0, 0, 0}; - zend_bool jit_initialization = (PG(auto_globals_jit) && !PG(register_globals) && !PG(register_long_arrays)); - struct auto_global_record { - char *name; - uint name_len; - char *long_name; - uint long_name_len; - zend_bool jit_initialization; - } auto_global_records[] = { - { "_POST", sizeof("_POST"), "HTTP_POST_VARS", sizeof("HTTP_POST_VARS"), 0 }, - { "_GET", sizeof("_GET"), "HTTP_GET_VARS", sizeof("HTTP_GET_VARS"), 0 }, - { "_COOKIE", sizeof("_COOKIE"), "HTTP_COOKIE_VARS", sizeof("HTTP_COOKIE_VARS"), 0 }, - { "_SERVER", sizeof("_SERVER"), "HTTP_SERVER_VARS", sizeof("HTTP_SERVER_VARS"), 1 }, - { "_ENV", sizeof("_ENV"), "HTTP_ENV_VARS", sizeof("HTTP_ENV_VARS"), 1 }, - { "_FILES", sizeof("_FILES"), "HTTP_POST_FILES", sizeof("HTTP_POST_FILES"), 0 }, - }; - size_t num_track_vars = sizeof(auto_global_records)/sizeof(struct auto_global_record); - size_t i; - - /* jit_initialization = 0; */ - for (i=0; i<num_track_vars; i++) { - PG(http_globals)[i] = NULL; - } - - for (p=PG(variables_order); p && *p; p++) { - switch(*p) { - case 'p': - case 'P': - if (!_gpc_flags[0] && !SG(headers_sent) && SG(request_info).request_method && !strcasecmp(SG(request_info).request_method, "POST")) { - sapi_module.treat_data(PARSE_POST, NULL, NULL TSRMLS_CC); /* POST Data */ - _gpc_flags[0] = 1; - if (PG(register_globals)) { - php_autoglobal_merge(&EG(symbol_table), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_POST]) TSRMLS_CC); - } - } - break; - case 'c': - case 'C': - if (!_gpc_flags[1]) { - sapi_module.treat_data(PARSE_COOKIE, NULL, NULL TSRMLS_CC); /* Cookie Data */ - _gpc_flags[1] = 1; - if (PG(register_globals)) { - php_autoglobal_merge(&EG(symbol_table), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]) TSRMLS_CC); - } - } - break; - case 'g': - case 'G': - if (!_gpc_flags[2]) { - sapi_module.treat_data(PARSE_GET, NULL, NULL TSRMLS_CC); /* GET Data */ - _gpc_flags[2] = 1; - if (PG(register_globals)) { - php_autoglobal_merge(&EG(symbol_table), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]) TSRMLS_CC); - } - } - break; - case 'e': - case 'E': - if (!jit_initialization && !_gpc_flags[3]) { - zend_auto_global_disable_jit("_ENV", sizeof("_ENV")-1 TSRMLS_CC); - php_auto_globals_create_env("_ENV", sizeof("_ENV")-1 TSRMLS_CC); - _gpc_flags[3] = 1; - if (PG(register_globals)) { - php_autoglobal_merge(&EG(symbol_table), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_ENV]) TSRMLS_CC); - } - } - break; - case 's': - case 'S': - if (!jit_initialization && !_gpc_flags[4]) { - zend_auto_global_disable_jit("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC); - php_register_server_variables(TSRMLS_C); - _gpc_flags[4] = 1; - if (PG(register_globals)) { - php_autoglobal_merge(&EG(symbol_table), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]) TSRMLS_CC); - } - } - break; - } - } - - /* argv/argc support */ + memset(PG(http_globals), 0, sizeof(PG(http_globals))); + zend_activate_auto_globals(TSRMLS_C); if (PG(register_argc_argv)) { php_build_argv(SG(request_info).query_string, PG(http_globals)[TRACK_VARS_SERVER] TSRMLS_CC); } + return SUCCESS; +} +/* }}} */ - for (i=0; i<num_track_vars; i++) { - if (jit_initialization && auto_global_records[i].jit_initialization) { - continue; +static zend_bool php_auto_globals_create_get(const char *name, uint name_len TSRMLS_DC) +{ + zval *vars; + + if (PG(variables_order) && (strchr(PG(variables_order),'G') || strchr(PG(variables_order),'g'))) { + sapi_module.treat_data(PARSE_GET, NULL, NULL TSRMLS_CC); + vars = PG(http_globals)[TRACK_VARS_GET]; + } else { + ALLOC_ZVAL(vars); + array_init(vars); + INIT_PZVAL(vars); + if (PG(http_globals)[TRACK_VARS_GET]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_GET]); } - if (!PG(http_globals)[i]) { - ALLOC_ZVAL(PG(http_globals)[i]); - array_init(PG(http_globals)[i]); - INIT_PZVAL(PG(http_globals)[i]); + PG(http_globals)[TRACK_VARS_GET] = vars; + } + + zend_hash_update(&EG(symbol_table), name, name_len + 1, &vars, sizeof(zval *), NULL); + Z_ADDREF_P(vars); + + return 0; /* don't rearm */ +} + +static zend_bool php_auto_globals_create_post(const char *name, uint name_len TSRMLS_DC) +{ + zval *vars; + + if (PG(variables_order) && + (strchr(PG(variables_order),'P') || strchr(PG(variables_order),'p')) && + !SG(headers_sent) && + SG(request_info).request_method && + !strcasecmp(SG(request_info).request_method, "POST")) { + sapi_module.treat_data(PARSE_POST, NULL, NULL TSRMLS_CC); + vars = PG(http_globals)[TRACK_VARS_POST]; + } else { + ALLOC_ZVAL(vars); + array_init(vars); + INIT_PZVAL(vars); + if (PG(http_globals)[TRACK_VARS_POST]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_POST]); } + PG(http_globals)[TRACK_VARS_POST] = vars; + } + + zend_hash_update(&EG(symbol_table), name, name_len + 1, &vars, sizeof(zval *), NULL); + Z_ADDREF_P(vars); + + return 0; /* don't rearm */ +} + +static zend_bool php_auto_globals_create_cookie(const char *name, uint name_len TSRMLS_DC) +{ + zval *vars; - Z_ADDREF_P(PG(http_globals)[i]); - zend_hash_update(&EG(symbol_table), auto_global_records[i].name, auto_global_records[i].name_len, &PG(http_globals)[i], sizeof(zval *), NULL); - if (PG(register_long_arrays)) { - zend_hash_update(&EG(symbol_table), auto_global_records[i].long_name, auto_global_records[i].long_name_len, &PG(http_globals)[i], sizeof(zval *), NULL); - Z_ADDREF_P(PG(http_globals)[i]); + if (PG(variables_order) && (strchr(PG(variables_order),'C') || strchr(PG(variables_order),'c'))) { + sapi_module.treat_data(PARSE_COOKIE, NULL, NULL TSRMLS_CC); + vars = PG(http_globals)[TRACK_VARS_COOKIE]; + } else { + ALLOC_ZVAL(vars); + array_init(vars); + INIT_PZVAL(vars); + if (PG(http_globals)[TRACK_VARS_COOKIE]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_COOKIE]); } + PG(http_globals)[TRACK_VARS_COOKIE] = vars; } - /* Create _REQUEST */ - if (!jit_initialization) { - zend_auto_global_disable_jit("_REQUEST", sizeof("_REQUEST")-1 TSRMLS_CC); - php_auto_globals_create_request("_REQUEST", sizeof("_REQUEST")-1 TSRMLS_CC); + zend_hash_update(&EG(symbol_table), name, name_len + 1, &vars, sizeof(zval *), NULL); + Z_ADDREF_P(vars); + + return 0; /* don't rearm */ +} + +static zend_bool php_auto_globals_create_files(const char *name, uint name_len TSRMLS_DC) +{ + zval *vars; + + if (PG(http_globals)[TRACK_VARS_FILES]) { + vars = PG(http_globals)[TRACK_VARS_FILES]; + } else { + ALLOC_ZVAL(vars); + array_init(vars); + INIT_PZVAL(vars); + PG(http_globals)[TRACK_VARS_FILES] = vars; } - return SUCCESS; + zend_hash_update(&EG(symbol_table), name, name_len + 1, &vars, sizeof(zval *), NULL); + Z_ADDREF_P(vars); + + return 0; /* don't rearm */ } -/* }}} */ -static zend_bool php_auto_globals_create_server(char *name, uint name_len TSRMLS_DC) +static zend_bool php_auto_globals_create_server(const char *name, uint name_len TSRMLS_DC) { if (PG(variables_order) && (strchr(PG(variables_order),'S') || strchr(PG(variables_order),'s'))) { php_register_server_variables(TSRMLS_C); @@ -804,7 +749,7 @@ static zend_bool php_auto_globals_create_server(char *name, uint name_len TSRMLS zval **argc, **argv; if (zend_hash_find(&EG(symbol_table), "argc", sizeof("argc"), (void**)&argc) == SUCCESS && - zend_hash_find(&EG(symbol_table), "argv", sizeof("argv"), (void**)&argv) == SUCCESS) { + zend_hash_find(&EG(symbol_table), "argv", sizeof("argv"), (void**)&argv) == SUCCESS) { Z_ADDREF_PP(argc); Z_ADDREF_PP(argv); zend_hash_update(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "argv", sizeof("argv"), argv, sizeof(zval *), NULL); @@ -828,16 +773,11 @@ static zend_bool php_auto_globals_create_server(char *name, uint name_len TSRMLS zend_hash_update(&EG(symbol_table), name, name_len + 1, &PG(http_globals)[TRACK_VARS_SERVER], sizeof(zval *), NULL); Z_ADDREF_P(PG(http_globals)[TRACK_VARS_SERVER]); - - if (PG(register_long_arrays)) { - zend_hash_update(&EG(symbol_table), "HTTP_SERVER_VARS", sizeof("HTTP_SERVER_VARS"), &PG(http_globals)[TRACK_VARS_SERVER], sizeof(zval *), NULL); - Z_ADDREF_P(PG(http_globals)[TRACK_VARS_SERVER]); - } return 0; /* don't rearm */ } -static zend_bool php_auto_globals_create_env(char *name, uint name_len TSRMLS_DC) +static zend_bool php_auto_globals_create_env(const char *name, uint name_len TSRMLS_DC) { zval *env_vars = NULL; ALLOC_ZVAL(env_vars); @@ -855,15 +795,10 @@ static zend_bool php_auto_globals_create_env(char *name, uint name_len TSRMLS_DC zend_hash_update(&EG(symbol_table), name, name_len + 1, &PG(http_globals)[TRACK_VARS_ENV], sizeof(zval *), NULL); Z_ADDREF_P(PG(http_globals)[TRACK_VARS_ENV]); - if (PG(register_long_arrays)) { - zend_hash_update(&EG(symbol_table), "HTTP_ENV_VARS", sizeof("HTTP_ENV_VARS"), &PG(http_globals)[TRACK_VARS_ENV], sizeof(zval *), NULL); - Z_ADDREF_P(PG(http_globals)[TRACK_VARS_ENV]); - } - return 0; /* don't rearm */ } -static zend_bool php_auto_globals_create_request(char *name, uint name_len TSRMLS_DC) +static zend_bool php_auto_globals_create_request(const char *name, uint name_len TSRMLS_DC) { zval *form_variables; unsigned char _gpc_flags[3] = {0, 0, 0}; @@ -873,7 +808,7 @@ static zend_bool php_auto_globals_create_request(char *name, uint name_len TSRML array_init(form_variables); INIT_PZVAL(form_variables); - if(PG(request_order) != NULL) { + if (PG(request_order) != NULL) { p = PG(request_order); } else { p = PG(variables_order); @@ -905,19 +840,19 @@ static zend_bool php_auto_globals_create_request(char *name, uint name_len TSRML } } - zend_hash_update(&EG(symbol_table), "_REQUEST", sizeof("_REQUEST"), &form_variables, sizeof(zval *), NULL); + zend_hash_update(&EG(symbol_table), name, name_len + 1, &form_variables, sizeof(zval *), NULL); return 0; } void php_startup_auto_globals(TSRMLS_D) { - zend_register_auto_global("_GET", sizeof("_GET")-1, NULL TSRMLS_CC); - zend_register_auto_global("_POST", sizeof("_POST")-1, NULL TSRMLS_CC); - zend_register_auto_global("_COOKIE", sizeof("_COOKIE")-1, NULL TSRMLS_CC); - zend_register_auto_global("_SERVER", sizeof("_SERVER")-1, php_auto_globals_create_server TSRMLS_CC); - zend_register_auto_global("_ENV", sizeof("_ENV")-1, php_auto_globals_create_env TSRMLS_CC); - zend_register_auto_global("_REQUEST", sizeof("_REQUEST")-1, php_auto_globals_create_request TSRMLS_CC); - zend_register_auto_global("_FILES", sizeof("_FILES")-1, NULL TSRMLS_CC); + zend_register_auto_global(ZEND_STRL("_GET"), 0, php_auto_globals_create_get TSRMLS_CC); + zend_register_auto_global(ZEND_STRL("_POST"), 0, php_auto_globals_create_post TSRMLS_CC); + zend_register_auto_global(ZEND_STRL("_COOKIE"), 0, php_auto_globals_create_cookie TSRMLS_CC); + zend_register_auto_global(ZEND_STRL("_SERVER"), PG(auto_globals_jit), php_auto_globals_create_server TSRMLS_CC); + zend_register_auto_global(ZEND_STRL("_ENV"), PG(auto_globals_jit), php_auto_globals_create_env TSRMLS_CC); + zend_register_auto_global(ZEND_STRL("_REQUEST"), PG(auto_globals_jit), php_auto_globals_create_request TSRMLS_CC); + zend_register_auto_global(ZEND_STRL("_FILES"), 0, php_auto_globals_create_files TSRMLS_CC); } /* diff --git a/main/php_version.h b/main/php_version.h index e4ba15eba9..8dfe49630f 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -1,8 +1,8 @@ /* automatically generated by configure */ /* edit configure.in to change version number */ #define PHP_MAJOR_VERSION 5 -#define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 19 +#define PHP_MINOR_VERSION 4 +#define PHP_RELEASE_VERSION 9 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "5.3.19-dev" -#define PHP_VERSION_ID 50319 +#define PHP_VERSION "5.4.9-dev" +#define PHP_VERSION_ID 50409 diff --git a/main/rfc1867.c b/main/rfc1867.c index 31605cfc17..5da3a99355 100644 --- a/main/rfc1867.c +++ b/main/rfc1867.c @@ -36,92 +36,24 @@ #define DEBUG_FILE_UPLOAD ZEND_DEBUG -PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra TSRMLS_DC) = NULL; - -#if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) -#include "ext/mbstring/mbstring.h" - -static void safe_php_register_variable(char *var, char *strval, int val_len, zval *track_vars_array, zend_bool override_protection TSRMLS_DC); - -#define SAFE_RETURN { \ - php_mb_flush_gpc_variables(num_vars, val_list, len_list, array_ptr TSRMLS_CC); \ - if (lbuf) efree(lbuf); \ - if (abuf) efree(abuf); \ - if (array_index) efree(array_index); \ - zend_hash_destroy(&PG(rfc1867_protected_variables)); \ - zend_llist_destroy(&header); \ - if (mbuff->boundary_next) efree(mbuff->boundary_next); \ - if (mbuff->boundary) efree(mbuff->boundary); \ - if (mbuff->buffer) efree(mbuff->buffer); \ - if (mbuff) efree(mbuff); \ - return; } - -void php_mb_flush_gpc_variables(int num_vars, char **val_list, int *len_list, zval *array_ptr TSRMLS_DC) /* {{{ */ +static int dummy_encoding_translation(TSRMLS_D) { - int i; - if (php_mb_encoding_translation(TSRMLS_C)) { - if (num_vars > 0 && - php_mb_gpc_encoding_detector(val_list, len_list, num_vars, NULL TSRMLS_CC) == SUCCESS) { - php_mb_gpc_encoding_converter(val_list, len_list, num_vars, NULL, NULL TSRMLS_CC); - } - for (i = 0; i<num_vars; i += 2) { - safe_php_register_variable(val_list[i], val_list[i+1], len_list[i+1], array_ptr, 0 TSRMLS_CC); - efree(val_list[i]); - efree(val_list[i+1]); - } - efree(val_list); - efree(len_list); - } -} -/*Â }}}Â */ - -void php_mb_gpc_realloc_buffer(char ***pval_list, int **plen_list, int *num_vars_max, int inc TSRMLS_DC) /* {{{ */ -{ - /* allow only even increments */ - if (inc & 1) { - inc++; - } - (*num_vars_max) += inc; - *pval_list = (char **)erealloc(*pval_list, (*num_vars_max+2)*sizeof(char *)); - *plen_list = (int *)erealloc(*plen_list, (*num_vars_max+2)*sizeof(int)); + return 0; } -/* }}} */ -void php_mb_gpc_stack_variable(char *param, char *value, char ***pval_list, int **plen_list, int *num_vars, int *num_vars_max TSRMLS_DC) /* {{{Â */ -{ - char **val_list = *pval_list; - int *len_list = *plen_list; - - if (*num_vars >= *num_vars_max) { - php_mb_gpc_realloc_buffer(pval_list, plen_list, num_vars_max, 16 TSRMLS_CC); - /* in case realloc relocated the buffer */ - val_list = *pval_list; - len_list = *plen_list; - } +static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop TSRMLS_DC); +static char *php_ap_getword_conf(const zend_encoding *encoding, char *str TSRMLS_DC); - val_list[*num_vars] = (char *)estrdup(param); - len_list[*num_vars] = strlen(param); - (*num_vars)++; - val_list[*num_vars] = (char *)estrdup(value); - len_list[*num_vars] = strlen(value); - (*num_vars)++; -} -/* }}}Â */ +static php_rfc1867_encoding_translation_t php_rfc1867_encoding_translation = dummy_encoding_translation; +static php_rfc1867_get_detect_order_t php_rfc1867_get_detect_order = NULL; +static php_rfc1867_set_input_encoding_t php_rfc1867_set_input_encoding = NULL; +static php_rfc1867_getword_t php_rfc1867_getword = php_ap_getword; +static php_rfc1867_getword_conf_t php_rfc1867_getword_conf = php_ap_getword_conf; +static php_rfc1867_basename_t php_rfc1867_basename = NULL; -#else +PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra TSRMLS_DC) = NULL; -#define SAFE_RETURN { \ - if (lbuf) efree(lbuf); \ - if (abuf) efree(abuf); \ - if (array_index) efree(array_index); \ - zend_hash_destroy(&PG(rfc1867_protected_variables)); \ - zend_llist_destroy(&header); \ - if (mbuff->boundary_next) efree(mbuff->boundary_next); \ - if (mbuff->boundary) efree(mbuff->boundary); \ - if (mbuff->buffer) efree(mbuff->buffer); \ - if (mbuff) efree(mbuff); \ - return; } -#endif +static void safe_php_register_variable(char *var, char *strval, int val_len, zval *track_vars_array, zend_bool override_protection TSRMLS_DC); /* The longest property name we use in an uploaded file array */ #define MAX_SIZE_OF_INDEX sizeof("[tmp_name]") @@ -139,7 +71,7 @@ void php_mb_gpc_stack_variable(char *param, char *value, char ***pval_list, int #define UPLOAD_ERROR_F 7 /* Failed to write file to disk */ #define UPLOAD_ERROR_X 8 /* File upload stopped by extension */ -void php_rfc1867_register_constants(TSRMLS_D) /* {{{Â */ +void php_rfc1867_register_constants(TSRMLS_D) /* {{{ */ { REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_OK", UPLOAD_ERROR_OK, CONST_CS | CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_INI_SIZE", UPLOAD_ERROR_A, CONST_CS | CONST_PERSISTENT); @@ -208,16 +140,16 @@ static void normalize_protected_variable(char *varname TSRMLS_DC) /* {{{ */ } *s = '\0'; } -/* }}}Â */ +/* }}} */ -static void add_protected_variable(char *varname TSRMLS_DC) /* {{{Â */ +static void add_protected_variable(char *varname TSRMLS_DC) /* {{{ */ { int dummy = 1; normalize_protected_variable(varname TSRMLS_CC); zend_hash_add(&PG(rfc1867_protected_variables), varname, strlen(varname)+1, &dummy, sizeof(int), NULL); } -/* }}}Â */ +/* }}} */ static zend_bool is_protected_variable(char *varname TSRMLS_DC) /* {{{ */ { @@ -226,41 +158,33 @@ static zend_bool is_protected_variable(char *varname TSRMLS_DC) /* {{{ */ } /* }}} */ -static void safe_php_register_variable(char *var, char *strval, int val_len, zval *track_vars_array, zend_bool override_protection TSRMLS_DC) /* {{{Â */ +static void safe_php_register_variable(char *var, char *strval, int val_len, zval *track_vars_array, zend_bool override_protection TSRMLS_DC) /* {{{ */ { if (override_protection || !is_protected_variable(var TSRMLS_CC)) { php_register_variable_safe(var, strval, val_len, track_vars_array TSRMLS_CC); } } -/* }}}Â */ +/* }}} */ -static void safe_php_register_variable_ex(char *var, zval *val, zval *track_vars_array, zend_bool override_protection TSRMLS_DC) /* {{{Â */ +static void safe_php_register_variable_ex(char *var, zval *val, zval *track_vars_array, zend_bool override_protection TSRMLS_DC) /* {{{ */ { if (override_protection || !is_protected_variable(var TSRMLS_CC)) { php_register_variable_ex(var, val, track_vars_array TSRMLS_CC); } } -/* }}}Â */ +/* }}} */ -static void register_http_post_files_variable(char *strvar, char *val, zval *http_post_files, zend_bool override_protection TSRMLS_DC) /* {{{Â */ +static void register_http_post_files_variable(char *strvar, char *val, zval *http_post_files, zend_bool override_protection TSRMLS_DC) /* {{{ */ { - int register_globals = PG(register_globals); - - PG(register_globals) = 0; safe_php_register_variable(strvar, val, strlen(val), http_post_files, override_protection TSRMLS_CC); - PG(register_globals) = register_globals; } -/* }}}Â */ +/* }}} */ static void register_http_post_files_variable_ex(char *var, zval *val, zval *http_post_files, zend_bool override_protection TSRMLS_DC) /* {{{ */ { - int register_globals = PG(register_globals); - - PG(register_globals) = 0; safe_php_register_variable_ex(var, val, http_post_files, override_protection TSRMLS_CC); - PG(register_globals) = register_globals; } -/* }}}Â */ +/* }}} */ static int unlink_filename(char **filename TSRMLS_DC) /* {{{ */ { @@ -277,7 +201,7 @@ void destroy_uploaded_files_hash(TSRMLS_D) /* {{{ */ } /* }}} */ -/* {{{Â Following code is based on apache_multipart_buffer.c from libapreq-0.33 package. */ +/* {{{ Following code is based on apache_multipart_buffer.c from libapreq-0.33 package. */ #define FILLUNIT (1024 * 5) @@ -294,6 +218,9 @@ typedef struct { char *boundary_next; int boundary_next_len; + const zend_encoding *input_encoding; + const zend_encoding **detect_order; + size_t detect_order_size; } multipart_buffer; typedef struct { @@ -351,7 +278,7 @@ static int multipart_buffer_eof(multipart_buffer *self TSRMLS_DC) } /* create new multipart_buffer structure */ -static multipart_buffer *multipart_buffer_new(char *boundary, int boundary_len) +static multipart_buffer *multipart_buffer_new(char *boundary, int boundary_len TSRMLS_DC) { multipart_buffer *self = (multipart_buffer *) ecalloc(1, sizeof(multipart_buffer)); @@ -368,6 +295,15 @@ static multipart_buffer *multipart_buffer_new(char *boundary, int boundary_len) self->buf_begin = self->buffer; self->bytes_in_buffer = 0; + if (php_rfc1867_encoding_translation(TSRMLS_C)) { + php_rfc1867_get_detect_order(&self->detect_order, &self->detect_order_size TSRMLS_CC); + } else { + self->detect_order = NULL; + self->detect_order_size = 0; + } + + self->input_encoding = NULL; + return self; } @@ -478,6 +414,10 @@ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header T char *key = line; char *value = NULL; + if (php_rfc1867_encoding_translation(TSRMLS_C)) { + self->input_encoding = zend_multibyte_encoding_detector(line, strlen(line), self->detect_order, self->detect_order_size TSRMLS_CC); + } + /* space in the beginning means same header */ if (!isspace(line[0])) { value = strchr(line, ':'); @@ -533,7 +473,7 @@ static char *php_mime_get_hdr_value(zend_llist header, char *key) return NULL; } -static char *php_ap_getword(char **line, char stop) +static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop TSRMLS_DC) { char *pos = *line, quote; char *res; @@ -569,29 +509,17 @@ static char *php_ap_getword(char **line, char stop) return res; } -static char *substring_conf(char *start, int len, char quote TSRMLS_DC) +static char *substring_conf(char *start, int len, char quote) { - char *result = emalloc(len + 2); + char *result = emalloc(len + 1); char *resp = result; int i; - for (i = 0; i < len; ++i) { + for (i = 0; i < len && start[i] != quote; ++i) { if (start[i] == '\\' && (start[i + 1] == '\\' || (quote && start[i + 1] == quote))) { *resp++ = start[++i]; } else { -#if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) - if (php_mb_encoding_translation(TSRMLS_C)) { - size_t j = php_mb_gpc_mbchar_bytes(start+i TSRMLS_CC); - while (j-- > 0 && i < len) { - *resp++ = start[i++]; - } - --i; - } else { - *resp++ = start[i]; - } -#else *resp++ = start[i]; -#endif } } @@ -599,65 +527,49 @@ static char *substring_conf(char *start, int len, char quote TSRMLS_DC) return result; } -static char *php_ap_getword_conf(char **line TSRMLS_DC) +static char *php_ap_getword_conf(const zend_encoding *encoding, char *str TSRMLS_DC) { - char *str = *line, *strend, *res, quote; - -#if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) - if (php_mb_encoding_translation(TSRMLS_C)) { - int len=strlen(str); - php_mb_gpc_encoding_detector(&str, &len, 1, NULL TSRMLS_CC); - } -#endif - while (*str && isspace(*str)) { ++str; } if (!*str) { - *line = str; return estrdup(""); } - if ((quote = *str) == '"' || quote == '\'') { - strend = str + 1; -look_for_quote: - while (*strend && *strend != quote) { - if (*strend == '\\' && strend[1] && strend[1] == quote) { - strend += 2; - } else { - ++strend; - } - } - if (*strend && *strend == quote) { - char p = *(strend + 1); - if (p != '\r' && p != '\n' && p != '\0') { - strend++; - goto look_for_quote; - } - } - - res = substring_conf(str + 1, strend - str - 1, quote TSRMLS_CC); - - if (*strend == quote) { - ++strend; - } + if (*str == '"' || *str == '\'') { + char quote = *str; + str++; + return substring_conf(str, strlen(str), quote); } else { + char *strend = str; - strend = str; while (*strend && !isspace(*strend)) { ++strend; } - res = substring_conf(str, strend - str, 0 TSRMLS_CC); + return substring_conf(str, strend - str, 0); } +} - while (*strend && isspace(*strend)) { - ++strend; - } +static char *php_ap_basename(const zend_encoding *encoding, char *path TSRMLS_DC) +{ + char *s = strrchr(path, '\\'); + char *s2 = strrchr(path, '/'); - *line = strend; - return res; + if (s && s2) { + if (s > s2) { + ++s; + } else { + s = ++s2; + } + return s; + } else if (s) { + return ++s; + } else if (s2) { + return ++s2; + } + return path; } /* @@ -760,7 +672,7 @@ static char *multipart_buffer_read_body(multipart_buffer *self, unsigned int *le * */ -SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ +SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ { char *boundary, *s = NULL, *boundary_end = NULL, *start_arr = NULL, *array_index = NULL; char *temp_filename = NULL, *lbuf = NULL, *abuf = NULL; @@ -768,19 +680,29 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ int max_file_size = 0, skip_upload = 0, anonindex = 0, is_anonymous; zval *http_post_files = NULL; HashTable *uploaded_files = NULL; -#if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) - int str_len = 0, num_vars = 0, num_vars_max = 2*10, *len_list = NULL; - char **val_list = NULL; -#endif multipart_buffer *mbuff; zval *array_ptr = (zval *) arg; int fd = -1; zend_llist header; void *event_extra_data = NULL; - int llen = 0; + unsigned int llen = 0; int upload_cnt = INI_INT("max_file_uploads"); + const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding(TSRMLS_C); + php_rfc1867_getword_t getword; + php_rfc1867_getword_conf_t getword_conf; + php_rfc1867_basename_t _basename; long count = 0; + if (php_rfc1867_encoding_translation(TSRMLS_C) && internal_encoding) { + getword = php_rfc1867_getword; + getword_conf = php_rfc1867_getword_conf; + _basename = php_rfc1867_basename; + } else { + getword = php_ap_getword; + getword_conf = php_ap_getword_conf; + _basename = php_ap_basename; + } + if (SG(post_max_size) > 0 && SG(request_info).content_length > SG(post_max_size)) { sapi_module.sapi_error(E_WARNING, "POST Content-Length of %ld bytes exceeds the limit of %ld bytes", SG(request_info).content_length, SG(post_max_size)); return; @@ -825,7 +747,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ } /* Initialize the buffer */ - if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) { + if (!(mbuff = multipart_buffer_new(boundary, boundary_len TSRMLS_CC))) { sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer"); return; } @@ -842,12 +764,6 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ INIT_PZVAL(http_post_files); PG(http_globals)[TRACK_VARS_FILES] = http_post_files; -#if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) - if (php_mb_encoding_translation(TSRMLS_C)) { - val_list = (char **)ecalloc(num_vars_max+2, sizeof(char *)); - len_list = (int *)ecalloc(num_vars_max+2, sizeof(int)); - } -#endif zend_llist_init(&header, sizeof(mime_header_entry), (llist_dtor_func_t) php_free_hdr_entry, 0); if (php_rfc1867_callback != NULL) { @@ -880,7 +796,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ ++cd; } - while (*cd && (pair = php_ap_getword(&cd, ';'))) + while (*cd && (pair = getword(mbuff->input_encoding, &cd, ';' TSRMLS_CC))) { char *key = NULL, *word = pair; @@ -889,18 +805,34 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ } if (strchr(pair, '=')) { - key = php_ap_getword(&pair, '='); + key = getword(mbuff->input_encoding, &pair, '=' TSRMLS_CC); if (!strcasecmp(key, "name")) { if (param) { efree(param); } - param = php_ap_getword_conf(&pair TSRMLS_CC); + param = getword_conf(mbuff->input_encoding, pair TSRMLS_CC); + if (mbuff->input_encoding && internal_encoding) { + unsigned char *new_param; + size_t new_param_len; + if ((size_t)-1 != zend_multibyte_encoding_converter(&new_param, &new_param_len, (unsigned char *)param, strlen(param), internal_encoding, mbuff->input_encoding TSRMLS_CC)) { + efree(param); + param = (char *)new_param; + } + } } else if (!strcasecmp(key, "filename")) { if (filename) { efree(filename); } - filename = php_ap_getword_conf(&pair TSRMLS_CC); + filename = getword_conf(mbuff->input_encoding, pair TSRMLS_CC); + if (mbuff->input_encoding && internal_encoding) { + unsigned char *new_filename; + size_t new_filename_len; + if ((size_t)-1 != zend_multibyte_encoding_converter(&new_filename, &new_filename_len, (unsigned char *)filename, strlen(filename), internal_encoding, mbuff->input_encoding TSRMLS_CC)) { + efree(filename); + filename = (char *)new_filename; + } + } } } if (key) { @@ -917,6 +849,17 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ if (!value) { value = estrdup(""); + value_len = 0; + } + + if (mbuff->input_encoding && internal_encoding) { + unsigned char *new_value; + size_t new_value_len; + if ((size_t)-1 != zend_multibyte_encoding_converter(&new_value, &new_value_len, (unsigned char *)value, value_len, internal_encoding, mbuff->input_encoding TSRMLS_CC)) { + efree(value); + value = (char *)new_value; + value_len = new_value_len; + } } if (++count <= PG(max_input_vars) && sapi_module.input_filter(PARSE_POST, param, &value, value_len, &new_val_len TSRMLS_CC)) { @@ -936,21 +879,12 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ } new_val_len = newlength; } - -#if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) - if (php_mb_encoding_translation(TSRMLS_C)) { - php_mb_gpc_stack_variable(param, value, &val_list, &len_list, &num_vars, &num_vars_max TSRMLS_CC); - } else { - safe_php_register_variable(param, value, new_val_len, array_ptr, 0 TSRMLS_CC); - } -#else safe_php_register_variable(param, value, new_val_len, array_ptr, 0 TSRMLS_CC); -#endif } else { if (count == PG(max_input_vars) + 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); } - + if (php_rfc1867_callback != NULL) { multipart_event_formdata event_formdata; @@ -1054,7 +988,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ offset = 0; end = 0; - + if (!cancel_upload) { /* only bother to open temp file if we have data */ blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end TSRMLS_CC); @@ -1089,12 +1023,12 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ } } - if (PG(upload_max_filesize) > 0 && (total_bytes+blen) > PG(upload_max_filesize)) { + if (PG(upload_max_filesize) > 0 && (long)(total_bytes+blen) > PG(upload_max_filesize)) { #if DEBUG_FILE_UPLOAD sapi_module.sapi_error(E_NOTICE, "upload_max_filesize of %ld bytes exceeded - file [%s=%s] not saved", PG(upload_max_filesize), param, filename); #endif cancel_upload = UPLOAD_ERROR_A; - } else if (max_file_size && ((total_bytes+blen) > max_file_size)) { + } else if (max_file_size && ((long)(total_bytes+blen) > max_file_size)) { #if DEBUG_FILE_UPLOAD sapi_module.sapi_error(E_NOTICE, "MAX_FILE_SIZE of %ld bytes exceeded - file [%s=%s] not saved", max_file_size, param, filename); #endif @@ -1190,56 +1124,18 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{Â */ snprintf(lbuf, llen, "%s_name", param); } -#if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) - if (php_mb_encoding_translation(TSRMLS_C)) { - if (num_vars >= num_vars_max) { - php_mb_gpc_realloc_buffer(&val_list, &len_list, &num_vars_max, 1 TSRMLS_CC); - } - val_list[num_vars] = filename; - len_list[num_vars] = strlen(filename); - num_vars++; - if (php_mb_gpc_encoding_detector(val_list, len_list, num_vars, NULL TSRMLS_CC) == SUCCESS) { - str_len = strlen(filename); - php_mb_gpc_encoding_converter(&filename, &str_len, 1, NULL, NULL TSRMLS_CC); - } - s = php_mb_strrchr(filename, '\\' TSRMLS_CC); - if ((tmp = php_mb_strrchr(filename, '/' TSRMLS_CC)) > s) { - s = tmp; - } - num_vars--; - goto filedone; - } -#endif /* The \ check should technically be needed for win32 systems only where * it is a valid path separator. However, IE in all it's wisdom always sends * the full path of the file on the user's filesystem, which means that unless * the user does basename() they get a bogus file name. Until IE's user base drops * to nill or problem is fixed this code must remain enabled for all systems. */ - s = strrchr(filename, '\\'); - if ((tmp = strrchr(filename, '/')) > s) { - s = tmp; - } -#ifdef PHP_WIN32 - if (PG(magic_quotes_gpc)) { - if ((tmp = strrchr(s ? s : filename, '\'')) > s) { - s = tmp; - } - if ((tmp = strrchr(s ? s : filename, '"')) > s) { - s = tmp; - } + s = _basename(internal_encoding, filename TSRMLS_CC); + if (!s) { + s = filename; } -#endif - -#if HAVE_MBSTRING && !defined(COMPILE_DL_MBSTRING) -filedone: -#endif if (!is_anonymous) { - if (s && s >= filename) { - safe_php_register_variable(lbuf, s+1, strlen(s+1), NULL, 0 TSRMLS_CC); - } else { - safe_php_register_variable(lbuf, filename, strlen(filename), NULL, 0 TSRMLS_CC); - } + safe_php_register_variable(lbuf, s, strlen(s), NULL, 0 TSRMLS_CC); } /* Add $foo[name] */ @@ -1248,11 +1144,7 @@ filedone: } else { snprintf(lbuf, llen, "%s[name]", param); } - if (s && s >= filename) { - register_http_post_files_variable(lbuf, s+1, http_post_files, 0 TSRMLS_CC); - } else { - register_http_post_files_variable(lbuf, filename, http_post_files, 0 TSRMLS_CC); - } + register_http_post_files_variable(lbuf, s, http_post_files, 0 TSRMLS_CC); efree(filename); s = NULL; @@ -1292,7 +1184,7 @@ filedone: s = ""; { - /* store temp_filename as-is (without magic_quotes_gpc-ing it, in case upload_tmp_dir + /* store temp_filename as-is (in case upload_tmp_dir * contains escapeable characters. escape only the variable name.) */ zval zfilename; @@ -1368,7 +1260,32 @@ fileupload_done: php_rfc1867_callback(MULTIPART_EVENT_END, &event_end, &event_extra_data TSRMLS_CC); } - SAFE_RETURN; + if (lbuf) efree(lbuf); + if (abuf) efree(abuf); + if (array_index) efree(array_index); + zend_hash_destroy(&PG(rfc1867_protected_variables)); + zend_llist_destroy(&header); + if (mbuff->boundary_next) efree(mbuff->boundary_next); + if (mbuff->boundary) efree(mbuff->boundary); + if (mbuff->buffer) efree(mbuff->buffer); + if (mbuff) efree(mbuff); +} +/* }}} */ + +SAPI_API void php_rfc1867_set_multibyte_callbacks( + php_rfc1867_encoding_translation_t encoding_translation, + php_rfc1867_get_detect_order_t get_detect_order, + php_rfc1867_set_input_encoding_t set_input_encoding, + php_rfc1867_getword_t getword, + php_rfc1867_getword_conf_t getword_conf, + php_rfc1867_basename_t basename) /* {{{ */ +{ + php_rfc1867_encoding_translation = encoding_translation; + php_rfc1867_get_detect_order = get_detect_order; + php_rfc1867_set_input_encoding = set_input_encoding; + php_rfc1867_getword = getword; + php_rfc1867_getword_conf = getword_conf; + php_rfc1867_basename = basename; } /* }}} */ diff --git a/main/rfc1867.h b/main/rfc1867.h index 7f6c48905c..618458be34 100644 --- a/main/rfc1867.h +++ b/main/rfc1867.h @@ -67,10 +67,25 @@ typedef struct _multipart_event_end { size_t post_bytes_processed; } multipart_event_end; +typedef int (*php_rfc1867_encoding_translation_t)(TSRMLS_D); +typedef void (*php_rfc1867_get_detect_order_t)(const zend_encoding ***list, size_t *list_size TSRMLS_DC); +typedef void (*php_rfc1867_set_input_encoding_t)(const zend_encoding *encoding TSRMLS_DC); +typedef char* (*php_rfc1867_getword_t)(const zend_encoding *encoding, char **line, char stop TSRMLS_DC); +typedef char* (*php_rfc1867_getword_conf_t)(const zend_encoding *encoding, char *str TSRMLS_DC); +typedef char* (*php_rfc1867_basename_t)(const zend_encoding *encoding, char *str TSRMLS_DC); + SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler); void destroy_uploaded_files_hash(TSRMLS_D); void php_rfc1867_register_constants(TSRMLS_D); extern PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra TSRMLS_DC); +SAPI_API void php_rfc1867_set_multibyte_callbacks( + php_rfc1867_encoding_translation_t encoding_translation, + php_rfc1867_get_detect_order_t get_detect_order, + php_rfc1867_set_input_encoding_t set_input_encoding, + php_rfc1867_getword_t getword, + php_rfc1867_getword_conf_t getword_conf, + php_rfc1867_basename_t basename); + #endif /* RFC1867_H */ diff --git a/main/safe_mode.c b/main/safe_mode.c deleted file mode 100644 index 889becb853..0000000000 --- a/main/safe_mode.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 5 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2012 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Rasmus Lerdorf <rasmus@lerdorf.on.ca> | - +----------------------------------------------------------------------+ - */ - -/* $Id$ */ - -#include "php.h" - -#include <stdio.h> -#include <stdlib.h> - -#if HAVE_UNISTD_H -#include <unistd.h> -#endif -#include <sys/stat.h> -#include "ext/standard/pageinfo.h" -#include "safe_mode.h" -#include "SAPI.h" -#include "php_globals.h" - -/* - * php_checkuid - * - * This function has six modes: - * - * 0 - return invalid (0) if file does not exist - * 1 - return valid (1) if file does not exist - * 2 - if file does not exist, check directory - * 3 - only check directory (needed for mkdir) - * 4 - check mode and param - * 5 - only check file - */ - -PHPAPI int php_checkuid_ex(const char *filename, const char *fopen_mode, int mode, int flags) -{ - struct stat sb; - int ret, nofile=0; - long uid=0L, gid=0L, duid=0L, dgid=0L; - char path[MAXPATHLEN]; - char *s, filenamecopy[MAXPATHLEN]; - TSRMLS_FETCH(); - - path[0] = '\0'; - - if (!filename) { - return 0; /* path must be provided */ - } - - if (strlcpy(filenamecopy, filename, MAXPATHLEN)>=MAXPATHLEN) { - return 0; - } - filename=(char *)&filenamecopy; - - if (fopen_mode) { - if (fopen_mode[0] == 'r') { - mode = CHECKUID_DISALLOW_FILE_NOT_EXISTS; - } else { - mode = CHECKUID_CHECK_FILE_AND_DIR; - } - } - - /* First we see if the file is owned by the same user... - * If that fails, passthrough and check directory... - */ - if (mode != CHECKUID_ALLOW_ONLY_DIR) { -#if HAVE_BROKEN_GETCWD - char ftest[MAXPATHLEN]; - - strcpy(ftest, filename); - if (VCWD_GETCWD(ftest, sizeof(ftest)) == NULL) { - strcpy(path, filename); - } else -#endif - expand_filepath(filename, path TSRMLS_CC); - - ret = VCWD_STAT(path, &sb); - if (ret < 0) { - if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) { - if ((flags & CHECKUID_NO_ERRORS) == 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename); - } - return 0; - } else if (mode == CHECKUID_ALLOW_FILE_NOT_EXISTS) { - if ((flags & CHECKUID_NO_ERRORS) == 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename); - } - return 1; - } - nofile = 1; - } else { - uid = sb.st_uid; - gid = sb.st_gid; - if (uid == php_getuid()) { - return 1; - } else if (PG(safe_mode_gid) && gid == php_getgid()) { - return 1; - } - } - - /* Trim off filename */ - if ((s = strrchr(path, DEFAULT_SLASH))) { - if (*(s + 1) == '\0' && s != path) { /* make sure that the / is not the last character */ - *s = '\0'; - s = strrchr(path, DEFAULT_SLASH); - } - if (s) { - if (s == path) { - path[1] = '\0'; - } else { - *s = '\0'; - } - } - } - } else { /* CHECKUID_ALLOW_ONLY_DIR */ - s = strrchr(filename, DEFAULT_SLASH); - - if (s == filename) { - /* root dir */ - path[0] = DEFAULT_SLASH; - path[1] = '\0'; - } else if (s && *(s + 1) != '\0') { /* make sure that the / is not the last character */ - *s = '\0'; - VCWD_REALPATH(filename, path); - *s = DEFAULT_SLASH; - } else { - /* Under Solaris, getcwd() can fail if there are no - * read permissions on a component of the path, even - * though it has the required x permissions */ - path[0] = '.'; - path[1] = '\0'; - VCWD_GETCWD(path, sizeof(path)); - } - } /* end CHECKUID_ALLOW_ONLY_DIR */ - - if (mode != CHECKUID_ALLOW_ONLY_FILE) { - /* check directory */ - ret = VCWD_STAT(path, &sb); - if (ret < 0) { - if ((flags & CHECKUID_NO_ERRORS) == 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename); - } - return 0; - } - duid = sb.st_uid; - dgid = sb.st_gid; - if (duid == php_getuid()) { - return 1; - } else if (PG(safe_mode_gid) && dgid == php_getgid()) { - return 1; - } else { - if (SG(rfc1867_uploaded_files)) { - if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) { - return 1; - } - } - } - } - - if (mode == CHECKUID_ALLOW_ONLY_DIR) { - uid = duid; - gid = dgid; - if (s) { - *s = 0; - } - } - - if (nofile) { - uid = duid; - gid = dgid; - filename = path; - } - - if ((flags & CHECKUID_NO_ERRORS) == 0) { - if (PG(safe_mode_gid)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "SAFE MODE Restriction in effect. The script whose uid/gid is %ld/%ld is not allowed to access %s owned by uid/gid %ld/%ld", php_getuid(), php_getgid(), filename, uid, gid); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "SAFE MODE Restriction in effect. The script whose uid is %ld is not allowed to access %s owned by uid %ld", php_getuid(), filename, uid); - } - } - - return 0; -} - -PHPAPI int php_checkuid(const char *filename, const char *fopen_mode, int mode) -{ -#ifdef NETWARE -/* NetWare don't have uid*/ - return 1; -#else - return php_checkuid_ex(filename, fopen_mode, mode, 0); -#endif -} - -PHPAPI char *php_get_current_user(void) -{ - struct stat *pstat; - TSRMLS_FETCH(); - - if (SG(request_info).current_user) { - return SG(request_info).current_user; - } - - /* FIXME: I need to have this somehow handled if - USE_SAPI is defined, because cgi will also be - interfaced in USE_SAPI */ - - pstat = sapi_get_stat(TSRMLS_C); - - if (!pstat) { - return ""; - } else { -#ifdef PHP_WIN32 - char name[256]; - DWORD len = sizeof(name)-1; - - if (!GetUserName(name, &len)) { - return ""; - } - name[len] = '\0'; - SG(request_info).current_user_length = len; - SG(request_info).current_user = estrndup(name, len); - return SG(request_info).current_user; -#else - struct passwd *pwd; -#if defined(ZTS) && defined(HAVE_GETPWUID_R) && defined(_SC_GETPW_R_SIZE_MAX) - struct passwd _pw; - struct passwd *retpwptr = NULL; - int pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); - char *pwbuf; - - if (pwbuflen < 1) { - return ""; - } - pwbuf = emalloc(pwbuflen); - if (getpwuid_r(pstat->st_uid, &_pw, pwbuf, pwbuflen, &retpwptr) != 0) { - efree(pwbuf); - return ""; - } - pwd = &_pw; -#else - if ((pwd=getpwuid(pstat->st_uid))==NULL) { - return ""; - } -#endif - SG(request_info).current_user_length = strlen(pwd->pw_name); - SG(request_info).current_user = estrndup(pwd->pw_name, SG(request_info).current_user_length); -#if defined(ZTS) && defined(HAVE_GETPWUID_R) && defined(_SC_GETPW_R_SIZE_MAX) - efree(pwbuf); -#endif - return SG(request_info).current_user; -#endif - } -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ diff --git a/main/safe_mode.h b/main/safe_mode.h deleted file mode 100644 index 2b21960c4a..0000000000 --- a/main/safe_mode.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 5 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2012 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: | - +----------------------------------------------------------------------+ -*/ - -/* $Id$ */ - -#ifndef SAFE_MODE_H -#define SAFE_MODE_H - -/* mode's for php_checkuid() */ -#define CHECKUID_DISALLOW_FILE_NOT_EXISTS 0 -#define CHECKUID_ALLOW_FILE_NOT_EXISTS 1 -#define CHECKUID_CHECK_FILE_AND_DIR 2 -#define CHECKUID_ALLOW_ONLY_DIR 3 -#define CHECKUID_CHECK_MODE_PARAM 4 -#define CHECKUID_ALLOW_ONLY_FILE 5 - -/* flags for php_checkuid_ex() */ -#define CHECKUID_NO_ERRORS 0x01 - -BEGIN_EXTERN_C() -PHPAPI int php_checkuid(const char *filename, const char *fopen_mode, int mode); -PHPAPI int php_checkuid_ex(const char *filename, const char *fopen_mode, int mode, int flags); -PHPAPI char *php_get_current_user(void); -END_EXTERN_C() - -#endif diff --git a/main/snprintf.c b/main/snprintf.c index 1e0e45510d..bda110d339 100644 --- a/main/snprintf.c +++ b/main/snprintf.c @@ -18,7 +18,7 @@ /* $Id$ */ - +#define _GNU_SOURCE #include "php.h" #include <zend_strtod.h> @@ -1222,7 +1222,7 @@ static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_ PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */ { - int cc; + unsigned int cc; va_list ap; va_start(ap, format); @@ -1238,7 +1238,7 @@ PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */ { - int cc; + unsigned int cc; strx_printv(&cc, buf, len, format, ap); if (cc >= len) { diff --git a/main/spprintf.c b/main/spprintf.c index e3c18abe4a..ef51cc5f41 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -123,7 +123,7 @@ * NDIG = 320 * NUM_BUF_SIZE >= strlen("-") + Emax + strlrn(".") + NDIG + strlen("E+1023") + 1; */ -#define NUM_BUF_SIZE 2048 +#define NUM_BUF_SIZE 2048 /* * The INS_CHAR macro inserts a character in the buffer. diff --git a/main/streams/cast.c b/main/streams/cast.c index 0dcdddac3c..da56e86679 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -271,7 +271,7 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show newstream = php_stream_fopen_tmpfile(); if (newstream) { - size_t retcopy = php_stream_copy_to_stream_ex(stream, newstream, PHP_STREAM_COPY_ALL, NULL); + int retcopy = php_stream_copy_to_stream_ex(stream, newstream, PHP_STREAM_COPY_ALL, NULL); if (retcopy != SUCCESS) { php_stream_close(newstream); diff --git a/main/streams/glob_wrapper.c b/main/streams/glob_wrapper.c index 4a35153722..7b568501ee 100644 --- a/main/streams/glob_wrapper.c +++ b/main/streams/glob_wrapper.c @@ -145,7 +145,7 @@ static size_t php_glob_stream_read(php_stream *stream, char *buf, size_t count T /* avoid problems if someone mis-uses the stream */ if (count == sizeof(php_stream_dirent) && pglob) { - if (pglob->index < pglob->glob.gl_pathc) { + if (pglob->index < (size_t)pglob->glob.gl_pathc) { php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[pglob->index++], pglob->flags & GLOB_APPEND, &path TSRMLS_CC); PHP_STRLCPY(ent->d_name, path, sizeof(ent->d_name), strlen(path)); return sizeof(php_stream_dirent); diff --git a/main/streams/memory.c b/main/streams/memory.c index 7a78d55c41..9e0fae6d1e 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -42,7 +42,6 @@ typedef struct { size_t fsize; size_t smax; int mode; - php_stream **owner_ptr; } php_stream_memory_data; @@ -112,9 +111,6 @@ static int php_stream_memory_close(php_stream *stream, int close_handle TSRMLS_D if (ms->data && close_handle && ms->mode != TEMP_STREAM_READONLY) { efree(ms->data); } - if (ms->owner_ptr) { - *ms->owner_ptr = NULL; - } efree(ms); return 0; } @@ -301,7 +297,6 @@ PHPAPI php_stream *_php_stream_memory_create(int mode STREAMS_DC TSRMLS_DC) self->fsize = 0; self->smax = ~0u; self->mode = mode; - self->owner_ptr = NULL; stream = php_stream_alloc_rel(&php_stream_memory_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b"); stream->flags |= PHP_STREAM_FLAG_NO_BUFFER; @@ -376,8 +371,9 @@ static size_t php_stream_temp_write(php_stream *stream, const char *buf, size_t if (memsize + count >= ts->smax) { php_stream *file = php_stream_fopen_tmpfile(); php_stream_write(file, membuf, memsize); - php_stream_close(ts->innerstream); + php_stream_free_enclosed(ts->innerstream, PHP_STREAM_FREE_CLOSE); ts->innerstream = file; + php_stream_encloses(stream, ts->innerstream); } } return php_stream_write(ts->innerstream, buf, count); @@ -415,7 +411,7 @@ static int php_stream_temp_close(php_stream *stream, int close_handle TSRMLS_DC) assert(ts != NULL); if (ts->innerstream) { - ret = php_stream_free(ts->innerstream, PHP_STREAM_FREE_CLOSE | (close_handle ? 0 : PHP_STREAM_FREE_PRESERVE_HANDLE)); + ret = php_stream_free_enclosed(ts->innerstream, PHP_STREAM_FREE_CLOSE | (close_handle ? 0 : PHP_STREAM_FREE_PRESERVE_HANDLE)); } else { ret = 0; } @@ -499,9 +495,10 @@ static int php_stream_temp_cast(php_stream *stream, int castas, void **ret TSRML file = php_stream_fopen_tmpfile(); php_stream_write(file, membuf, memsize); pos = php_stream_tell(ts->innerstream); - - php_stream_close(ts->innerstream); + + php_stream_free_enclosed(ts->innerstream, PHP_STREAM_FREE_CLOSE); ts->innerstream = file; + php_stream_encloses(stream, ts->innerstream); php_stream_seek(ts->innerstream, pos, SEEK_SET); return php_stream_cast(ts->innerstream, castas, ret, 1); @@ -563,8 +560,7 @@ PHPAPI php_stream *_php_stream_temp_create(int mode, size_t max_memory_usage STR stream = php_stream_alloc_rel(&php_stream_temp_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b"); stream->flags |= PHP_STREAM_FLAG_NO_BUFFER; self->innerstream = php_stream_memory_create_rel(mode); - php_stream_auto_cleanup(self->innerstream); /* do not warn if innerstream is GC'ed before stream */ - ((php_stream_memory_data*)self->innerstream->abstract)->owner_ptr = &self->innerstream; + php_stream_encloses(stream, self->innerstream); return stream; } diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index a0500db2bd..5767b74d26 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -33,10 +33,10 @@ typedef void (*php_stream_notification_func)(php_stream_context *context, If no context was passed, use the default context The default context has not yet been created, do it now. */ #define php_stream_context_from_zval(zcontext, nocontext) ( \ - (zcontext) ? zend_fetch_resource(&(zcontext) TSRMLS_CC, -1, "Stream-Context", NULL, 1, php_le_stream_context()) : \ + (zcontext) ? zend_fetch_resource(&(zcontext) TSRMLS_CC, -1, "Stream-Context", NULL, 1, php_le_stream_context(TSRMLS_C)) : \ (nocontext) ? NULL : \ FG(default_context) ? FG(default_context) : \ - (FG(default_context) = php_stream_context_alloc()) ) + (FG(default_context) = php_stream_context_alloc(TSRMLS_C)) ) #define php_stream_context_to_zval(context, zval) { ZVAL_RESOURCE(zval, (context)->rsrc_id); zend_list_addref((context)->rsrc_id); } @@ -59,7 +59,7 @@ struct _php_stream_context { BEGIN_EXTERN_C() PHPAPI void php_stream_context_free(php_stream_context *context); -PHPAPI php_stream_context *php_stream_context_alloc(void); +PHPAPI php_stream_context *php_stream_context_alloc(TSRMLS_D); PHPAPI int php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname, zval ***optionvalue); PHPAPI int php_stream_context_set_option(php_stream_context *context, diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index cd5f26148b..77e411377e 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -48,6 +48,11 @@ #define php_stream_fopen_from_file_int(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC TSRMLS_CC) #define php_stream_fopen_from_file_int_rel(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_REL_CC TSRMLS_CC) +#if !defined(WINDOWS) && !defined(NETWARE) +extern int php_get_uid_by_name(const char *name, uid_t *uid TSRMLS_DC); +extern int php_get_gid_by_name(const char *name, gid_t *gid TSRMLS_DC); +#endif + /* parse standard "fopen" modes into open() flags */ PHPAPI int php_stream_parse_fopen_modes(const char *mode, int *open_flags) { @@ -108,7 +113,7 @@ typedef struct { unsigned is_pipe:1; /* don't try and seek */ unsigned cached_fstat:1; /* sb is valid */ unsigned _reserved:29; - + int lock_flag; /* stores the lock state */ char *temp_file_name; /* if non-null, this is the path to a temporary file that * is to be deleted when the stream is closed */ @@ -134,7 +139,7 @@ static int do_fstat(php_stdio_stream_data *d, int force) if (!d->cached_fstat || force) { int fd; int r; - + PHP_STDIOP_GET_FD(fd, d); r = fstat(fd, &d->sb); d->cached_fstat = r == 0; @@ -147,7 +152,7 @@ static int do_fstat(php_stdio_stream_data *d, int force) static php_stream *_php_stream_fopen_from_fd_int(int fd, const char *mode, const char *persistent_id STREAMS_DC TSRMLS_DC) { php_stdio_stream_data *self; - + self = pemalloc_rel_orig(sizeof(*self), persistent_id); memset(self, 0, sizeof(*self)); self->file = NULL; @@ -156,14 +161,14 @@ static php_stream *_php_stream_fopen_from_fd_int(int fd, const char *mode, const self->is_process_pipe = 0; self->temp_file_name = NULL; self->fd = fd; - + return php_stream_alloc_rel(&php_stream_stdio_ops, self, persistent_id, mode); } static php_stream *_php_stream_fopen_from_file_int(FILE *file, const char *mode STREAMS_DC TSRMLS_DC) { php_stdio_stream_data *self; - + self = emalloc_rel_orig(sizeof(*self)); memset(self, 0, sizeof(*self)); self->file = file; @@ -208,7 +213,7 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC) self->temp_file_name = opened_path; self->lock_flag = LOCK_UN; - + return stream; } close(fd); @@ -241,7 +246,7 @@ PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const cha } } #endif - + if (self->is_pipe) { stream->flags |= PHP_STREAM_FLAG_NO_SEEK; } else { @@ -280,7 +285,7 @@ PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STRE } } #endif - + if (self->is_pipe) { stream->flags |= PHP_STREAM_FLAG_NO_SEEK; } else { @@ -349,9 +354,9 @@ static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count TSRMLS so script can retry if desired */ ret = read(data->fd, buf, count); } - + stream->eof = (ret == 0 || (ret == (size_t)-1 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF)); - + } else { #if HAVE_FLUSHIO if (!data->is_pipe && data->last_op == 'w') @@ -388,7 +393,7 @@ static int php_stdiop_close(php_stream *stream, int close_handle TSRMLS_DC) data->file_mapping = NULL; } #endif - + if (close_handle) { if (data->file) { if (data->is_process_pipe) { @@ -458,14 +463,14 @@ static int php_stdiop_seek(php_stream *stream, off_t offset, int whence, off_t * if (data->fd >= 0) { off_t result; - + result = lseek(data->fd, offset, whence); if (result == (off_t)-1) return -1; *newoffset = result; return 0; - + } else { ret = fseek(data->file, offset, whence); *newoffset = ftell(data->file); @@ -479,7 +484,7 @@ static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; assert(data != NULL); - + /* as soon as someone touches the stdio layer, buffering may ensue, * so we need to stop using the fd directly in that case */ @@ -497,7 +502,7 @@ static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) return FAILURE; } } - + *(FILE**)ret = data->file; data->fd = -1; } @@ -553,9 +558,9 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void int flags; int oldval; #endif - + PHP_STDIOP_GET_FD(fd, data); - + switch(option) { case PHP_STREAM_OPTION_BLOCKING: if (fd == -1) @@ -567,20 +572,20 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; - + if (-1 == fcntl(fd, F_SETFL, flags)) return -1; return oldval; #else return -1; /* not yet implemented */ #endif - + case PHP_STREAM_OPTION_WRITE_BUFFER: if (data->file == NULL) { return -1; } - + if (ptrparam) size = *(size_t *)ptrparam; else @@ -588,22 +593,19 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void switch(value) { case PHP_STREAM_BUFFER_NONE: - stream->flags |= PHP_STREAM_FLAG_NO_BUFFER; return setvbuf(data->file, NULL, _IONBF, 0); - + case PHP_STREAM_BUFFER_LINE: - stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER; return setvbuf(data->file, NULL, _IOLBF, size); - + case PHP_STREAM_BUFFER_FULL: - stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER; return setvbuf(data->file, NULL, _IOFBF, size); default: return -1; } break; - + case PHP_STREAM_OPTION_LOCKING: if (fd == -1) { return -1; @@ -626,7 +628,7 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void { php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam; int prot, flags; - + switch (value) { case PHP_STREAM_MMAP_SUPPORTED: return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; @@ -791,7 +793,7 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void return ftruncate(fd, new_size) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; } } - + default: return PHP_STREAM_OPTION_RETURN_NOTIMPL; } @@ -864,11 +866,7 @@ static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path TSRMLS_CC)) { return NULL; } - - if (PG(safe_mode) &&(!php_checkuid(path, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { - return NULL; - } - + dir = VCWD_OPENDIR(path); #ifdef PHP_WIN32 @@ -886,7 +884,7 @@ static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char if (stream == NULL) closedir(dir); } - + return stream; } /* }}} */ @@ -934,7 +932,7 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, cha return ret; } } - + fd = open(realpath, open_flags, 0666); if (fd != -1) { @@ -998,28 +996,14 @@ static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, ch return NULL; } - if ((php_check_safe_mode_include_dir(path TSRMLS_CC)) == 0) { - return php_stream_fopen_rel(path, mode, opened_path, options); - } - - if ((options & ENFORCE_SAFE_MODE) && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM))) - return NULL; - return php_stream_fopen_rel(path, mode, opened_path, options); } static int php_plain_files_url_stater(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) { - char *p; - if ((p = strstr(url, "://")) != NULL) { - if (p < strchr(url, '/')) { - url = p + 3; - } - } - - if (PG(safe_mode) &&(!php_checkuid_ex(url, NULL, CHECKUID_CHECK_FILE_AND_DIR, (flags & PHP_STREAM_URL_STAT_QUIET) ? CHECKUID_NO_ERRORS : 0))) { - return -1; + if (strncmp(url, "file://", sizeof("file://") - 1) == 0) { + url += sizeof("file://") - 1; } if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1 TSRMLS_CC)) { @@ -1048,19 +1032,11 @@ static int php_plain_files_unlink(php_stream_wrapper *wrapper, char *url, int op int ret; if ((p = strstr(url, "://")) != NULL) { - if (p < strchr(url, '/')) { - url = p + 3; - } + url = p + 3; } - if (options & ENFORCE_SAFE_MODE) { - if (PG(safe_mode) && !php_checkuid(url, NULL, CHECKUID_CHECK_FILE_AND_DIR)) { - return 0; - } - - if (php_check_open_basedir(url TSRMLS_CC)) { - return 0; - } + if (php_check_open_basedir(url TSRMLS_CC)) { + return 0; } ret = VCWD_UNLINK(url); @@ -1098,20 +1074,11 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, char *url_from, c #endif if ((p = strstr(url_from, "://")) != NULL) { - if (p < strchr(url_from, '/')) { - url_from = p + 3; - } + url_from = p + 3; } if ((p = strstr(url_to, "://")) != NULL) { - if (p < strchr(url_to, '/')) { - url_to = p + 3; - } - } - - if (PG(safe_mode) && (!php_checkuid(url_from, NULL, CHECKUID_CHECK_FILE_AND_DIR) || - !php_checkuid(url_to, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { - return 0; + url_to = p + 3; } if (php_check_open_basedir(url_from TSRMLS_CC) || php_check_open_basedir(url_to TSRMLS_CC)) { @@ -1177,40 +1144,32 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, char *dir, int mod char *p; if ((p = strstr(dir, "://")) != NULL) { - if (p < strchr(dir, '/')) { - dir = p + 3; - } + dir = p + 3; } if (!recursive) { ret = php_mkdir(dir, mode TSRMLS_CC); } else { /* we look for directory separator from the end of string, thus hopefuly reducing our work load */ - char *e, *buf; + char *e; struct stat sb; int dir_len = strlen(dir); int offset = 0; + char buf[MAXPATHLEN]; - buf = estrndup(dir, dir_len); - -#ifdef PHP_WIN32 - e = buf; - while (*e) { - if (*e == '/') { - *e = DEFAULT_SLASH; - } - e++; + if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid path"); + return 0; } -#else - e = buf + dir_len; -#endif + + e = buf + strlen(buf); if ((p = memchr(buf, DEFAULT_SLASH, dir_len))) { offset = p - buf + 1; } if (p && dir_len == 1) { - /* buf == "DEFAULT_SLASH" */ + /* buf == "DEFAULT_SLASH" */ } else { /* find a top level directory we need to create */ @@ -1255,7 +1214,6 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, char *dir, int mod } } } - efree(buf); } if (ret < 0) { /* Failure */ @@ -1271,10 +1229,6 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, char *url, int opt #if PHP_WIN32 int url_len = strlen(url); #endif - if (PG(safe_mode) &&(!php_checkuid(url, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { - return 0; - } - if (php_check_open_basedir(url TSRMLS_CC)) { return 0; } @@ -1297,6 +1251,92 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, char *url, int opt return 1; } +static int php_plain_files_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context TSRMLS_DC) +{ + struct utimbuf *newtime; + char *p; +#if !defined(WINDOWS) && !defined(NETWARE) + uid_t uid; + gid_t gid; +#endif + mode_t mode; + int ret = 0; +#if PHP_WIN32 + int url_len = strlen(url); +#endif + +#if PHP_WIN32 + if (!php_win32_check_trailing_space(url, url_len)) { + php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "%s", strerror(ENOENT)); + return 0; + } +#endif + + if ((p = strstr(url, "://")) != NULL) { + url = p + 3; + } + + if (php_check_open_basedir(url TSRMLS_CC)) { + return 0; + } + + switch(option) { + case PHP_STREAM_META_TOUCH: + newtime = (struct utimbuf *)value; + if (VCWD_ACCESS(url, F_OK) != 0) { + FILE *file = VCWD_FOPEN(url, "w"); + if (file == NULL) { + php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Unable to create file %s because %s", url, strerror(errno)); + return 0; + } + fclose(file); + } + + ret = VCWD_UTIME(url, newtime); + break; +#if !defined(WINDOWS) && !defined(NETWARE) + case PHP_STREAM_META_OWNER_NAME: + case PHP_STREAM_META_OWNER: + if(option == PHP_STREAM_META_OWNER_NAME) { + if(php_get_uid_by_name((char *)value, &uid TSRMLS_CC) != SUCCESS) { + php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Unable to find uid for %s", (char *)value); + return 0; + } + } else { + uid = (uid_t)*(long *)value; + } + ret = VCWD_CHOWN(url, uid, -1); + break; + case PHP_STREAM_META_GROUP: + case PHP_STREAM_META_GROUP_NAME: + if(option == PHP_STREAM_META_OWNER_NAME) { + if(php_get_gid_by_name((char *)value, &gid TSRMLS_CC) != SUCCESS) { + php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Unable to find gid for %s", (char *)value); + return 0; + } + } else { + gid = (gid_t)*(long *)value; + } + ret = VCWD_CHOWN(url, -1, gid); + break; +#endif + case PHP_STREAM_META_ACCESS: + mode = (mode_t)*(long *)value; + ret = VCWD_CHMOD(url, mode); + break; + default: + php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Unknown option %d for stream_metadata", option); + return 0; + } + if (ret == -1) { + php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "Operation failed: %s", strerror(errno)); + return 0; + } + php_clear_stat_cache(0, NULL, 0 TSRMLS_CC); + return 1; +} + + static php_stream_wrapper_ops php_plain_files_wrapper_ops = { php_plain_files_stream_opener, NULL, @@ -1307,7 +1347,8 @@ static php_stream_wrapper_ops php_plain_files_wrapper_ops = { php_plain_files_unlink, php_plain_files_rename, php_plain_files_mkdir, - php_plain_files_rmdir + php_plain_files_rmdir, + php_plain_files_metadata }; php_stream_wrapper php_plain_files_wrapper = { @@ -1321,7 +1362,7 @@ PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char { /* code ripped off from fopen_wrappers.c */ char *pathbuf, *ptr, *end; - char *exec_fname; + const char *exec_fname; char trypath[MAXPATHLEN]; php_stream *stream; int path_length; @@ -1354,17 +1395,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char return NULL; } - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } return php_stream_fopen_rel(filename, mode, opened_path, options); } - /* - * files in safe_mode_include_dir (or subdir) are excluded from - * safe mode GID/UID checks - */ - not_relative_path: /* Absolute path open */ @@ -1374,16 +1407,9 @@ not_relative_path: return NULL; } - if ((php_check_safe_mode_include_dir(filename TSRMLS_CC)) == 0) - /* filename is in safe_mode_include_dir (or subdir) */ - return php_stream_fopen_rel(filename, mode, opened_path, options); - - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) - return NULL; - return php_stream_fopen_rel(filename, mode, opened_path, options); } - + #ifdef PHP_WIN32 if (IS_SLASH(filename[0])) { size_t cwd_len; @@ -1391,31 +1417,22 @@ not_relative_path: cwd = virtual_getcwd_ex(&cwd_len TSRMLS_CC); /* getcwd() will return always return [DRIVE_LETTER]:/) on windows. */ *(cwd+3) = '\0'; - + if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); } - + free(cwd); - + if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(trypath TSRMLS_CC)) { return NULL; } - if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC)) == 0) { - return php_stream_fopen_rel(trypath, mode, opened_path, options); - } - if (PG(safe_mode) && (!php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } - + return php_stream_fopen_rel(trypath, mode, opened_path, options); } #endif if (!path || (path && !*path)) { - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } return php_stream_fopen_rel(filename, mode, opened_path, options); } @@ -1462,24 +1479,9 @@ not_relative_path: if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0 TSRMLS_CC)) { goto stream_skip; } - - if (PG(safe_mode)) { - struct stat sb; - if (VCWD_STAT(trypath, &sb) == 0) { - /* file exists ... check permission */ - if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0) || - php_checkuid_ex(trypath, mode, CHECKUID_CHECK_MODE_PARAM, CHECKUID_NO_ERRORS)) { - /* UID ok, or trypath is in safe_mode_include_dir */ - stream = php_stream_fopen_rel(trypath, mode, opened_path, options); - goto stream_done; - } - } - goto stream_skip; - } stream = php_stream_fopen_rel(trypath, mode, opened_path, options); if (stream) { -stream_done: efree(pathbuf); return stream; } diff --git a/main/streams/streams.c b/main/streams/streams.c index dfd60940fc..e9c2e07320 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -105,6 +105,15 @@ PHP_RSHUTDOWN_FUNCTION(streams) return SUCCESS; } +PHPAPI php_stream *php_stream_encloses(php_stream *enclosing, php_stream *enclosed) +{ + php_stream *orig = enclosed->enclosing_stream; + + php_stream_auto_cleanup(enclosed); + enclosed->enclosing_stream = enclosing; + return orig; +} + PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream **stream TSRMLS_DC) { zend_rsrc_list_entry *le; @@ -324,19 +333,56 @@ fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persiste ret->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ret, persistent_id ? le_pstream : le_stream); strlcpy(ret->mode, mode, sizeof(ret->mode)); + ret->wrapper = NULL; + ret->wrapperthis = NULL; + ret->wrapperdata = NULL; + ret->stdiocast = NULL; + ret->orig_path = NULL; + ret->context = NULL; + ret->readbuf = NULL; + ret->enclosing_stream = NULL; + return ret; } /* }}} */ +PHPAPI int _php_stream_free_enclosed(php_stream *stream_enclosed, int close_options TSRMLS_DC) /* {{{ */ +{ + return _php_stream_free(stream_enclosed, + close_options | PHP_STREAM_FREE_IGNORE_ENCLOSING TSRMLS_CC); +} +/* }}} */ + +#if STREAM_DEBUG +static const char *_php_stream_pretty_free_options(int close_options, char *out) +{ + if (close_options & PHP_STREAM_FREE_CALL_DTOR) + strcat(out, "CALL_DTOR, "); + if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) + strcat(out, "RELEASE_STREAM, "); + if (close_options & PHP_STREAM_FREE_PRESERVE_HANDLE) + strcat(out, "PREVERSE_HANDLE, "); + if (close_options & PHP_STREAM_FREE_RSRC_DTOR) + strcat(out, "RSRC_DTOR, "); + if (close_options & PHP_STREAM_FREE_PERSISTENT) + strcat(out, "PERSISTENT, "); + if (close_options & PHP_STREAM_FREE_IGNORE_ENCLOSING) + strcat(out, "IGNORE_ENCLOSING, "); + if (out[0] != '\0') + out[strlen(out) - 2] = '\0'; + return out; +} +#endif + static int _php_stream_free_persistent(zend_rsrc_list_entry *le, void *pStream TSRMLS_DC) { return le->ptr == pStream; } + PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* {{{ */ { int ret = 1; - int remove_rsrc = 1; int preserve_handle = close_options & PHP_STREAM_FREE_PRESERVE_HANDLE ? 1 : 0; int release_cast = 1; php_stream_context *context = NULL; @@ -353,16 +399,40 @@ PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* } #if STREAM_DEBUG -fprintf(stderr, "stream_free: %s:%p[%s] in_free=%d opts=%08x\n", stream->ops->label, stream, stream->orig_path, stream->in_free, close_options); + { + char out[200] = ""; + fprintf(stderr, "stream_free: %s:%p[%s] in_free=%d opts=%s\n", + stream->ops->label, stream, stream->orig_path, stream->in_free, _php_stream_pretty_free_options(close_options, out)); + } + #endif - /* recursion protection */ if (stream->in_free) { - return 1; + /* hopefully called recursively from the enclosing stream; the pointer was NULLed below */ + if ((stream->in_free == 1) && (close_options & PHP_STREAM_FREE_IGNORE_ENCLOSING) && (stream->enclosing_stream == NULL)) { + close_options |= PHP_STREAM_FREE_RSRC_DTOR; /* restore flag */ + } else { + return 1; /* recursion protection */ + } } stream->in_free++; + /* force correct order on enclosing/enclosed stream destruction (only from resource + * destructor as in when reverse destroying the resource list) */ + if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && + !(close_options & PHP_STREAM_FREE_IGNORE_ENCLOSING) && + (close_options & (PHP_STREAM_FREE_CALL_DTOR | PHP_STREAM_FREE_RELEASE_STREAM)) && /* always? */ + (stream->enclosing_stream != NULL)) { + php_stream *enclosing_stream = stream->enclosing_stream; + stream->enclosing_stream = NULL; + /* we force PHP_STREAM_CALL_DTOR because that's from where the + * enclosing stream can free this stream. We remove rsrc_dtor because + * we want the enclosing stream to be deleted from the resource list */ + return _php_stream_free(enclosing_stream, + (close_options | PHP_STREAM_FREE_CALL_DTOR) & ~PHP_STREAM_FREE_RSRC_DTOR TSRMLS_CC); + } + /* if we are releasing the stream only (and preserving the underlying handle), * we need to do things a little differently. * We are only ever called like this when the stream is cast to a FILE* @@ -383,14 +453,15 @@ fprintf(stderr, "stream_free: %s:%p[%s] in_free=%d opts=%08x\n", stream->ops->la #if STREAM_DEBUG fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remove_rsrc=%d\n", - stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast, remove_rsrc); + stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast, + (close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0); #endif /* make sure everything is saved */ _php_stream_flush(stream, 1 TSRMLS_CC); /* If not called from the resource dtor, remove the stream from the resource list. */ - if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0 && remove_rsrc) { + if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0) { /* zend_list_delete actually only decreases the refcount; if we're * releasing the stream, we want to actually delete the resource from * the resource list, otherwise the resource will point to invalid memory. @@ -1410,7 +1481,7 @@ PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen } /* Returns SUCCESS/FAILURE and sets *len to the number of bytes moved */ -PHPAPI size_t _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t *len STREAMS_DC TSRMLS_DC) +PHPAPI int _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t *len STREAMS_DC TSRMLS_DC) { char buf[CHUNK_SIZE]; size_t readchunk; @@ -1811,7 +1882,7 @@ PHPAPI int _php_stream_mkdir(char *path, int mode, int options, php_stream_conte { php_stream_wrapper *wrapper = NULL; - wrapper = php_stream_locate_url_wrapper(path, NULL, ENFORCE_SAFE_MODE TSRMLS_CC); + wrapper = php_stream_locate_url_wrapper(path, NULL, 0 TSRMLS_CC); if (!wrapper || !wrapper->wops || !wrapper->wops->stream_mkdir) { return 0; } @@ -1826,7 +1897,7 @@ PHPAPI int _php_stream_rmdir(char *path, int options, php_stream_context *contex { php_stream_wrapper *wrapper = NULL; - wrapper = php_stream_locate_url_wrapper(path, NULL, ENFORCE_SAFE_MODE TSRMLS_CC); + wrapper = php_stream_locate_url_wrapper(path, NULL, 0 TSRMLS_CC); if (!wrapper || !wrapper->wops || !wrapper->wops->stream_rmdir) { return 0; } @@ -1855,7 +1926,7 @@ PHPAPI int _php_stream_stat_path(char *path, int flags, php_stream_statbuf *ssb, } } - wrapper = php_stream_locate_url_wrapper(path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC); + wrapper = php_stream_locate_url_wrapper(path, &path_to_open, 0 TSRMLS_CC); if (wrapper && wrapper->wops->url_stat) { ret = wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context TSRMLS_CC); if (ret == 0) { @@ -1940,7 +2011,6 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio char *resolved_path = NULL; char *copy_of_path = NULL; - if (opened_path) { *opened_path = NULL; } @@ -2117,7 +2187,7 @@ PHPAPI void php_stream_context_free(php_stream_context *context) efree(context); } -PHPAPI php_stream_context *php_stream_context_alloc(void) +PHPAPI php_stream_context *php_stream_context_alloc(TSRMLS_D) { php_stream_context *context; @@ -2126,7 +2196,7 @@ PHPAPI php_stream_context *php_stream_context_alloc(void) MAKE_STD_ZVAL(context->options); array_init(context->options); - context->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, context, php_le_stream_context()); + context->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, context, php_le_stream_context(TSRMLS_C)); return context; } @@ -2269,7 +2339,7 @@ PHPAPI int _php_stream_scandir(char *dirname, char **namelist[], int flags, php_ return FAILURE; } - stream = php_stream_opendir(dirname, ENFORCE_SAFE_MODE | REPORT_ERRORS, context); + stream = php_stream_opendir(dirname, REPORT_ERRORS, context); if (!stream) { return FAILURE; } @@ -2292,6 +2362,11 @@ PHPAPI int _php_stream_scandir(char *dirname, char **namelist[], int flags, php_ vector[nfiles] = estrdup(sdp.d_name); nfiles++; + if(vector_size < 10 || nfiles == 0) { + /* overflow */ + efree(vector); + return FAILURE; + } } php_stream_closedir(stream); diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 8299cba43a..3b277160d6 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -26,6 +26,15 @@ #ifdef HAVE_SYS_FILE_H #include <sys/file.h> #endif +#include <stddef.h> + +#if HAVE_UTIME +# ifdef PHP_WIN32 +# include <sys/utime.h> +# else +# include <utime.h> +# endif +#endif static int le_protocols; @@ -42,6 +51,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC); static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC); static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC); +static int user_wrapper_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context TSRMLS_DC); static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); @@ -55,7 +65,8 @@ static php_stream_wrapper_ops user_stream_wops = { user_wrapper_unlink, user_wrapper_rename, user_wrapper_mkdir, - user_wrapper_rmdir + user_wrapper_rmdir, + user_wrapper_metadata }; @@ -77,7 +88,6 @@ PHP_MINIT_FUNCTION(user_streams) REGISTER_LONG_CONSTANT("STREAM_USE_PATH", USE_PATH, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_IGNORE_URL", IGNORE_URL, CONST_CS|CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("STREAM_ENFORCE_SAFE_MODE", ENFORCE_SAFE_MODE, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_REPORT_ERRORS", REPORT_ERRORS, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_MUST_SEEK", STREAM_MUST_SEEK, CONST_CS|CONST_PERSISTENT); @@ -99,6 +109,12 @@ PHP_MINIT_FUNCTION(user_streams) REGISTER_LONG_CONSTANT("STREAM_CAST_AS_STREAM", PHP_STREAM_AS_STDIO, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CAST_FOR_SELECT", PHP_STREAM_AS_FD_FOR_SELECT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_META_TOUCH", PHP_STREAM_META_TOUCH, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_META_OWNER", PHP_STREAM_META_OWNER, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_META_OWNER_NAME", PHP_STREAM_META_OWNER_NAME, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_META_GROUP", PHP_STREAM_META_GROUP, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_META_GROUP_NAME", PHP_STREAM_META_GROUP_NAME, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_META_ACCESS", PHP_STREAM_META_ACCESS, CONST_CS|CONST_PERSISTENT); return SUCCESS; } @@ -130,35 +146,37 @@ typedef struct _php_userstream_data php_userstream_data_t; #define USERSTREAM_LOCK "stream_lock" #define USERSTREAM_CAST "stream_cast" #define USERSTREAM_SET_OPTION "stream_set_option" +#define USERSTREAM_TRUNCATE "stream_truncate" +#define USERSTREAM_METADATA "stream_metadata" /* {{{ class should have methods like these: - + function stream_open($path, $mode, $options, &$opened_path) { return true/false; } - + function stream_read($count) { return false on error; else return string; } - + function stream_write($data) { return false on error; else return count written; } - + function stream_close() { } - + function stream_flush() { return true/false; } - + function stream_seek($offset, $whence) { return true/false; @@ -255,74 +273,55 @@ typedef struct _php_userstream_data php_userstream_data_t; { return true / false; } - + + function stream_truncate($new_size) + { + return true / false; + } + }}} **/ -static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +static zval *user_stream_create_object(struct php_user_stream_wrapper *uwrap, php_stream_context *context TSRMLS_DC) { - struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; - php_userstream_data_t *us; - zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname; - zval **args[4]; - int call_result; - php_stream *stream = NULL; - zend_bool old_in_user_include; + zval *object; + /* create an instance of our class */ + ALLOC_ZVAL(object); + object_init_ex(object, uwrap->ce); + Z_SET_REFCOUNT_P(object, 1); + Z_SET_ISREF_P(object); - /* Try to catch bad usage without preventing flexibility */ - if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented"); - return NULL; - } - FG(user_stream_current_filename) = filename; - - /* if the user stream was registered as local and we are in include context, - we add allow_url_include restrictions to allow_url_fopen ones */ - /* we need only is_url == 0 here since if is_url == 1 and remote wrappers - were restricted we wouldn't get here */ - old_in_user_include = PG(in_user_include); - if(uwrap->wrapper.is_url == 0 && - (options & STREAM_OPEN_FOR_INCLUDE) && - !PG(allow_url_include)) { - PG(in_user_include) = 1; + if (context) { + add_property_resource(object, "context", context->rsrc_id); + zend_list_addref(context->rsrc_id); + } else { + add_property_null(object, "context"); } - us = emalloc(sizeof(*us)); - us->wrapper = uwrap; - - /* create an instance of our class */ - ALLOC_ZVAL(us->object); - object_init_ex(us->object, uwrap->ce); - Z_SET_REFCOUNT_P(us->object, 1); - Z_SET_ISREF_P(us->object); - if (uwrap->ce->constructor) { zend_fcall_info fci; zend_fcall_info_cache fcc; zval *retval_ptr; - + fci.size = sizeof(fci); fci.function_table = &uwrap->ce->function_table; fci.function_name = NULL; fci.symbol_table = NULL; - fci.object_ptr = us->object; + fci.object_ptr = object; fci.retval_ptr_ptr = &retval_ptr; fci.param_count = 0; fci.params = NULL; fci.no_separation = 1; - + fcc.initialized = 1; fcc.function_handler = uwrap->ce->constructor; fcc.calling_scope = EG(scope); - fcc.called_scope = Z_OBJCE_P(us->object); - fcc.object_ptr = us->object; + fcc.called_scope = Z_OBJCE_P(object); + fcc.object_ptr = object; if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute %s::%s()", uwrap->ce->name, uwrap->ce->constructor->common.function_name); - zval_dtor(us->object); - FREE_ZVAL(us->object); - efree(us); - FG(user_stream_current_filename) = NULL; - PG(in_user_include) = old_in_user_include; + zval_dtor(object); + FREE_ZVAL(object); return NULL; } else { if (retval_ptr) { @@ -330,14 +329,48 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena } } } + return object; +} - if (context) { - add_property_resource(us->object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(us->object, "context"); +static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; + php_userstream_data_t *us; + zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname; + zval **args[4]; + int call_result; + php_stream *stream = NULL; + zend_bool old_in_user_include; + + /* Try to catch bad usage without preventing flexibility */ + if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented"); + return NULL; + } + FG(user_stream_current_filename) = filename; + + /* if the user stream was registered as local and we are in include context, + we add allow_url_include restrictions to allow_url_fopen ones */ + /* we need only is_url == 0 here since if is_url == 1 and remote wrappers + were restricted we wouldn't get here */ + old_in_user_include = PG(in_user_include); + if(uwrap->wrapper.is_url == 0 && + (options & STREAM_OPEN_FOR_INCLUDE) && + !PG(allow_url_include)) { + PG(in_user_include) = 1; + } + + us = emalloc(sizeof(*us)); + us->wrapper = uwrap; + + us->object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(us->object == NULL) { + FG(user_stream_current_filename) = NULL; + PG(in_user_include) = old_in_user_include; + efree(us); + return NULL; } - + /* call it's stream_open method - set up params first */ MAKE_STD_ZVAL(zfilename); ZVAL_STRING(zfilename, filename, 1); @@ -359,14 +392,14 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena MAKE_STD_ZVAL(zfuncname); ZVAL_STRING(zfuncname, USERSTREAM_OPEN, 1); - + call_result = call_user_function_ex(NULL, &us->object, zfuncname, &zretval, 4, args, 0, NULL TSRMLS_CC); - + if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) { /* the stream is now open! */ stream = php_stream_alloc_rel(&php_stream_userspace_ops, us, 0, mode); @@ -383,7 +416,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_OPEN "\" call failed", us->wrapper->classname); } - + /* destroy everything else */ if (stream == NULL) { zval_ptr_dtor(&us->object); @@ -391,7 +424,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena } if (zretval) zval_ptr_dtor(&zretval); - + zval_ptr_dtor(&zfuncname); zval_ptr_dtor(&zopened); zval_ptr_dtor(&zoptions); @@ -399,7 +432,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena zval_ptr_dtor(&zfilename); FG(user_stream_current_filename) = NULL; - + PG(in_user_include) = old_in_user_include; return stream; } @@ -410,7 +443,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; php_userstream_data_t *us; zval *zfilename, *zoptions, *zretval = NULL, *zfuncname; - zval **args[2]; + zval **args[2]; int call_result; php_stream *stream = NULL; @@ -420,23 +453,17 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen return NULL; } FG(user_stream_current_filename) = filename; - - us = emalloc(sizeof(*us)); - us->wrapper = uwrap; - /* create an instance of our class */ - ALLOC_ZVAL(us->object); - object_init_ex(us->object, uwrap->ce); - Z_SET_REFCOUNT_P(us->object, 1); - Z_SET_ISREF_P(us->object); + us = emalloc(sizeof(*us)); + us->wrapper = uwrap; - if (context) { - add_property_resource(us->object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(us->object, "context"); + us->object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(us == NULL) { + FG(user_stream_current_filename) = NULL; + efree(us); + return NULL; } - + /* call it's dir_open method - set up params first */ MAKE_STD_ZVAL(zfilename); ZVAL_STRING(zfilename, filename, 1); @@ -448,14 +475,14 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen MAKE_STD_ZVAL(zfuncname); ZVAL_STRING(zfuncname, USERSTREAM_DIR_OPEN, 1); - + call_result = call_user_function_ex(NULL, &us->object, zfuncname, &zretval, 2, args, 0, NULL TSRMLS_CC); - + if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) { /* the stream is now open! */ stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, mode); @@ -467,7 +494,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", us->wrapper->classname); } - + /* destroy everything else */ if (stream == NULL) { zval_ptr_dtor(&us->object); @@ -475,13 +502,13 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen } if (zretval) zval_ptr_dtor(&zretval); - + zval_ptr_dtor(&zfuncname); zval_ptr_dtor(&zoptions); zval_ptr_dtor(&zfilename); FG(user_stream_current_filename) = NULL; - + return stream; } @@ -495,11 +522,11 @@ PHP_FUNCTION(stream_wrapper_register) struct php_user_stream_wrapper * uwrap; int rsrc_id; long flags = 0; - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &protocol, &protocol_len, &classname, &classname_len, &flags) == FAILURE) { RETURN_FALSE; } - + uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap)); uwrap->protoname = estrndup(protocol, protocol_len); uwrap->classname = estrndup(classname, classname_len); @@ -585,7 +612,7 @@ PHP_FUNCTION(stream_wrapper_restore) if (php_register_url_stream_wrapper_volatile(protocol, wrapper TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to restore original %s:// wrapper", protocol); RETURN_FALSE; - } + } RETURN_TRUE; } @@ -633,10 +660,10 @@ static size_t php_userstreamop_write(php_stream *stream, const char *buf, size_t (long)(didwrite - count), (long)didwrite, (long)count); didwrite = count; } - + if (retval) zval_ptr_dtor(&retval); - + return didwrite; } @@ -721,9 +748,9 @@ static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; assert(us != NULL); - + ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1, 0); - + call_user_function_ex(NULL, &us->object, &func_name, @@ -732,11 +759,11 @@ static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC if (retval) zval_ptr_dtor(&retval); - + zval_ptr_dtor(&us->object); efree(us); - + return 0; } @@ -750,7 +777,7 @@ static int php_userstreamop_flush(php_stream *stream TSRMLS_DC) assert(us != NULL); ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1, 0); - + call_result = call_user_function_ex(NULL, &us->object, &func_name, @@ -761,10 +788,10 @@ static int php_userstreamop_flush(php_stream *stream TSRMLS_DC) call_result = 0; else call_result = -1; - + if (retval) zval_ptr_dtor(&retval); - + return call_result; } @@ -803,10 +830,10 @@ static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence, o /* stream_seek is not implemented, so disable seeks for this stream */ stream->flags |= PHP_STREAM_FLAG_NO_SEEK; /* there should be no retval to clean up */ - - if (retval) + + if (retval) zval_ptr_dtor(&retval); - + return -1; } else if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) { ret = 0; @@ -890,8 +917,8 @@ static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC) STAT_PROP_ENTRY(blocks); #endif -#undef STAT_PROP_ENTRY -#undef STAT_PROP_ENTRY_EX +#undef STAT_PROP_ENTRY +#undef STAT_PROP_ENTRY_EX return SUCCESS; } @@ -921,9 +948,9 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb TSR } } - if (retval) + if (retval) zval_ptr_dtor(&retval); - + return ret; } @@ -933,7 +960,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value zval *retval = NULL; int call_result; php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; - int ret = -1; + int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL; zval *zvalue = NULL; zval **args[3]; @@ -971,37 +998,83 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value } args[0] = &zvalue; - + /* TODO wouldblock */ ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1, 0); - + call_result = call_user_function_ex(NULL, &us->object, &func_name, &retval, 1, args, 0, NULL TSRMLS_CC); - + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) { ret = !Z_LVAL_P(retval); } else if (call_result == FAILURE) { - if (value == 0) { + if (value == 0) { /* lock support test (TODO: more check) */ - ret = 0; + ret = PHP_STREAM_OPTION_RETURN_OK; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!", us->wrapper->classname); + ret = PHP_STREAM_OPTION_RETURN_ERR; } } break; - + + case PHP_STREAM_OPTION_TRUNCATE_API: + ZVAL_STRINGL(&func_name, USERSTREAM_TRUNCATE, sizeof(USERSTREAM_TRUNCATE)-1, 0); + + switch (value) { + case PHP_STREAM_TRUNCATE_SUPPORTED: + if (zend_is_callable_ex(&func_name, us->object, IS_CALLABLE_CHECK_SILENT, + NULL, NULL, NULL, NULL TSRMLS_CC)) + ret = PHP_STREAM_OPTION_RETURN_OK; + else + ret = PHP_STREAM_OPTION_RETURN_ERR; + break; + + case PHP_STREAM_TRUNCATE_SET_SIZE: { + ptrdiff_t new_size = *(ptrdiff_t*) ptrparam; + if (new_size >= 0 && new_size <= (ptrdiff_t)LONG_MAX) { + MAKE_STD_ZVAL(zvalue); + ZVAL_LONG(zvalue, (long)new_size); + args[0] = &zvalue; + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, 0, NULL TSRMLS_CC); + if (call_result == SUCCESS && retval != NULL) { + if (Z_TYPE_P(retval) == IS_BOOL) { + ret = Z_LVAL_P(retval) ? PHP_STREAM_OPTION_RETURN_OK : + PHP_STREAM_OPTION_RETURN_ERR; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "%s::" USERSTREAM_TRUNCATE " did not return a boolean!", + us->wrapper->classname); + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "%s::" USERSTREAM_TRUNCATE " is not implemented!", + us->wrapper->classname); + } + } else { /* bad new size */ + ret = PHP_STREAM_OPTION_RETURN_ERR; + } + break; + } + } + break; + case PHP_STREAM_OPTION_READ_BUFFER: case PHP_STREAM_OPTION_WRITE_BUFFER: case PHP_STREAM_OPTION_READ_TIMEOUT: case PHP_STREAM_OPTION_BLOCKING: { zval *zoption = NULL; zval *zptrparam = NULL; - + ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1, 0); ALLOC_INIT_ZVAL(zoption); @@ -1042,17 +1115,16 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value &func_name, &retval, 3, args, 0, NULL TSRMLS_CC); - - do { - if (call_result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!", - us->wrapper->classname); - break; - } - if (retval && zend_is_true(retval)) { - ret = PHP_STREAM_OPTION_RETURN_OK; - } - } while (0); + + if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!", + us->wrapper->classname); + ret = PHP_STREAM_OPTION_RETURN_ERR; + } else if (retval && zend_is_true(retval)) { + ret = PHP_STREAM_OPTION_RETURN_OK; + } else { + ret = PHP_STREAM_OPTION_RETURN_ERR; + } if (zoption) { zval_ptr_dtor(&zoption); @@ -1069,7 +1141,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value if (retval) { zval_ptr_dtor(&retval); } - + if (zvalue) { zval_ptr_dtor(&zvalue); @@ -1089,16 +1161,9 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio int ret = 0; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call the unlink method */ @@ -1108,7 +1173,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio MAKE_STD_ZVAL(zfuncname); ZVAL_STRING(zfuncname, USERSTREAM_UNLINK, 1); - + call_result = call_user_function_ex(NULL, &object, zfuncname, @@ -1126,7 +1191,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio zval_ptr_dtor(&object); if (zretval) zval_ptr_dtor(&zretval); - + zval_ptr_dtor(&zfuncname); zval_ptr_dtor(&zfilename); @@ -1143,16 +1208,9 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char int ret = 0; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call the rename method */ @@ -1166,7 +1224,7 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char MAKE_STD_ZVAL(zfuncname); ZVAL_STRING(zfuncname, USERSTREAM_RENAME, 1); - + call_result = call_user_function_ex(NULL, &object, zfuncname, @@ -1184,7 +1242,7 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char zval_ptr_dtor(&object); if (zretval) zval_ptr_dtor(&zretval); - + zval_ptr_dtor(&zfuncname); zval_ptr_dtor(&zold_name); zval_ptr_dtor(&znew_name); @@ -1202,16 +1260,9 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int ret = 0; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call the mkdir method */ @@ -1229,7 +1280,7 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, MAKE_STD_ZVAL(zfuncname); ZVAL_STRING(zfuncname, USERSTREAM_MKDIR, 1); - + call_result = call_user_function_ex(NULL, &object, zfuncname, @@ -1248,7 +1299,7 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, if (zretval) { zval_ptr_dtor(&zretval); } - + zval_ptr_dtor(&zfuncname); zval_ptr_dtor(&zfilename); zval_ptr_dtor(&zmode); @@ -1267,16 +1318,9 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int option int ret = 0; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call the rmdir method */ @@ -1290,7 +1334,7 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int option MAKE_STD_ZVAL(zfuncname); ZVAL_STRING(zfuncname, USERSTREAM_RMDIR, 1); - + call_result = call_user_function_ex(NULL, &object, zfuncname, @@ -1309,7 +1353,7 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int option if (zretval) { zval_ptr_dtor(&zretval); } - + zval_ptr_dtor(&zfuncname); zval_ptr_dtor(&zfilename); zval_ptr_dtor(&zoptions); @@ -1317,26 +1361,102 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int option return ret; } +static int user_wrapper_metadata(php_stream_wrapper *wrapper, char *url, int option, void *value, php_stream_context *context TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; + zval *zfilename, *zoption, *zvalue, *zfuncname, *zretval; + zval **args[3]; + int call_result; + zval *object; + int ret = 0; + + MAKE_STD_ZVAL(zvalue); + switch(option) { + case PHP_STREAM_META_TOUCH: + array_init(zvalue); + if(value) { + struct utimbuf *newtime = (struct utimbuf *)value; + add_index_long(zvalue, 0, newtime->modtime); + add_index_long(zvalue, 1, newtime->actime); + } + break; + case PHP_STREAM_META_GROUP: + case PHP_STREAM_META_OWNER: + case PHP_STREAM_META_ACCESS: + ZVAL_LONG(zvalue, *(long *)value); + break; + case PHP_STREAM_META_GROUP_NAME: + case PHP_STREAM_META_OWNER_NAME: + ZVAL_STRING(zvalue, value, 1); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option); + zval_ptr_dtor(&zvalue); + return ret; + } + + /* create an instance of our class */ + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + zval_ptr_dtor(&zvalue); + return ret; + } + + /* call the mkdir method */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, url, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zoption); + ZVAL_LONG(zoption, option); + args[1] = &zoption; + + args[2] = &zvalue; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_METADATA, 1); + + call_result = call_user_function_ex(NULL, + &object, + zfuncname, + &zretval, + 3, args, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) { + ret = Z_LVAL_P(zretval); + } else if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", uwrap->classname); + } + + /* clean up */ + zval_ptr_dtor(&object); + if (zretval) { + zval_ptr_dtor(&zretval); + } + + zval_ptr_dtor(&zfuncname); + zval_ptr_dtor(&zfilename); + zval_ptr_dtor(&zoption); + zval_ptr_dtor(&zvalue); + + return ret; +} + + static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) { struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; zval *zfilename, *zfuncname, *zretval, *zflags; - zval **args[2]; + zval **args[2]; int call_result; zval *object; int ret = -1; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call it's stat_url method - set up params first */ @@ -1350,14 +1470,14 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int fla MAKE_STD_ZVAL(zfuncname); ZVAL_STRING(zfuncname, USERSTREAM_STATURL, 1); - + call_result = call_user_function_ex(NULL, &object, zfuncname, &zretval, 2, args, 0, NULL TSRMLS_CC); - + if (call_result == SUCCESS && zretval != NULL && Z_TYPE_P(zretval) == IS_ARRAY) { /* We got the info we needed */ if (SUCCESS == statbuf_from_array(zretval, ssb TSRMLS_CC)) @@ -1368,16 +1488,16 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int fla uwrap->classname); } } - + /* clean up */ zval_ptr_dtor(&object); if (zretval) zval_ptr_dtor(&zretval); - + zval_ptr_dtor(&zfuncname); zval_ptr_dtor(&zfilename); zval_ptr_dtor(&zflags); - + return ret; } @@ -1427,9 +1547,9 @@ static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; assert(us != NULL); - + ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1, 0); - + call_user_function_ex(NULL, &us->object, &func_name, @@ -1438,11 +1558,11 @@ static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS if (retval) zval_ptr_dtor(&retval); - + zval_ptr_dtor(&us->object); efree(us); - + return 0; } @@ -1453,7 +1573,7 @@ static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int when php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1, 0); - + call_user_function_ex(NULL, &us->object, &func_name, @@ -1462,7 +1582,7 @@ static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int when if (retval) zval_ptr_dtor(&retval); - + return 0; } @@ -1537,7 +1657,7 @@ php_stream_ops php_stream_userspace_ops = { "user-space", php_userstreamop_seek, php_userstreamop_cast, - php_userstreamop_stat, + php_userstreamop_stat, php_userstreamop_set_option, }; diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index 64555ca1df..7c3a553102 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -400,10 +400,6 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void } #endif - case PHP_STREAM_OPTION_WRITE_BUFFER: - php_stream_set_chunk_size(stream, (ptrparam ? *(size_t *)ptrparam : PHP_SOCK_CHUNK_SIZE)); - return PHP_STREAM_OPTION_RETURN_OK; - default: return PHP_STREAM_OPTION_RETURN_NOTIMPL; } |